acp-devtools 0.1.0 → 0.2.1
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/index.js
CHANGED
|
@@ -5,9 +5,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
13
|
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
11
18
|
var __copyProps = (to, from, except, desc) => {
|
|
12
19
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
20
|
for (let key of __getOwnPropNames(from))
|
|
@@ -25,6 +32,124 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
32
|
mod
|
|
26
33
|
));
|
|
27
34
|
|
|
35
|
+
// ../core/dist/acp/types.js
|
|
36
|
+
var init_types = __esm({
|
|
37
|
+
"../core/dist/acp/types.js"() {
|
|
38
|
+
"use strict";
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ../core/dist/acp/parser.js
|
|
43
|
+
function isPlainObject(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
function classify(payload) {
|
|
47
|
+
if ("method" in payload && "id" in payload) {
|
|
48
|
+
const req = payload;
|
|
49
|
+
return { kind: "request", method: req.method, rpcId: req.id };
|
|
50
|
+
}
|
|
51
|
+
if ("method" in payload) {
|
|
52
|
+
const note = payload;
|
|
53
|
+
return { kind: "notification", method: note.method };
|
|
54
|
+
}
|
|
55
|
+
if ("error" in payload) {
|
|
56
|
+
const err = payload;
|
|
57
|
+
return { kind: "error", rpcId: err.id ?? null };
|
|
58
|
+
}
|
|
59
|
+
if ("result" in payload) {
|
|
60
|
+
const ok = payload;
|
|
61
|
+
return { kind: "response", rpcId: ok.id ?? null };
|
|
62
|
+
}
|
|
63
|
+
return { kind: "unknown" };
|
|
64
|
+
}
|
|
65
|
+
function parseFrame(raw) {
|
|
66
|
+
let json;
|
|
67
|
+
try {
|
|
68
|
+
json = JSON.parse(raw);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
71
|
+
return { raw, payload: null, kind: "unknown", parseError: `invalid JSON: ${message}` };
|
|
72
|
+
}
|
|
73
|
+
if (!isPlainObject(json) || json.jsonrpc !== "2.0") {
|
|
74
|
+
return { raw, payload: null, kind: "unknown", parseError: "not a JSON-RPC 2.0 message" };
|
|
75
|
+
}
|
|
76
|
+
const payload = json;
|
|
77
|
+
return { raw, payload, ...classify(payload) };
|
|
78
|
+
}
|
|
79
|
+
var LineFramer;
|
|
80
|
+
var init_parser = __esm({
|
|
81
|
+
"../core/dist/acp/parser.js"() {
|
|
82
|
+
"use strict";
|
|
83
|
+
LineFramer = class {
|
|
84
|
+
buffer = "";
|
|
85
|
+
/** Push a chunk; returns zero or more complete frames (without the trailing `\n`). */
|
|
86
|
+
feed(chunk) {
|
|
87
|
+
this.buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
88
|
+
const lines = [];
|
|
89
|
+
let nl = this.buffer.indexOf("\n");
|
|
90
|
+
while (nl >= 0) {
|
|
91
|
+
let line = this.buffer.slice(0, nl);
|
|
92
|
+
this.buffer = this.buffer.slice(nl + 1);
|
|
93
|
+
if (line.endsWith("\r"))
|
|
94
|
+
line = line.slice(0, -1);
|
|
95
|
+
if (line.length > 0)
|
|
96
|
+
lines.push(line);
|
|
97
|
+
nl = this.buffer.indexOf("\n");
|
|
98
|
+
}
|
|
99
|
+
return lines;
|
|
100
|
+
}
|
|
101
|
+
/** Return and discard any unterminated trailing data. */
|
|
102
|
+
flush() {
|
|
103
|
+
if (this.buffer.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
const remaining = this.buffer;
|
|
106
|
+
this.buffer = "";
|
|
107
|
+
return remaining.length > 0 ? remaining : null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ../core/dist/acp/text.js
|
|
114
|
+
function extractTextPreview(m) {
|
|
115
|
+
if (!m.payload || typeof m.payload !== "object")
|
|
116
|
+
return null;
|
|
117
|
+
const params = m.payload.params;
|
|
118
|
+
if (!params || typeof params !== "object")
|
|
119
|
+
return null;
|
|
120
|
+
if (m.method === "session/prompt") {
|
|
121
|
+
const blocks = params.prompt;
|
|
122
|
+
if (!Array.isArray(blocks))
|
|
123
|
+
return null;
|
|
124
|
+
const parts = [];
|
|
125
|
+
for (const block of blocks) {
|
|
126
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
127
|
+
parts.push(block.text);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const joined = parts.join(" ").trim();
|
|
131
|
+
return joined.length > 0 ? joined : null;
|
|
132
|
+
}
|
|
133
|
+
if (m.method === "session/update") {
|
|
134
|
+
const update = params.update;
|
|
135
|
+
if (!update)
|
|
136
|
+
return null;
|
|
137
|
+
const text = update.content?.text ?? update.text;
|
|
138
|
+
if (typeof text === "string" && text.length > 0)
|
|
139
|
+
return text;
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function isUserPrompt(m) {
|
|
145
|
+
return m.method === "session/prompt" && m.kind === "request";
|
|
146
|
+
}
|
|
147
|
+
var init_text = __esm({
|
|
148
|
+
"../core/dist/acp/text.js"() {
|
|
149
|
+
"use strict";
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
28
153
|
// ../core/node_modules/ajv/dist/compile/codegen/code.js
|
|
29
154
|
var require_code = __commonJS({
|
|
30
155
|
"../core/node_modules/ajv/dist/compile/codegen/code.js"(exports) {
|
|
@@ -7139,111 +7264,7 @@ var require__ = __commonJS({
|
|
|
7139
7264
|
}
|
|
7140
7265
|
});
|
|
7141
7266
|
|
|
7142
|
-
// ../core/dist/acp/parser.js
|
|
7143
|
-
var LineFramer = class {
|
|
7144
|
-
buffer = "";
|
|
7145
|
-
/** Push a chunk; returns zero or more complete frames (without the trailing `\n`). */
|
|
7146
|
-
feed(chunk) {
|
|
7147
|
-
this.buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
7148
|
-
const lines = [];
|
|
7149
|
-
let nl = this.buffer.indexOf("\n");
|
|
7150
|
-
while (nl >= 0) {
|
|
7151
|
-
let line = this.buffer.slice(0, nl);
|
|
7152
|
-
this.buffer = this.buffer.slice(nl + 1);
|
|
7153
|
-
if (line.endsWith("\r"))
|
|
7154
|
-
line = line.slice(0, -1);
|
|
7155
|
-
if (line.length > 0)
|
|
7156
|
-
lines.push(line);
|
|
7157
|
-
nl = this.buffer.indexOf("\n");
|
|
7158
|
-
}
|
|
7159
|
-
return lines;
|
|
7160
|
-
}
|
|
7161
|
-
/** Return and discard any unterminated trailing data. */
|
|
7162
|
-
flush() {
|
|
7163
|
-
if (this.buffer.length === 0)
|
|
7164
|
-
return null;
|
|
7165
|
-
const remaining = this.buffer;
|
|
7166
|
-
this.buffer = "";
|
|
7167
|
-
return remaining.length > 0 ? remaining : null;
|
|
7168
|
-
}
|
|
7169
|
-
};
|
|
7170
|
-
function isPlainObject(value) {
|
|
7171
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7172
|
-
}
|
|
7173
|
-
function classify(payload) {
|
|
7174
|
-
if ("method" in payload && "id" in payload) {
|
|
7175
|
-
const req = payload;
|
|
7176
|
-
return { kind: "request", method: req.method, rpcId: req.id };
|
|
7177
|
-
}
|
|
7178
|
-
if ("method" in payload) {
|
|
7179
|
-
const note = payload;
|
|
7180
|
-
return { kind: "notification", method: note.method };
|
|
7181
|
-
}
|
|
7182
|
-
if ("error" in payload) {
|
|
7183
|
-
const err = payload;
|
|
7184
|
-
return { kind: "error", rpcId: err.id ?? null };
|
|
7185
|
-
}
|
|
7186
|
-
if ("result" in payload) {
|
|
7187
|
-
const ok = payload;
|
|
7188
|
-
return { kind: "response", rpcId: ok.id ?? null };
|
|
7189
|
-
}
|
|
7190
|
-
return { kind: "unknown" };
|
|
7191
|
-
}
|
|
7192
|
-
function parseFrame(raw) {
|
|
7193
|
-
let json;
|
|
7194
|
-
try {
|
|
7195
|
-
json = JSON.parse(raw);
|
|
7196
|
-
} catch (err) {
|
|
7197
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
7198
|
-
return { raw, payload: null, kind: "unknown", parseError: `invalid JSON: ${message}` };
|
|
7199
|
-
}
|
|
7200
|
-
if (!isPlainObject(json) || json.jsonrpc !== "2.0") {
|
|
7201
|
-
return { raw, payload: null, kind: "unknown", parseError: "not a JSON-RPC 2.0 message" };
|
|
7202
|
-
}
|
|
7203
|
-
const payload = json;
|
|
7204
|
-
return { raw, payload, ...classify(payload) };
|
|
7205
|
-
}
|
|
7206
|
-
|
|
7207
|
-
// ../core/dist/acp/text.js
|
|
7208
|
-
function extractTextPreview(m) {
|
|
7209
|
-
if (!m.payload || typeof m.payload !== "object")
|
|
7210
|
-
return null;
|
|
7211
|
-
const params = m.payload.params;
|
|
7212
|
-
if (!params || typeof params !== "object")
|
|
7213
|
-
return null;
|
|
7214
|
-
if (m.method === "session/prompt") {
|
|
7215
|
-
const blocks = params.prompt;
|
|
7216
|
-
if (!Array.isArray(blocks))
|
|
7217
|
-
return null;
|
|
7218
|
-
const parts = [];
|
|
7219
|
-
for (const block of blocks) {
|
|
7220
|
-
if (block?.type === "text" && typeof block.text === "string") {
|
|
7221
|
-
parts.push(block.text);
|
|
7222
|
-
}
|
|
7223
|
-
}
|
|
7224
|
-
const joined = parts.join(" ").trim();
|
|
7225
|
-
return joined.length > 0 ? joined : null;
|
|
7226
|
-
}
|
|
7227
|
-
if (m.method === "session/update") {
|
|
7228
|
-
const update = params.update;
|
|
7229
|
-
if (!update)
|
|
7230
|
-
return null;
|
|
7231
|
-
const text = update.content?.text ?? update.text;
|
|
7232
|
-
if (typeof text === "string" && text.length > 0)
|
|
7233
|
-
return text;
|
|
7234
|
-
return null;
|
|
7235
|
-
}
|
|
7236
|
-
return null;
|
|
7237
|
-
}
|
|
7238
|
-
|
|
7239
|
-
// ../core/dist/acp/validate.js
|
|
7240
|
-
import { createRequire } from "module";
|
|
7241
|
-
|
|
7242
7267
|
// ../core/dist/acp/validate-factory.js
|
|
7243
|
-
var import__ = __toESM(require__(), 1);
|
|
7244
|
-
var ajvModule = import__.default;
|
|
7245
|
-
var Ajv2020 = ajvModule.default ?? ajvModule;
|
|
7246
|
-
var SCHEMA_ID = "acp";
|
|
7247
7268
|
function toError(e) {
|
|
7248
7269
|
const v = {
|
|
7249
7270
|
path: e.instancePath || "/",
|
|
@@ -7326,13 +7347,31 @@ function createAcpValidator(schema3) {
|
|
|
7326
7347
|
}
|
|
7327
7348
|
return { validateAcpMessage: validateAcpMessage2, knownAcpMethods: knownAcpMethods2 };
|
|
7328
7349
|
}
|
|
7350
|
+
var import__, ajvModule, Ajv2020, SCHEMA_ID;
|
|
7351
|
+
var init_validate_factory = __esm({
|
|
7352
|
+
"../core/dist/acp/validate-factory.js"() {
|
|
7353
|
+
"use strict";
|
|
7354
|
+
import__ = __toESM(require__(), 1);
|
|
7355
|
+
ajvModule = import__.default;
|
|
7356
|
+
Ajv2020 = ajvModule.default ?? ajvModule;
|
|
7357
|
+
SCHEMA_ID = "acp";
|
|
7358
|
+
}
|
|
7359
|
+
});
|
|
7329
7360
|
|
|
7330
7361
|
// ../core/dist/acp/validate.js
|
|
7331
|
-
|
|
7332
|
-
var schema
|
|
7333
|
-
var
|
|
7334
|
-
|
|
7335
|
-
|
|
7362
|
+
import { createRequire } from "module";
|
|
7363
|
+
var require2, schema, validator, validateAcpMessage, knownAcpMethods;
|
|
7364
|
+
var init_validate = __esm({
|
|
7365
|
+
"../core/dist/acp/validate.js"() {
|
|
7366
|
+
"use strict";
|
|
7367
|
+
init_validate_factory();
|
|
7368
|
+
require2 = createRequire(import.meta.url);
|
|
7369
|
+
schema = require2("@agentclientprotocol/sdk/schema/schema.json");
|
|
7370
|
+
validator = createAcpValidator(schema);
|
|
7371
|
+
validateAcpMessage = validator.validateAcpMessage;
|
|
7372
|
+
knownAcpMethods = validator.knownAcpMethods;
|
|
7373
|
+
}
|
|
7374
|
+
});
|
|
7336
7375
|
|
|
7337
7376
|
// ../core/dist/acp/session-metadata.js
|
|
7338
7377
|
function asRecord(value) {
|
|
@@ -7517,6 +7556,11 @@ function extractSessionMetadata(messages) {
|
|
|
7517
7556
|
extensions: extractExtensions(initReq)
|
|
7518
7557
|
};
|
|
7519
7558
|
}
|
|
7559
|
+
var init_session_metadata = __esm({
|
|
7560
|
+
"../core/dist/acp/session-metadata.js"() {
|
|
7561
|
+
"use strict";
|
|
7562
|
+
}
|
|
7563
|
+
});
|
|
7520
7564
|
|
|
7521
7565
|
// ../core/dist/acp/method-stats.js
|
|
7522
7566
|
function buildPairIndex(messages) {
|
|
@@ -7610,9 +7654,18 @@ function percentile(sortedAsc, p) {
|
|
|
7610
7654
|
return a;
|
|
7611
7655
|
return a + (b - a) * (rank - lo);
|
|
7612
7656
|
}
|
|
7657
|
+
function mean(values) {
|
|
7658
|
+
if (values.length === 0)
|
|
7659
|
+
return 0;
|
|
7660
|
+
return values.reduce((acc, x) => acc + x, 0) / values.length;
|
|
7661
|
+
}
|
|
7662
|
+
var init_method_stats = __esm({
|
|
7663
|
+
"../core/dist/acp/method-stats.js"() {
|
|
7664
|
+
"use strict";
|
|
7665
|
+
}
|
|
7666
|
+
});
|
|
7613
7667
|
|
|
7614
7668
|
// ../core/dist/acp/session-diff.js
|
|
7615
|
-
var DEFAULT_MAX_CHANGES = 100;
|
|
7616
7669
|
function buildSessionDiff(a, b, options = {}) {
|
|
7617
7670
|
const maxChanges = options.maxChangesPerRow ?? DEFAULT_MAX_CHANGES;
|
|
7618
7671
|
const sigA = signatures(a);
|
|
@@ -7789,6 +7842,14 @@ function deepDiff(a, b, path, out, max) {
|
|
|
7789
7842
|
function leafEqual(a, b) {
|
|
7790
7843
|
return Object.is(a, b);
|
|
7791
7844
|
}
|
|
7845
|
+
var DEFAULT_MAX_CHANGES;
|
|
7846
|
+
var init_session_diff = __esm({
|
|
7847
|
+
"../core/dist/acp/session-diff.js"() {
|
|
7848
|
+
"use strict";
|
|
7849
|
+
init_method_stats();
|
|
7850
|
+
DEFAULT_MAX_CHANGES = 100;
|
|
7851
|
+
}
|
|
7852
|
+
});
|
|
7792
7853
|
|
|
7793
7854
|
// ../core/dist/acp/session-compare.js
|
|
7794
7855
|
function comparableMetadata(m) {
|
|
@@ -7840,17 +7901,16 @@ function deltaOf(a, b) {
|
|
|
7840
7901
|
return null;
|
|
7841
7902
|
return b - a;
|
|
7842
7903
|
}
|
|
7904
|
+
var init_session_compare = __esm({
|
|
7905
|
+
"../core/dist/acp/session-compare.js"() {
|
|
7906
|
+
"use strict";
|
|
7907
|
+
init_session_diff();
|
|
7908
|
+
init_method_stats();
|
|
7909
|
+
init_session_metadata();
|
|
7910
|
+
}
|
|
7911
|
+
});
|
|
7843
7912
|
|
|
7844
7913
|
// ../core/dist/acp/perf-insights.js
|
|
7845
|
-
var DEFAULTS = {
|
|
7846
|
-
minSamplesForTail: 3,
|
|
7847
|
-
longTailRatio: 5,
|
|
7848
|
-
minBusiestCount: 50,
|
|
7849
|
-
minBusiestRatio: 2.5,
|
|
7850
|
-
minHotspotShare: 0.4,
|
|
7851
|
-
minSamplesForOutlier: 5,
|
|
7852
|
-
outlierRatio: 3
|
|
7853
|
-
};
|
|
7854
7914
|
function buildPerformanceInsights(messages, perMethod, cfg = {}) {
|
|
7855
7915
|
const config = { ...DEFAULTS, ...cfg };
|
|
7856
7916
|
const out = [];
|
|
@@ -7945,9 +8005,23 @@ function formatMs(ms) {
|
|
|
7945
8005
|
const s = Math.round(ms % 6e4 / 1e3);
|
|
7946
8006
|
return `${m}m${s}s`;
|
|
7947
8007
|
}
|
|
8008
|
+
var DEFAULTS;
|
|
8009
|
+
var init_perf_insights = __esm({
|
|
8010
|
+
"../core/dist/acp/perf-insights.js"() {
|
|
8011
|
+
"use strict";
|
|
8012
|
+
DEFAULTS = {
|
|
8013
|
+
minSamplesForTail: 3,
|
|
8014
|
+
longTailRatio: 5,
|
|
8015
|
+
minBusiestCount: 50,
|
|
8016
|
+
minBusiestRatio: 2.5,
|
|
8017
|
+
minHotspotShare: 0.4,
|
|
8018
|
+
minSamplesForOutlier: 5,
|
|
8019
|
+
outlierRatio: 3
|
|
8020
|
+
};
|
|
8021
|
+
}
|
|
8022
|
+
});
|
|
7948
8023
|
|
|
7949
8024
|
// ../core/dist/acp/sparkline.js
|
|
7950
|
-
var BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
7951
8025
|
function sampleEvenly(values, targetCount) {
|
|
7952
8026
|
if (values.length <= targetCount)
|
|
7953
8027
|
return values;
|
|
@@ -7970,9 +8044,13 @@ function asciiSparkline(values, targetWidth = 8) {
|
|
|
7970
8044
|
return BLOCKS[idx];
|
|
7971
8045
|
}).join("");
|
|
7972
8046
|
}
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
8047
|
+
var BLOCKS;
|
|
8048
|
+
var init_sparkline = __esm({
|
|
8049
|
+
"../core/dist/acp/sparkline.js"() {
|
|
8050
|
+
"use strict";
|
|
8051
|
+
BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
8052
|
+
}
|
|
8053
|
+
});
|
|
7976
8054
|
|
|
7977
8055
|
// ../core/dist/acp/spec-decoder-factory.js
|
|
7978
8056
|
function createSpecDecoder(schema3) {
|
|
@@ -8214,56 +8292,126 @@ function createSpecDecoder(schema3) {
|
|
|
8214
8292
|
}
|
|
8215
8293
|
return { resolveForMessage, knownMethods };
|
|
8216
8294
|
}
|
|
8295
|
+
var init_spec_decoder_factory = __esm({
|
|
8296
|
+
"../core/dist/acp/spec-decoder-factory.js"() {
|
|
8297
|
+
"use strict";
|
|
8298
|
+
}
|
|
8299
|
+
});
|
|
8217
8300
|
|
|
8218
8301
|
// ../core/dist/acp/spec-decoder.js
|
|
8219
|
-
|
|
8220
|
-
var schema2
|
|
8221
|
-
var
|
|
8222
|
-
|
|
8223
|
-
|
|
8302
|
+
import { createRequire as createRequire2 } from "module";
|
|
8303
|
+
var require3, schema2, decoder, resolveSpecForMessage, knownAcpSpecMethods;
|
|
8304
|
+
var init_spec_decoder = __esm({
|
|
8305
|
+
"../core/dist/acp/spec-decoder.js"() {
|
|
8306
|
+
"use strict";
|
|
8307
|
+
init_spec_decoder_factory();
|
|
8308
|
+
require3 = createRequire2(import.meta.url);
|
|
8309
|
+
schema2 = require3("@agentclientprotocol/sdk/schema/schema.json");
|
|
8310
|
+
decoder = createSpecDecoder(schema2);
|
|
8311
|
+
resolveSpecForMessage = decoder.resolveForMessage;
|
|
8312
|
+
knownAcpSpecMethods = decoder.knownMethods;
|
|
8313
|
+
}
|
|
8314
|
+
});
|
|
8315
|
+
|
|
8316
|
+
// ../core/dist/acp/timeline-layout.js
|
|
8317
|
+
function buildTimelineLayout(messages) {
|
|
8318
|
+
const empty = {
|
|
8319
|
+
events: [],
|
|
8320
|
+
startTs: 0,
|
|
8321
|
+
endTs: 0,
|
|
8322
|
+
durationMs: 0,
|
|
8323
|
+
laneCounts: { "editor-req": 0, "agent-req": 0, notification: 0 }
|
|
8324
|
+
};
|
|
8325
|
+
if (messages.length === 0)
|
|
8326
|
+
return empty;
|
|
8327
|
+
const pairs = buildPairIndex(messages);
|
|
8328
|
+
const bySeq = /* @__PURE__ */ new Map();
|
|
8329
|
+
for (const m of messages)
|
|
8330
|
+
bySeq.set(m.seq, m);
|
|
8331
|
+
let startTs = Infinity;
|
|
8332
|
+
let endTs = -Infinity;
|
|
8333
|
+
const events = [];
|
|
8334
|
+
const laneCounts = {
|
|
8335
|
+
"editor-req": 0,
|
|
8336
|
+
"agent-req": 0,
|
|
8337
|
+
notification: 0
|
|
8338
|
+
};
|
|
8339
|
+
for (const m of messages) {
|
|
8340
|
+
if (m.timestamp < startTs)
|
|
8341
|
+
startTs = m.timestamp;
|
|
8342
|
+
if (m.timestamp > endTs)
|
|
8343
|
+
endTs = m.timestamp;
|
|
8344
|
+
if (m.kind === "request") {
|
|
8345
|
+
const lane = m.direction === "editor-to-agent" ? "editor-req" : "agent-req";
|
|
8346
|
+
const pair = pairs.get(m.seq);
|
|
8347
|
+
const pairedMsg = pair ? bySeq.get(pair.pairSeq) : void 0;
|
|
8348
|
+
const erroredOut = pairedMsg?.kind === "error";
|
|
8349
|
+
const evtEnd = pairedMsg ? pairedMsg.timestamp : m.timestamp;
|
|
8350
|
+
if (evtEnd > endTs)
|
|
8351
|
+
endTs = evtEnd;
|
|
8352
|
+
events.push({
|
|
8353
|
+
seq: m.seq,
|
|
8354
|
+
startTs: m.timestamp,
|
|
8355
|
+
endTs: evtEnd,
|
|
8356
|
+
lane,
|
|
8357
|
+
method: m.method ?? null,
|
|
8358
|
+
kind: m.kind,
|
|
8359
|
+
erroredOut,
|
|
8360
|
+
pairedSeq: pairedMsg ? pairedMsg.seq : null
|
|
8361
|
+
});
|
|
8362
|
+
laneCounts[lane] += 1;
|
|
8363
|
+
} else if (m.kind === "notification") {
|
|
8364
|
+
events.push({
|
|
8365
|
+
seq: m.seq,
|
|
8366
|
+
startTs: m.timestamp,
|
|
8367
|
+
endTs: m.timestamp,
|
|
8368
|
+
lane: "notification",
|
|
8369
|
+
method: m.method ?? null,
|
|
8370
|
+
kind: m.kind,
|
|
8371
|
+
erroredOut: false,
|
|
8372
|
+
pairedSeq: null
|
|
8373
|
+
});
|
|
8374
|
+
laneCounts.notification += 1;
|
|
8375
|
+
}
|
|
8376
|
+
}
|
|
8377
|
+
if (!Number.isFinite(startTs) || !Number.isFinite(endTs))
|
|
8378
|
+
return empty;
|
|
8379
|
+
return {
|
|
8380
|
+
events,
|
|
8381
|
+
startTs,
|
|
8382
|
+
endTs,
|
|
8383
|
+
durationMs: Math.max(0, endTs - startTs),
|
|
8384
|
+
laneCounts
|
|
8385
|
+
};
|
|
8386
|
+
}
|
|
8387
|
+
var init_timeline_layout = __esm({
|
|
8388
|
+
"../core/dist/acp/timeline-layout.js"() {
|
|
8389
|
+
"use strict";
|
|
8390
|
+
init_method_stats();
|
|
8391
|
+
}
|
|
8392
|
+
});
|
|
8224
8393
|
|
|
8225
8394
|
// ../core/dist/acp/index.js
|
|
8226
8395
|
import { AGENT_METHODS, CLIENT_METHODS, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
8396
|
+
var init_acp = __esm({
|
|
8397
|
+
"../core/dist/acp/index.js"() {
|
|
8398
|
+
"use strict";
|
|
8399
|
+
init_types();
|
|
8400
|
+
init_parser();
|
|
8401
|
+
init_text();
|
|
8402
|
+
init_validate();
|
|
8403
|
+
init_session_metadata();
|
|
8404
|
+
init_method_stats();
|
|
8405
|
+
init_session_diff();
|
|
8406
|
+
init_session_compare();
|
|
8407
|
+
init_perf_insights();
|
|
8408
|
+
init_sparkline();
|
|
8409
|
+
init_spec_decoder();
|
|
8410
|
+
init_timeline_layout();
|
|
8411
|
+
}
|
|
8412
|
+
});
|
|
8227
8413
|
|
|
8228
8414
|
// ../core/dist/agents/registry.js
|
|
8229
|
-
var AGENT_REGISTRY = {
|
|
8230
|
-
"claude-code": {
|
|
8231
|
-
shortName: "claude-code",
|
|
8232
|
-
displayName: "Claude Code",
|
|
8233
|
-
description: "Anthropic Claude Code via @agentclientprotocol/claude-agent-acp",
|
|
8234
|
-
command: "npx",
|
|
8235
|
-
args: ["-y", "@agentclientprotocol/claude-agent-acp"],
|
|
8236
|
-
aliases: [
|
|
8237
|
-
// The original Zed-scoped package is deprecated as of late 2026,
|
|
8238
|
-
// but still very common in existing captures + IDE configs.
|
|
8239
|
-
["npx", "-y", "@zed-industries/claude-code-acp"]
|
|
8240
|
-
]
|
|
8241
|
-
},
|
|
8242
|
-
codex: {
|
|
8243
|
-
shortName: "codex",
|
|
8244
|
-
displayName: "Codex",
|
|
8245
|
-
description: "OpenAI Codex via @zed-industries/codex-acp",
|
|
8246
|
-
command: "npx",
|
|
8247
|
-
args: ["-y", "@zed-industries/codex-acp"]
|
|
8248
|
-
},
|
|
8249
|
-
goose: {
|
|
8250
|
-
shortName: "goose",
|
|
8251
|
-
displayName: "Goose",
|
|
8252
|
-
description: "Block Goose \u2014 install separately, then this runs `goose acp`",
|
|
8253
|
-
command: "goose",
|
|
8254
|
-
args: ["acp"],
|
|
8255
|
-
requiresExternalInstall: true
|
|
8256
|
-
},
|
|
8257
|
-
opencode: {
|
|
8258
|
-
shortName: "opencode",
|
|
8259
|
-
displayName: "OpenCode",
|
|
8260
|
-
description: "SST OpenCode \u2014 install via opencode.ai/install, then `opencode acp`",
|
|
8261
|
-
command: "opencode",
|
|
8262
|
-
args: ["acp"],
|
|
8263
|
-
requiresExternalInstall: true
|
|
8264
|
-
}
|
|
8265
|
-
};
|
|
8266
|
-
var DEFAULT_AGENT = "claude-code";
|
|
8267
8415
|
function resolveAgent(shortName) {
|
|
8268
8416
|
const agent = AGENT_REGISTRY[shortName];
|
|
8269
8417
|
if (!agent) {
|
|
@@ -8278,9 +8426,77 @@ function isAgentShortcut(name) {
|
|
|
8278
8426
|
function listAgents() {
|
|
8279
8427
|
return Object.values(AGENT_REGISTRY).slice();
|
|
8280
8428
|
}
|
|
8429
|
+
function detectAgentShortcut(agentCommand) {
|
|
8430
|
+
const trimmed = agentCommand.trim();
|
|
8431
|
+
if (trimmed.length === 0)
|
|
8432
|
+
return null;
|
|
8433
|
+
for (const def of Object.values(AGENT_REGISTRY)) {
|
|
8434
|
+
const variants = [
|
|
8435
|
+
[def.command, ...def.args],
|
|
8436
|
+
...def.aliases ?? []
|
|
8437
|
+
];
|
|
8438
|
+
for (const variant of variants) {
|
|
8439
|
+
const exact = variant.join(" ");
|
|
8440
|
+
if (trimmed === exact)
|
|
8441
|
+
return def;
|
|
8442
|
+
if (trimmed.startsWith(exact + " "))
|
|
8443
|
+
return def;
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
8446
|
+
return null;
|
|
8447
|
+
}
|
|
8448
|
+
var AGENT_REGISTRY, DEFAULT_AGENT;
|
|
8449
|
+
var init_registry = __esm({
|
|
8450
|
+
"../core/dist/agents/registry.js"() {
|
|
8451
|
+
"use strict";
|
|
8452
|
+
AGENT_REGISTRY = {
|
|
8453
|
+
"claude-code": {
|
|
8454
|
+
shortName: "claude-code",
|
|
8455
|
+
displayName: "Claude Code",
|
|
8456
|
+
description: "Anthropic Claude Code via @agentclientprotocol/claude-agent-acp",
|
|
8457
|
+
command: "npx",
|
|
8458
|
+
args: ["-y", "@agentclientprotocol/claude-agent-acp"],
|
|
8459
|
+
aliases: [
|
|
8460
|
+
// The original Zed-scoped package is deprecated as of late 2026,
|
|
8461
|
+
// but still very common in existing captures + IDE configs.
|
|
8462
|
+
["npx", "-y", "@zed-industries/claude-code-acp"]
|
|
8463
|
+
]
|
|
8464
|
+
},
|
|
8465
|
+
codex: {
|
|
8466
|
+
shortName: "codex",
|
|
8467
|
+
displayName: "Codex",
|
|
8468
|
+
description: "OpenAI Codex via @zed-industries/codex-acp",
|
|
8469
|
+
command: "npx",
|
|
8470
|
+
args: ["-y", "@zed-industries/codex-acp"]
|
|
8471
|
+
},
|
|
8472
|
+
goose: {
|
|
8473
|
+
shortName: "goose",
|
|
8474
|
+
displayName: "Goose",
|
|
8475
|
+
description: "Block Goose \u2014 install separately, then this runs `goose acp`",
|
|
8476
|
+
command: "goose",
|
|
8477
|
+
args: ["acp"],
|
|
8478
|
+
requiresExternalInstall: true
|
|
8479
|
+
},
|
|
8480
|
+
opencode: {
|
|
8481
|
+
shortName: "opencode",
|
|
8482
|
+
displayName: "OpenCode",
|
|
8483
|
+
description: "SST OpenCode \u2014 install via opencode.ai/install, then `opencode acp`",
|
|
8484
|
+
command: "opencode",
|
|
8485
|
+
args: ["acp"],
|
|
8486
|
+
requiresExternalInstall: true
|
|
8487
|
+
}
|
|
8488
|
+
};
|
|
8489
|
+
DEFAULT_AGENT = "claude-code";
|
|
8490
|
+
}
|
|
8491
|
+
});
|
|
8281
8492
|
|
|
8282
|
-
// ../core/dist/
|
|
8283
|
-
|
|
8493
|
+
// ../core/dist/agents/index.js
|
|
8494
|
+
var init_agents = __esm({
|
|
8495
|
+
"../core/dist/agents/index.js"() {
|
|
8496
|
+
"use strict";
|
|
8497
|
+
init_registry();
|
|
8498
|
+
}
|
|
8499
|
+
});
|
|
8284
8500
|
|
|
8285
8501
|
// ../core/dist/proxy/subprocess.js
|
|
8286
8502
|
import { spawn } from "child_process";
|
|
@@ -8304,164 +8520,101 @@ function spawnAgent(options) {
|
|
|
8304
8520
|
kill: (signal = "SIGTERM") => child.kill(signal)
|
|
8305
8521
|
};
|
|
8306
8522
|
}
|
|
8523
|
+
var init_subprocess = __esm({
|
|
8524
|
+
"../core/dist/proxy/subprocess.js"() {
|
|
8525
|
+
"use strict";
|
|
8526
|
+
}
|
|
8527
|
+
});
|
|
8307
8528
|
|
|
8308
8529
|
// ../core/dist/proxy/proxy.js
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
direction
|
|
8372
|
-
|
|
8373
|
-
raw
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8530
|
+
import { EventEmitter } from "events";
|
|
8531
|
+
var AcpProxy;
|
|
8532
|
+
var init_proxy = __esm({
|
|
8533
|
+
"../core/dist/proxy/proxy.js"() {
|
|
8534
|
+
"use strict";
|
|
8535
|
+
init_parser();
|
|
8536
|
+
init_subprocess();
|
|
8537
|
+
AcpProxy = class extends EventEmitter {
|
|
8538
|
+
options;
|
|
8539
|
+
agent = null;
|
|
8540
|
+
seq = 0;
|
|
8541
|
+
editorFramer = new LineFramer();
|
|
8542
|
+
agentFramer = new LineFramer();
|
|
8543
|
+
constructor(options) {
|
|
8544
|
+
super();
|
|
8545
|
+
this.options = options;
|
|
8546
|
+
}
|
|
8547
|
+
on(event, listener) {
|
|
8548
|
+
return super.on(event, listener);
|
|
8549
|
+
}
|
|
8550
|
+
async start() {
|
|
8551
|
+
if (this.agent)
|
|
8552
|
+
throw new Error("proxy already started");
|
|
8553
|
+
const agent = spawnAgent(this.options);
|
|
8554
|
+
this.agent = agent;
|
|
8555
|
+
this.emit("started", { pid: agent.pid });
|
|
8556
|
+
const editorIn = this.options.editorInput ?? process.stdin;
|
|
8557
|
+
const editorOut = this.options.editorOutput ?? process.stdout;
|
|
8558
|
+
const agentErr = this.options.agentErrorOutput === void 0 ? process.stderr : this.options.agentErrorOutput;
|
|
8559
|
+
editorIn.on("data", (chunk) => {
|
|
8560
|
+
this.capture("editor-to-agent", chunk);
|
|
8561
|
+
agent.stdin.write(chunk);
|
|
8562
|
+
});
|
|
8563
|
+
editorIn.once("end", () => {
|
|
8564
|
+
this.emit("editor-end");
|
|
8565
|
+
agent.stdin.end();
|
|
8566
|
+
});
|
|
8567
|
+
editorIn.on("error", (err) => this.emit("error", err));
|
|
8568
|
+
agent.stdout.on("data", (chunk) => {
|
|
8569
|
+
this.capture("agent-to-editor", chunk);
|
|
8570
|
+
editorOut.write(chunk);
|
|
8571
|
+
});
|
|
8572
|
+
agent.stdout.on("error", (err) => this.emit("error", err));
|
|
8573
|
+
if (agentErr) {
|
|
8574
|
+
agent.stderr.on("data", (chunk) => {
|
|
8575
|
+
this.emit("agent-stderr", chunk);
|
|
8576
|
+
agentErr.write(chunk);
|
|
8577
|
+
});
|
|
8578
|
+
}
|
|
8579
|
+
agent.exited.then((info) => this.emit("agent-exit", info)).catch((err) => this.emit("error", err));
|
|
8580
|
+
}
|
|
8581
|
+
/** Wait for the underlying agent to exit. */
|
|
8582
|
+
waitForExit() {
|
|
8583
|
+
if (!this.agent)
|
|
8584
|
+
throw new Error("proxy not started");
|
|
8585
|
+
return this.agent.exited;
|
|
8586
|
+
}
|
|
8587
|
+
/** Send a signal to the agent. */
|
|
8588
|
+
kill(signal = "SIGTERM") {
|
|
8589
|
+
this.agent?.kill(signal);
|
|
8590
|
+
}
|
|
8591
|
+
capture(direction, chunk) {
|
|
8592
|
+
const framer = direction === "editor-to-agent" ? this.editorFramer : this.agentFramer;
|
|
8593
|
+
const frames = framer.feed(chunk);
|
|
8594
|
+
for (const raw of frames) {
|
|
8595
|
+
const parsed = parseFrame(raw);
|
|
8596
|
+
const msg = {
|
|
8597
|
+
seq: ++this.seq,
|
|
8598
|
+
timestamp: Date.now(),
|
|
8599
|
+
direction,
|
|
8600
|
+
kind: parsed.kind,
|
|
8601
|
+
raw,
|
|
8602
|
+
payload: parsed.payload
|
|
8603
|
+
};
|
|
8604
|
+
if (parsed.method !== void 0)
|
|
8605
|
+
msg.method = parsed.method;
|
|
8606
|
+
if (parsed.rpcId !== void 0)
|
|
8607
|
+
msg.rpcId = parsed.rpcId;
|
|
8608
|
+
if (parsed.parseError !== void 0)
|
|
8609
|
+
msg.parseError = parsed.parseError;
|
|
8610
|
+
this.emit("message", msg);
|
|
8611
|
+
}
|
|
8612
|
+
}
|
|
8613
|
+
};
|
|
8384
8614
|
}
|
|
8385
|
-
};
|
|
8615
|
+
});
|
|
8386
8616
|
|
|
8387
8617
|
// ../core/dist/proxy/playback.js
|
|
8388
|
-
var OUR_DIRECTION_FOR_ROLE = {
|
|
8389
|
-
// mock-agent emits agent→editor and waits for editor→agent
|
|
8390
|
-
agent: "agent-to-editor",
|
|
8391
|
-
editor: "editor-to-agent"
|
|
8392
|
-
};
|
|
8393
|
-
var PlaybackEngine = class {
|
|
8394
|
-
messages;
|
|
8395
|
-
pointer = 0;
|
|
8396
|
-
scriptIdToRealId = /* @__PURE__ */ new Map();
|
|
8397
|
-
ourDirection;
|
|
8398
|
-
constructor(messages, role) {
|
|
8399
|
-
this.messages = messages;
|
|
8400
|
-
this.ourDirection = OUR_DIRECTION_FOR_ROLE[role];
|
|
8401
|
-
}
|
|
8402
|
-
/**
|
|
8403
|
-
* Advance through the script until either we have a line to EMIT, we
|
|
8404
|
-
* need to WAIT for the other side, or the script is DONE. Notifications
|
|
8405
|
-
* on our side are emitted immediately (no waiting).
|
|
8406
|
-
*/
|
|
8407
|
-
next() {
|
|
8408
|
-
while (this.pointer < this.messages.length) {
|
|
8409
|
-
const msg = this.messages[this.pointer];
|
|
8410
|
-
if (msg.direction === this.ourDirection) {
|
|
8411
|
-
const prev = this.pointer > 0 ? this.messages[this.pointer - 1] : null;
|
|
8412
|
-
const deltaSincePrevMs = prev ? Math.max(0, msg.timestamp - prev.timestamp) : 0;
|
|
8413
|
-
this.pointer += 1;
|
|
8414
|
-
return {
|
|
8415
|
-
kind: "emit",
|
|
8416
|
-
line: this.buildOutbound(msg),
|
|
8417
|
-
message: msg,
|
|
8418
|
-
deltaSincePrevMs
|
|
8419
|
-
};
|
|
8420
|
-
}
|
|
8421
|
-
return { kind: "wait", expected: msg };
|
|
8422
|
-
}
|
|
8423
|
-
return { kind: "done" };
|
|
8424
|
-
}
|
|
8425
|
-
/**
|
|
8426
|
-
* Record that the other side sent something the engine was WAITing for.
|
|
8427
|
-
* Advances past the matching script frame; remembers the rpc_id mapping
|
|
8428
|
-
* so any later response we emit can carry the wire's actual id.
|
|
8429
|
-
*
|
|
8430
|
-
* Returns `true` when an expected frame was consumed, `false` when there
|
|
8431
|
-
* was nothing to consume (script already done or our turn) — caller can
|
|
8432
|
-
* decide whether the off-script input is an error or just gets dropped.
|
|
8433
|
-
*/
|
|
8434
|
-
onIncoming(line) {
|
|
8435
|
-
if (this.pointer >= this.messages.length)
|
|
8436
|
-
return false;
|
|
8437
|
-
const msg = this.messages[this.pointer];
|
|
8438
|
-
if (msg.direction === this.ourDirection)
|
|
8439
|
-
return false;
|
|
8440
|
-
const realId = extractRpcId(line);
|
|
8441
|
-
if (msg.rpcId !== void 0 && msg.rpcId !== null && realId !== void 0) {
|
|
8442
|
-
this.scriptIdToRealId.set(String(msg.rpcId), realId);
|
|
8443
|
-
}
|
|
8444
|
-
this.pointer += 1;
|
|
8445
|
-
return true;
|
|
8446
|
-
}
|
|
8447
|
-
/** For tests / status output. */
|
|
8448
|
-
get position() {
|
|
8449
|
-
return this.pointer;
|
|
8450
|
-
}
|
|
8451
|
-
get exhausted() {
|
|
8452
|
-
return this.pointer >= this.messages.length;
|
|
8453
|
-
}
|
|
8454
|
-
buildOutbound(msg) {
|
|
8455
|
-
if (msg.kind !== "response" && msg.kind !== "error")
|
|
8456
|
-
return msg.raw;
|
|
8457
|
-
if (msg.rpcId === void 0 || msg.rpcId === null)
|
|
8458
|
-
return msg.raw;
|
|
8459
|
-
const realId = this.scriptIdToRealId.get(String(msg.rpcId));
|
|
8460
|
-
if (realId === void 0)
|
|
8461
|
-
return msg.raw;
|
|
8462
|
-
return substituteRpcId(msg, realId);
|
|
8463
|
-
}
|
|
8464
|
-
};
|
|
8465
8618
|
function substituteRpcId(msg, realId) {
|
|
8466
8619
|
if (!msg.payload)
|
|
8467
8620
|
return msg.raw;
|
|
@@ -8478,57 +8631,128 @@ function extractRpcId(line) {
|
|
|
8478
8631
|
return void 0;
|
|
8479
8632
|
}
|
|
8480
8633
|
}
|
|
8634
|
+
var OUR_DIRECTION_FOR_ROLE, PlaybackEngine;
|
|
8635
|
+
var init_playback = __esm({
|
|
8636
|
+
"../core/dist/proxy/playback.js"() {
|
|
8637
|
+
"use strict";
|
|
8638
|
+
OUR_DIRECTION_FOR_ROLE = {
|
|
8639
|
+
// mock-agent emits agent→editor and waits for editor→agent
|
|
8640
|
+
agent: "agent-to-editor",
|
|
8641
|
+
editor: "editor-to-agent"
|
|
8642
|
+
};
|
|
8643
|
+
PlaybackEngine = class {
|
|
8644
|
+
messages;
|
|
8645
|
+
pointer = 0;
|
|
8646
|
+
scriptIdToRealId = /* @__PURE__ */ new Map();
|
|
8647
|
+
ourDirection;
|
|
8648
|
+
constructor(messages, role) {
|
|
8649
|
+
this.messages = messages;
|
|
8650
|
+
this.ourDirection = OUR_DIRECTION_FOR_ROLE[role];
|
|
8651
|
+
}
|
|
8652
|
+
/**
|
|
8653
|
+
* Advance through the script until either we have a line to EMIT, we
|
|
8654
|
+
* need to WAIT for the other side, or the script is DONE. Notifications
|
|
8655
|
+
* on our side are emitted immediately (no waiting).
|
|
8656
|
+
*/
|
|
8657
|
+
next() {
|
|
8658
|
+
while (this.pointer < this.messages.length) {
|
|
8659
|
+
const msg = this.messages[this.pointer];
|
|
8660
|
+
if (msg.direction === this.ourDirection) {
|
|
8661
|
+
const prev = this.pointer > 0 ? this.messages[this.pointer - 1] : null;
|
|
8662
|
+
const deltaSincePrevMs = prev ? Math.max(0, msg.timestamp - prev.timestamp) : 0;
|
|
8663
|
+
this.pointer += 1;
|
|
8664
|
+
return {
|
|
8665
|
+
kind: "emit",
|
|
8666
|
+
line: this.buildOutbound(msg),
|
|
8667
|
+
message: msg,
|
|
8668
|
+
deltaSincePrevMs
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8671
|
+
return { kind: "wait", expected: msg };
|
|
8672
|
+
}
|
|
8673
|
+
return { kind: "done" };
|
|
8674
|
+
}
|
|
8675
|
+
/**
|
|
8676
|
+
* Record that the other side sent something the engine was WAITing for.
|
|
8677
|
+
* Advances past the matching script frame; remembers the rpc_id mapping
|
|
8678
|
+
* so any later response we emit can carry the wire's actual id.
|
|
8679
|
+
*
|
|
8680
|
+
* Returns `true` when an expected frame was consumed, `false` when there
|
|
8681
|
+
* was nothing to consume (script already done or our turn) — caller can
|
|
8682
|
+
* decide whether the off-script input is an error or just gets dropped.
|
|
8683
|
+
*/
|
|
8684
|
+
onIncoming(line) {
|
|
8685
|
+
if (this.pointer >= this.messages.length)
|
|
8686
|
+
return false;
|
|
8687
|
+
const msg = this.messages[this.pointer];
|
|
8688
|
+
if (msg.direction === this.ourDirection)
|
|
8689
|
+
return false;
|
|
8690
|
+
const realId = extractRpcId(line);
|
|
8691
|
+
if (msg.rpcId !== void 0 && msg.rpcId !== null && realId !== void 0) {
|
|
8692
|
+
this.scriptIdToRealId.set(String(msg.rpcId), realId);
|
|
8693
|
+
}
|
|
8694
|
+
this.pointer += 1;
|
|
8695
|
+
return true;
|
|
8696
|
+
}
|
|
8697
|
+
/** For tests / status output. */
|
|
8698
|
+
get position() {
|
|
8699
|
+
return this.pointer;
|
|
8700
|
+
}
|
|
8701
|
+
get exhausted() {
|
|
8702
|
+
return this.pointer >= this.messages.length;
|
|
8703
|
+
}
|
|
8704
|
+
buildOutbound(msg) {
|
|
8705
|
+
if (msg.kind !== "response" && msg.kind !== "error")
|
|
8706
|
+
return msg.raw;
|
|
8707
|
+
if (msg.rpcId === void 0 || msg.rpcId === null)
|
|
8708
|
+
return msg.raw;
|
|
8709
|
+
const realId = this.scriptIdToRealId.get(String(msg.rpcId));
|
|
8710
|
+
if (realId === void 0)
|
|
8711
|
+
return msg.raw;
|
|
8712
|
+
return substituteRpcId(msg, realId);
|
|
8713
|
+
}
|
|
8714
|
+
};
|
|
8715
|
+
}
|
|
8716
|
+
});
|
|
8717
|
+
|
|
8718
|
+
// ../core/dist/proxy/index.js
|
|
8719
|
+
var init_proxy2 = __esm({
|
|
8720
|
+
"../core/dist/proxy/index.js"() {
|
|
8721
|
+
"use strict";
|
|
8722
|
+
init_proxy();
|
|
8723
|
+
init_subprocess();
|
|
8724
|
+
init_playback();
|
|
8725
|
+
}
|
|
8726
|
+
});
|
|
8481
8727
|
|
|
8482
8728
|
// ../core/dist/storage/sqlite.js
|
|
8483
|
-
import { existsSync } from "fs";
|
|
8729
|
+
import { chmodSync, existsSync, statSync } from "fs";
|
|
8484
8730
|
import Database from "better-sqlite3";
|
|
8485
|
-
var SCHEMA_VERSION = 4;
|
|
8486
|
-
var SCHEMA_SQL = `
|
|
8487
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
8488
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8489
|
-
name TEXT,
|
|
8490
|
-
agent_command TEXT,
|
|
8491
|
-
started_at INTEGER NOT NULL,
|
|
8492
|
-
ended_at INTEGER,
|
|
8493
|
-
client_name TEXT,
|
|
8494
|
-
imported_at INTEGER,
|
|
8495
|
-
client_version TEXT,
|
|
8496
|
-
client_platform TEXT,
|
|
8497
|
-
agent_name TEXT,
|
|
8498
|
-
agent_version TEXT,
|
|
8499
|
-
protocol_version INTEGER,
|
|
8500
|
-
current_mode TEXT,
|
|
8501
|
-
current_model TEXT,
|
|
8502
|
-
agent_capabilities_json TEXT
|
|
8503
|
-
);
|
|
8504
|
-
|
|
8505
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
8506
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8507
|
-
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
8508
|
-
seq INTEGER NOT NULL,
|
|
8509
|
-
timestamp INTEGER NOT NULL,
|
|
8510
|
-
direction TEXT NOT NULL,
|
|
8511
|
-
kind TEXT NOT NULL,
|
|
8512
|
-
method TEXT,
|
|
8513
|
-
rpc_id TEXT,
|
|
8514
|
-
raw TEXT NOT NULL,
|
|
8515
|
-
payload_json TEXT,
|
|
8516
|
-
parse_error TEXT
|
|
8517
|
-
);
|
|
8518
|
-
|
|
8519
|
-
CREATE INDEX IF NOT EXISTS idx_messages_session_seq ON messages(session_id, seq);
|
|
8520
|
-
CREATE INDEX IF NOT EXISTS idx_messages_session_rpc ON messages(session_id, rpc_id);
|
|
8521
|
-
`;
|
|
8522
8731
|
function hasColumn(db, table, column) {
|
|
8523
8732
|
const cols = db.pragma(`table_info(${table})`);
|
|
8524
8733
|
return cols.some((c) => c.name === column);
|
|
8525
8734
|
}
|
|
8735
|
+
function restrictDbFile(path) {
|
|
8736
|
+
if (path === ":memory:" || process.platform === "win32")
|
|
8737
|
+
return;
|
|
8738
|
+
try {
|
|
8739
|
+
if (!existsSync(path))
|
|
8740
|
+
return;
|
|
8741
|
+
const mode = statSync(path).mode & 511;
|
|
8742
|
+
if (mode !== 384)
|
|
8743
|
+
chmodSync(path, 384);
|
|
8744
|
+
} catch {
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8526
8747
|
function openDatabase(path) {
|
|
8527
8748
|
const db = new Database(path);
|
|
8749
|
+
restrictDbFile(path);
|
|
8528
8750
|
db.pragma("busy_timeout = 5000");
|
|
8529
8751
|
db.pragma("journal_mode = WAL");
|
|
8530
8752
|
db.pragma("foreign_keys = ON");
|
|
8531
8753
|
db.pragma("synchronous = NORMAL");
|
|
8754
|
+
restrictDbFile(`${path}-wal`);
|
|
8755
|
+
restrictDbFile(`${path}-shm`);
|
|
8532
8756
|
db.exec(SCHEMA_SQL);
|
|
8533
8757
|
if (!hasColumn(db, "sessions", "client_name")) {
|
|
8534
8758
|
db.exec(`ALTER TABLE sessions ADD COLUMN client_name TEXT`);
|
|
@@ -8560,13 +8784,55 @@ function openExistingDatabase(path) {
|
|
|
8560
8784
|
}
|
|
8561
8785
|
return openDatabase(path);
|
|
8562
8786
|
}
|
|
8787
|
+
function getSchemaVersion(db) {
|
|
8788
|
+
const row2 = db.pragma("user_version", { simple: true });
|
|
8789
|
+
return typeof row2 === "number" ? row2 : Number(row2);
|
|
8790
|
+
}
|
|
8791
|
+
var SCHEMA_VERSION, SCHEMA_SQL;
|
|
8792
|
+
var init_sqlite = __esm({
|
|
8793
|
+
"../core/dist/storage/sqlite.js"() {
|
|
8794
|
+
"use strict";
|
|
8795
|
+
SCHEMA_VERSION = 4;
|
|
8796
|
+
SCHEMA_SQL = `
|
|
8797
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
8798
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8799
|
+
name TEXT,
|
|
8800
|
+
agent_command TEXT,
|
|
8801
|
+
started_at INTEGER NOT NULL,
|
|
8802
|
+
ended_at INTEGER,
|
|
8803
|
+
client_name TEXT,
|
|
8804
|
+
imported_at INTEGER,
|
|
8805
|
+
client_version TEXT,
|
|
8806
|
+
client_platform TEXT,
|
|
8807
|
+
agent_name TEXT,
|
|
8808
|
+
agent_version TEXT,
|
|
8809
|
+
protocol_version INTEGER,
|
|
8810
|
+
current_mode TEXT,
|
|
8811
|
+
current_model TEXT,
|
|
8812
|
+
agent_capabilities_json TEXT
|
|
8813
|
+
);
|
|
8814
|
+
|
|
8815
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
8816
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8817
|
+
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
8818
|
+
seq INTEGER NOT NULL,
|
|
8819
|
+
timestamp INTEGER NOT NULL,
|
|
8820
|
+
direction TEXT NOT NULL,
|
|
8821
|
+
kind TEXT NOT NULL,
|
|
8822
|
+
method TEXT,
|
|
8823
|
+
rpc_id TEXT,
|
|
8824
|
+
raw TEXT NOT NULL,
|
|
8825
|
+
payload_json TEXT,
|
|
8826
|
+
parse_error TEXT
|
|
8827
|
+
);
|
|
8828
|
+
|
|
8829
|
+
CREATE INDEX IF NOT EXISTS idx_messages_session_seq ON messages(session_id, seq);
|
|
8830
|
+
CREATE INDEX IF NOT EXISTS idx_messages_session_rpc ON messages(session_id, rpc_id);
|
|
8831
|
+
`;
|
|
8832
|
+
}
|
|
8833
|
+
});
|
|
8563
8834
|
|
|
8564
8835
|
// ../core/dist/storage/session.js
|
|
8565
|
-
var SESSION_SELECT = `SELECT id, name, agent_command, started_at, ended_at,
|
|
8566
|
-
client_name, imported_at, client_version, client_platform,
|
|
8567
|
-
agent_name, agent_version, protocol_version, current_mode, current_model,
|
|
8568
|
-
agent_capabilities_json
|
|
8569
|
-
FROM sessions`;
|
|
8570
8836
|
function rowToInfo(row2) {
|
|
8571
8837
|
return {
|
|
8572
8838
|
id: row2.id,
|
|
@@ -8607,84 +8873,94 @@ function parseRpcId(stored) {
|
|
|
8607
8873
|
const asNum = Number(stored);
|
|
8608
8874
|
return Number.isFinite(asNum) && String(asNum) === stored ? asNum : stored;
|
|
8609
8875
|
}
|
|
8610
|
-
var Session
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8876
|
+
var SESSION_SELECT, Session;
|
|
8877
|
+
var init_session = __esm({
|
|
8878
|
+
"../core/dist/storage/session.js"() {
|
|
8879
|
+
"use strict";
|
|
8880
|
+
init_session_metadata();
|
|
8881
|
+
SESSION_SELECT = `SELECT id, name, agent_command, started_at, ended_at,
|
|
8882
|
+
client_name, imported_at, client_version, client_platform,
|
|
8883
|
+
agent_name, agent_version, protocol_version, current_mode, current_model,
|
|
8884
|
+
agent_capabilities_json
|
|
8885
|
+
FROM sessions`;
|
|
8886
|
+
Session = class _Session {
|
|
8887
|
+
db;
|
|
8888
|
+
info;
|
|
8889
|
+
insertStmt;
|
|
8890
|
+
endStmt;
|
|
8891
|
+
listStmt;
|
|
8892
|
+
constructor(db, info) {
|
|
8893
|
+
this.db = db;
|
|
8894
|
+
this.info = info;
|
|
8895
|
+
this.insertStmt = db.prepare(`INSERT INTO messages (
|
|
8620
8896
|
session_id, seq, timestamp, direction, kind,
|
|
8621
8897
|
method, rpc_id, raw, payload_json, parse_error
|
|
8622
8898
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
8623
|
-
|
|
8624
|
-
|
|
8899
|
+
this.endStmt = db.prepare(`UPDATE sessions SET ended_at = ? WHERE id = ?`);
|
|
8900
|
+
this.listStmt = db.prepare(`SELECT seq, timestamp, direction, kind, method, rpc_id, raw, payload_json, parse_error
|
|
8625
8901
|
FROM messages WHERE session_id = ? ORDER BY seq ASC`);
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8902
|
+
}
|
|
8903
|
+
static start(db, options = {}) {
|
|
8904
|
+
const startedAt = options.startedAt ?? Date.now();
|
|
8905
|
+
const importedAt = options.importedAt ?? null;
|
|
8906
|
+
const stmt = db.prepare(`INSERT INTO sessions (name, agent_command, started_at, imported_at) VALUES (?, ?, ?, ?)`);
|
|
8907
|
+
const result = stmt.run(options.name ?? null, options.agentCommand ?? null, startedAt, importedAt);
|
|
8908
|
+
const id = Number(result.lastInsertRowid);
|
|
8909
|
+
return new _Session(db, {
|
|
8910
|
+
id,
|
|
8911
|
+
name: options.name ?? null,
|
|
8912
|
+
agentCommand: options.agentCommand ?? null,
|
|
8913
|
+
startedAt,
|
|
8914
|
+
endedAt: null,
|
|
8915
|
+
clientName: null,
|
|
8916
|
+
importedAt,
|
|
8917
|
+
clientVersion: null,
|
|
8918
|
+
clientPlatform: null,
|
|
8919
|
+
agentName: null,
|
|
8920
|
+
agentVersion: null,
|
|
8921
|
+
protocolVersion: null,
|
|
8922
|
+
currentMode: null,
|
|
8923
|
+
currentModel: null,
|
|
8924
|
+
agentCapabilitiesJson: null
|
|
8925
|
+
});
|
|
8926
|
+
}
|
|
8927
|
+
static load(db, sessionId) {
|
|
8928
|
+
const row2 = db.prepare(`${SESSION_SELECT} WHERE id = ?`).get(sessionId);
|
|
8929
|
+
if (!row2)
|
|
8930
|
+
throw new Error(`session ${sessionId} not found`);
|
|
8931
|
+
return new _Session(db, rowToInfo(row2));
|
|
8932
|
+
}
|
|
8933
|
+
static latest(db) {
|
|
8934
|
+
const row2 = db.prepare(`${SESSION_SELECT} ORDER BY id DESC LIMIT 1`).get();
|
|
8935
|
+
if (!row2)
|
|
8936
|
+
throw new Error("database has no sessions");
|
|
8937
|
+
return new _Session(db, rowToInfo(row2));
|
|
8938
|
+
}
|
|
8939
|
+
/**
|
|
8940
|
+
* Persist a detected client name on the session row. Called once when the
|
|
8941
|
+
* first `initialize` request is seen by the proxy. Subsequent calls
|
|
8942
|
+
* overwrite — the latest title wins.
|
|
8943
|
+
*/
|
|
8944
|
+
setClientName(name) {
|
|
8945
|
+
this.db.prepare(`UPDATE sessions SET client_name = ? WHERE id = ?`).run(name, this.info.id);
|
|
8946
|
+
this.info = { ...this.info, clientName: name };
|
|
8947
|
+
}
|
|
8948
|
+
/**
|
|
8949
|
+
* Write the structured-metadata columns from a derived `SessionMetadata`.
|
|
8950
|
+
* Used by:
|
|
8951
|
+
* - The proxy on every metadata-relevant message (initialize, set_mode,
|
|
8952
|
+
* set_model, current_mode_update) so the UI's SessionPicker can label
|
|
8953
|
+
* `#21 · Zed 1.3.5 · claude-agent-acp 0.37.0` mid-session.
|
|
8954
|
+
* - The `backfill-metadata` CLI command for pre-v4 / imported sessions.
|
|
8955
|
+
*
|
|
8956
|
+
* Idempotent — running on the same messages produces the same writes.
|
|
8957
|
+
*/
|
|
8958
|
+
setMetadataFromMessages(messages) {
|
|
8959
|
+
this.setMetadataFrom(extractSessionMetadata(messages));
|
|
8960
|
+
}
|
|
8961
|
+
setMetadataFrom(meta) {
|
|
8962
|
+
const agentCapsJson = JSON.stringify(meta.agentCapabilities);
|
|
8963
|
+
this.db.prepare(`UPDATE sessions SET
|
|
8688
8964
|
client_version = ?,
|
|
8689
8965
|
client_platform = ?,
|
|
8690
8966
|
agent_name = ?,
|
|
@@ -8694,34 +8970,35 @@ var Session = class _Session {
|
|
|
8694
8970
|
current_model = ?,
|
|
8695
8971
|
agent_capabilities_json = ?
|
|
8696
8972
|
WHERE id = ?`).run(meta.client.version, meta.client.platform, meta.agent.name, meta.agent.version, meta.protocolVersion, meta.runtime.currentMode, meta.runtime.currentModel, agentCapsJson, this.info.id);
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8973
|
+
this.info = {
|
|
8974
|
+
...this.info,
|
|
8975
|
+
clientVersion: meta.client.version,
|
|
8976
|
+
clientPlatform: meta.client.platform,
|
|
8977
|
+
agentName: meta.agent.name,
|
|
8978
|
+
agentVersion: meta.agent.version,
|
|
8979
|
+
protocolVersion: meta.protocolVersion,
|
|
8980
|
+
currentMode: meta.runtime.currentMode,
|
|
8981
|
+
currentModel: meta.runtime.currentModel,
|
|
8982
|
+
agentCapabilitiesJson: agentCapsJson
|
|
8983
|
+
};
|
|
8984
|
+
}
|
|
8985
|
+
record(message) {
|
|
8986
|
+
this.insertStmt.run(this.info.id, message.seq, message.timestamp, message.direction, message.kind, message.method ?? null, message.rpcId === void 0 || message.rpcId === null ? null : String(message.rpcId), message.raw, message.payload ? JSON.stringify(message.payload) : null, message.parseError ?? null);
|
|
8987
|
+
}
|
|
8988
|
+
close(endedAt = Date.now()) {
|
|
8989
|
+
this.endStmt.run(endedAt, this.info.id);
|
|
8990
|
+
this.info = { ...this.info, endedAt };
|
|
8991
|
+
}
|
|
8992
|
+
*messages() {
|
|
8993
|
+
for (const row2 of this.listStmt.iterate(this.info.id)) {
|
|
8994
|
+
yield rowToMessage(row2);
|
|
8995
|
+
}
|
|
8996
|
+
}
|
|
8707
8997
|
};
|
|
8708
8998
|
}
|
|
8709
|
-
|
|
8710
|
-
this.insertStmt.run(this.info.id, message.seq, message.timestamp, message.direction, message.kind, message.method ?? null, message.rpcId === void 0 || message.rpcId === null ? null : String(message.rpcId), message.raw, message.payload ? JSON.stringify(message.payload) : null, message.parseError ?? null);
|
|
8711
|
-
}
|
|
8712
|
-
close(endedAt = Date.now()) {
|
|
8713
|
-
this.endStmt.run(endedAt, this.info.id);
|
|
8714
|
-
this.info = { ...this.info, endedAt };
|
|
8715
|
-
}
|
|
8716
|
-
*messages() {
|
|
8717
|
-
for (const row2 of this.listStmt.iterate(this.info.id)) {
|
|
8718
|
-
yield rowToMessage(row2);
|
|
8719
|
-
}
|
|
8720
|
-
}
|
|
8721
|
-
};
|
|
8999
|
+
});
|
|
8722
9000
|
|
|
8723
9001
|
// ../core/dist/storage/export.js
|
|
8724
|
-
var EXPORT_VERSION = 1;
|
|
8725
9002
|
function exportSession(session, options) {
|
|
8726
9003
|
return exportSessionFromParts(session.info, session.messages(), options);
|
|
8727
9004
|
}
|
|
@@ -8744,12 +9021,6 @@ function exportSessionFromParts(info, messages, options) {
|
|
|
8744
9021
|
function serializeExport(exp, indent = 4) {
|
|
8745
9022
|
return JSON.stringify(exp, null, indent) + "\n";
|
|
8746
9023
|
}
|
|
8747
|
-
var ExportParseError = class extends Error {
|
|
8748
|
-
constructor(message) {
|
|
8749
|
-
super(message);
|
|
8750
|
-
this.name = "ExportParseError";
|
|
8751
|
-
}
|
|
8752
|
-
};
|
|
8753
9024
|
function parseExport(input) {
|
|
8754
9025
|
let raw;
|
|
8755
9026
|
try {
|
|
@@ -8851,110 +9122,272 @@ function parseMessage(raw, idx) {
|
|
|
8851
9122
|
function isObject(value) {
|
|
8852
9123
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8853
9124
|
}
|
|
9125
|
+
var EXPORT_VERSION, ExportParseError;
|
|
9126
|
+
var init_export = __esm({
|
|
9127
|
+
"../core/dist/storage/export.js"() {
|
|
9128
|
+
"use strict";
|
|
9129
|
+
EXPORT_VERSION = 1;
|
|
9130
|
+
ExportParseError = class extends Error {
|
|
9131
|
+
constructor(message) {
|
|
9132
|
+
super(message);
|
|
9133
|
+
this.name = "ExportParseError";
|
|
9134
|
+
}
|
|
9135
|
+
};
|
|
9136
|
+
}
|
|
9137
|
+
});
|
|
9138
|
+
|
|
9139
|
+
// ../core/dist/storage/redact.js
|
|
9140
|
+
function redactMessage(message) {
|
|
9141
|
+
if (message.payload == null) {
|
|
9142
|
+
return { redacted: message, count: 0 };
|
|
9143
|
+
}
|
|
9144
|
+
const { value, count } = redactValue(message.payload, { inProxyConfig: false });
|
|
9145
|
+
if (count === 0) {
|
|
9146
|
+
return { redacted: message, count: 0 };
|
|
9147
|
+
}
|
|
9148
|
+
const redactedPayload = value;
|
|
9149
|
+
return {
|
|
9150
|
+
redacted: {
|
|
9151
|
+
...message,
|
|
9152
|
+
payload: redactedPayload,
|
|
9153
|
+
raw: JSON.stringify(redactedPayload)
|
|
9154
|
+
},
|
|
9155
|
+
count
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
function redactValue(value, ctx) {
|
|
9159
|
+
if (Array.isArray(value)) {
|
|
9160
|
+
let count = 0;
|
|
9161
|
+
const out = new Array(value.length);
|
|
9162
|
+
for (let i = 0; i < value.length; i++) {
|
|
9163
|
+
const r = redactValue(value[i], ctx);
|
|
9164
|
+
out[i] = r.value;
|
|
9165
|
+
count += r.count;
|
|
9166
|
+
}
|
|
9167
|
+
return { value: out, count };
|
|
9168
|
+
}
|
|
9169
|
+
if (value !== null && typeof value === "object") {
|
|
9170
|
+
let count = 0;
|
|
9171
|
+
const out = {};
|
|
9172
|
+
for (const [k, v] of Object.entries(value)) {
|
|
9173
|
+
if (typeof v === "string" && SENSITIVE_HEADER_NAMES.has(k.toLowerCase())) {
|
|
9174
|
+
out[k] = REDACTED_PLACEHOLDER;
|
|
9175
|
+
count += 1;
|
|
9176
|
+
continue;
|
|
9177
|
+
}
|
|
9178
|
+
if (ctx.inProxyConfig && k === "headers" && v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
9179
|
+
const r2 = redactHeadersBlock(v);
|
|
9180
|
+
out[k] = r2.value;
|
|
9181
|
+
count += r2.count;
|
|
9182
|
+
continue;
|
|
9183
|
+
}
|
|
9184
|
+
const childCtx = ctx.inProxyConfig || k.toLowerCase() === "proxyconfig" ? { inProxyConfig: true } : ctx;
|
|
9185
|
+
const r = redactValue(v, childCtx);
|
|
9186
|
+
out[k] = r.value;
|
|
9187
|
+
count += r.count;
|
|
9188
|
+
}
|
|
9189
|
+
return { value: out, count };
|
|
9190
|
+
}
|
|
9191
|
+
return { value, count: 0 };
|
|
9192
|
+
}
|
|
9193
|
+
function redactHeadersBlock(obj) {
|
|
9194
|
+
let count = 0;
|
|
9195
|
+
const out = {};
|
|
9196
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
9197
|
+
if (typeof v === "string") {
|
|
9198
|
+
out[k] = REDACTED_PLACEHOLDER;
|
|
9199
|
+
count += 1;
|
|
9200
|
+
} else {
|
|
9201
|
+
out[k] = v;
|
|
9202
|
+
}
|
|
9203
|
+
}
|
|
9204
|
+
return { value: out, count };
|
|
9205
|
+
}
|
|
9206
|
+
function redactSessionExport(exp) {
|
|
9207
|
+
let messagesAffected = 0;
|
|
9208
|
+
let fieldsRedacted = 0;
|
|
9209
|
+
const messages = exp.messages.map((msg) => {
|
|
9210
|
+
const r = redactMessage(msg);
|
|
9211
|
+
if (r.count > 0) {
|
|
9212
|
+
messagesAffected += 1;
|
|
9213
|
+
fieldsRedacted += r.count;
|
|
9214
|
+
}
|
|
9215
|
+
return r.redacted;
|
|
9216
|
+
});
|
|
9217
|
+
if (fieldsRedacted === 0) {
|
|
9218
|
+
return { export: exp, messagesAffected: 0, fieldsRedacted: 0 };
|
|
9219
|
+
}
|
|
9220
|
+
return {
|
|
9221
|
+
export: { ...exp, messages },
|
|
9222
|
+
messagesAffected,
|
|
9223
|
+
fieldsRedacted
|
|
9224
|
+
};
|
|
9225
|
+
}
|
|
9226
|
+
var REDACTED_PLACEHOLDER, SENSITIVE_HEADER_NAMES;
|
|
9227
|
+
var init_redact = __esm({
|
|
9228
|
+
"../core/dist/storage/redact.js"() {
|
|
9229
|
+
"use strict";
|
|
9230
|
+
REDACTED_PLACEHOLDER = "<REDACTED>";
|
|
9231
|
+
SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
9232
|
+
"authorization",
|
|
9233
|
+
"proxy-authorization",
|
|
9234
|
+
"proxy-authentication",
|
|
9235
|
+
"x-api-key",
|
|
9236
|
+
"x-api-token",
|
|
9237
|
+
"x-auth-token",
|
|
9238
|
+
"api-key",
|
|
9239
|
+
"api_key",
|
|
9240
|
+
"proxy_key",
|
|
9241
|
+
"cookie",
|
|
9242
|
+
"set-cookie"
|
|
9243
|
+
]);
|
|
9244
|
+
}
|
|
9245
|
+
});
|
|
9246
|
+
|
|
9247
|
+
// ../core/dist/storage/index.js
|
|
9248
|
+
var init_storage = __esm({
|
|
9249
|
+
"../core/dist/storage/index.js"() {
|
|
9250
|
+
"use strict";
|
|
9251
|
+
init_sqlite();
|
|
9252
|
+
init_session();
|
|
9253
|
+
init_export();
|
|
9254
|
+
init_redact();
|
|
9255
|
+
}
|
|
9256
|
+
});
|
|
9257
|
+
|
|
9258
|
+
// ../core/dist/ws/events.js
|
|
9259
|
+
var WS_PROTOCOL_VERSION;
|
|
9260
|
+
var init_events = __esm({
|
|
9261
|
+
"../core/dist/ws/events.js"() {
|
|
9262
|
+
"use strict";
|
|
9263
|
+
WS_PROTOCOL_VERSION = 1;
|
|
9264
|
+
}
|
|
9265
|
+
});
|
|
8854
9266
|
|
|
8855
9267
|
// ../core/dist/ws/server.js
|
|
8856
9268
|
import { WebSocketServer } from "ws";
|
|
8857
|
-
var WsBroadcaster
|
|
8858
|
-
|
|
8859
|
-
server
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
9269
|
+
var WsBroadcaster;
|
|
9270
|
+
var init_server = __esm({
|
|
9271
|
+
"../core/dist/ws/server.js"() {
|
|
9272
|
+
"use strict";
|
|
9273
|
+
WsBroadcaster = class {
|
|
9274
|
+
options;
|
|
9275
|
+
server = null;
|
|
9276
|
+
session = null;
|
|
9277
|
+
messages = [];
|
|
9278
|
+
sessionEnded = false;
|
|
9279
|
+
constructor(options) {
|
|
9280
|
+
this.options = options;
|
|
9281
|
+
}
|
|
9282
|
+
/** Start the server. Resolves when it is listening; rejects on bind errors. */
|
|
9283
|
+
start() {
|
|
9284
|
+
if (this.server)
|
|
9285
|
+
throw new Error("broadcaster already started");
|
|
9286
|
+
return new Promise((resolve2, reject) => {
|
|
9287
|
+
const host = this.options.host ?? "127.0.0.1";
|
|
9288
|
+
const wss = new WebSocketServer({ host, port: this.options.port });
|
|
9289
|
+
this.server = wss;
|
|
9290
|
+
const onError = (err) => reject(err);
|
|
9291
|
+
wss.once("error", onError);
|
|
9292
|
+
wss.once("listening", () => {
|
|
9293
|
+
wss.off("error", onError);
|
|
9294
|
+
wss.on("error", () => void 0);
|
|
9295
|
+
wss.on("connection", (ws) => this.onConnection(ws));
|
|
9296
|
+
const address = wss.address();
|
|
9297
|
+
resolve2({ port: address.port, url: `ws://${host}:${address.port}` });
|
|
9298
|
+
});
|
|
9299
|
+
});
|
|
9300
|
+
}
|
|
9301
|
+
async stop() {
|
|
9302
|
+
const wss = this.server;
|
|
9303
|
+
if (!wss)
|
|
9304
|
+
return;
|
|
9305
|
+
this.server = null;
|
|
9306
|
+
for (const client of wss.clients)
|
|
9307
|
+
client.close(1e3, "shutdown");
|
|
9308
|
+
await Promise.race([
|
|
9309
|
+
new Promise((resolve2) => wss.close(() => resolve2())),
|
|
9310
|
+
new Promise((resolve2) => setTimeout(resolve2, 500))
|
|
9311
|
+
]);
|
|
9312
|
+
for (const client of wss.clients)
|
|
9313
|
+
client.terminate();
|
|
9314
|
+
}
|
|
9315
|
+
publishSessionStart(session) {
|
|
9316
|
+
this.session = session;
|
|
9317
|
+
this.messages = [];
|
|
9318
|
+
this.sessionEnded = false;
|
|
9319
|
+
this.broadcast({ type: "session.start", session });
|
|
9320
|
+
}
|
|
9321
|
+
publishMessage(message) {
|
|
9322
|
+
if (!this.session) {
|
|
9323
|
+
throw new Error("publishMessage called before publishSessionStart");
|
|
9324
|
+
}
|
|
9325
|
+
this.messages.push(message);
|
|
9326
|
+
this.broadcast({ type: "message", message });
|
|
9327
|
+
}
|
|
9328
|
+
publishSessionEnd(session) {
|
|
9329
|
+
this.session = session;
|
|
9330
|
+
this.sessionEnded = true;
|
|
9331
|
+
this.broadcast({ type: "session.end", session });
|
|
9332
|
+
}
|
|
9333
|
+
/**
|
|
9334
|
+
* Pre-load a finished session for the replay command. Must be called before
|
|
9335
|
+
* clients connect; live `publish*` calls afterwards are not supported.
|
|
9336
|
+
*/
|
|
9337
|
+
loadHistory(session, messages) {
|
|
9338
|
+
this.session = session;
|
|
9339
|
+
this.messages = [...messages];
|
|
9340
|
+
this.sessionEnded = session.endedAt !== null;
|
|
9341
|
+
}
|
|
9342
|
+
onConnection(ws) {
|
|
9343
|
+
if (this.session) {
|
|
9344
|
+
this.send(ws, { type: "session.start", session: this.session });
|
|
9345
|
+
for (const m of this.messages) {
|
|
9346
|
+
this.send(ws, { type: "message", message: m });
|
|
9347
|
+
}
|
|
9348
|
+
this.send(ws, { type: "replay.done" });
|
|
9349
|
+
if (this.sessionEnded) {
|
|
9350
|
+
this.send(ws, { type: "session.end", session: this.session });
|
|
9351
|
+
}
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
broadcast(event) {
|
|
9355
|
+
const wss = this.server;
|
|
9356
|
+
if (!wss)
|
|
9357
|
+
return;
|
|
9358
|
+
const data = JSON.stringify(event);
|
|
9359
|
+
for (const client of wss.clients) {
|
|
9360
|
+
if (client.readyState === 1)
|
|
9361
|
+
client.send(data);
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
send(ws, event) {
|
|
9365
|
+
ws.send(JSON.stringify(event));
|
|
9366
|
+
}
|
|
9367
|
+
};
|
|
8884
9368
|
}
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
new Promise((resolve2) => wss.close(() => resolve2())),
|
|
8894
|
-
new Promise((resolve2) => setTimeout(resolve2, 500))
|
|
8895
|
-
]);
|
|
8896
|
-
for (const client of wss.clients)
|
|
8897
|
-
client.terminate();
|
|
8898
|
-
}
|
|
8899
|
-
publishSessionStart(session) {
|
|
8900
|
-
this.session = session;
|
|
8901
|
-
this.messages = [];
|
|
8902
|
-
this.sessionEnded = false;
|
|
8903
|
-
this.broadcast({ type: "session.start", session });
|
|
8904
|
-
}
|
|
8905
|
-
publishMessage(message) {
|
|
8906
|
-
if (!this.session) {
|
|
8907
|
-
throw new Error("publishMessage called before publishSessionStart");
|
|
8908
|
-
}
|
|
8909
|
-
this.messages.push(message);
|
|
8910
|
-
this.broadcast({ type: "message", message });
|
|
8911
|
-
}
|
|
8912
|
-
publishSessionEnd(session) {
|
|
8913
|
-
this.session = session;
|
|
8914
|
-
this.sessionEnded = true;
|
|
8915
|
-
this.broadcast({ type: "session.end", session });
|
|
8916
|
-
}
|
|
8917
|
-
/**
|
|
8918
|
-
* Pre-load a finished session for the replay command. Must be called before
|
|
8919
|
-
* clients connect; live `publish*` calls afterwards are not supported.
|
|
8920
|
-
*/
|
|
8921
|
-
loadHistory(session, messages) {
|
|
8922
|
-
this.session = session;
|
|
8923
|
-
this.messages = [...messages];
|
|
8924
|
-
this.sessionEnded = session.endedAt !== null;
|
|
8925
|
-
}
|
|
8926
|
-
onConnection(ws) {
|
|
8927
|
-
if (this.session) {
|
|
8928
|
-
this.send(ws, { type: "session.start", session: this.session });
|
|
8929
|
-
for (const m of this.messages) {
|
|
8930
|
-
this.send(ws, { type: "message", message: m });
|
|
8931
|
-
}
|
|
8932
|
-
this.send(ws, { type: "replay.done" });
|
|
8933
|
-
if (this.sessionEnded) {
|
|
8934
|
-
this.send(ws, { type: "session.end", session: this.session });
|
|
8935
|
-
}
|
|
8936
|
-
}
|
|
8937
|
-
}
|
|
8938
|
-
broadcast(event) {
|
|
8939
|
-
const wss = this.server;
|
|
8940
|
-
if (!wss)
|
|
8941
|
-
return;
|
|
8942
|
-
const data = JSON.stringify(event);
|
|
8943
|
-
for (const client of wss.clients) {
|
|
8944
|
-
if (client.readyState === 1)
|
|
8945
|
-
client.send(data);
|
|
8946
|
-
}
|
|
9369
|
+
});
|
|
9370
|
+
|
|
9371
|
+
// ../core/dist/ws/index.js
|
|
9372
|
+
var init_ws = __esm({
|
|
9373
|
+
"../core/dist/ws/index.js"() {
|
|
9374
|
+
"use strict";
|
|
9375
|
+
init_events();
|
|
9376
|
+
init_server();
|
|
8947
9377
|
}
|
|
8948
|
-
|
|
8949
|
-
|
|
9378
|
+
});
|
|
9379
|
+
|
|
9380
|
+
// ../core/dist/discovery/types.js
|
|
9381
|
+
var init_types2 = __esm({
|
|
9382
|
+
"../core/dist/discovery/types.js"() {
|
|
9383
|
+
"use strict";
|
|
8950
9384
|
}
|
|
8951
|
-
};
|
|
9385
|
+
});
|
|
8952
9386
|
|
|
8953
9387
|
// ../core/dist/discovery/registry.js
|
|
8954
9388
|
import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
8955
9389
|
import { homedir } from "os";
|
|
8956
9390
|
import { join } from "path";
|
|
8957
|
-
var DEFAULT_BASE = join(homedir(), ".acp-devtools");
|
|
8958
9391
|
function discoveryBaseDir() {
|
|
8959
9392
|
return process.env.ACP_DEVTOOLS_HOME ?? DEFAULT_BASE;
|
|
8960
9393
|
}
|
|
@@ -9027,6 +9460,22 @@ function listActive(options = {}) {
|
|
|
9027
9460
|
out.sort((a, b) => b.startedAt - a.startedAt);
|
|
9028
9461
|
return out;
|
|
9029
9462
|
}
|
|
9463
|
+
var DEFAULT_BASE;
|
|
9464
|
+
var init_registry2 = __esm({
|
|
9465
|
+
"../core/dist/discovery/registry.js"() {
|
|
9466
|
+
"use strict";
|
|
9467
|
+
DEFAULT_BASE = join(homedir(), ".acp-devtools");
|
|
9468
|
+
}
|
|
9469
|
+
});
|
|
9470
|
+
|
|
9471
|
+
// ../core/dist/discovery/index.js
|
|
9472
|
+
var init_discovery = __esm({
|
|
9473
|
+
"../core/dist/discovery/index.js"() {
|
|
9474
|
+
"use strict";
|
|
9475
|
+
init_types2();
|
|
9476
|
+
init_registry2();
|
|
9477
|
+
}
|
|
9478
|
+
});
|
|
9030
9479
|
|
|
9031
9480
|
// ../core/dist/server/paths.js
|
|
9032
9481
|
import { homedir as homedir2 } from "os";
|
|
@@ -9037,6 +9486,11 @@ function acpHomeDir() {
|
|
|
9037
9486
|
function defaultCapturesDbPath() {
|
|
9038
9487
|
return join2(acpHomeDir(), "captures.db");
|
|
9039
9488
|
}
|
|
9489
|
+
var init_paths = __esm({
|
|
9490
|
+
"../core/dist/server/paths.js"() {
|
|
9491
|
+
"use strict";
|
|
9492
|
+
}
|
|
9493
|
+
});
|
|
9040
9494
|
|
|
9041
9495
|
// ../core/dist/server/queries.js
|
|
9042
9496
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -9067,7 +9521,6 @@ function listSessionsSummary(dbPath, limit = 200) {
|
|
|
9067
9521
|
db.close();
|
|
9068
9522
|
}
|
|
9069
9523
|
}
|
|
9070
|
-
var FIND_BY_CLIENT_POOL_SIZE = 500;
|
|
9071
9524
|
function findSessionsByClient(dbPath, needle, limit = 200) {
|
|
9072
9525
|
const pool = listSessionsSummary(dbPath, FIND_BY_CLIENT_POOL_SIZE);
|
|
9073
9526
|
const lower = needle.toLowerCase();
|
|
@@ -9115,17 +9568,18 @@ function deleteSession(dbPath, sessionId) {
|
|
|
9115
9568
|
db.close();
|
|
9116
9569
|
}
|
|
9117
9570
|
}
|
|
9571
|
+
var FIND_BY_CLIENT_POOL_SIZE;
|
|
9572
|
+
var init_queries = __esm({
|
|
9573
|
+
"../core/dist/server/queries.js"() {
|
|
9574
|
+
"use strict";
|
|
9575
|
+
init_session();
|
|
9576
|
+
init_sqlite();
|
|
9577
|
+
FIND_BY_CLIENT_POOL_SIZE = 500;
|
|
9578
|
+
}
|
|
9579
|
+
});
|
|
9118
9580
|
|
|
9119
9581
|
// ../core/dist/server/replay.js
|
|
9120
9582
|
import { existsSync as existsSync3 } from "fs";
|
|
9121
|
-
var DEFAULT_LOGGER = {
|
|
9122
|
-
log: (message) => {
|
|
9123
|
-
console.log(message);
|
|
9124
|
-
},
|
|
9125
|
-
warn: (message, err) => {
|
|
9126
|
-
console.warn(message, err);
|
|
9127
|
-
}
|
|
9128
|
-
};
|
|
9129
9583
|
function streamReplay(ws, sessionId, dbPath, logger = DEFAULT_LOGGER) {
|
|
9130
9584
|
const tag = `[replay #${sessionId}]`;
|
|
9131
9585
|
if (!existsSync3(dbPath)) {
|
|
@@ -9198,6 +9652,22 @@ function safeClose(ws) {
|
|
|
9198
9652
|
} catch {
|
|
9199
9653
|
}
|
|
9200
9654
|
}
|
|
9655
|
+
var DEFAULT_LOGGER;
|
|
9656
|
+
var init_replay = __esm({
|
|
9657
|
+
"../core/dist/server/replay.js"() {
|
|
9658
|
+
"use strict";
|
|
9659
|
+
init_session();
|
|
9660
|
+
init_sqlite();
|
|
9661
|
+
DEFAULT_LOGGER = {
|
|
9662
|
+
log: (message) => {
|
|
9663
|
+
console.log(message);
|
|
9664
|
+
},
|
|
9665
|
+
warn: (message, err) => {
|
|
9666
|
+
console.warn(message, err);
|
|
9667
|
+
}
|
|
9668
|
+
};
|
|
9669
|
+
}
|
|
9670
|
+
});
|
|
9201
9671
|
|
|
9202
9672
|
// ../core/dist/server/http.js
|
|
9203
9673
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -9343,7 +9813,6 @@ function handleSessionMessages(req, res, dbPath, sessionId) {
|
|
|
9343
9813
|
db.close();
|
|
9344
9814
|
}
|
|
9345
9815
|
}
|
|
9346
|
-
var IMPORT_MAX_BYTES = 64 * 1024 * 1024;
|
|
9347
9816
|
function handleImport(req, res, dbPath) {
|
|
9348
9817
|
if (req.method !== "POST") {
|
|
9349
9818
|
res.statusCode = 405;
|
|
@@ -9423,9 +9892,113 @@ function attachReplayUpgrade(httpServer, options) {
|
|
|
9423
9892
|
});
|
|
9424
9893
|
});
|
|
9425
9894
|
}
|
|
9895
|
+
var IMPORT_MAX_BYTES;
|
|
9896
|
+
var init_http = __esm({
|
|
9897
|
+
"../core/dist/server/http.js"() {
|
|
9898
|
+
"use strict";
|
|
9899
|
+
init_registry2();
|
|
9900
|
+
init_export();
|
|
9901
|
+
init_session();
|
|
9902
|
+
init_sqlite();
|
|
9903
|
+
init_queries();
|
|
9904
|
+
init_replay();
|
|
9905
|
+
IMPORT_MAX_BYTES = 64 * 1024 * 1024;
|
|
9906
|
+
}
|
|
9907
|
+
});
|
|
9426
9908
|
|
|
9427
|
-
//
|
|
9428
|
-
|
|
9909
|
+
// ../core/dist/server/index.js
|
|
9910
|
+
var init_server2 = __esm({
|
|
9911
|
+
"../core/dist/server/index.js"() {
|
|
9912
|
+
"use strict";
|
|
9913
|
+
init_paths();
|
|
9914
|
+
init_queries();
|
|
9915
|
+
init_replay();
|
|
9916
|
+
init_http();
|
|
9917
|
+
}
|
|
9918
|
+
});
|
|
9919
|
+
|
|
9920
|
+
// ../core/dist/index.js
|
|
9921
|
+
var dist_exports = {};
|
|
9922
|
+
__export(dist_exports, {
|
|
9923
|
+
AGENT_METHODS: () => AGENT_METHODS,
|
|
9924
|
+
AGENT_REGISTRY: () => AGENT_REGISTRY,
|
|
9925
|
+
AcpProxy: () => AcpProxy,
|
|
9926
|
+
CLIENT_METHODS: () => CLIENT_METHODS,
|
|
9927
|
+
DEFAULT_AGENT: () => DEFAULT_AGENT,
|
|
9928
|
+
EXPORT_VERSION: () => EXPORT_VERSION,
|
|
9929
|
+
ExportParseError: () => ExportParseError,
|
|
9930
|
+
LineFramer: () => LineFramer,
|
|
9931
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
9932
|
+
PlaybackEngine: () => PlaybackEngine,
|
|
9933
|
+
REDACTED_PLACEHOLDER: () => REDACTED_PLACEHOLDER,
|
|
9934
|
+
SCHEMA_VERSION: () => SCHEMA_VERSION,
|
|
9935
|
+
Session: () => Session,
|
|
9936
|
+
WS_PROTOCOL_VERSION: () => WS_PROTOCOL_VERSION,
|
|
9937
|
+
WsBroadcaster: () => WsBroadcaster,
|
|
9938
|
+
acpHomeDir: () => acpHomeDir,
|
|
9939
|
+
activeDir: () => activeDir,
|
|
9940
|
+
asciiSparkline: () => asciiSparkline,
|
|
9941
|
+
attachReplayUpgrade: () => attachReplayUpgrade,
|
|
9942
|
+
buildMetadataDiff: () => buildMetadataDiff,
|
|
9943
|
+
buildMethodStatsDiff: () => buildMethodStatsDiff,
|
|
9944
|
+
buildPairIndex: () => buildPairIndex,
|
|
9945
|
+
buildPerMethodStats: () => buildPerMethodStats,
|
|
9946
|
+
buildPerformanceInsights: () => buildPerformanceInsights,
|
|
9947
|
+
buildSessionDiff: () => buildSessionDiff,
|
|
9948
|
+
buildTimelineLayout: () => buildTimelineLayout,
|
|
9949
|
+
createApiHandler: () => createApiHandler,
|
|
9950
|
+
defaultCapturesDbPath: () => defaultCapturesDbPath,
|
|
9951
|
+
deleteSession: () => deleteSession,
|
|
9952
|
+
detectAgentShortcut: () => detectAgentShortcut,
|
|
9953
|
+
diffJson: () => diffJson,
|
|
9954
|
+
discoveryBaseDir: () => discoveryBaseDir,
|
|
9955
|
+
exportSession: () => exportSession,
|
|
9956
|
+
exportSessionFromParts: () => exportSessionFromParts,
|
|
9957
|
+
extractRpcId: () => extractRpcId,
|
|
9958
|
+
extractSessionMetadata: () => extractSessionMetadata,
|
|
9959
|
+
extractTextPreview: () => extractTextPreview,
|
|
9960
|
+
findSessionsByClient: () => findSessionsByClient,
|
|
9961
|
+
getSchemaVersion: () => getSchemaVersion,
|
|
9962
|
+
insertImportedSession: () => insertImportedSession,
|
|
9963
|
+
isAgentShortcut: () => isAgentShortcut,
|
|
9964
|
+
isPidAlive: () => isPidAlive,
|
|
9965
|
+
isUserPrompt: () => isUserPrompt,
|
|
9966
|
+
knownAcpMethods: () => knownAcpMethods,
|
|
9967
|
+
knownAcpSpecMethods: () => knownAcpSpecMethods,
|
|
9968
|
+
listActive: () => listActive,
|
|
9969
|
+
listAgents: () => listAgents,
|
|
9970
|
+
listSessionsSummary: () => listSessionsSummary,
|
|
9971
|
+
mean: () => mean,
|
|
9972
|
+
openDatabase: () => openDatabase,
|
|
9973
|
+
openExistingDatabase: () => openExistingDatabase,
|
|
9974
|
+
parseExport: () => parseExport,
|
|
9975
|
+
parseFrame: () => parseFrame,
|
|
9976
|
+
percentile: () => percentile,
|
|
9977
|
+
redactMessage: () => redactMessage,
|
|
9978
|
+
redactSessionExport: () => redactSessionExport,
|
|
9979
|
+
removeActiveFile: () => removeActiveFile,
|
|
9980
|
+
resolveAgent: () => resolveAgent,
|
|
9981
|
+
resolveSpecForMessage: () => resolveSpecForMessage,
|
|
9982
|
+
sampleEvenly: () => sampleEvenly,
|
|
9983
|
+
serializeExport: () => serializeExport,
|
|
9984
|
+
spawnAgent: () => spawnAgent,
|
|
9985
|
+
streamReplay: () => streamReplay,
|
|
9986
|
+
substituteRpcId: () => substituteRpcId,
|
|
9987
|
+
validateAcpMessage: () => validateAcpMessage,
|
|
9988
|
+
writeActiveFile: () => writeActiveFile
|
|
9989
|
+
});
|
|
9990
|
+
var init_dist = __esm({
|
|
9991
|
+
"../core/dist/index.js"() {
|
|
9992
|
+
"use strict";
|
|
9993
|
+
init_acp();
|
|
9994
|
+
init_agents();
|
|
9995
|
+
init_proxy2();
|
|
9996
|
+
init_storage();
|
|
9997
|
+
init_ws();
|
|
9998
|
+
init_discovery();
|
|
9999
|
+
init_server2();
|
|
10000
|
+
}
|
|
10001
|
+
});
|
|
9429
10002
|
|
|
9430
10003
|
// src/commands/backfill-metadata.ts
|
|
9431
10004
|
function backfillSession(db, id) {
|
|
@@ -9463,8 +10036,8 @@ function listSessionIds(db) {
|
|
|
9463
10036
|
const rows = db.prepare(`SELECT id FROM sessions ORDER BY id ASC`).all();
|
|
9464
10037
|
return rows.map((r) => r.id);
|
|
9465
10038
|
}
|
|
9466
|
-
function registerBackfillMetadataCommand(
|
|
9467
|
-
|
|
10039
|
+
function registerBackfillMetadataCommand(program) {
|
|
10040
|
+
program.command("backfill-metadata").description(
|
|
9468
10041
|
"Recompute structured session metadata (client/agent/runtime) for saved sessions by re-scanning their captured messages. Used for sessions that pre-date the v4 schema or were imported from JSON."
|
|
9469
10042
|
).argument("[id]", "backfill a single session only (default: all)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--json", "machine-readable JSON output").action((idArg, opts) => {
|
|
9470
10043
|
let targetId = null;
|
|
@@ -9510,11 +10083,17 @@ function registerBackfillMetadataCommand(program2) {
|
|
|
9510
10083
|
}
|
|
9511
10084
|
});
|
|
9512
10085
|
}
|
|
10086
|
+
var init_backfill_metadata = __esm({
|
|
10087
|
+
"src/commands/backfill-metadata.ts"() {
|
|
10088
|
+
"use strict";
|
|
10089
|
+
init_dist();
|
|
10090
|
+
}
|
|
10091
|
+
});
|
|
9513
10092
|
|
|
9514
10093
|
// src/commands/delete.ts
|
|
9515
10094
|
import { existsSync as existsSync5 } from "fs";
|
|
9516
|
-
function registerDeleteCommand(
|
|
9517
|
-
|
|
10095
|
+
function registerDeleteCommand(program) {
|
|
10096
|
+
program.command("delete").description(
|
|
9518
10097
|
"Delete one or more saved sessions from the captures database forever. Cascades to messages via the schema. No interactive prompt \u2014 pair with `acp-devtools list` to verify ids before invoking."
|
|
9519
10098
|
).argument("<id...>", "one or more session ids to delete").option("--db <path>", "captures database", defaultCapturesDbPath()).action((rawIds, opts) => {
|
|
9520
10099
|
const ids = [];
|
|
@@ -9562,23 +10141,14 @@ function labelFor(s) {
|
|
|
9562
10141
|
if (s.client_name) return s.client_name;
|
|
9563
10142
|
return s.agent_command ?? "";
|
|
9564
10143
|
}
|
|
10144
|
+
var init_delete = __esm({
|
|
10145
|
+
"src/commands/delete.ts"() {
|
|
10146
|
+
"use strict";
|
|
10147
|
+
init_dist();
|
|
10148
|
+
}
|
|
10149
|
+
});
|
|
9565
10150
|
|
|
9566
10151
|
// src/lib/style.ts
|
|
9567
|
-
var ESC = String.fromCharCode(27);
|
|
9568
|
-
var CODES = {
|
|
9569
|
-
reset: 0,
|
|
9570
|
-
bold: 1,
|
|
9571
|
-
dim: 2,
|
|
9572
|
-
italic: 3,
|
|
9573
|
-
underline: 4,
|
|
9574
|
-
red: 31,
|
|
9575
|
-
green: 32,
|
|
9576
|
-
yellow: 33,
|
|
9577
|
-
blue: 34,
|
|
9578
|
-
magenta: 35,
|
|
9579
|
-
cyan: 36,
|
|
9580
|
-
gray: 90
|
|
9581
|
-
};
|
|
9582
10152
|
function colorEnabled(stream = process.stdout, env = process.env) {
|
|
9583
10153
|
if (env.FORCE_COLOR === "0" || env.FORCE_COLOR === "false") return false;
|
|
9584
10154
|
if (env.FORCE_COLOR !== void 0) return true;
|
|
@@ -9606,13 +10176,34 @@ function createStyler(enabled = colorEnabled()) {
|
|
|
9606
10176
|
gray: wrap("gray")
|
|
9607
10177
|
};
|
|
9608
10178
|
}
|
|
9609
|
-
var ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
|
|
9610
10179
|
function stripAnsi(s) {
|
|
9611
10180
|
return s.replace(ANSI_PATTERN, "");
|
|
9612
10181
|
}
|
|
9613
10182
|
function visibleWidth(s) {
|
|
9614
10183
|
return stripAnsi(s).length;
|
|
9615
10184
|
}
|
|
10185
|
+
var ESC, CODES, ANSI_PATTERN;
|
|
10186
|
+
var init_style = __esm({
|
|
10187
|
+
"src/lib/style.ts"() {
|
|
10188
|
+
"use strict";
|
|
10189
|
+
ESC = String.fromCharCode(27);
|
|
10190
|
+
CODES = {
|
|
10191
|
+
reset: 0,
|
|
10192
|
+
bold: 1,
|
|
10193
|
+
dim: 2,
|
|
10194
|
+
italic: 3,
|
|
10195
|
+
underline: 4,
|
|
10196
|
+
red: 31,
|
|
10197
|
+
green: 32,
|
|
10198
|
+
yellow: 33,
|
|
10199
|
+
blue: 34,
|
|
10200
|
+
magenta: 35,
|
|
10201
|
+
cyan: 36,
|
|
10202
|
+
gray: 90
|
|
10203
|
+
};
|
|
10204
|
+
ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
|
|
10205
|
+
}
|
|
10206
|
+
});
|
|
9616
10207
|
|
|
9617
10208
|
// src/lib/palette.ts
|
|
9618
10209
|
function colorKind(s, kind, text) {
|
|
@@ -9641,6 +10232,11 @@ function colorLatency(s, ms, text) {
|
|
|
9641
10232
|
function colorSessionKind(s, imported, text) {
|
|
9642
10233
|
return imported ? s.yellow(text) : s.green(text);
|
|
9643
10234
|
}
|
|
10235
|
+
var init_palette = __esm({
|
|
10236
|
+
"src/lib/palette.ts"() {
|
|
10237
|
+
"use strict";
|
|
10238
|
+
}
|
|
10239
|
+
});
|
|
9644
10240
|
|
|
9645
10241
|
// src/lib/table.ts
|
|
9646
10242
|
function renderTable(s, columns, rows, opts = {}) {
|
|
@@ -9675,10 +10271,16 @@ function renderTable(s, columns, rows, opts = {}) {
|
|
|
9675
10271
|
}
|
|
9676
10272
|
return out.map((l) => l + "\n").join("");
|
|
9677
10273
|
}
|
|
10274
|
+
var init_table = __esm({
|
|
10275
|
+
"src/lib/table.ts"() {
|
|
10276
|
+
"use strict";
|
|
10277
|
+
init_style();
|
|
10278
|
+
}
|
|
10279
|
+
});
|
|
9678
10280
|
|
|
9679
10281
|
// src/commands/diff.ts
|
|
9680
|
-
function registerDiffCommand(
|
|
9681
|
-
|
|
10282
|
+
function registerDiffCommand(program) {
|
|
10283
|
+
program.command("diff").description(
|
|
9682
10284
|
'Align two saved sessions and show what changed between them \u2014 added / removed frames and field-level payload differences. Useful for "worked yesterday, broke today" and A/B comparisons of two agents on the same prompt.'
|
|
9683
10285
|
).argument("<a>", "baseline session id (left side)").argument("<b>", "new session id (right side)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--full", "print unchanged frames too (default collapses equal runs)").option("--json", "machine-readable JSON instead of human-readable text").action((rawA, rawB, opts) => {
|
|
9684
10286
|
const idA = parseId(rawA);
|
|
@@ -9737,16 +10339,9 @@ function registerDiffCommand(program2) {
|
|
|
9737
10339
|
);
|
|
9738
10340
|
});
|
|
9739
10341
|
}
|
|
9740
|
-
var defaultStyler = () => createStyler(colorEnabled(process.stdout));
|
|
9741
10342
|
function heading(st, label) {
|
|
9742
10343
|
return st.bold(st.yellow(label));
|
|
9743
10344
|
}
|
|
9744
|
-
var OP_COLOR = {
|
|
9745
|
-
equal: (st, t) => st.dim(t),
|
|
9746
|
-
changed: (st, t) => st.yellow(t),
|
|
9747
|
-
added: (st, t) => st.green(t),
|
|
9748
|
-
removed: (st, t) => st.red(t)
|
|
9749
|
-
};
|
|
9750
10345
|
function parseId(raw) {
|
|
9751
10346
|
const id = Number(raw);
|
|
9752
10347
|
if (!Number.isInteger(id) || id <= 0) {
|
|
@@ -9770,19 +10365,6 @@ function shortLabel(info) {
|
|
|
9770
10365
|
if (bits.length === 0 && info.name) bits.push(info.name);
|
|
9771
10366
|
return bits.join(" \xB7 ") || "\u2014";
|
|
9772
10367
|
}
|
|
9773
|
-
var KIND_LABEL = {
|
|
9774
|
-
request: "REQ",
|
|
9775
|
-
response: "RSP",
|
|
9776
|
-
notification: "NTF",
|
|
9777
|
-
error: "ERR",
|
|
9778
|
-
unknown: "UNK"
|
|
9779
|
-
};
|
|
9780
|
-
var OP_MARKER = {
|
|
9781
|
-
equal: "=",
|
|
9782
|
-
changed: "\u2260",
|
|
9783
|
-
added: "\u25B8",
|
|
9784
|
-
removed: "\u25C2"
|
|
9785
|
-
};
|
|
9786
10368
|
function dirArrow(m) {
|
|
9787
10369
|
return m.direction === "editor-to-agent" ? "\u2192A" : "A\u2190";
|
|
9788
10370
|
}
|
|
@@ -9859,7 +10441,6 @@ function renderInfoSection(meta, st = defaultStyler()) {
|
|
|
9859
10441
|
}
|
|
9860
10442
|
return lines.join("");
|
|
9861
10443
|
}
|
|
9862
|
-
var PERF_METHOD_MAX = 40;
|
|
9863
10444
|
function colorDelta(st, ms) {
|
|
9864
10445
|
if (ms == null) return st.dim("\u2014");
|
|
9865
10446
|
const text = signedLatency(ms);
|
|
@@ -9903,7 +10484,6 @@ function signedLatency(ms) {
|
|
|
9903
10484
|
const sign = ms > 0 ? "+" : "\u2212";
|
|
9904
10485
|
return sign + formatLatency(Math.abs(ms));
|
|
9905
10486
|
}
|
|
9906
|
-
var VALUE_MAX = 60;
|
|
9907
10487
|
function fmtValue(v) {
|
|
9908
10488
|
let s;
|
|
9909
10489
|
try {
|
|
@@ -9916,17 +10496,43 @@ function fmtValue(v) {
|
|
|
9916
10496
|
if (s.length > VALUE_MAX) s = s.slice(0, VALUE_MAX - 1) + "\u2026";
|
|
9917
10497
|
return s;
|
|
9918
10498
|
}
|
|
10499
|
+
var defaultStyler, OP_COLOR, KIND_LABEL, OP_MARKER, PERF_METHOD_MAX, VALUE_MAX;
|
|
10500
|
+
var init_diff = __esm({
|
|
10501
|
+
"src/commands/diff.ts"() {
|
|
10502
|
+
"use strict";
|
|
10503
|
+
init_dist();
|
|
10504
|
+
init_style();
|
|
10505
|
+
init_palette();
|
|
10506
|
+
init_table();
|
|
10507
|
+
defaultStyler = () => createStyler(colorEnabled(process.stdout));
|
|
10508
|
+
OP_COLOR = {
|
|
10509
|
+
equal: (st, t) => st.dim(t),
|
|
10510
|
+
changed: (st, t) => st.yellow(t),
|
|
10511
|
+
added: (st, t) => st.green(t),
|
|
10512
|
+
removed: (st, t) => st.red(t)
|
|
10513
|
+
};
|
|
10514
|
+
KIND_LABEL = {
|
|
10515
|
+
request: "REQ",
|
|
10516
|
+
response: "RSP",
|
|
10517
|
+
notification: "NTF",
|
|
10518
|
+
error: "ERR",
|
|
10519
|
+
unknown: "UNK"
|
|
10520
|
+
};
|
|
10521
|
+
OP_MARKER = {
|
|
10522
|
+
equal: "=",
|
|
10523
|
+
changed: "\u2260",
|
|
10524
|
+
added: "\u25B8",
|
|
10525
|
+
removed: "\u25C2"
|
|
10526
|
+
};
|
|
10527
|
+
PERF_METHOD_MAX = 40;
|
|
10528
|
+
VALUE_MAX = 60;
|
|
10529
|
+
}
|
|
10530
|
+
});
|
|
9919
10531
|
|
|
9920
10532
|
// src/commands/doctor.ts
|
|
9921
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
|
|
10533
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
9922
10534
|
import { homedir as homedir3, platform, release } from "os";
|
|
9923
10535
|
import { join as join3 } from "path";
|
|
9924
|
-
var STATUS_MARK = {
|
|
9925
|
-
ok: "\u2713",
|
|
9926
|
-
warn: "!",
|
|
9927
|
-
fail: "\u2717",
|
|
9928
|
-
info: "\xB7"
|
|
9929
|
-
};
|
|
9930
10536
|
function colorStatus(s, status, text) {
|
|
9931
10537
|
switch (status) {
|
|
9932
10538
|
case "ok":
|
|
@@ -9983,7 +10589,7 @@ function checkState() {
|
|
|
9983
10589
|
) m ON m.session_id = s.id`
|
|
9984
10590
|
).get();
|
|
9985
10591
|
db.close();
|
|
9986
|
-
const size =
|
|
10592
|
+
const size = statSync2(dbPath).size;
|
|
9987
10593
|
results.push({
|
|
9988
10594
|
status: "ok",
|
|
9989
10595
|
label: `captures database: ${row2.sessions} session${row2.sessions === 1 ? "" : "s"}, ${row2.messages} message${row2.messages === 1 ? "" : "s"}`,
|
|
@@ -10040,8 +10646,6 @@ function probeZed() {
|
|
|
10040
10646
|
);
|
|
10041
10647
|
return { name: "Zed", path, exists: existsSync6(path) };
|
|
10042
10648
|
}
|
|
10043
|
-
var JETBRAINS_VERSIONED = /^[A-Z][A-Za-z]+\d{4}\.\d/;
|
|
10044
|
-
var JETBRAINS_UNVERSIONED = /* @__PURE__ */ new Set(["Fleet", "Toolbox"]);
|
|
10045
10649
|
function probeJetBrains() {
|
|
10046
10650
|
const baseDirs = [];
|
|
10047
10651
|
if (platform() === "darwin") {
|
|
@@ -10065,7 +10669,7 @@ function probeJetBrains() {
|
|
|
10065
10669
|
if (!JETBRAINS_VERSIONED.test(entry) && !JETBRAINS_UNVERSIONED.has(entry)) continue;
|
|
10066
10670
|
const full = join3(base, entry);
|
|
10067
10671
|
try {
|
|
10068
|
-
if (!
|
|
10672
|
+
if (!statSync2(full).isDirectory()) continue;
|
|
10069
10673
|
} catch {
|
|
10070
10674
|
continue;
|
|
10071
10675
|
}
|
|
@@ -10135,8 +10739,8 @@ function exitCodeFromSections(sections) {
|
|
|
10135
10739
|
}
|
|
10136
10740
|
return 0;
|
|
10137
10741
|
}
|
|
10138
|
-
function registerDoctorCommand(
|
|
10139
|
-
|
|
10742
|
+
function registerDoctorCommand(program) {
|
|
10743
|
+
program.command("doctor").description("Diagnose acp-devtools setup and report any issues").option("--json", "output machine-readable JSON instead of human text").option("--no-color", "disable ANSI colour output").action((opts) => {
|
|
10140
10744
|
const sections = [
|
|
10141
10745
|
{ title: "Environment", results: checkEnvironment() },
|
|
10142
10746
|
{ title: "State", results: checkState() },
|
|
@@ -10156,16 +10760,40 @@ function registerDoctorCommand(program2) {
|
|
|
10156
10760
|
process.exit(exitCodeFromSections(sections));
|
|
10157
10761
|
});
|
|
10158
10762
|
}
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10763
|
+
var STATUS_MARK, JETBRAINS_VERSIONED, JETBRAINS_UNVERSIONED;
|
|
10764
|
+
var init_doctor = __esm({
|
|
10765
|
+
"src/commands/doctor.ts"() {
|
|
10766
|
+
"use strict";
|
|
10767
|
+
init_dist();
|
|
10768
|
+
init_style();
|
|
10769
|
+
STATUS_MARK = {
|
|
10770
|
+
ok: "\u2713",
|
|
10771
|
+
warn: "!",
|
|
10772
|
+
fail: "\u2717",
|
|
10773
|
+
info: "\xB7"
|
|
10774
|
+
};
|
|
10775
|
+
JETBRAINS_VERSIONED = /^[A-Z][A-Za-z]+\d{4}\.\d/;
|
|
10776
|
+
JETBRAINS_UNVERSIONED = /* @__PURE__ */ new Set(["Fleet", "Toolbox"]);
|
|
10777
|
+
}
|
|
10778
|
+
});
|
|
10162
10779
|
|
|
10163
10780
|
// src/version.ts
|
|
10164
|
-
var CLI_VERSION
|
|
10781
|
+
var CLI_VERSION;
|
|
10782
|
+
var init_version = __esm({
|
|
10783
|
+
"src/version.ts"() {
|
|
10784
|
+
"use strict";
|
|
10785
|
+
CLI_VERSION = "0.2.1";
|
|
10786
|
+
}
|
|
10787
|
+
});
|
|
10165
10788
|
|
|
10166
10789
|
// src/commands/export.ts
|
|
10167
|
-
|
|
10168
|
-
|
|
10790
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
10791
|
+
function registerExportCommand(program) {
|
|
10792
|
+
program.command("export").description("Export a recorded session as self-contained JSON (suitable for sharing and for mock-replay)").argument("[id]", "session id to export (default: latest in the database)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("-o, --output <file>", "write to a file instead of stdout").option("--no-pretty", "emit compact JSON (no indent, single line)").option(
|
|
10793
|
+
"--raw",
|
|
10794
|
+
"skip default redaction of auth headers / proxy tokens (use only when the export stays on YOUR machine)",
|
|
10795
|
+
false
|
|
10796
|
+
).action((idArg, opts) => {
|
|
10169
10797
|
let id = null;
|
|
10170
10798
|
if (idArg !== void 0) {
|
|
10171
10799
|
id = Number(idArg);
|
|
@@ -10194,28 +10822,46 @@ function registerExportCommand(program2) {
|
|
|
10194
10822
|
db.close();
|
|
10195
10823
|
process.exit(1);
|
|
10196
10824
|
}
|
|
10197
|
-
const
|
|
10825
|
+
const rawExp = exportSession(session, {
|
|
10198
10826
|
tool: { name: "acp-devtools", version: CLI_VERSION }
|
|
10199
10827
|
});
|
|
10200
|
-
const
|
|
10828
|
+
const { export: finalExp, fieldsRedacted, messagesAffected } = opts.raw ? { export: rawExp, fieldsRedacted: 0, messagesAffected: 0 } : redactSessionExport(rawExp);
|
|
10829
|
+
const json = serializeExport(finalExp, opts.pretty ? 4 : 0);
|
|
10201
10830
|
db.close();
|
|
10202
10831
|
if (opts.output) {
|
|
10203
10832
|
writeFileSync2(opts.output, json);
|
|
10204
10833
|
process.stderr.write(
|
|
10205
|
-
`acp-devtools: exported session #${
|
|
10834
|
+
`acp-devtools: exported session #${finalExp.session.id} (${finalExp.messages.length} messages) \u2192 ${opts.output}
|
|
10206
10835
|
`
|
|
10207
10836
|
);
|
|
10208
10837
|
} else {
|
|
10209
10838
|
process.stdout.write(json);
|
|
10210
10839
|
}
|
|
10840
|
+
if (opts.raw) {
|
|
10841
|
+
process.stderr.write(
|
|
10842
|
+
"acp-devtools: --raw was set; export may contain auth headers / proxy tokens \u2014 do not share publicly\n"
|
|
10843
|
+
);
|
|
10844
|
+
} else if (fieldsRedacted > 0) {
|
|
10845
|
+
process.stderr.write(
|
|
10846
|
+
`acp-devtools: redacted ${fieldsRedacted} field(s) across ${messagesAffected} message(s) \u2014 re-run with --raw to keep them
|
|
10847
|
+
`
|
|
10848
|
+
);
|
|
10849
|
+
}
|
|
10211
10850
|
});
|
|
10212
10851
|
}
|
|
10852
|
+
var init_export2 = __esm({
|
|
10853
|
+
"src/commands/export.ts"() {
|
|
10854
|
+
"use strict";
|
|
10855
|
+
init_dist();
|
|
10856
|
+
init_version();
|
|
10857
|
+
}
|
|
10858
|
+
});
|
|
10213
10859
|
|
|
10214
10860
|
// src/commands/import.ts
|
|
10215
10861
|
import { readFileSync as readFileSync2 } from "fs";
|
|
10216
10862
|
import { basename } from "path";
|
|
10217
|
-
function registerImportCommand(
|
|
10218
|
-
|
|
10863
|
+
function registerImportCommand(program) {
|
|
10864
|
+
program.command("import").description(
|
|
10219
10865
|
"Insert a JSON session export into the captures database as a new saved session. The new session appears in the inspector picker under its IMPORTED section, with imported_at set to now."
|
|
10220
10866
|
).argument("<file>", "path to a JSON export produced by `acp-devtools export`").option(
|
|
10221
10867
|
"--db <path>",
|
|
@@ -10253,21 +10899,16 @@ function registerImportCommand(program2) {
|
|
|
10253
10899
|
}
|
|
10254
10900
|
});
|
|
10255
10901
|
}
|
|
10902
|
+
var init_import = __esm({
|
|
10903
|
+
"src/commands/import.ts"() {
|
|
10904
|
+
"use strict";
|
|
10905
|
+
init_dist();
|
|
10906
|
+
}
|
|
10907
|
+
});
|
|
10256
10908
|
|
|
10257
10909
|
// src/commands/inspect.ts
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
in: "agent-to-editor"
|
|
10261
|
-
};
|
|
10262
|
-
var KIND_FROM_CODE = {
|
|
10263
|
-
req: "request",
|
|
10264
|
-
rsp: "response",
|
|
10265
|
-
ntf: "notification",
|
|
10266
|
-
err: "error",
|
|
10267
|
-
unk: "unknown"
|
|
10268
|
-
};
|
|
10269
|
-
function registerInspectCommand(program2) {
|
|
10270
|
-
program2.command("inspect").description(
|
|
10910
|
+
function registerInspectCommand(program) {
|
|
10911
|
+
program.command("inspect").description(
|
|
10271
10912
|
"Print messages of a saved session to stdout \u2014 the terminal equivalent of the inspector timeline. Combine with grep / jq for shell-side analysis."
|
|
10272
10913
|
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--limit <n>", "maximum messages to print", "500").option("--from-seq <n>", "start from this seq number (inclusive)").option(
|
|
10273
10914
|
"--dir <codes>",
|
|
@@ -10411,13 +11052,6 @@ function passes(m, f) {
|
|
|
10411
11052
|
if (f.grep && !m.raw.toLowerCase().includes(f.grep)) return false;
|
|
10412
11053
|
return true;
|
|
10413
11054
|
}
|
|
10414
|
-
var KIND_LABEL2 = {
|
|
10415
|
-
request: "REQ",
|
|
10416
|
-
response: "RSP",
|
|
10417
|
-
notification: "NTF",
|
|
10418
|
-
error: "ERR",
|
|
10419
|
-
unknown: "UNK"
|
|
10420
|
-
};
|
|
10421
11055
|
function toTableRow(m, pair, specCell = "") {
|
|
10422
11056
|
let paired = "\u2014";
|
|
10423
11057
|
let latency = "\u2014";
|
|
@@ -10477,9 +11111,6 @@ function formatBytes2(n) {
|
|
|
10477
11111
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
10478
11112
|
return `${(n / 1024 / 1024).toFixed(1)}MB`;
|
|
10479
11113
|
}
|
|
10480
|
-
var PREVIEW_MAX = 50;
|
|
10481
|
-
var METHOD_MAX = 40;
|
|
10482
|
-
var RPC_MAX = 10;
|
|
10483
11114
|
function colorSpec(s, cell) {
|
|
10484
11115
|
if (cell === "\u2713") return s.green(cell);
|
|
10485
11116
|
if (cell.startsWith("\u26A0")) return s.red(cell);
|
|
@@ -10524,10 +11155,41 @@ function truncate(s, max) {
|
|
|
10524
11155
|
if (s.length <= max) return s;
|
|
10525
11156
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
10526
11157
|
}
|
|
11158
|
+
var DIR_FROM_CODE, KIND_FROM_CODE, KIND_LABEL2, PREVIEW_MAX, METHOD_MAX, RPC_MAX;
|
|
11159
|
+
var init_inspect = __esm({
|
|
11160
|
+
"src/commands/inspect.ts"() {
|
|
11161
|
+
"use strict";
|
|
11162
|
+
init_dist();
|
|
11163
|
+
init_style();
|
|
11164
|
+
init_palette();
|
|
11165
|
+
init_table();
|
|
11166
|
+
DIR_FROM_CODE = {
|
|
11167
|
+
out: "editor-to-agent",
|
|
11168
|
+
in: "agent-to-editor"
|
|
11169
|
+
};
|
|
11170
|
+
KIND_FROM_CODE = {
|
|
11171
|
+
req: "request",
|
|
11172
|
+
rsp: "response",
|
|
11173
|
+
ntf: "notification",
|
|
11174
|
+
err: "error",
|
|
11175
|
+
unk: "unknown"
|
|
11176
|
+
};
|
|
11177
|
+
KIND_LABEL2 = {
|
|
11178
|
+
request: "REQ",
|
|
11179
|
+
response: "RSP",
|
|
11180
|
+
notification: "NTF",
|
|
11181
|
+
error: "ERR",
|
|
11182
|
+
unknown: "UNK"
|
|
11183
|
+
};
|
|
11184
|
+
PREVIEW_MAX = 50;
|
|
11185
|
+
METHOD_MAX = 40;
|
|
11186
|
+
RPC_MAX = 10;
|
|
11187
|
+
}
|
|
11188
|
+
});
|
|
10527
11189
|
|
|
10528
11190
|
// src/commands/list.ts
|
|
10529
|
-
function registerListCommand(
|
|
10530
|
-
|
|
11191
|
+
function registerListCommand(program) {
|
|
11192
|
+
program.command("list").description(
|
|
10531
11193
|
"List saved sessions in the captures database, newest first. Use `acp-devtools doctor` for live captures (those are not in the database)."
|
|
10532
11194
|
).option("--db <path>", "captures database", defaultCapturesDbPath()).option("--limit <n>", "maximum rows to show", "50").option("--imported", "only show imported sessions").option("--saved", "only show non-imported (live-captured) saved sessions").option(
|
|
10533
11195
|
"--client <substring>",
|
|
@@ -10577,13 +11239,6 @@ function formatAge(deltaMs) {
|
|
|
10577
11239
|
if (h < 24) return `${h}h${m % 60 > 0 ? `${m % 60}m` : ""}`;
|
|
10578
11240
|
return `${Math.floor(h / 24)}d`;
|
|
10579
11241
|
}
|
|
10580
|
-
var COLUMNS = [
|
|
10581
|
-
{ title: "ID", align: "left" },
|
|
10582
|
-
{ title: "AGE", align: "right" },
|
|
10583
|
-
{ title: "KIND", align: "left" },
|
|
10584
|
-
{ title: "MSGS", align: "right" },
|
|
10585
|
-
{ title: "SESSION", align: "left" }
|
|
10586
|
-
];
|
|
10587
11242
|
function formatTable(rows) {
|
|
10588
11243
|
const s = createStyler(colorEnabled(process.stdout));
|
|
10589
11244
|
const now = Date.now();
|
|
@@ -10599,54 +11254,28 @@ function formatTable(rows) {
|
|
|
10599
11254
|
});
|
|
10600
11255
|
return renderTable(s, COLUMNS, body);
|
|
10601
11256
|
}
|
|
11257
|
+
var COLUMNS;
|
|
11258
|
+
var init_list = __esm({
|
|
11259
|
+
"src/commands/list.ts"() {
|
|
11260
|
+
"use strict";
|
|
11261
|
+
init_dist();
|
|
11262
|
+
init_style();
|
|
11263
|
+
init_palette();
|
|
11264
|
+
init_table();
|
|
11265
|
+
COLUMNS = [
|
|
11266
|
+
{ title: "ID", align: "left" },
|
|
11267
|
+
{ title: "AGE", align: "right" },
|
|
11268
|
+
{ title: "KIND", align: "left" },
|
|
11269
|
+
{ title: "MSGS", align: "right" },
|
|
11270
|
+
{ title: "SESSION", align: "left" }
|
|
11271
|
+
];
|
|
11272
|
+
}
|
|
11273
|
+
});
|
|
10602
11274
|
|
|
10603
11275
|
// src/commands/mcp.ts
|
|
10604
11276
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10605
11277
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10606
11278
|
import { z } from "zod";
|
|
10607
|
-
var SERVER_INSTRUCTIONS = `acp-devtools exposes a local SQLite store of captured
|
|
10608
|
-
Agent Client Protocol (ACP) sessions. ACP is the editor\u2194agent JSON-RPC
|
|
10609
|
-
protocol used by Claude Code, Goose, Codex, and other IDE agents.
|
|
10610
|
-
|
|
10611
|
-
Every tool here is **read-only** \u2014 nothing in this server writes to the
|
|
10612
|
-
database. Use it to investigate:
|
|
10613
|
-
|
|
10614
|
-
- a slow session (\`get_latency_stats\` returns per-method p50/p99/max
|
|
10615
|
-
plus auto-detected insights: hotspot, long-tail, outlier, busiest,
|
|
10616
|
-
errors)
|
|
10617
|
-
- a misbehaving agent (\`find_spec_violations\` validates every frame
|
|
10618
|
-
against the ACP JSON schema)
|
|
10619
|
-
- specific events (\`get_session_messages\` + \`get_message\` for the
|
|
10620
|
-
raw JSON-RPC payload)
|
|
10621
|
-
- capture inventory (\`list_sessions\`, \`find_sessions_by_client\`)
|
|
10622
|
-
- one-shot digest of a session (\`get_session_summary\` bundles
|
|
10623
|
-
metadata + latency stats + insights in one call \u2014 preferred over
|
|
10624
|
-
three separate calls when you just need an overview)
|
|
10625
|
-
- a regression or A/B comparison between two sessions
|
|
10626
|
-
(\`diff_sessions\` aligns two captures and reports added / removed
|
|
10627
|
-
frames and field-level payload changes)
|
|
10628
|
-
|
|
10629
|
-
A typical investigation:
|
|
10630
|
-
1. \`list_sessions\` to see what is captured
|
|
10631
|
-
2. \`get_session_summary({session_id})\` for the headline numbers and
|
|
10632
|
-
auto-detected insights \u2014 start here when you don't know what's
|
|
10633
|
-
wrong yet
|
|
10634
|
-
3. drill into a specific method or message with
|
|
10635
|
-
\`get_session_messages\` / \`get_message\` / \`get_paired\`
|
|
10636
|
-
|
|
10637
|
-
For common investigations the server also exposes canned prompts via
|
|
10638
|
-
\`prompts/list\` \u2014 \`triage_slow_session\`, \`audit_spec_violations\`,
|
|
10639
|
-
\`compare_clients\`. Each prompt seeds the right tool sequence.
|
|
10640
|
-
|
|
10641
|
-
Every tool declares an \`outputSchema\`, so results include both
|
|
10642
|
-
\`content\` (text JSON) and \`structuredContent\` (typed object matching
|
|
10643
|
-
the schema). Prefer \`structuredContent\` if your client supports it \u2014
|
|
10644
|
-
it avoids re-parsing the text payload.
|
|
10645
|
-
|
|
10646
|
-
Schema notes: sessions have a structured-metadata layer populated by the
|
|
10647
|
-
\`backfill-metadata\` CLI; \`client_name\` / \`client_version\` /
|
|
10648
|
-
\`client_platform\` may be null on freshly imported captures until that
|
|
10649
|
-
command runs.`;
|
|
10650
11279
|
function annotations(meta) {
|
|
10651
11280
|
return {
|
|
10652
11281
|
title: meta.title,
|
|
@@ -10661,60 +11290,8 @@ function structuredResult(payload) {
|
|
|
10661
11290
|
structuredContent: payload
|
|
10662
11291
|
};
|
|
10663
11292
|
}
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
name: z.string().nullable(),
|
|
10667
|
-
agent_command: z.string().nullable(),
|
|
10668
|
-
started_at: z.number(),
|
|
10669
|
-
ended_at: z.number().nullable(),
|
|
10670
|
-
message_count: z.number(),
|
|
10671
|
-
client_name: z.string().nullable(),
|
|
10672
|
-
imported_at: z.number().nullable(),
|
|
10673
|
-
client_version: z.string().nullable(),
|
|
10674
|
-
client_platform: z.string().nullable(),
|
|
10675
|
-
agent_name: z.string().nullable(),
|
|
10676
|
-
agent_version: z.string().nullable(),
|
|
10677
|
-
protocol_version: z.number().nullable(),
|
|
10678
|
-
current_mode: z.string().nullable(),
|
|
10679
|
-
current_model: z.string().nullable()
|
|
10680
|
-
});
|
|
10681
|
-
var insightSchema = z.object({
|
|
10682
|
-
kind: z.enum(["hotspot", "long-tail", "outlier", "busiest", "errors"]),
|
|
10683
|
-
summary: z.string(),
|
|
10684
|
-
detail: z.string().optional(),
|
|
10685
|
-
methods: z.array(z.string())
|
|
10686
|
-
});
|
|
10687
|
-
var perMethodSchema = z.object({
|
|
10688
|
-
method: z.string(),
|
|
10689
|
-
kind: z.enum(["request", "notification"]),
|
|
10690
|
-
count: z.number(),
|
|
10691
|
-
sampleSize: z.number(),
|
|
10692
|
-
p50: z.number().nullable(),
|
|
10693
|
-
p99: z.number().nullable(),
|
|
10694
|
-
max: z.number().nullable(),
|
|
10695
|
-
totalLatencyMs: z.number().nullable(),
|
|
10696
|
-
latencies: z.array(z.number())
|
|
10697
|
-
});
|
|
10698
|
-
var messageSchema = z.object({
|
|
10699
|
-
seq: z.number(),
|
|
10700
|
-
timestamp: z.number(),
|
|
10701
|
-
direction: z.enum(["editor-to-agent", "agent-to-editor"]),
|
|
10702
|
-
kind: z.enum(["request", "response", "notification", "error", "unknown"]),
|
|
10703
|
-
method: z.string().optional(),
|
|
10704
|
-
rpcId: z.union([z.string(), z.number()]).optional(),
|
|
10705
|
-
raw: z.string(),
|
|
10706
|
-
payload: z.unknown().nullable(),
|
|
10707
|
-
parseError: z.string().optional()
|
|
10708
|
-
});
|
|
10709
|
-
var latencySchema = z.object({
|
|
10710
|
-
sampleSize: z.number(),
|
|
10711
|
-
p50: z.number().nullable(),
|
|
10712
|
-
p90: z.number().nullable(),
|
|
10713
|
-
p99: z.number().nullable(),
|
|
10714
|
-
max: z.number().nullable()
|
|
10715
|
-
});
|
|
10716
|
-
function registerMcpCommand(program2) {
|
|
10717
|
-
program2.command("mcp").description(
|
|
11293
|
+
function registerMcpCommand(program) {
|
|
11294
|
+
program.command("mcp").description(
|
|
10718
11295
|
'Run an MCP server over stdio that exposes saved captures as read-only tools. Wire into Claude Code via .claude/mcp_servers.json \u2014 then ask Claude to "find spec violations in last 10 sessions" or "compare p99 of WebStorm vs Zed".'
|
|
10719
11296
|
).option("--db <path>", "captures database", defaultCapturesDbPath()).option("--name <name>", "server name advertised in MCP handshake", "acp-devtools").action(async (opts) => {
|
|
10720
11297
|
const server = buildMcpServer(opts);
|
|
@@ -10779,7 +11356,7 @@ function buildMcpServer(opts) {
|
|
|
10779
11356
|
annotations: annotations({ title: "Get session metadata" })
|
|
10780
11357
|
},
|
|
10781
11358
|
async ({ session_id }) => {
|
|
10782
|
-
const messages =
|
|
11359
|
+
const messages = redactedReadMessages(opts.db, session_id);
|
|
10783
11360
|
return structuredResult({
|
|
10784
11361
|
session_id,
|
|
10785
11362
|
metadata: extractSessionMetadata(messages)
|
|
@@ -10854,7 +11431,7 @@ function buildMcpServer(opts) {
|
|
|
10854
11431
|
annotations: annotations({ title: "One-call session digest" })
|
|
10855
11432
|
},
|
|
10856
11433
|
async ({ session_id }) => {
|
|
10857
|
-
const messages =
|
|
11434
|
+
const messages = redactedReadMessages(opts.db, session_id);
|
|
10858
11435
|
const perMethod = buildPerMethodStats(messages);
|
|
10859
11436
|
const insights = buildPerformanceInsights(messages, perMethod);
|
|
10860
11437
|
const pairs = buildPairIndex(messages);
|
|
@@ -10932,7 +11509,7 @@ function buildMcpServer(opts) {
|
|
|
10932
11509
|
total,
|
|
10933
11510
|
offset: start,
|
|
10934
11511
|
returned: end - start,
|
|
10935
|
-
messages: filtered.slice(start, end)
|
|
11512
|
+
messages: filtered.slice(start, end).map((m) => redactMessage(m).redacted)
|
|
10936
11513
|
});
|
|
10937
11514
|
}
|
|
10938
11515
|
);
|
|
@@ -10956,7 +11533,7 @@ function buildMcpServer(opts) {
|
|
|
10956
11533
|
if (!msg) {
|
|
10957
11534
|
return errorResult(`session ${session_id} has no message #${seq}`);
|
|
10958
11535
|
}
|
|
10959
|
-
return structuredResult({ session_id, message: msg });
|
|
11536
|
+
return structuredResult({ session_id, message: redactMessage(msg).redacted });
|
|
10960
11537
|
}
|
|
10961
11538
|
);
|
|
10962
11539
|
server.registerTool(
|
|
@@ -11010,7 +11587,8 @@ function buildMcpServer(opts) {
|
|
|
11010
11587
|
const matches = [];
|
|
11011
11588
|
for (const m of messages) {
|
|
11012
11589
|
if (m.raw.toLowerCase().includes(needle)) {
|
|
11013
|
-
|
|
11590
|
+
const safe = redactMessage(m).redacted;
|
|
11591
|
+
matches.push({ seq: m.seq, method: m.method ?? null, raw: safe.raw });
|
|
11014
11592
|
if (matches.length >= (limit ?? 50)) break;
|
|
11015
11593
|
}
|
|
11016
11594
|
}
|
|
@@ -11133,8 +11711,8 @@ function buildMcpServer(opts) {
|
|
|
11133
11711
|
annotations: annotations({ title: "Diff two sessions" })
|
|
11134
11712
|
},
|
|
11135
11713
|
async ({ session_a, session_b, include_equal }) => {
|
|
11136
|
-
const a =
|
|
11137
|
-
const b =
|
|
11714
|
+
const a = redactedReadMessages(opts.db, session_a);
|
|
11715
|
+
const b = redactedReadMessages(opts.db, session_b);
|
|
11138
11716
|
const diff = buildSessionDiff(a, b);
|
|
11139
11717
|
const meta = buildMetadataDiff(a, b);
|
|
11140
11718
|
const perMethod = buildMethodStatsDiff(a, b);
|
|
@@ -11257,6 +11835,9 @@ Cite the chosen session ids and their message counts so I can replicate in the i
|
|
|
11257
11835
|
})
|
|
11258
11836
|
);
|
|
11259
11837
|
}
|
|
11838
|
+
function redactedReadMessages(dbPath, sessionId) {
|
|
11839
|
+
return readMessages(dbPath, sessionId).map((m) => redactMessage(m).redacted);
|
|
11840
|
+
}
|
|
11260
11841
|
function readMessages(dbPath, sessionId) {
|
|
11261
11842
|
const db = openDatabase(dbPath);
|
|
11262
11843
|
try {
|
|
@@ -11274,10 +11855,125 @@ function errorResult(message) {
|
|
|
11274
11855
|
content: [{ type: "text", text: message }]
|
|
11275
11856
|
};
|
|
11276
11857
|
}
|
|
11858
|
+
var SERVER_INSTRUCTIONS, sessionSummaryRowSchema, insightSchema, perMethodSchema, messageSchema, latencySchema;
|
|
11859
|
+
var init_mcp = __esm({
|
|
11860
|
+
"src/commands/mcp.ts"() {
|
|
11861
|
+
"use strict";
|
|
11862
|
+
init_dist();
|
|
11863
|
+
init_version();
|
|
11864
|
+
SERVER_INSTRUCTIONS = `acp-devtools exposes a local SQLite store of captured
|
|
11865
|
+
Agent Client Protocol (ACP) sessions. ACP is the editor\u2194agent JSON-RPC
|
|
11866
|
+
protocol used by Claude Code, Goose, Codex, and other IDE agents.
|
|
11277
11867
|
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11868
|
+
Every tool here is **read-only** \u2014 nothing in this server writes to the
|
|
11869
|
+
database. Use it to investigate:
|
|
11870
|
+
|
|
11871
|
+
- a slow session (\`get_latency_stats\` returns per-method p50/p99/max
|
|
11872
|
+
plus auto-detected insights: hotspot, long-tail, outlier, busiest,
|
|
11873
|
+
errors)
|
|
11874
|
+
- a misbehaving agent (\`find_spec_violations\` validates every frame
|
|
11875
|
+
against the ACP JSON schema)
|
|
11876
|
+
- specific events (\`get_session_messages\` + \`get_message\` for the
|
|
11877
|
+
raw JSON-RPC payload)
|
|
11878
|
+
- capture inventory (\`list_sessions\`, \`find_sessions_by_client\`)
|
|
11879
|
+
- one-shot digest of a session (\`get_session_summary\` bundles
|
|
11880
|
+
metadata + latency stats + insights in one call \u2014 preferred over
|
|
11881
|
+
three separate calls when you just need an overview)
|
|
11882
|
+
- a regression or A/B comparison between two sessions
|
|
11883
|
+
(\`diff_sessions\` aligns two captures and reports added / removed
|
|
11884
|
+
frames and field-level payload changes)
|
|
11885
|
+
|
|
11886
|
+
A typical investigation:
|
|
11887
|
+
1. \`list_sessions\` to see what is captured
|
|
11888
|
+
2. \`get_session_summary({session_id})\` for the headline numbers and
|
|
11889
|
+
auto-detected insights \u2014 start here when you don't know what's
|
|
11890
|
+
wrong yet
|
|
11891
|
+
3. drill into a specific method or message with
|
|
11892
|
+
\`get_session_messages\` / \`get_message\` / \`get_paired\`
|
|
11893
|
+
|
|
11894
|
+
For common investigations the server also exposes canned prompts via
|
|
11895
|
+
\`prompts/list\` \u2014 \`triage_slow_session\`, \`audit_spec_violations\`,
|
|
11896
|
+
\`compare_clients\`. Each prompt seeds the right tool sequence.
|
|
11897
|
+
|
|
11898
|
+
Every tool declares an \`outputSchema\`, so results include both
|
|
11899
|
+
\`content\` (text JSON) and \`structuredContent\` (typed object matching
|
|
11900
|
+
the schema). Prefer \`structuredContent\` if your client supports it \u2014
|
|
11901
|
+
it avoids re-parsing the text payload.
|
|
11902
|
+
|
|
11903
|
+
Schema notes: sessions have a structured-metadata layer populated by the
|
|
11904
|
+
\`backfill-metadata\` CLI; \`client_name\` / \`client_version\` /
|
|
11905
|
+
\`client_platform\` may be null on freshly imported captures until that
|
|
11906
|
+
command runs.
|
|
11907
|
+
|
|
11908
|
+
Redaction: every tool that returns frame contents OR derived views
|
|
11909
|
+
of them (\`get_message\`, \`get_session_messages\`, \`search_messages\`,
|
|
11910
|
+
\`get_session_metadata\`, \`get_session_summary\`, \`diff_sessions\`)
|
|
11911
|
+
redacts auth headers and proxy tokens (\`Authorization\`,
|
|
11912
|
+
\`X-Api-Key\`, JetBrains \`_meta.proxyConfig.proxies[*].proxy.headers.*\`,
|
|
11913
|
+
etc). Sensitive fields appear as \`<REDACTED>\`. This is
|
|
11914
|
+
unconditional; there is no opt-out flag, because the human owns the
|
|
11915
|
+
share decision (use \`acp-devtools export <id> --raw\` on the CLI when
|
|
11916
|
+
the export stays on your machine). \`search_messages\` indexes the
|
|
11917
|
+
pre-redaction bytes so you can still find a frame by a token fragment,
|
|
11918
|
+
but the returned \`raw\` is the redacted copy \u2014 do not quote the live
|
|
11919
|
+
secret back to the user. \`diff_sessions\` operates on already-redacted
|
|
11920
|
+
frames, so a token that rotated between two sessions shows up as equal
|
|
11921
|
+
("<REDACTED>" on both sides) rather than as a change \u2014 this is
|
|
11922
|
+
intentional, treating "token rotated" as a leaked side channel.`;
|
|
11923
|
+
sessionSummaryRowSchema = z.object({
|
|
11924
|
+
id: z.number(),
|
|
11925
|
+
name: z.string().nullable(),
|
|
11926
|
+
agent_command: z.string().nullable(),
|
|
11927
|
+
started_at: z.number(),
|
|
11928
|
+
ended_at: z.number().nullable(),
|
|
11929
|
+
message_count: z.number(),
|
|
11930
|
+
client_name: z.string().nullable(),
|
|
11931
|
+
imported_at: z.number().nullable(),
|
|
11932
|
+
client_version: z.string().nullable(),
|
|
11933
|
+
client_platform: z.string().nullable(),
|
|
11934
|
+
agent_name: z.string().nullable(),
|
|
11935
|
+
agent_version: z.string().nullable(),
|
|
11936
|
+
protocol_version: z.number().nullable(),
|
|
11937
|
+
current_mode: z.string().nullable(),
|
|
11938
|
+
current_model: z.string().nullable()
|
|
11939
|
+
});
|
|
11940
|
+
insightSchema = z.object({
|
|
11941
|
+
kind: z.enum(["hotspot", "long-tail", "outlier", "busiest", "errors"]),
|
|
11942
|
+
summary: z.string(),
|
|
11943
|
+
detail: z.string().optional(),
|
|
11944
|
+
methods: z.array(z.string())
|
|
11945
|
+
});
|
|
11946
|
+
perMethodSchema = z.object({
|
|
11947
|
+
method: z.string(),
|
|
11948
|
+
kind: z.enum(["request", "notification"]),
|
|
11949
|
+
count: z.number(),
|
|
11950
|
+
sampleSize: z.number(),
|
|
11951
|
+
p50: z.number().nullable(),
|
|
11952
|
+
p99: z.number().nullable(),
|
|
11953
|
+
max: z.number().nullable(),
|
|
11954
|
+
totalLatencyMs: z.number().nullable(),
|
|
11955
|
+
latencies: z.array(z.number())
|
|
11956
|
+
});
|
|
11957
|
+
messageSchema = z.object({
|
|
11958
|
+
seq: z.number(),
|
|
11959
|
+
timestamp: z.number(),
|
|
11960
|
+
direction: z.enum(["editor-to-agent", "agent-to-editor"]),
|
|
11961
|
+
kind: z.enum(["request", "response", "notification", "error", "unknown"]),
|
|
11962
|
+
method: z.string().optional(),
|
|
11963
|
+
rpcId: z.union([z.string(), z.number()]).optional(),
|
|
11964
|
+
raw: z.string(),
|
|
11965
|
+
payload: z.unknown().nullable(),
|
|
11966
|
+
parseError: z.string().optional()
|
|
11967
|
+
});
|
|
11968
|
+
latencySchema = z.object({
|
|
11969
|
+
sampleSize: z.number(),
|
|
11970
|
+
p50: z.number().nullable(),
|
|
11971
|
+
p90: z.number().nullable(),
|
|
11972
|
+
p99: z.number().nullable(),
|
|
11973
|
+
max: z.number().nullable()
|
|
11974
|
+
});
|
|
11975
|
+
}
|
|
11976
|
+
});
|
|
11281
11977
|
|
|
11282
11978
|
// src/commands/playback-source.ts
|
|
11283
11979
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -11323,10 +12019,18 @@ function loadPlaybackScript(opts) {
|
|
|
11323
12019
|
db.close();
|
|
11324
12020
|
}
|
|
11325
12021
|
}
|
|
12022
|
+
var init_playback_source = __esm({
|
|
12023
|
+
"src/commands/playback-source.ts"() {
|
|
12024
|
+
"use strict";
|
|
12025
|
+
init_dist();
|
|
12026
|
+
}
|
|
12027
|
+
});
|
|
11326
12028
|
|
|
11327
12029
|
// src/commands/mock-agent.ts
|
|
11328
|
-
|
|
11329
|
-
|
|
12030
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
12031
|
+
import { dirname as dirname2 } from "path";
|
|
12032
|
+
function registerMockAgentCommand(program) {
|
|
12033
|
+
program.command("mock-agent").description(
|
|
11330
12034
|
"Pretend to be an ACP agent by replaying a recorded session script. Reads JSON-RPC from stdin (real editor), writes recorded responses to stdout. Wire it into an IDE `agent_servers` config to drive plugin tests without a live LLM. By default reads from the shared captures database (latest session); use --session for a specific id or --script for a JSON export from a teammate."
|
|
11331
12035
|
).option("--session <id>", "session id from the captures database (default: latest)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--script <file>", "load from a JSON export instead of the database (mutually exclusive with --session)").option("--log <mode>", "echo every wire frame to stderr (json | pretty | none)", "none").option(
|
|
11332
12036
|
"--realtime",
|
|
@@ -11460,12 +12164,19 @@ function registerMockAgentCommand(program2) {
|
|
|
11460
12164
|
drain();
|
|
11461
12165
|
});
|
|
11462
12166
|
}
|
|
12167
|
+
var init_mock_agent = __esm({
|
|
12168
|
+
"src/commands/mock-agent.ts"() {
|
|
12169
|
+
"use strict";
|
|
12170
|
+
init_dist();
|
|
12171
|
+
init_playback_source();
|
|
12172
|
+
}
|
|
12173
|
+
});
|
|
11463
12174
|
|
|
11464
12175
|
// src/commands/mock-editor.ts
|
|
11465
12176
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
11466
12177
|
import { dirname as dirname3 } from "path";
|
|
11467
|
-
function registerMockEditorCommand(
|
|
11468
|
-
|
|
12178
|
+
function registerMockEditorCommand(program) {
|
|
12179
|
+
program.command("mock-editor").description(
|
|
11469
12180
|
"Pretend to be an editor by replaying a recorded session script. Spawns the given agent as a child process, sends it the recorded editor-side frames in order, and reads agent responses. Useful for CI tests of your own ACP agent without a real IDE. By default reads from the shared captures database (latest session); use --session for a specific id or --script for a JSON export from a teammate."
|
|
11470
12181
|
).passThroughOptions().argument("<agent>", "agent executable (e.g. `node fixtures/mock-agent.js` or the path to a real ACP agent)").argument("[agent-args...]", "arguments forwarded to the agent").option("--session <id>", "session id from the captures database (default: latest)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--script <file>", "load from a JSON export instead of the database (mutually exclusive with --session)").option("--cwd <dir>", "working directory for the agent").option("--log <mode>", "echo every wire frame to stderr (json | pretty | none)", "none").option(
|
|
11471
12182
|
"--realtime",
|
|
@@ -11628,14 +12339,17 @@ function registerMockEditorCommand(program2) {
|
|
|
11628
12339
|
}
|
|
11629
12340
|
});
|
|
11630
12341
|
}
|
|
12342
|
+
var init_mock_editor = __esm({
|
|
12343
|
+
"src/commands/mock-editor.ts"() {
|
|
12344
|
+
"use strict";
|
|
12345
|
+
init_dist();
|
|
12346
|
+
init_playback_source();
|
|
12347
|
+
}
|
|
12348
|
+
});
|
|
11631
12349
|
|
|
11632
12350
|
// src/commands/proxy.ts
|
|
11633
12351
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11634
12352
|
import { dirname as dirname4 } from "path";
|
|
11635
|
-
var DIRECTION_LABEL = {
|
|
11636
|
-
"editor-to-agent": "\u2192 AGENT",
|
|
11637
|
-
"agent-to-editor": "\u2190 AGENT"
|
|
11638
|
-
};
|
|
11639
12353
|
function formatPretty(msg) {
|
|
11640
12354
|
const time = new Date(msg.timestamp).toISOString().slice(11, 23);
|
|
11641
12355
|
const dir = DIRECTION_LABEL[msg.direction];
|
|
@@ -11645,9 +12359,9 @@ function formatPretty(msg) {
|
|
|
11645
12359
|
return `${head}${id}${err}
|
|
11646
12360
|
${msg.raw}`;
|
|
11647
12361
|
}
|
|
11648
|
-
function registerProxyCommand(
|
|
12362
|
+
function registerProxyCommand(program) {
|
|
11649
12363
|
const knownAgentList = listAgents().map((a) => a.shortName).join(", ");
|
|
11650
|
-
|
|
12364
|
+
program.command("proxy").description("Run an ACP agent through a capturing proxy").passThroughOptions().argument("[agent]", 'agent executable (e.g. "goose"); optional when --agent is set').argument("[agent-args...]", "arguments forwarded to the agent").option(
|
|
11651
12365
|
"--agent <name>",
|
|
11652
12366
|
`preset for a known ACP agent (${knownAgentList})`
|
|
11653
12367
|
).option("--log <mode>", "message log format: json | pretty | none", "none").option("--cwd <dir>", "working directory for the agent").option(
|
|
@@ -11873,6 +12587,17 @@ function registerProxyCommand(program2) {
|
|
|
11873
12587
|
}
|
|
11874
12588
|
});
|
|
11875
12589
|
}
|
|
12590
|
+
var DIRECTION_LABEL;
|
|
12591
|
+
var init_proxy3 = __esm({
|
|
12592
|
+
"src/commands/proxy.ts"() {
|
|
12593
|
+
"use strict";
|
|
12594
|
+
init_dist();
|
|
12595
|
+
DIRECTION_LABEL = {
|
|
12596
|
+
"editor-to-agent": "\u2192 AGENT",
|
|
12597
|
+
"agent-to-editor": "\u2190 AGENT"
|
|
12598
|
+
};
|
|
12599
|
+
}
|
|
12600
|
+
});
|
|
11876
12601
|
|
|
11877
12602
|
// src/commands/replay.ts
|
|
11878
12603
|
import { readFileSync as readFileSync4 } from "fs";
|
|
@@ -11928,8 +12653,8 @@ function loadFromSqlite(dbPath, idArg) {
|
|
|
11928
12653
|
cleanup: () => db.close()
|
|
11929
12654
|
};
|
|
11930
12655
|
}
|
|
11931
|
-
function registerReplayCommand(
|
|
11932
|
-
|
|
12656
|
+
function registerReplayCommand(program) {
|
|
12657
|
+
program.command("replay").description(
|
|
11933
12658
|
"Serve a recorded session over WebSocket for replay in the UI. By default replays the latest session in the captures database; pass a session id, or --file to replay a JSON export."
|
|
11934
12659
|
).argument("[id]", "session id to replay (default: latest in the database)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--file <path>", "replay a JSON export file instead of a saved session").option("--ws-port <port>", "WebSocket port", "3737").option("--ws-host <host>", "WebSocket bind address", "127.0.0.1").action(async (idArg, opts) => {
|
|
11935
12660
|
const wsPort = Number(opts.wsPort);
|
|
@@ -11986,10 +12711,16 @@ function registerReplayCommand(program2) {
|
|
|
11986
12711
|
source.cleanup();
|
|
11987
12712
|
});
|
|
11988
12713
|
}
|
|
12714
|
+
var init_replay2 = __esm({
|
|
12715
|
+
"src/commands/replay.ts"() {
|
|
12716
|
+
"use strict";
|
|
12717
|
+
init_dist();
|
|
12718
|
+
}
|
|
12719
|
+
});
|
|
11989
12720
|
|
|
11990
12721
|
// src/commands/search.ts
|
|
11991
|
-
function registerSearchCommand(
|
|
11992
|
-
|
|
12722
|
+
function registerSearchCommand(program) {
|
|
12723
|
+
program.command("search").description(
|
|
11993
12724
|
"Find messages containing a substring across every saved session. Substring match is case-insensitive. Use `inspect` to read individual messages once you know where to look."
|
|
11994
12725
|
).argument("<query>", "substring to search for (case-insensitive)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--limit <n>", "maximum hits to print", "50").option("--session <id>", "restrict search to a single session").option("--in-method", "match only method names (default: match anywhere in the raw frame)").option("--in-payload", "match only inside the payload (skip method-only hits)").option("--json", "emit machine-readable JSON instead of a table").action((query, opts) => {
|
|
11995
12726
|
const limit = Number(opts.limit);
|
|
@@ -12061,7 +12792,6 @@ function registerSearchCommand(program2) {
|
|
|
12061
12792
|
process.stdout.write(renderHits(s, hits, needle));
|
|
12062
12793
|
});
|
|
12063
12794
|
}
|
|
12064
|
-
var SNIPPET_LEN = 80;
|
|
12065
12795
|
function makeSnippet(raw, needle) {
|
|
12066
12796
|
const idx = raw.toLowerCase().indexOf(needle);
|
|
12067
12797
|
if (idx < 0) return raw.slice(0, SNIPPET_LEN);
|
|
@@ -12071,12 +12801,6 @@ function makeSnippet(raw, needle) {
|
|
|
12071
12801
|
const suffix = end < raw.length ? "\u2026" : "";
|
|
12072
12802
|
return prefix + raw.slice(start, end).replace(/\s+/g, " ") + suffix;
|
|
12073
12803
|
}
|
|
12074
|
-
var METHOD_MAX2 = 32;
|
|
12075
|
-
var COLUMNS2 = [
|
|
12076
|
-
{ title: "LOC", align: "left" },
|
|
12077
|
-
{ title: "METHOD", align: "left" },
|
|
12078
|
-
{ title: "MATCH", align: "left" }
|
|
12079
|
-
];
|
|
12080
12804
|
function renderHits(s, hits, needle) {
|
|
12081
12805
|
const body = hits.map((h) => [
|
|
12082
12806
|
s.cyan(`#${h.sessionId}/${h.seq}`),
|
|
@@ -12105,10 +12829,26 @@ function truncate2(s, max) {
|
|
|
12105
12829
|
if (s.length <= max) return s;
|
|
12106
12830
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
12107
12831
|
}
|
|
12832
|
+
var SNIPPET_LEN, METHOD_MAX2, COLUMNS2;
|
|
12833
|
+
var init_search = __esm({
|
|
12834
|
+
"src/commands/search.ts"() {
|
|
12835
|
+
"use strict";
|
|
12836
|
+
init_dist();
|
|
12837
|
+
init_style();
|
|
12838
|
+
init_table();
|
|
12839
|
+
SNIPPET_LEN = 80;
|
|
12840
|
+
METHOD_MAX2 = 32;
|
|
12841
|
+
COLUMNS2 = [
|
|
12842
|
+
{ title: "LOC", align: "left" },
|
|
12843
|
+
{ title: "METHOD", align: "left" },
|
|
12844
|
+
{ title: "MATCH", align: "left" }
|
|
12845
|
+
];
|
|
12846
|
+
}
|
|
12847
|
+
});
|
|
12108
12848
|
|
|
12109
12849
|
// src/commands/session-info.ts
|
|
12110
|
-
function registerSessionInfoCommand(
|
|
12111
|
-
|
|
12850
|
+
function registerSessionInfoCommand(program) {
|
|
12851
|
+
program.command("session-info").description(
|
|
12112
12852
|
"Print derived client/agent metadata for a saved session \u2014 the terminal equivalent of the inspector's session info panel."
|
|
12113
12853
|
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--json", "machine-readable JSON instead of human-readable text").action((rawId, opts) => {
|
|
12114
12854
|
const id = Number(rawId);
|
|
@@ -12248,11 +12988,17 @@ function shortAgent(agentCommand) {
|
|
|
12248
12988
|
}
|
|
12249
12989
|
return parts[0] ?? null;
|
|
12250
12990
|
}
|
|
12991
|
+
var init_session_info = __esm({
|
|
12992
|
+
"src/commands/session-info.ts"() {
|
|
12993
|
+
"use strict";
|
|
12994
|
+
init_dist();
|
|
12995
|
+
init_style();
|
|
12996
|
+
}
|
|
12997
|
+
});
|
|
12251
12998
|
|
|
12252
12999
|
// src/commands/stats.ts
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
program2.command("stats").description(
|
|
13000
|
+
function registerStatsCommand(program) {
|
|
13001
|
+
program.command("stats").description(
|
|
12256
13002
|
"Aggregate counts and latency percentiles for one saved session \u2014 the equivalent of the inspector's footer StatsBar."
|
|
12257
13003
|
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--by-method", "add a per-method breakdown table (method \xB7 count \xB7 p50 \xB7 p99)").option("--json", "machine-readable JSON instead of human-readable text").action((rawId, opts) => {
|
|
12258
13004
|
const id = Number(rawId);
|
|
@@ -12360,13 +13106,13 @@ function computeStats(info, messages) {
|
|
|
12360
13106
|
p90: latencies.length ? percentile2(latencies, 90) : null,
|
|
12361
13107
|
p99: latencies.length ? percentile2(latencies, 99) : null,
|
|
12362
13108
|
max: latencies.length ? latencies[latencies.length - 1] : null,
|
|
12363
|
-
mean: latencies.length ? Math.round(
|
|
13109
|
+
mean: latencies.length ? Math.round(mean2(latencies)) : null
|
|
12364
13110
|
},
|
|
12365
13111
|
perMethod,
|
|
12366
13112
|
insights
|
|
12367
13113
|
};
|
|
12368
13114
|
}
|
|
12369
|
-
function
|
|
13115
|
+
function mean2(xs) {
|
|
12370
13116
|
return xs.reduce((acc, x) => acc + x, 0) / xs.length;
|
|
12371
13117
|
}
|
|
12372
13118
|
function formatLatency3(ms) {
|
|
@@ -12471,27 +13217,6 @@ function renderStats(s, stats, byMethod) {
|
|
|
12471
13217
|
}
|
|
12472
13218
|
return lines.join("");
|
|
12473
13219
|
}
|
|
12474
|
-
var INSIGHT_GLYPH = {
|
|
12475
|
-
hotspot: "!",
|
|
12476
|
-
"long-tail": "~",
|
|
12477
|
-
outlier: "*",
|
|
12478
|
-
busiest: "#",
|
|
12479
|
-
errors: "x"
|
|
12480
|
-
};
|
|
12481
|
-
var INSIGHT_LABEL = {
|
|
12482
|
-
hotspot: "HOTSPOT ",
|
|
12483
|
-
"long-tail": "LONG TAIL",
|
|
12484
|
-
outlier: "OUTLIER ",
|
|
12485
|
-
busiest: "BUSIEST ",
|
|
12486
|
-
errors: "ERRORS "
|
|
12487
|
-
};
|
|
12488
|
-
var INSIGHT_COLOR = {
|
|
12489
|
-
hotspot: "red",
|
|
12490
|
-
"long-tail": "yellow",
|
|
12491
|
-
outlier: "magenta",
|
|
12492
|
-
busiest: "cyan",
|
|
12493
|
-
errors: "red"
|
|
12494
|
-
};
|
|
12495
13220
|
function renderInsights(s, insights) {
|
|
12496
13221
|
const lines = [heading3(s, "INSIGHTS") + "\n"];
|
|
12497
13222
|
for (const i of insights) {
|
|
@@ -12504,7 +13229,6 @@ function renderInsights(s, insights) {
|
|
|
12504
13229
|
}
|
|
12505
13230
|
return lines.join("");
|
|
12506
13231
|
}
|
|
12507
|
-
var METHOD_MAX3 = 40;
|
|
12508
13232
|
function renderPerMethod(s, rows) {
|
|
12509
13233
|
const columns = [
|
|
12510
13234
|
{ title: "METHOD", align: "left" },
|
|
@@ -12529,33 +13253,46 @@ function renderPerMethod(s, rows) {
|
|
|
12529
13253
|
});
|
|
12530
13254
|
return renderTable(s, columns, body);
|
|
12531
13255
|
}
|
|
13256
|
+
var percentile2, INSIGHT_GLYPH, INSIGHT_LABEL, INSIGHT_COLOR, METHOD_MAX3;
|
|
13257
|
+
var init_stats = __esm({
|
|
13258
|
+
"src/commands/stats.ts"() {
|
|
13259
|
+
"use strict";
|
|
13260
|
+
init_dist();
|
|
13261
|
+
init_style();
|
|
13262
|
+
init_palette();
|
|
13263
|
+
init_table();
|
|
13264
|
+
percentile2 = percentile;
|
|
13265
|
+
INSIGHT_GLYPH = {
|
|
13266
|
+
hotspot: "!",
|
|
13267
|
+
"long-tail": "~",
|
|
13268
|
+
outlier: "*",
|
|
13269
|
+
busiest: "#",
|
|
13270
|
+
errors: "x"
|
|
13271
|
+
};
|
|
13272
|
+
INSIGHT_LABEL = {
|
|
13273
|
+
hotspot: "HOTSPOT ",
|
|
13274
|
+
"long-tail": "LONG TAIL",
|
|
13275
|
+
outlier: "OUTLIER ",
|
|
13276
|
+
busiest: "BUSIEST ",
|
|
13277
|
+
errors: "ERRORS "
|
|
13278
|
+
};
|
|
13279
|
+
INSIGHT_COLOR = {
|
|
13280
|
+
hotspot: "red",
|
|
13281
|
+
"long-tail": "yellow",
|
|
13282
|
+
outlier: "magenta",
|
|
13283
|
+
busiest: "cyan",
|
|
13284
|
+
errors: "red"
|
|
13285
|
+
};
|
|
13286
|
+
METHOD_MAX3 = 40;
|
|
13287
|
+
}
|
|
13288
|
+
});
|
|
12532
13289
|
|
|
12533
13290
|
// src/commands/ui.ts
|
|
12534
|
-
import { createReadStream, existsSync as existsSync7, statSync as
|
|
13291
|
+
import { createReadStream, existsSync as existsSync7, statSync as statSync3 } from "fs";
|
|
12535
13292
|
import { createServer } from "http";
|
|
12536
13293
|
import { dirname as dirname5, extname, join as join4, normalize, resolve, sep } from "path";
|
|
12537
13294
|
import { fileURLToPath } from "url";
|
|
12538
13295
|
import { spawn as spawn2 } from "child_process";
|
|
12539
|
-
var MIME_TYPES = {
|
|
12540
|
-
".html": "text/html; charset=utf-8",
|
|
12541
|
-
".js": "application/javascript; charset=utf-8",
|
|
12542
|
-
".mjs": "application/javascript; charset=utf-8",
|
|
12543
|
-
".css": "text/css; charset=utf-8",
|
|
12544
|
-
".json": "application/json; charset=utf-8",
|
|
12545
|
-
".map": "application/json; charset=utf-8",
|
|
12546
|
-
".svg": "image/svg+xml",
|
|
12547
|
-
".png": "image/png",
|
|
12548
|
-
".jpg": "image/jpeg",
|
|
12549
|
-
".jpeg": "image/jpeg",
|
|
12550
|
-
".gif": "image/gif",
|
|
12551
|
-
".webp": "image/webp",
|
|
12552
|
-
".ico": "image/x-icon",
|
|
12553
|
-
".woff": "font/woff",
|
|
12554
|
-
".woff2": "font/woff2",
|
|
12555
|
-
".ttf": "font/ttf",
|
|
12556
|
-
".otf": "font/otf",
|
|
12557
|
-
".txt": "text/plain; charset=utf-8"
|
|
12558
|
-
};
|
|
12559
13296
|
function uiDistDir() {
|
|
12560
13297
|
const here = dirname5(fileURLToPath(import.meta.url));
|
|
12561
13298
|
return resolve(here, "ui");
|
|
@@ -12578,7 +13315,7 @@ function serveStatic(req, res, rootDir) {
|
|
|
12578
13315
|
return;
|
|
12579
13316
|
}
|
|
12580
13317
|
let filePath = candidate;
|
|
12581
|
-
if (!existsSync7(filePath) || !
|
|
13318
|
+
if (!existsSync7(filePath) || !statSync3(filePath).isFile()) {
|
|
12582
13319
|
filePath = join4(rootDir, "index.html");
|
|
12583
13320
|
if (!existsSync7(filePath)) {
|
|
12584
13321
|
res.statusCode = 404;
|
|
@@ -12587,7 +13324,7 @@ function serveStatic(req, res, rootDir) {
|
|
|
12587
13324
|
return;
|
|
12588
13325
|
}
|
|
12589
13326
|
}
|
|
12590
|
-
const stat =
|
|
13327
|
+
const stat = statSync3(filePath);
|
|
12591
13328
|
res.statusCode = 200;
|
|
12592
13329
|
res.setHeader("content-type", contentTypeFor(filePath));
|
|
12593
13330
|
res.setHeader("content-length", stat.size);
|
|
@@ -12610,8 +13347,8 @@ function openBrowser(url) {
|
|
|
12610
13347
|
} catch {
|
|
12611
13348
|
}
|
|
12612
13349
|
}
|
|
12613
|
-
function registerUiCommand(
|
|
12614
|
-
|
|
13350
|
+
function registerUiCommand(program) {
|
|
13351
|
+
program.command("ui").description("Serve the acp-devtools UI from an embedded static bundle").option("--port <port>", "HTTP port to bind", "3737").option("--host <host>", "HTTP bind address", "127.0.0.1").option("--no-open", "don't auto-open the browser").option(
|
|
12615
13352
|
"--captures-db <file>",
|
|
12616
13353
|
"path to the captures SQLite database (default: ~/.acp-devtools/captures.db)"
|
|
12617
13354
|
).action(async (opts) => {
|
|
@@ -12674,10 +13411,37 @@ function registerUiCommand(program2) {
|
|
|
12674
13411
|
});
|
|
12675
13412
|
});
|
|
12676
13413
|
}
|
|
13414
|
+
var MIME_TYPES;
|
|
13415
|
+
var init_ui = __esm({
|
|
13416
|
+
"src/commands/ui.ts"() {
|
|
13417
|
+
"use strict";
|
|
13418
|
+
init_dist();
|
|
13419
|
+
MIME_TYPES = {
|
|
13420
|
+
".html": "text/html; charset=utf-8",
|
|
13421
|
+
".js": "application/javascript; charset=utf-8",
|
|
13422
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
13423
|
+
".css": "text/css; charset=utf-8",
|
|
13424
|
+
".json": "application/json; charset=utf-8",
|
|
13425
|
+
".map": "application/json; charset=utf-8",
|
|
13426
|
+
".svg": "image/svg+xml",
|
|
13427
|
+
".png": "image/png",
|
|
13428
|
+
".jpg": "image/jpeg",
|
|
13429
|
+
".jpeg": "image/jpeg",
|
|
13430
|
+
".gif": "image/gif",
|
|
13431
|
+
".webp": "image/webp",
|
|
13432
|
+
".ico": "image/x-icon",
|
|
13433
|
+
".woff": "font/woff",
|
|
13434
|
+
".woff2": "font/woff2",
|
|
13435
|
+
".ttf": "font/ttf",
|
|
13436
|
+
".otf": "font/otf",
|
|
13437
|
+
".txt": "text/plain; charset=utf-8"
|
|
13438
|
+
};
|
|
13439
|
+
}
|
|
13440
|
+
});
|
|
12677
13441
|
|
|
12678
13442
|
// src/commands/validate.ts
|
|
12679
|
-
function registerValidateCommand(
|
|
12680
|
-
|
|
13443
|
+
function registerValidateCommand(program) {
|
|
13444
|
+
program.command("validate").description(
|
|
12681
13445
|
"Check every frame in a session against the official ACP JSON schema (`@agentclientprotocol/sdk`). Use to confirm that an editor or agent you do not control speaks the protocol correctly \u2014 same data the inspector's \u26A0 SPEC badges surface."
|
|
12682
13446
|
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--limit <n>", "maximum violations to print", "200").option("--method <pattern>", "only check messages whose method contains this substring").option("--json", "machine-readable JSON").action((rawId, opts) => {
|
|
12683
13447
|
const id = Number(rawId);
|
|
@@ -12781,16 +13545,6 @@ function registerValidateCommand(program2) {
|
|
|
12781
13545
|
process.exit(1);
|
|
12782
13546
|
});
|
|
12783
13547
|
}
|
|
12784
|
-
var METHOD_MAX4 = 28;
|
|
12785
|
-
var SCHEMA_MAX = 28;
|
|
12786
|
-
var PATH_MAX = 38;
|
|
12787
|
-
var COLUMNS3 = [
|
|
12788
|
-
{ title: "SEQ", align: "right" },
|
|
12789
|
-
{ title: "METHOD", align: "left" },
|
|
12790
|
-
{ title: "SCHEMA", align: "left" },
|
|
12791
|
-
{ title: "PATH", align: "left" },
|
|
12792
|
-
{ title: "ERROR", align: "left" }
|
|
12793
|
-
];
|
|
12794
13548
|
function renderViolations(s, violations) {
|
|
12795
13549
|
const body = violations.map((v) => [
|
|
12796
13550
|
s.dim(`#${v.seq}`),
|
|
@@ -12805,6 +13559,26 @@ function truncate3(s, max) {
|
|
|
12805
13559
|
if (s.length <= max) return s;
|
|
12806
13560
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
12807
13561
|
}
|
|
13562
|
+
var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS3;
|
|
13563
|
+
var init_validate2 = __esm({
|
|
13564
|
+
"src/commands/validate.ts"() {
|
|
13565
|
+
"use strict";
|
|
13566
|
+
init_dist();
|
|
13567
|
+
init_inspect();
|
|
13568
|
+
init_style();
|
|
13569
|
+
init_table();
|
|
13570
|
+
METHOD_MAX4 = 28;
|
|
13571
|
+
SCHEMA_MAX = 28;
|
|
13572
|
+
PATH_MAX = 38;
|
|
13573
|
+
COLUMNS3 = [
|
|
13574
|
+
{ title: "SEQ", align: "right" },
|
|
13575
|
+
{ title: "METHOD", align: "left" },
|
|
13576
|
+
{ title: "SCHEMA", align: "left" },
|
|
13577
|
+
{ title: "PATH", align: "left" },
|
|
13578
|
+
{ title: "ERROR", align: "left" }
|
|
13579
|
+
];
|
|
13580
|
+
}
|
|
13581
|
+
});
|
|
12808
13582
|
|
|
12809
13583
|
// src/lib/help.ts
|
|
12810
13584
|
import { homedir as homedir4 } from "os";
|
|
@@ -12814,102 +13588,6 @@ function tidyHome(text, home = homedir4()) {
|
|
|
12814
13588
|
const collapsed = text.split(escapedHome).join("~").split(home).join("~");
|
|
12815
13589
|
return collapsed.replace(/~[^"\s)]*/g, (segment) => segment.replace(/\\+/g, "/"));
|
|
12816
13590
|
}
|
|
12817
|
-
var GROUPS = [
|
|
12818
|
-
{ title: "Capture", commands: ["proxy", "mock-agent", "mock-editor"] },
|
|
12819
|
-
{
|
|
12820
|
-
title: "Inspect",
|
|
12821
|
-
commands: ["list", "inspect", "search", "stats", "diff", "session-info", "validate"]
|
|
12822
|
-
},
|
|
12823
|
-
{ title: "View", commands: ["ui", "replay"] },
|
|
12824
|
-
{ title: "Manage", commands: ["export", "import", "delete", "backfill-metadata"] },
|
|
12825
|
-
{ title: "Setup", commands: ["doctor", "mcp"] }
|
|
12826
|
-
];
|
|
12827
|
-
var SUMMARIES = {
|
|
12828
|
-
proxy: "Capture an agent's traffic through a proxy",
|
|
12829
|
-
"mock-agent": "Replay a session as a fake ACP agent",
|
|
12830
|
-
"mock-editor": "Replay a session as a fake editor",
|
|
12831
|
-
list: "List saved sessions, newest first",
|
|
12832
|
-
inspect: "Print a session's frames to stdout",
|
|
12833
|
-
search: "Search frame payloads across sessions",
|
|
12834
|
-
stats: "Latency percentiles for one session",
|
|
12835
|
-
diff: "Compare two sessions frame by frame",
|
|
12836
|
-
"session-info": "Show a session's client/agent metadata",
|
|
12837
|
-
validate: "Check frames against the ACP spec",
|
|
12838
|
-
ui: "Open the web inspector",
|
|
12839
|
-
replay: "Stream a session to the UI over WebSocket",
|
|
12840
|
-
export: "Export a session to self-contained JSON",
|
|
12841
|
-
import: "Import a JSON export into the store",
|
|
12842
|
-
delete: "Delete saved sessions permanently",
|
|
12843
|
-
"backfill-metadata": "Recompute session metadata columns",
|
|
12844
|
-
doctor: "Diagnose setup and report issues",
|
|
12845
|
-
mcp: "Expose captures to Claude over MCP"
|
|
12846
|
-
};
|
|
12847
|
-
var ROOT_EXAMPLES = [
|
|
12848
|
-
"# Capture a Claude Code session (Zed adapter)",
|
|
12849
|
-
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
12850
|
-
"",
|
|
12851
|
-
"# Browse captured sessions in the web inspector",
|
|
12852
|
-
"acp-devtools ui",
|
|
12853
|
-
"",
|
|
12854
|
-
"# List sessions, then print one to the terminal",
|
|
12855
|
-
"acp-devtools list",
|
|
12856
|
-
"acp-devtools inspect 12",
|
|
12857
|
-
"",
|
|
12858
|
-
"# Diagnose your setup when nothing's being captured",
|
|
12859
|
-
"acp-devtools doctor"
|
|
12860
|
-
];
|
|
12861
|
-
var EXAMPLES = {
|
|
12862
|
-
proxy: [
|
|
12863
|
-
"# Wrap the Zed Claude Code adapter and capture every frame",
|
|
12864
|
-
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
12865
|
-
"",
|
|
12866
|
-
"# Capture headless, into a specific database",
|
|
12867
|
-
"acp-devtools proxy --no-ws --save-to /tmp/run.db node ./my-agent.js"
|
|
12868
|
-
],
|
|
12869
|
-
ui: [
|
|
12870
|
-
"# Serve the inspector on :3737 and open a browser",
|
|
12871
|
-
"acp-devtools ui",
|
|
12872
|
-
"",
|
|
12873
|
-
"# Pick a port and stay headless",
|
|
12874
|
-
"acp-devtools ui --port 4000 --no-open"
|
|
12875
|
-
],
|
|
12876
|
-
list: [
|
|
12877
|
-
"acp-devtools list",
|
|
12878
|
-
"",
|
|
12879
|
-
"# Machine-readable JSON",
|
|
12880
|
-
"acp-devtools list --json"
|
|
12881
|
-
],
|
|
12882
|
-
inspect: [
|
|
12883
|
-
"acp-devtools inspect 12",
|
|
12884
|
-
"",
|
|
12885
|
-
"# Only request/response frames for one method",
|
|
12886
|
-
"acp-devtools inspect 12 --method session/prompt --paired"
|
|
12887
|
-
],
|
|
12888
|
-
search: ["acp-devtools search session/prompt", "acp-devtools search rate_limit --in-payload"],
|
|
12889
|
-
stats: ["acp-devtools stats 12", "acp-devtools stats 12 --by-method"],
|
|
12890
|
-
diff: ["# Worked yesterday, broke today?", "acp-devtools diff 41 42"],
|
|
12891
|
-
export: [
|
|
12892
|
-
"acp-devtools export 12 -o session-12.json",
|
|
12893
|
-
"",
|
|
12894
|
-
"# Straight to stdout for sharing",
|
|
12895
|
-
"acp-devtools export 12 > session-12.json"
|
|
12896
|
-
],
|
|
12897
|
-
import: ["acp-devtools import session-12.json"],
|
|
12898
|
-
delete: ["acp-devtools delete 12", "acp-devtools delete 12 13 14"],
|
|
12899
|
-
replay: ["acp-devtools replay 12", "acp-devtools replay --file session-12.json"],
|
|
12900
|
-
validate: ["acp-devtools validate 12"],
|
|
12901
|
-
"session-info": ["acp-devtools session-info 12"],
|
|
12902
|
-
doctor: ["acp-devtools doctor"],
|
|
12903
|
-
mcp: ["# Read-only MCP server over stdio (wire into .claude/mcp_servers.json)", "acp-devtools mcp"],
|
|
12904
|
-
"backfill-metadata": [
|
|
12905
|
-
"acp-devtools backfill-metadata",
|
|
12906
|
-
"",
|
|
12907
|
-
"# Recompute a single session",
|
|
12908
|
-
"acp-devtools backfill-metadata 12"
|
|
12909
|
-
]
|
|
12910
|
-
};
|
|
12911
|
-
var INDENT = " ";
|
|
12912
|
-
var GAP = 3;
|
|
12913
13591
|
function heading4(s, title) {
|
|
12914
13592
|
return s.bold(s.yellow(title.toUpperCase()));
|
|
12915
13593
|
}
|
|
@@ -13037,8 +13715,8 @@ function formatCommandHelp(cmd, helper, s, width) {
|
|
|
13037
13715
|
}
|
|
13038
13716
|
return out.join("\n");
|
|
13039
13717
|
}
|
|
13040
|
-
function configureCliHelp(
|
|
13041
|
-
|
|
13718
|
+
function configureCliHelp(program) {
|
|
13719
|
+
program.configureHelp({
|
|
13042
13720
|
helpWidth: process.stdout.columns ?? 80,
|
|
13043
13721
|
formatHelp(cmd, helper) {
|
|
13044
13722
|
const s = createStyler(colorEnabled(process.stdout));
|
|
@@ -13047,37 +13725,252 @@ function configureCliHelp(program2) {
|
|
|
13047
13725
|
}
|
|
13048
13726
|
});
|
|
13049
13727
|
const err = createStyler(colorEnabled(process.stderr));
|
|
13050
|
-
|
|
13728
|
+
program.configureOutput({
|
|
13051
13729
|
outputError(str, write) {
|
|
13052
13730
|
write(err.red(str));
|
|
13053
13731
|
}
|
|
13054
13732
|
});
|
|
13055
13733
|
}
|
|
13734
|
+
var GROUPS, SUMMARIES, ROOT_EXAMPLES, EXAMPLES, INDENT, GAP;
|
|
13735
|
+
var init_help = __esm({
|
|
13736
|
+
"src/lib/help.ts"() {
|
|
13737
|
+
"use strict";
|
|
13738
|
+
init_style();
|
|
13739
|
+
GROUPS = [
|
|
13740
|
+
{ title: "Capture", commands: ["proxy", "mock-agent", "mock-editor"] },
|
|
13741
|
+
{
|
|
13742
|
+
title: "Inspect",
|
|
13743
|
+
commands: ["list", "inspect", "search", "stats", "diff", "session-info", "validate"]
|
|
13744
|
+
},
|
|
13745
|
+
{ title: "View", commands: ["ui", "replay"] },
|
|
13746
|
+
{ title: "Manage", commands: ["export", "import", "delete", "backfill-metadata"] },
|
|
13747
|
+
{ title: "Setup", commands: ["doctor", "mcp"] }
|
|
13748
|
+
];
|
|
13749
|
+
SUMMARIES = {
|
|
13750
|
+
proxy: "Capture an agent's traffic through a proxy",
|
|
13751
|
+
"mock-agent": "Replay a session as a fake ACP agent",
|
|
13752
|
+
"mock-editor": "Replay a session as a fake editor",
|
|
13753
|
+
list: "List saved sessions, newest first",
|
|
13754
|
+
inspect: "Print a session's frames to stdout",
|
|
13755
|
+
search: "Search frame payloads across sessions",
|
|
13756
|
+
stats: "Latency percentiles for one session",
|
|
13757
|
+
diff: "Compare two sessions frame by frame",
|
|
13758
|
+
"session-info": "Show a session's client/agent metadata",
|
|
13759
|
+
validate: "Check frames against the ACP spec",
|
|
13760
|
+
ui: "Open the web inspector",
|
|
13761
|
+
replay: "Stream a session to the UI over WebSocket",
|
|
13762
|
+
export: "Export a session to self-contained JSON",
|
|
13763
|
+
import: "Import a JSON export into the store",
|
|
13764
|
+
delete: "Delete saved sessions permanently",
|
|
13765
|
+
"backfill-metadata": "Recompute session metadata columns",
|
|
13766
|
+
doctor: "Diagnose setup and report issues",
|
|
13767
|
+
mcp: "Expose captures to Claude over MCP"
|
|
13768
|
+
};
|
|
13769
|
+
ROOT_EXAMPLES = [
|
|
13770
|
+
"# Capture a Claude Code session (Zed adapter)",
|
|
13771
|
+
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
13772
|
+
"",
|
|
13773
|
+
"# Browse captured sessions in the web inspector",
|
|
13774
|
+
"acp-devtools ui",
|
|
13775
|
+
"",
|
|
13776
|
+
"# List sessions, then print one to the terminal",
|
|
13777
|
+
"acp-devtools list",
|
|
13778
|
+
"acp-devtools inspect 12",
|
|
13779
|
+
"",
|
|
13780
|
+
"# Diagnose your setup when nothing's being captured",
|
|
13781
|
+
"acp-devtools doctor"
|
|
13782
|
+
];
|
|
13783
|
+
EXAMPLES = {
|
|
13784
|
+
proxy: [
|
|
13785
|
+
"# Wrap the Zed Claude Code adapter and capture every frame",
|
|
13786
|
+
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
13787
|
+
"",
|
|
13788
|
+
"# Capture headless, into a specific database",
|
|
13789
|
+
"acp-devtools proxy --no-ws --save-to /tmp/run.db node ./my-agent.js"
|
|
13790
|
+
],
|
|
13791
|
+
ui: [
|
|
13792
|
+
"# Serve the inspector on :3737 and open a browser",
|
|
13793
|
+
"acp-devtools ui",
|
|
13794
|
+
"",
|
|
13795
|
+
"# Pick a port and stay headless",
|
|
13796
|
+
"acp-devtools ui --port 4000 --no-open"
|
|
13797
|
+
],
|
|
13798
|
+
list: [
|
|
13799
|
+
"acp-devtools list",
|
|
13800
|
+
"",
|
|
13801
|
+
"# Machine-readable JSON",
|
|
13802
|
+
"acp-devtools list --json"
|
|
13803
|
+
],
|
|
13804
|
+
inspect: [
|
|
13805
|
+
"acp-devtools inspect 12",
|
|
13806
|
+
"",
|
|
13807
|
+
"# Only request/response frames for one method",
|
|
13808
|
+
"acp-devtools inspect 12 --method session/prompt --paired"
|
|
13809
|
+
],
|
|
13810
|
+
search: ["acp-devtools search session/prompt", "acp-devtools search rate_limit --in-payload"],
|
|
13811
|
+
stats: ["acp-devtools stats 12", "acp-devtools stats 12 --by-method"],
|
|
13812
|
+
diff: ["# Worked yesterday, broke today?", "acp-devtools diff 41 42"],
|
|
13813
|
+
export: [
|
|
13814
|
+
"acp-devtools export 12 -o session-12.json",
|
|
13815
|
+
"",
|
|
13816
|
+
"# Straight to stdout for sharing",
|
|
13817
|
+
"acp-devtools export 12 > session-12.json"
|
|
13818
|
+
],
|
|
13819
|
+
import: ["acp-devtools import session-12.json"],
|
|
13820
|
+
delete: ["acp-devtools delete 12", "acp-devtools delete 12 13 14"],
|
|
13821
|
+
replay: ["acp-devtools replay 12", "acp-devtools replay --file session-12.json"],
|
|
13822
|
+
validate: ["acp-devtools validate 12"],
|
|
13823
|
+
"session-info": ["acp-devtools session-info 12"],
|
|
13824
|
+
doctor: ["acp-devtools doctor"],
|
|
13825
|
+
mcp: ["# Read-only MCP server over stdio (wire into .claude/mcp_servers.json)", "acp-devtools mcp"],
|
|
13826
|
+
"backfill-metadata": [
|
|
13827
|
+
"acp-devtools backfill-metadata",
|
|
13828
|
+
"",
|
|
13829
|
+
"# Recompute a single session",
|
|
13830
|
+
"acp-devtools backfill-metadata 12"
|
|
13831
|
+
]
|
|
13832
|
+
};
|
|
13833
|
+
INDENT = " ";
|
|
13834
|
+
GAP = 3;
|
|
13835
|
+
}
|
|
13836
|
+
});
|
|
13056
13837
|
|
|
13057
13838
|
// src/program.ts
|
|
13839
|
+
var program_exports = {};
|
|
13840
|
+
__export(program_exports, {
|
|
13841
|
+
buildProgram: () => buildProgram
|
|
13842
|
+
});
|
|
13843
|
+
import { Command } from "commander";
|
|
13058
13844
|
function buildProgram() {
|
|
13059
|
-
const
|
|
13060
|
-
|
|
13061
|
-
configureCliHelp(
|
|
13062
|
-
registerProxyCommand(
|
|
13063
|
-
registerReplayCommand(
|
|
13064
|
-
registerUiCommand(
|
|
13065
|
-
registerDoctorCommand(
|
|
13066
|
-
registerExportCommand(
|
|
13067
|
-
registerImportCommand(
|
|
13068
|
-
registerDeleteCommand(
|
|
13069
|
-
registerDiffCommand(
|
|
13070
|
-
registerListCommand(
|
|
13071
|
-
registerInspectCommand(
|
|
13072
|
-
registerSearchCommand(
|
|
13073
|
-
registerSessionInfoCommand(
|
|
13074
|
-
registerStatsCommand(
|
|
13075
|
-
registerBackfillMetadataCommand(
|
|
13076
|
-
registerMcpCommand(
|
|
13077
|
-
registerMockAgentCommand(
|
|
13078
|
-
registerMockEditorCommand(
|
|
13079
|
-
registerValidateCommand(
|
|
13080
|
-
return
|
|
13845
|
+
const program = new Command();
|
|
13846
|
+
program.name("acp-devtools").description("Visual debugger / inspector for the Agent Client Protocol (ACP)").version(CLI_VERSION).enablePositionalOptions();
|
|
13847
|
+
configureCliHelp(program);
|
|
13848
|
+
registerProxyCommand(program);
|
|
13849
|
+
registerReplayCommand(program);
|
|
13850
|
+
registerUiCommand(program);
|
|
13851
|
+
registerDoctorCommand(program);
|
|
13852
|
+
registerExportCommand(program);
|
|
13853
|
+
registerImportCommand(program);
|
|
13854
|
+
registerDeleteCommand(program);
|
|
13855
|
+
registerDiffCommand(program);
|
|
13856
|
+
registerListCommand(program);
|
|
13857
|
+
registerInspectCommand(program);
|
|
13858
|
+
registerSearchCommand(program);
|
|
13859
|
+
registerSessionInfoCommand(program);
|
|
13860
|
+
registerStatsCommand(program);
|
|
13861
|
+
registerBackfillMetadataCommand(program);
|
|
13862
|
+
registerMcpCommand(program);
|
|
13863
|
+
registerMockAgentCommand(program);
|
|
13864
|
+
registerMockEditorCommand(program);
|
|
13865
|
+
registerValidateCommand(program);
|
|
13866
|
+
return program;
|
|
13867
|
+
}
|
|
13868
|
+
var init_program = __esm({
|
|
13869
|
+
"src/program.ts"() {
|
|
13870
|
+
"use strict";
|
|
13871
|
+
init_backfill_metadata();
|
|
13872
|
+
init_delete();
|
|
13873
|
+
init_diff();
|
|
13874
|
+
init_doctor();
|
|
13875
|
+
init_export2();
|
|
13876
|
+
init_import();
|
|
13877
|
+
init_inspect();
|
|
13878
|
+
init_list();
|
|
13879
|
+
init_mcp();
|
|
13880
|
+
init_mock_agent();
|
|
13881
|
+
init_mock_editor();
|
|
13882
|
+
init_proxy3();
|
|
13883
|
+
init_replay2();
|
|
13884
|
+
init_search();
|
|
13885
|
+
init_session_info();
|
|
13886
|
+
init_stats();
|
|
13887
|
+
init_ui();
|
|
13888
|
+
init_validate2();
|
|
13889
|
+
init_help();
|
|
13890
|
+
init_version();
|
|
13891
|
+
}
|
|
13892
|
+
});
|
|
13893
|
+
|
|
13894
|
+
// src/lib/native-error.ts
|
|
13895
|
+
var BINDING_PATTERNS = [
|
|
13896
|
+
/Could not locate the bindings file/i,
|
|
13897
|
+
/better[_-]sqlite3/i,
|
|
13898
|
+
/node[_-]gyp/i,
|
|
13899
|
+
/prebuild[-_]install/i,
|
|
13900
|
+
/\.node['"]?\)?:?\s/i,
|
|
13901
|
+
/Cannot find module .*\.node/i,
|
|
13902
|
+
/NODE_MODULE_VERSION/i
|
|
13903
|
+
];
|
|
13904
|
+
function isNativeBindingError(err) {
|
|
13905
|
+
if (!err) return false;
|
|
13906
|
+
const msg = err instanceof Error ? `${err.message}
|
|
13907
|
+
${err.stack ?? ""}` : String(err);
|
|
13908
|
+
return BINDING_PATTERNS.some((re) => re.test(msg));
|
|
13909
|
+
}
|
|
13910
|
+
function detectInstallSource(ctx) {
|
|
13911
|
+
const p = (ctx.binaryPath ?? "").toLowerCase();
|
|
13912
|
+
if (!p) return "unknown";
|
|
13913
|
+
if (p.includes("/cellar/acp-devtools/") || p.includes("/homebrew/")) {
|
|
13914
|
+
return "homebrew";
|
|
13915
|
+
}
|
|
13916
|
+
if (p.includes("linuxbrew")) return "homebrew";
|
|
13917
|
+
if (p.includes("/lib/node_modules/acp-devtools") || p.includes("\\node_modules\\acp-devtools") || p.includes("/npm/node_modules/acp-devtools") || p.includes("\\npm\\acp-devtools")) {
|
|
13918
|
+
return "npm-global";
|
|
13919
|
+
}
|
|
13920
|
+
return "unknown";
|
|
13921
|
+
}
|
|
13922
|
+
function formatNativeBindingMessage(err, ctx) {
|
|
13923
|
+
const source = detectInstallSource(ctx);
|
|
13924
|
+
const detail = err instanceof Error ? err.message : String(err ?? "unknown error");
|
|
13925
|
+
const lines = [
|
|
13926
|
+
"acp-devtools: native module `better-sqlite3` failed to load.",
|
|
13927
|
+
"",
|
|
13928
|
+
"The SQLite binding (`build/Release/better_sqlite3.node`) is missing or",
|
|
13929
|
+
"incompatible with this Node version. acp-devtools needs it to read and",
|
|
13930
|
+
"write the captures database \u2014 every command depends on it.",
|
|
13931
|
+
""
|
|
13932
|
+
];
|
|
13933
|
+
if (source === "homebrew") {
|
|
13934
|
+
lines.push(
|
|
13935
|
+
"You installed via Homebrew. Rebuild the binding in place:",
|
|
13936
|
+
"",
|
|
13937
|
+
' PREFIX="$(brew --prefix acp-devtools)/libexec/lib/node_modules/acp-devtools"',
|
|
13938
|
+
' npm rebuild --prefix "$PREFIX" better-sqlite3',
|
|
13939
|
+
"",
|
|
13940
|
+
"Or reinstall once the formula fix lands:",
|
|
13941
|
+
"",
|
|
13942
|
+
" brew update && brew reinstall acp-devtools",
|
|
13943
|
+
""
|
|
13944
|
+
);
|
|
13945
|
+
} else if (source === "npm-global") {
|
|
13946
|
+
lines.push(
|
|
13947
|
+
"You installed via `npm i -g`. Rebuild the binding:",
|
|
13948
|
+
"",
|
|
13949
|
+
" npm rebuild -g better-sqlite3",
|
|
13950
|
+
"",
|
|
13951
|
+
"If that fails the prebuild lookup probably has no match for your Node",
|
|
13952
|
+
"version, and source build needs Python 3 + a C++ toolchain (Xcode CLT",
|
|
13953
|
+
"on macOS, build-essential on Linux, Visual Studio Build Tools on",
|
|
13954
|
+
"Windows). Install those and retry, or pin to an older Node major.",
|
|
13955
|
+
""
|
|
13956
|
+
);
|
|
13957
|
+
} else {
|
|
13958
|
+
lines.push(
|
|
13959
|
+
"Try rebuilding the native dependency:",
|
|
13960
|
+
"",
|
|
13961
|
+
" npm rebuild better-sqlite3",
|
|
13962
|
+
"",
|
|
13963
|
+
"If you installed globally via npm, add `-g`. If via Homebrew, run",
|
|
13964
|
+
"`brew reinstall acp-devtools` once the formula is patched.",
|
|
13965
|
+
""
|
|
13966
|
+
);
|
|
13967
|
+
}
|
|
13968
|
+
lines.push(
|
|
13969
|
+
`Underlying error: ${detail}`,
|
|
13970
|
+
`Node: ${process.version} \xB7 platform: ${process.platform}/${process.arch}`,
|
|
13971
|
+
"Report unresolved cases at https://github.com/maksugr/acp-devtools/issues"
|
|
13972
|
+
);
|
|
13973
|
+
return lines.join("\n") + "\n";
|
|
13081
13974
|
}
|
|
13082
13975
|
|
|
13083
13976
|
// src/index.ts
|
|
@@ -13106,10 +13999,10 @@ var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
13106
13999
|
"-V",
|
|
13107
14000
|
"--version"
|
|
13108
14001
|
]);
|
|
13109
|
-
function expandArgv(rawArgs) {
|
|
14002
|
+
function expandArgv(rawArgs, defaultAgent, isAgentShortcut2) {
|
|
13110
14003
|
if (rawArgs.length === 0) {
|
|
13111
14004
|
if (!process.stdin.isTTY) {
|
|
13112
|
-
return ["proxy", "--agent",
|
|
14005
|
+
return ["proxy", "--agent", defaultAgent];
|
|
13113
14006
|
}
|
|
13114
14007
|
return rawArgs;
|
|
13115
14008
|
}
|
|
@@ -13117,16 +14010,50 @@ function expandArgv(rawArgs) {
|
|
|
13117
14010
|
if (KNOWN_SUBCOMMANDS.has(first)) {
|
|
13118
14011
|
return rawArgs;
|
|
13119
14012
|
}
|
|
13120
|
-
if (
|
|
14013
|
+
if (isAgentShortcut2(first)) {
|
|
13121
14014
|
return ["proxy", "--agent", first, ...rawArgs.slice(1)];
|
|
13122
14015
|
}
|
|
13123
14016
|
return ["proxy", ...rawArgs];
|
|
13124
14017
|
}
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
14018
|
+
function reportFatal(err, prefix = "acp-devtools") {
|
|
14019
|
+
if (isNativeBindingError(err)) {
|
|
14020
|
+
process.stderr.write(
|
|
14021
|
+
formatNativeBindingMessage(err, {
|
|
14022
|
+
binaryPath: process.argv[1] ?? ""
|
|
14023
|
+
})
|
|
14024
|
+
);
|
|
14025
|
+
process.exit(1);
|
|
14026
|
+
}
|
|
14027
|
+
process.stderr.write(
|
|
14028
|
+
`${prefix}: ${err instanceof Error ? err.message : String(err)}
|
|
14029
|
+
`
|
|
14030
|
+
);
|
|
13130
14031
|
process.exit(1);
|
|
13131
|
-
}
|
|
14032
|
+
}
|
|
14033
|
+
async function main() {
|
|
14034
|
+
let core;
|
|
14035
|
+
let buildProgram2;
|
|
14036
|
+
try {
|
|
14037
|
+
core = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
14038
|
+
({ buildProgram: buildProgram2 } = await Promise.resolve().then(() => (init_program(), program_exports)));
|
|
14039
|
+
} catch (err) {
|
|
14040
|
+
reportFatal(err, "acp-devtools: failed to start");
|
|
14041
|
+
}
|
|
14042
|
+
const expanded = expandArgv(
|
|
14043
|
+
process.argv.slice(2),
|
|
14044
|
+
core.DEFAULT_AGENT,
|
|
14045
|
+
core.isAgentShortcut
|
|
14046
|
+
);
|
|
14047
|
+
const program = buildProgram2();
|
|
14048
|
+
try {
|
|
14049
|
+
await program.parseAsync([
|
|
14050
|
+
process.argv[0],
|
|
14051
|
+
process.argv[1],
|
|
14052
|
+
...expanded
|
|
14053
|
+
]);
|
|
14054
|
+
} catch (err) {
|
|
14055
|
+
reportFatal(err);
|
|
14056
|
+
}
|
|
14057
|
+
}
|
|
14058
|
+
main();
|
|
13132
14059
|
//# sourceMappingURL=index.js.map
|