acp-devtools 0.2.0 → 0.3.0
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 +1942 -1051
- package/dist/ui/CNAME +1 -0
- package/dist/ui/assets/{index-C3oZwSx5.js → index-B3QArtIO.js} +17 -17
- package/dist/ui/index.html +1 -1
- package/package.json +2 -2
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,47 +8631,103 @@ 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
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);
|
|
@@ -8575,13 +8784,55 @@ function openExistingDatabase(path) {
|
|
|
8575
8784
|
}
|
|
8576
8785
|
return openDatabase(path);
|
|
8577
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
|
+
});
|
|
8578
8834
|
|
|
8579
8835
|
// ../core/dist/storage/session.js
|
|
8580
|
-
var SESSION_SELECT = `SELECT id, name, agent_command, started_at, ended_at,
|
|
8581
|
-
client_name, imported_at, client_version, client_platform,
|
|
8582
|
-
agent_name, agent_version, protocol_version, current_mode, current_model,
|
|
8583
|
-
agent_capabilities_json
|
|
8584
|
-
FROM sessions`;
|
|
8585
8836
|
function rowToInfo(row2) {
|
|
8586
8837
|
return {
|
|
8587
8838
|
id: row2.id,
|
|
@@ -8622,84 +8873,94 @@ function parseRpcId(stored) {
|
|
|
8622
8873
|
const asNum = Number(stored);
|
|
8623
8874
|
return Number.isFinite(asNum) && String(asNum) === stored ? asNum : stored;
|
|
8624
8875
|
}
|
|
8625
|
-
var Session
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
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 (
|
|
8635
8896
|
session_id, seq, timestamp, direction, kind,
|
|
8636
8897
|
method, rpc_id, raw, payload_json, parse_error
|
|
8637
8898
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
8638
|
-
|
|
8639
|
-
|
|
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
|
|
8640
8901
|
FROM messages WHERE session_id = ? ORDER BY seq ASC`);
|
|
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
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
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
|
|
8703
8964
|
client_version = ?,
|
|
8704
8965
|
client_platform = ?,
|
|
8705
8966
|
agent_name = ?,
|
|
@@ -8709,34 +8970,35 @@ var Session = class _Session {
|
|
|
8709
8970
|
current_model = ?,
|
|
8710
8971
|
agent_capabilities_json = ?
|
|
8711
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);
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
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
|
+
}
|
|
8722
8997
|
};
|
|
8723
8998
|
}
|
|
8724
|
-
|
|
8725
|
-
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);
|
|
8726
|
-
}
|
|
8727
|
-
close(endedAt = Date.now()) {
|
|
8728
|
-
this.endStmt.run(endedAt, this.info.id);
|
|
8729
|
-
this.info = { ...this.info, endedAt };
|
|
8730
|
-
}
|
|
8731
|
-
*messages() {
|
|
8732
|
-
for (const row2 of this.listStmt.iterate(this.info.id)) {
|
|
8733
|
-
yield rowToMessage(row2);
|
|
8734
|
-
}
|
|
8735
|
-
}
|
|
8736
|
-
};
|
|
8999
|
+
});
|
|
8737
9000
|
|
|
8738
9001
|
// ../core/dist/storage/export.js
|
|
8739
|
-
var EXPORT_VERSION = 1;
|
|
8740
9002
|
function exportSession(session, options) {
|
|
8741
9003
|
return exportSessionFromParts(session.info, session.messages(), options);
|
|
8742
9004
|
}
|
|
@@ -8759,12 +9021,6 @@ function exportSessionFromParts(info, messages, options) {
|
|
|
8759
9021
|
function serializeExport(exp, indent = 4) {
|
|
8760
9022
|
return JSON.stringify(exp, null, indent) + "\n";
|
|
8761
9023
|
}
|
|
8762
|
-
var ExportParseError = class extends Error {
|
|
8763
|
-
constructor(message) {
|
|
8764
|
-
super(message);
|
|
8765
|
-
this.name = "ExportParseError";
|
|
8766
|
-
}
|
|
8767
|
-
};
|
|
8768
9024
|
function parseExport(input) {
|
|
8769
9025
|
let raw;
|
|
8770
9026
|
try {
|
|
@@ -8866,22 +9122,21 @@ function parseMessage(raw, idx) {
|
|
|
8866
9122
|
function isObject(value) {
|
|
8867
9123
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8868
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
|
+
});
|
|
8869
9138
|
|
|
8870
9139
|
// ../core/dist/storage/redact.js
|
|
8871
|
-
var REDACTED_PLACEHOLDER = "<REDACTED>";
|
|
8872
|
-
var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
8873
|
-
"authorization",
|
|
8874
|
-
"proxy-authorization",
|
|
8875
|
-
"proxy-authentication",
|
|
8876
|
-
"x-api-key",
|
|
8877
|
-
"x-api-token",
|
|
8878
|
-
"x-auth-token",
|
|
8879
|
-
"api-key",
|
|
8880
|
-
"api_key",
|
|
8881
|
-
"proxy_key",
|
|
8882
|
-
"cookie",
|
|
8883
|
-
"set-cookie"
|
|
8884
|
-
]);
|
|
8885
9140
|
function redactMessage(message) {
|
|
8886
9141
|
if (message.payload == null) {
|
|
8887
9142
|
return { redacted: message, count: 0 };
|
|
@@ -8968,110 +9223,171 @@ function redactSessionExport(exp) {
|
|
|
8968
9223
|
fieldsRedacted
|
|
8969
9224
|
};
|
|
8970
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
|
+
});
|
|
8971
9266
|
|
|
8972
9267
|
// ../core/dist/ws/server.js
|
|
8973
9268
|
import { WebSocketServer } from "ws";
|
|
8974
|
-
var WsBroadcaster
|
|
8975
|
-
|
|
8976
|
-
server
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
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
|
+
};
|
|
9001
9368
|
}
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
new Promise((resolve2) => wss.close(() => resolve2())),
|
|
9011
|
-
new Promise((resolve2) => setTimeout(resolve2, 500))
|
|
9012
|
-
]);
|
|
9013
|
-
for (const client of wss.clients)
|
|
9014
|
-
client.terminate();
|
|
9015
|
-
}
|
|
9016
|
-
publishSessionStart(session) {
|
|
9017
|
-
this.session = session;
|
|
9018
|
-
this.messages = [];
|
|
9019
|
-
this.sessionEnded = false;
|
|
9020
|
-
this.broadcast({ type: "session.start", session });
|
|
9021
|
-
}
|
|
9022
|
-
publishMessage(message) {
|
|
9023
|
-
if (!this.session) {
|
|
9024
|
-
throw new Error("publishMessage called before publishSessionStart");
|
|
9025
|
-
}
|
|
9026
|
-
this.messages.push(message);
|
|
9027
|
-
this.broadcast({ type: "message", message });
|
|
9028
|
-
}
|
|
9029
|
-
publishSessionEnd(session) {
|
|
9030
|
-
this.session = session;
|
|
9031
|
-
this.sessionEnded = true;
|
|
9032
|
-
this.broadcast({ type: "session.end", session });
|
|
9033
|
-
}
|
|
9034
|
-
/**
|
|
9035
|
-
* Pre-load a finished session for the replay command. Must be called before
|
|
9036
|
-
* clients connect; live `publish*` calls afterwards are not supported.
|
|
9037
|
-
*/
|
|
9038
|
-
loadHistory(session, messages) {
|
|
9039
|
-
this.session = session;
|
|
9040
|
-
this.messages = [...messages];
|
|
9041
|
-
this.sessionEnded = session.endedAt !== null;
|
|
9042
|
-
}
|
|
9043
|
-
onConnection(ws) {
|
|
9044
|
-
if (this.session) {
|
|
9045
|
-
this.send(ws, { type: "session.start", session: this.session });
|
|
9046
|
-
for (const m of this.messages) {
|
|
9047
|
-
this.send(ws, { type: "message", message: m });
|
|
9048
|
-
}
|
|
9049
|
-
this.send(ws, { type: "replay.done" });
|
|
9050
|
-
if (this.sessionEnded) {
|
|
9051
|
-
this.send(ws, { type: "session.end", session: this.session });
|
|
9052
|
-
}
|
|
9053
|
-
}
|
|
9054
|
-
}
|
|
9055
|
-
broadcast(event) {
|
|
9056
|
-
const wss = this.server;
|
|
9057
|
-
if (!wss)
|
|
9058
|
-
return;
|
|
9059
|
-
const data = JSON.stringify(event);
|
|
9060
|
-
for (const client of wss.clients) {
|
|
9061
|
-
if (client.readyState === 1)
|
|
9062
|
-
client.send(data);
|
|
9063
|
-
}
|
|
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();
|
|
9064
9377
|
}
|
|
9065
|
-
|
|
9066
|
-
|
|
9378
|
+
});
|
|
9379
|
+
|
|
9380
|
+
// ../core/dist/discovery/types.js
|
|
9381
|
+
var init_types2 = __esm({
|
|
9382
|
+
"../core/dist/discovery/types.js"() {
|
|
9383
|
+
"use strict";
|
|
9067
9384
|
}
|
|
9068
|
-
};
|
|
9385
|
+
});
|
|
9069
9386
|
|
|
9070
9387
|
// ../core/dist/discovery/registry.js
|
|
9071
9388
|
import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
9072
9389
|
import { homedir } from "os";
|
|
9073
9390
|
import { join } from "path";
|
|
9074
|
-
var DEFAULT_BASE = join(homedir(), ".acp-devtools");
|
|
9075
9391
|
function discoveryBaseDir() {
|
|
9076
9392
|
return process.env.ACP_DEVTOOLS_HOME ?? DEFAULT_BASE;
|
|
9077
9393
|
}
|
|
@@ -9144,6 +9460,22 @@ function listActive(options = {}) {
|
|
|
9144
9460
|
out.sort((a, b) => b.startedAt - a.startedAt);
|
|
9145
9461
|
return out;
|
|
9146
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
|
+
});
|
|
9147
9479
|
|
|
9148
9480
|
// ../core/dist/server/paths.js
|
|
9149
9481
|
import { homedir as homedir2 } from "os";
|
|
@@ -9154,13 +9486,31 @@ function acpHomeDir() {
|
|
|
9154
9486
|
function defaultCapturesDbPath() {
|
|
9155
9487
|
return join2(acpHomeDir(), "captures.db");
|
|
9156
9488
|
}
|
|
9489
|
+
var init_paths = __esm({
|
|
9490
|
+
"../core/dist/server/paths.js"() {
|
|
9491
|
+
"use strict";
|
|
9492
|
+
}
|
|
9493
|
+
});
|
|
9157
9494
|
|
|
9158
9495
|
// ../core/dist/server/queries.js
|
|
9159
9496
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
9160
9497
|
import { dirname } from "path";
|
|
9161
|
-
function listSessionsSummary(dbPath, limit = 200) {
|
|
9498
|
+
function listSessionsSummary(dbPath, limit = 200, filters = {}) {
|
|
9162
9499
|
if (!existsSync2(dbPath))
|
|
9163
9500
|
return [];
|
|
9501
|
+
const where = [];
|
|
9502
|
+
const params = [];
|
|
9503
|
+
if (filters.imported === true)
|
|
9504
|
+
where.push("s.imported_at IS NOT NULL");
|
|
9505
|
+
if (filters.imported === false)
|
|
9506
|
+
where.push("s.imported_at IS NULL");
|
|
9507
|
+
if (filters.client) {
|
|
9508
|
+
where.push(`(instr(lower(coalesce(s.client_name, '')), ?)
|
|
9509
|
+
+ instr(lower(coalesce(s.client_version, '')), ?)
|
|
9510
|
+
+ instr(lower(coalesce(s.client_platform, '')), ?)) > 0`);
|
|
9511
|
+
const needle = filters.client.toLowerCase();
|
|
9512
|
+
params.push(needle, needle, needle);
|
|
9513
|
+
}
|
|
9164
9514
|
const db = openDatabase(dbPath);
|
|
9165
9515
|
try {
|
|
9166
9516
|
return db.prepare(
|
|
@@ -9177,22 +9527,16 @@ function listSessionsSummary(dbPath, limit = 200) {
|
|
|
9177
9527
|
s.current_mode, s.current_model,
|
|
9178
9528
|
(SELECT COUNT(*) FROM messages WHERE session_id = s.id) AS message_count
|
|
9179
9529
|
FROM sessions s
|
|
9530
|
+
${where.length > 0 ? `WHERE ${where.join(" AND ")}` : ""}
|
|
9180
9531
|
ORDER BY COALESCE(s.imported_at, s.started_at) DESC
|
|
9181
9532
|
LIMIT ?`
|
|
9182
|
-
).all(limit);
|
|
9533
|
+
).all(...params, limit);
|
|
9183
9534
|
} finally {
|
|
9184
9535
|
db.close();
|
|
9185
9536
|
}
|
|
9186
9537
|
}
|
|
9187
|
-
var FIND_BY_CLIENT_POOL_SIZE = 500;
|
|
9188
9538
|
function findSessionsByClient(dbPath, needle, limit = 200) {
|
|
9189
|
-
|
|
9190
|
-
const lower = needle.toLowerCase();
|
|
9191
|
-
const matches = pool.filter((s) => {
|
|
9192
|
-
const candidates = [s.client_name, s.client_version, s.client_platform].filter((v) => typeof v === "string");
|
|
9193
|
-
return candidates.some((c) => c.toLowerCase().includes(lower));
|
|
9194
|
-
});
|
|
9195
|
-
return matches.slice(0, limit);
|
|
9539
|
+
return listSessionsSummary(dbPath, limit, { client: needle });
|
|
9196
9540
|
}
|
|
9197
9541
|
function insertImportedSession(dbPath, exp, options = {}) {
|
|
9198
9542
|
mkdirSync2(dirname(dbPath), { recursive: true });
|
|
@@ -9232,17 +9576,16 @@ function deleteSession(dbPath, sessionId) {
|
|
|
9232
9576
|
db.close();
|
|
9233
9577
|
}
|
|
9234
9578
|
}
|
|
9579
|
+
var init_queries = __esm({
|
|
9580
|
+
"../core/dist/server/queries.js"() {
|
|
9581
|
+
"use strict";
|
|
9582
|
+
init_session();
|
|
9583
|
+
init_sqlite();
|
|
9584
|
+
}
|
|
9585
|
+
});
|
|
9235
9586
|
|
|
9236
9587
|
// ../core/dist/server/replay.js
|
|
9237
9588
|
import { existsSync as existsSync3 } from "fs";
|
|
9238
|
-
var DEFAULT_LOGGER = {
|
|
9239
|
-
log: (message) => {
|
|
9240
|
-
console.log(message);
|
|
9241
|
-
},
|
|
9242
|
-
warn: (message, err) => {
|
|
9243
|
-
console.warn(message, err);
|
|
9244
|
-
}
|
|
9245
|
-
};
|
|
9246
9589
|
function streamReplay(ws, sessionId, dbPath, logger = DEFAULT_LOGGER) {
|
|
9247
9590
|
const tag = `[replay #${sessionId}]`;
|
|
9248
9591
|
if (!existsSync3(dbPath)) {
|
|
@@ -9315,14 +9658,56 @@ function safeClose(ws) {
|
|
|
9315
9658
|
} catch {
|
|
9316
9659
|
}
|
|
9317
9660
|
}
|
|
9661
|
+
var DEFAULT_LOGGER;
|
|
9662
|
+
var init_replay = __esm({
|
|
9663
|
+
"../core/dist/server/replay.js"() {
|
|
9664
|
+
"use strict";
|
|
9665
|
+
init_session();
|
|
9666
|
+
init_sqlite();
|
|
9667
|
+
DEFAULT_LOGGER = {
|
|
9668
|
+
log: (message) => {
|
|
9669
|
+
console.log(message);
|
|
9670
|
+
},
|
|
9671
|
+
warn: (message, err) => {
|
|
9672
|
+
console.warn(message, err);
|
|
9673
|
+
}
|
|
9674
|
+
};
|
|
9675
|
+
}
|
|
9676
|
+
});
|
|
9318
9677
|
|
|
9319
9678
|
// ../core/dist/server/http.js
|
|
9320
9679
|
import { existsSync as existsSync4 } from "fs";
|
|
9321
9680
|
import { WebSocketServer as WebSocketServer2 } from "ws";
|
|
9681
|
+
function stripPort(host) {
|
|
9682
|
+
if (host.startsWith("[")) {
|
|
9683
|
+
const end = host.indexOf("]");
|
|
9684
|
+
return end === -1 ? host : host.slice(1, end);
|
|
9685
|
+
}
|
|
9686
|
+
const colon = host.lastIndexOf(":");
|
|
9687
|
+
if (colon !== -1 && host.indexOf(":") === colon)
|
|
9688
|
+
return host.slice(0, colon);
|
|
9689
|
+
return host;
|
|
9690
|
+
}
|
|
9691
|
+
function isAllowedHost(hostHeader, allowedHosts = []) {
|
|
9692
|
+
if (allowedHosts.some((h) => WILDCARD_BINDS.has(h)))
|
|
9693
|
+
return true;
|
|
9694
|
+
if (typeof hostHeader !== "string" || hostHeader.length === 0)
|
|
9695
|
+
return false;
|
|
9696
|
+
const host = stripPort(hostHeader.trim()).toLowerCase();
|
|
9697
|
+
if (LOOPBACK_HOSTS.has(host))
|
|
9698
|
+
return true;
|
|
9699
|
+
return allowedHosts.some((h) => stripPort(h).toLowerCase() === host);
|
|
9700
|
+
}
|
|
9322
9701
|
function createApiHandler(options) {
|
|
9323
9702
|
return (req, res) => {
|
|
9324
9703
|
const url = req.url ?? "";
|
|
9325
|
-
const path = url.split("?")[0];
|
|
9704
|
+
const path = url.split("?")[0] ?? "";
|
|
9705
|
+
if (path.startsWith("/api/") && !isAllowedHost(req.headers.host, options.allowedHosts)) {
|
|
9706
|
+
res.statusCode = 403;
|
|
9707
|
+
res.setHeader("content-type", "application/json");
|
|
9708
|
+
res.end(JSON.stringify({ error: "forbidden host" }));
|
|
9709
|
+
return true;
|
|
9710
|
+
}
|
|
9326
9711
|
if (path === "/api/active") {
|
|
9327
9712
|
handleActive(req, res);
|
|
9328
9713
|
return true;
|
|
@@ -9460,7 +9845,6 @@ function handleSessionMessages(req, res, dbPath, sessionId) {
|
|
|
9460
9845
|
db.close();
|
|
9461
9846
|
}
|
|
9462
9847
|
}
|
|
9463
|
-
var IMPORT_MAX_BYTES = 64 * 1024 * 1024;
|
|
9464
9848
|
function handleImport(req, res, dbPath) {
|
|
9465
9849
|
if (req.method !== "POST") {
|
|
9466
9850
|
res.statusCode = 405;
|
|
@@ -9534,15 +9918,126 @@ function attachReplayUpgrade(httpServer, options) {
|
|
|
9534
9918
|
const match = url.match(/^\/replay\/(\d+)/);
|
|
9535
9919
|
if (!match)
|
|
9536
9920
|
return;
|
|
9921
|
+
if (!isAllowedHost(req.headers.host, options.allowedHosts)) {
|
|
9922
|
+
socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
|
|
9923
|
+
socket.destroy();
|
|
9924
|
+
return;
|
|
9925
|
+
}
|
|
9537
9926
|
const sessionId = Number(match[1]);
|
|
9538
9927
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
9539
9928
|
streamReplay(ws, sessionId, options.capturesDbPath);
|
|
9540
9929
|
});
|
|
9541
9930
|
});
|
|
9542
9931
|
}
|
|
9932
|
+
var LOOPBACK_HOSTS, WILDCARD_BINDS, IMPORT_MAX_BYTES;
|
|
9933
|
+
var init_http = __esm({
|
|
9934
|
+
"../core/dist/server/http.js"() {
|
|
9935
|
+
"use strict";
|
|
9936
|
+
init_registry2();
|
|
9937
|
+
init_export();
|
|
9938
|
+
init_session();
|
|
9939
|
+
init_sqlite();
|
|
9940
|
+
init_queries();
|
|
9941
|
+
init_replay();
|
|
9942
|
+
LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
|
|
9943
|
+
WILDCARD_BINDS = /* @__PURE__ */ new Set(["0.0.0.0", "::", "*"]);
|
|
9944
|
+
IMPORT_MAX_BYTES = 64 * 1024 * 1024;
|
|
9945
|
+
}
|
|
9946
|
+
});
|
|
9543
9947
|
|
|
9544
|
-
//
|
|
9545
|
-
|
|
9948
|
+
// ../core/dist/server/index.js
|
|
9949
|
+
var init_server2 = __esm({
|
|
9950
|
+
"../core/dist/server/index.js"() {
|
|
9951
|
+
"use strict";
|
|
9952
|
+
init_paths();
|
|
9953
|
+
init_queries();
|
|
9954
|
+
init_replay();
|
|
9955
|
+
init_http();
|
|
9956
|
+
}
|
|
9957
|
+
});
|
|
9958
|
+
|
|
9959
|
+
// ../core/dist/index.js
|
|
9960
|
+
var dist_exports = {};
|
|
9961
|
+
__export(dist_exports, {
|
|
9962
|
+
AGENT_METHODS: () => AGENT_METHODS,
|
|
9963
|
+
AGENT_REGISTRY: () => AGENT_REGISTRY,
|
|
9964
|
+
AcpProxy: () => AcpProxy,
|
|
9965
|
+
CLIENT_METHODS: () => CLIENT_METHODS,
|
|
9966
|
+
DEFAULT_AGENT: () => DEFAULT_AGENT,
|
|
9967
|
+
EXPORT_VERSION: () => EXPORT_VERSION,
|
|
9968
|
+
ExportParseError: () => ExportParseError,
|
|
9969
|
+
LineFramer: () => LineFramer,
|
|
9970
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
9971
|
+
PlaybackEngine: () => PlaybackEngine,
|
|
9972
|
+
REDACTED_PLACEHOLDER: () => REDACTED_PLACEHOLDER,
|
|
9973
|
+
SCHEMA_VERSION: () => SCHEMA_VERSION,
|
|
9974
|
+
Session: () => Session,
|
|
9975
|
+
WS_PROTOCOL_VERSION: () => WS_PROTOCOL_VERSION,
|
|
9976
|
+
WsBroadcaster: () => WsBroadcaster,
|
|
9977
|
+
acpHomeDir: () => acpHomeDir,
|
|
9978
|
+
activeDir: () => activeDir,
|
|
9979
|
+
asciiSparkline: () => asciiSparkline,
|
|
9980
|
+
attachReplayUpgrade: () => attachReplayUpgrade,
|
|
9981
|
+
buildMetadataDiff: () => buildMetadataDiff,
|
|
9982
|
+
buildMethodStatsDiff: () => buildMethodStatsDiff,
|
|
9983
|
+
buildPairIndex: () => buildPairIndex,
|
|
9984
|
+
buildPerMethodStats: () => buildPerMethodStats,
|
|
9985
|
+
buildPerformanceInsights: () => buildPerformanceInsights,
|
|
9986
|
+
buildSessionDiff: () => buildSessionDiff,
|
|
9987
|
+
buildTimelineLayout: () => buildTimelineLayout,
|
|
9988
|
+
createApiHandler: () => createApiHandler,
|
|
9989
|
+
defaultCapturesDbPath: () => defaultCapturesDbPath,
|
|
9990
|
+
deleteSession: () => deleteSession,
|
|
9991
|
+
detectAgentShortcut: () => detectAgentShortcut,
|
|
9992
|
+
diffJson: () => diffJson,
|
|
9993
|
+
discoveryBaseDir: () => discoveryBaseDir,
|
|
9994
|
+
exportSession: () => exportSession,
|
|
9995
|
+
exportSessionFromParts: () => exportSessionFromParts,
|
|
9996
|
+
extractRpcId: () => extractRpcId,
|
|
9997
|
+
extractSessionMetadata: () => extractSessionMetadata,
|
|
9998
|
+
extractTextPreview: () => extractTextPreview,
|
|
9999
|
+
findSessionsByClient: () => findSessionsByClient,
|
|
10000
|
+
getSchemaVersion: () => getSchemaVersion,
|
|
10001
|
+
insertImportedSession: () => insertImportedSession,
|
|
10002
|
+
isAgentShortcut: () => isAgentShortcut,
|
|
10003
|
+
isPidAlive: () => isPidAlive,
|
|
10004
|
+
isUserPrompt: () => isUserPrompt,
|
|
10005
|
+
knownAcpMethods: () => knownAcpMethods,
|
|
10006
|
+
knownAcpSpecMethods: () => knownAcpSpecMethods,
|
|
10007
|
+
listActive: () => listActive,
|
|
10008
|
+
listAgents: () => listAgents,
|
|
10009
|
+
listSessionsSummary: () => listSessionsSummary,
|
|
10010
|
+
mean: () => mean,
|
|
10011
|
+
openDatabase: () => openDatabase,
|
|
10012
|
+
openExistingDatabase: () => openExistingDatabase,
|
|
10013
|
+
parseExport: () => parseExport,
|
|
10014
|
+
parseFrame: () => parseFrame,
|
|
10015
|
+
percentile: () => percentile,
|
|
10016
|
+
redactMessage: () => redactMessage,
|
|
10017
|
+
redactSessionExport: () => redactSessionExport,
|
|
10018
|
+
removeActiveFile: () => removeActiveFile,
|
|
10019
|
+
resolveAgent: () => resolveAgent,
|
|
10020
|
+
resolveSpecForMessage: () => resolveSpecForMessage,
|
|
10021
|
+
sampleEvenly: () => sampleEvenly,
|
|
10022
|
+
serializeExport: () => serializeExport,
|
|
10023
|
+
spawnAgent: () => spawnAgent,
|
|
10024
|
+
streamReplay: () => streamReplay,
|
|
10025
|
+
substituteRpcId: () => substituteRpcId,
|
|
10026
|
+
validateAcpMessage: () => validateAcpMessage,
|
|
10027
|
+
writeActiveFile: () => writeActiveFile
|
|
10028
|
+
});
|
|
10029
|
+
var init_dist = __esm({
|
|
10030
|
+
"../core/dist/index.js"() {
|
|
10031
|
+
"use strict";
|
|
10032
|
+
init_acp();
|
|
10033
|
+
init_agents();
|
|
10034
|
+
init_proxy2();
|
|
10035
|
+
init_storage();
|
|
10036
|
+
init_ws();
|
|
10037
|
+
init_discovery();
|
|
10038
|
+
init_server2();
|
|
10039
|
+
}
|
|
10040
|
+
});
|
|
9546
10041
|
|
|
9547
10042
|
// src/commands/backfill-metadata.ts
|
|
9548
10043
|
function backfillSession(db, id) {
|
|
@@ -9580,8 +10075,8 @@ function listSessionIds(db) {
|
|
|
9580
10075
|
const rows = db.prepare(`SELECT id FROM sessions ORDER BY id ASC`).all();
|
|
9581
10076
|
return rows.map((r) => r.id);
|
|
9582
10077
|
}
|
|
9583
|
-
function registerBackfillMetadataCommand(
|
|
9584
|
-
|
|
10078
|
+
function registerBackfillMetadataCommand(program) {
|
|
10079
|
+
program.command("backfill-metadata").description(
|
|
9585
10080
|
"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."
|
|
9586
10081
|
).argument("[id]", "backfill a single session only (default: all)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--json", "machine-readable JSON output").action((idArg, opts) => {
|
|
9587
10082
|
let targetId = null;
|
|
@@ -9627,11 +10122,17 @@ function registerBackfillMetadataCommand(program2) {
|
|
|
9627
10122
|
}
|
|
9628
10123
|
});
|
|
9629
10124
|
}
|
|
10125
|
+
var init_backfill_metadata = __esm({
|
|
10126
|
+
"src/commands/backfill-metadata.ts"() {
|
|
10127
|
+
"use strict";
|
|
10128
|
+
init_dist();
|
|
10129
|
+
}
|
|
10130
|
+
});
|
|
9630
10131
|
|
|
9631
10132
|
// src/commands/delete.ts
|
|
9632
10133
|
import { existsSync as existsSync5 } from "fs";
|
|
9633
|
-
function registerDeleteCommand(
|
|
9634
|
-
|
|
10134
|
+
function registerDeleteCommand(program) {
|
|
10135
|
+
program.command("delete").description(
|
|
9635
10136
|
"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."
|
|
9636
10137
|
).argument("<id...>", "one or more session ids to delete").option("--db <path>", "captures database", defaultCapturesDbPath()).action((rawIds, opts) => {
|
|
9637
10138
|
const ids = [];
|
|
@@ -9679,23 +10180,14 @@ function labelFor(s) {
|
|
|
9679
10180
|
if (s.client_name) return s.client_name;
|
|
9680
10181
|
return s.agent_command ?? "";
|
|
9681
10182
|
}
|
|
10183
|
+
var init_delete = __esm({
|
|
10184
|
+
"src/commands/delete.ts"() {
|
|
10185
|
+
"use strict";
|
|
10186
|
+
init_dist();
|
|
10187
|
+
}
|
|
10188
|
+
});
|
|
9682
10189
|
|
|
9683
10190
|
// src/lib/style.ts
|
|
9684
|
-
var ESC = String.fromCharCode(27);
|
|
9685
|
-
var CODES = {
|
|
9686
|
-
reset: 0,
|
|
9687
|
-
bold: 1,
|
|
9688
|
-
dim: 2,
|
|
9689
|
-
italic: 3,
|
|
9690
|
-
underline: 4,
|
|
9691
|
-
red: 31,
|
|
9692
|
-
green: 32,
|
|
9693
|
-
yellow: 33,
|
|
9694
|
-
blue: 34,
|
|
9695
|
-
magenta: 35,
|
|
9696
|
-
cyan: 36,
|
|
9697
|
-
gray: 90
|
|
9698
|
-
};
|
|
9699
10191
|
function colorEnabled(stream = process.stdout, env = process.env) {
|
|
9700
10192
|
if (env.FORCE_COLOR === "0" || env.FORCE_COLOR === "false") return false;
|
|
9701
10193
|
if (env.FORCE_COLOR !== void 0) return true;
|
|
@@ -9723,13 +10215,34 @@ function createStyler(enabled = colorEnabled()) {
|
|
|
9723
10215
|
gray: wrap("gray")
|
|
9724
10216
|
};
|
|
9725
10217
|
}
|
|
9726
|
-
var ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
|
|
9727
10218
|
function stripAnsi(s) {
|
|
9728
10219
|
return s.replace(ANSI_PATTERN, "");
|
|
9729
10220
|
}
|
|
9730
10221
|
function visibleWidth(s) {
|
|
9731
10222
|
return stripAnsi(s).length;
|
|
9732
10223
|
}
|
|
10224
|
+
var ESC, CODES, ANSI_PATTERN;
|
|
10225
|
+
var init_style = __esm({
|
|
10226
|
+
"src/lib/style.ts"() {
|
|
10227
|
+
"use strict";
|
|
10228
|
+
ESC = String.fromCharCode(27);
|
|
10229
|
+
CODES = {
|
|
10230
|
+
reset: 0,
|
|
10231
|
+
bold: 1,
|
|
10232
|
+
dim: 2,
|
|
10233
|
+
italic: 3,
|
|
10234
|
+
underline: 4,
|
|
10235
|
+
red: 31,
|
|
10236
|
+
green: 32,
|
|
10237
|
+
yellow: 33,
|
|
10238
|
+
blue: 34,
|
|
10239
|
+
magenta: 35,
|
|
10240
|
+
cyan: 36,
|
|
10241
|
+
gray: 90
|
|
10242
|
+
};
|
|
10243
|
+
ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
|
|
10244
|
+
}
|
|
10245
|
+
});
|
|
9733
10246
|
|
|
9734
10247
|
// src/lib/palette.ts
|
|
9735
10248
|
function colorKind(s, kind, text) {
|
|
@@ -9758,6 +10271,11 @@ function colorLatency(s, ms, text) {
|
|
|
9758
10271
|
function colorSessionKind(s, imported, text) {
|
|
9759
10272
|
return imported ? s.yellow(text) : s.green(text);
|
|
9760
10273
|
}
|
|
10274
|
+
var init_palette = __esm({
|
|
10275
|
+
"src/lib/palette.ts"() {
|
|
10276
|
+
"use strict";
|
|
10277
|
+
}
|
|
10278
|
+
});
|
|
9761
10279
|
|
|
9762
10280
|
// src/lib/table.ts
|
|
9763
10281
|
function renderTable(s, columns, rows, opts = {}) {
|
|
@@ -9792,12 +10310,22 @@ function renderTable(s, columns, rows, opts = {}) {
|
|
|
9792
10310
|
}
|
|
9793
10311
|
return out.map((l) => l + "\n").join("");
|
|
9794
10312
|
}
|
|
10313
|
+
var init_table = __esm({
|
|
10314
|
+
"src/lib/table.ts"() {
|
|
10315
|
+
"use strict";
|
|
10316
|
+
init_style();
|
|
10317
|
+
}
|
|
10318
|
+
});
|
|
9795
10319
|
|
|
9796
10320
|
// src/commands/diff.ts
|
|
9797
|
-
function registerDiffCommand(
|
|
9798
|
-
|
|
10321
|
+
function registerDiffCommand(program) {
|
|
10322
|
+
program.command("diff").description(
|
|
9799
10323
|
'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.'
|
|
9800
|
-
).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").
|
|
10324
|
+
).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").option(
|
|
10325
|
+
"--raw",
|
|
10326
|
+
"skip default redaction of auth headers / proxy tokens (use only when the output stays on YOUR machine)",
|
|
10327
|
+
false
|
|
10328
|
+
).action((rawA, rawB, opts) => {
|
|
9801
10329
|
const idA = parseId(rawA);
|
|
9802
10330
|
const idB = parseId(rawB);
|
|
9803
10331
|
let db;
|
|
@@ -9828,6 +10356,32 @@ function registerDiffCommand(program2) {
|
|
|
9828
10356
|
process.exit(1);
|
|
9829
10357
|
}
|
|
9830
10358
|
db.close();
|
|
10359
|
+
let fieldsRedacted = 0;
|
|
10360
|
+
let messagesAffected = 0;
|
|
10361
|
+
if (!opts.raw) {
|
|
10362
|
+
const redactAll = (msgs) => msgs.map((m) => {
|
|
10363
|
+
const r = redactMessage(m);
|
|
10364
|
+
if (r.count > 0) {
|
|
10365
|
+
messagesAffected += 1;
|
|
10366
|
+
fieldsRedacted += r.count;
|
|
10367
|
+
}
|
|
10368
|
+
return r.redacted;
|
|
10369
|
+
});
|
|
10370
|
+
msgsA = redactAll(msgsA);
|
|
10371
|
+
msgsB = redactAll(msgsB);
|
|
10372
|
+
}
|
|
10373
|
+
const writeRedactionNote = () => {
|
|
10374
|
+
if (opts.raw) {
|
|
10375
|
+
process.stderr.write(
|
|
10376
|
+
"acp-devtools: --raw was set; output may contain auth headers / proxy tokens \u2014 do not share publicly\n"
|
|
10377
|
+
);
|
|
10378
|
+
} else if (fieldsRedacted > 0) {
|
|
10379
|
+
process.stderr.write(
|
|
10380
|
+
`acp-devtools: redacted ${fieldsRedacted} field(s) across ${messagesAffected} message(s) \u2014 re-run with --raw to compare live values
|
|
10381
|
+
`
|
|
10382
|
+
);
|
|
10383
|
+
}
|
|
10384
|
+
};
|
|
9831
10385
|
const diff = buildSessionDiff(msgsA, msgsB);
|
|
9832
10386
|
const meta = buildMetadataDiff(msgsA, msgsB);
|
|
9833
10387
|
const perf = buildMethodStatsDiff(msgsA, msgsB);
|
|
@@ -9846,24 +10400,19 @@ function registerDiffCommand(program2) {
|
|
|
9846
10400
|
2
|
|
9847
10401
|
) + "\n"
|
|
9848
10402
|
);
|
|
10403
|
+
writeRedactionNote();
|
|
9849
10404
|
return;
|
|
9850
10405
|
}
|
|
9851
10406
|
const st = createStyler(colorEnabled(process.stdout));
|
|
9852
10407
|
process.stdout.write(
|
|
9853
10408
|
renderDiff(infoA, infoB, msgsA.length, msgsB.length, diff, meta, perf, Boolean(opts.full), st)
|
|
9854
10409
|
);
|
|
10410
|
+
writeRedactionNote();
|
|
9855
10411
|
});
|
|
9856
10412
|
}
|
|
9857
|
-
var defaultStyler = () => createStyler(colorEnabled(process.stdout));
|
|
9858
10413
|
function heading(st, label) {
|
|
9859
10414
|
return st.bold(st.yellow(label));
|
|
9860
10415
|
}
|
|
9861
|
-
var OP_COLOR = {
|
|
9862
|
-
equal: (st, t) => st.dim(t),
|
|
9863
|
-
changed: (st, t) => st.yellow(t),
|
|
9864
|
-
added: (st, t) => st.green(t),
|
|
9865
|
-
removed: (st, t) => st.red(t)
|
|
9866
|
-
};
|
|
9867
10416
|
function parseId(raw) {
|
|
9868
10417
|
const id = Number(raw);
|
|
9869
10418
|
if (!Number.isInteger(id) || id <= 0) {
|
|
@@ -9887,19 +10436,6 @@ function shortLabel(info) {
|
|
|
9887
10436
|
if (bits.length === 0 && info.name) bits.push(info.name);
|
|
9888
10437
|
return bits.join(" \xB7 ") || "\u2014";
|
|
9889
10438
|
}
|
|
9890
|
-
var KIND_LABEL = {
|
|
9891
|
-
request: "REQ",
|
|
9892
|
-
response: "RSP",
|
|
9893
|
-
notification: "NTF",
|
|
9894
|
-
error: "ERR",
|
|
9895
|
-
unknown: "UNK"
|
|
9896
|
-
};
|
|
9897
|
-
var OP_MARKER = {
|
|
9898
|
-
equal: "=",
|
|
9899
|
-
changed: "\u2260",
|
|
9900
|
-
added: "\u25B8",
|
|
9901
|
-
removed: "\u25C2"
|
|
9902
|
-
};
|
|
9903
10439
|
function dirArrow(m) {
|
|
9904
10440
|
return m.direction === "editor-to-agent" ? "\u2192A" : "A\u2190";
|
|
9905
10441
|
}
|
|
@@ -9976,7 +10512,6 @@ function renderInfoSection(meta, st = defaultStyler()) {
|
|
|
9976
10512
|
}
|
|
9977
10513
|
return lines.join("");
|
|
9978
10514
|
}
|
|
9979
|
-
var PERF_METHOD_MAX = 40;
|
|
9980
10515
|
function colorDelta(st, ms) {
|
|
9981
10516
|
if (ms == null) return st.dim("\u2014");
|
|
9982
10517
|
const text = signedLatency(ms);
|
|
@@ -10020,7 +10555,6 @@ function signedLatency(ms) {
|
|
|
10020
10555
|
const sign = ms > 0 ? "+" : "\u2212";
|
|
10021
10556
|
return sign + formatLatency(Math.abs(ms));
|
|
10022
10557
|
}
|
|
10023
|
-
var VALUE_MAX = 60;
|
|
10024
10558
|
function fmtValue(v) {
|
|
10025
10559
|
let s;
|
|
10026
10560
|
try {
|
|
@@ -10033,17 +10567,43 @@ function fmtValue(v) {
|
|
|
10033
10567
|
if (s.length > VALUE_MAX) s = s.slice(0, VALUE_MAX - 1) + "\u2026";
|
|
10034
10568
|
return s;
|
|
10035
10569
|
}
|
|
10570
|
+
var defaultStyler, OP_COLOR, KIND_LABEL, OP_MARKER, PERF_METHOD_MAX, VALUE_MAX;
|
|
10571
|
+
var init_diff = __esm({
|
|
10572
|
+
"src/commands/diff.ts"() {
|
|
10573
|
+
"use strict";
|
|
10574
|
+
init_dist();
|
|
10575
|
+
init_style();
|
|
10576
|
+
init_palette();
|
|
10577
|
+
init_table();
|
|
10578
|
+
defaultStyler = () => createStyler(colorEnabled(process.stdout));
|
|
10579
|
+
OP_COLOR = {
|
|
10580
|
+
equal: (st, t) => st.dim(t),
|
|
10581
|
+
changed: (st, t) => st.yellow(t),
|
|
10582
|
+
added: (st, t) => st.green(t),
|
|
10583
|
+
removed: (st, t) => st.red(t)
|
|
10584
|
+
};
|
|
10585
|
+
KIND_LABEL = {
|
|
10586
|
+
request: "REQ",
|
|
10587
|
+
response: "RSP",
|
|
10588
|
+
notification: "NTF",
|
|
10589
|
+
error: "ERR",
|
|
10590
|
+
unknown: "UNK"
|
|
10591
|
+
};
|
|
10592
|
+
OP_MARKER = {
|
|
10593
|
+
equal: "=",
|
|
10594
|
+
changed: "\u2260",
|
|
10595
|
+
added: "\u25B8",
|
|
10596
|
+
removed: "\u25C2"
|
|
10597
|
+
};
|
|
10598
|
+
PERF_METHOD_MAX = 40;
|
|
10599
|
+
VALUE_MAX = 60;
|
|
10600
|
+
}
|
|
10601
|
+
});
|
|
10036
10602
|
|
|
10037
10603
|
// src/commands/doctor.ts
|
|
10038
10604
|
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
10039
10605
|
import { homedir as homedir3, platform, release } from "os";
|
|
10040
10606
|
import { join as join3 } from "path";
|
|
10041
|
-
var STATUS_MARK = {
|
|
10042
|
-
ok: "\u2713",
|
|
10043
|
-
warn: "!",
|
|
10044
|
-
fail: "\u2717",
|
|
10045
|
-
info: "\xB7"
|
|
10046
|
-
};
|
|
10047
10607
|
function colorStatus(s, status, text) {
|
|
10048
10608
|
switch (status) {
|
|
10049
10609
|
case "ok":
|
|
@@ -10157,8 +10717,6 @@ function probeZed() {
|
|
|
10157
10717
|
);
|
|
10158
10718
|
return { name: "Zed", path, exists: existsSync6(path) };
|
|
10159
10719
|
}
|
|
10160
|
-
var JETBRAINS_VERSIONED = /^[A-Z][A-Za-z]+\d{4}\.\d/;
|
|
10161
|
-
var JETBRAINS_UNVERSIONED = /* @__PURE__ */ new Set(["Fleet", "Toolbox"]);
|
|
10162
10720
|
function probeJetBrains() {
|
|
10163
10721
|
const baseDirs = [];
|
|
10164
10722
|
if (platform() === "darwin") {
|
|
@@ -10252,8 +10810,8 @@ function exitCodeFromSections(sections) {
|
|
|
10252
10810
|
}
|
|
10253
10811
|
return 0;
|
|
10254
10812
|
}
|
|
10255
|
-
function registerDoctorCommand(
|
|
10256
|
-
|
|
10813
|
+
function registerDoctorCommand(program) {
|
|
10814
|
+
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) => {
|
|
10257
10815
|
const sections = [
|
|
10258
10816
|
{ title: "Environment", results: checkEnvironment() },
|
|
10259
10817
|
{ title: "State", results: checkState() },
|
|
@@ -10273,16 +10831,36 @@ function registerDoctorCommand(program2) {
|
|
|
10273
10831
|
process.exit(exitCodeFromSections(sections));
|
|
10274
10832
|
});
|
|
10275
10833
|
}
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10834
|
+
var STATUS_MARK, JETBRAINS_VERSIONED, JETBRAINS_UNVERSIONED;
|
|
10835
|
+
var init_doctor = __esm({
|
|
10836
|
+
"src/commands/doctor.ts"() {
|
|
10837
|
+
"use strict";
|
|
10838
|
+
init_dist();
|
|
10839
|
+
init_style();
|
|
10840
|
+
STATUS_MARK = {
|
|
10841
|
+
ok: "\u2713",
|
|
10842
|
+
warn: "!",
|
|
10843
|
+
fail: "\u2717",
|
|
10844
|
+
info: "\xB7"
|
|
10845
|
+
};
|
|
10846
|
+
JETBRAINS_VERSIONED = /^[A-Z][A-Za-z]+\d{4}\.\d/;
|
|
10847
|
+
JETBRAINS_UNVERSIONED = /* @__PURE__ */ new Set(["Fleet", "Toolbox"]);
|
|
10848
|
+
}
|
|
10849
|
+
});
|
|
10279
10850
|
|
|
10280
10851
|
// src/version.ts
|
|
10281
|
-
var CLI_VERSION
|
|
10852
|
+
var CLI_VERSION;
|
|
10853
|
+
var init_version = __esm({
|
|
10854
|
+
"src/version.ts"() {
|
|
10855
|
+
"use strict";
|
|
10856
|
+
CLI_VERSION = "0.3.0";
|
|
10857
|
+
}
|
|
10858
|
+
});
|
|
10282
10859
|
|
|
10283
10860
|
// src/commands/export.ts
|
|
10284
|
-
|
|
10285
|
-
|
|
10861
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
10862
|
+
function registerExportCommand(program) {
|
|
10863
|
+
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(
|
|
10286
10864
|
"--raw",
|
|
10287
10865
|
"skip default redaction of auth headers / proxy tokens (use only when the export stays on YOUR machine)",
|
|
10288
10866
|
false
|
|
@@ -10342,12 +10920,19 @@ function registerExportCommand(program2) {
|
|
|
10342
10920
|
}
|
|
10343
10921
|
});
|
|
10344
10922
|
}
|
|
10923
|
+
var init_export2 = __esm({
|
|
10924
|
+
"src/commands/export.ts"() {
|
|
10925
|
+
"use strict";
|
|
10926
|
+
init_dist();
|
|
10927
|
+
init_version();
|
|
10928
|
+
}
|
|
10929
|
+
});
|
|
10345
10930
|
|
|
10346
10931
|
// src/commands/import.ts
|
|
10347
10932
|
import { readFileSync as readFileSync2 } from "fs";
|
|
10348
10933
|
import { basename } from "path";
|
|
10349
|
-
function registerImportCommand(
|
|
10350
|
-
|
|
10934
|
+
function registerImportCommand(program) {
|
|
10935
|
+
program.command("import").description(
|
|
10351
10936
|
"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."
|
|
10352
10937
|
).argument("<file>", "path to a JSON export produced by `acp-devtools export`").option(
|
|
10353
10938
|
"--db <path>",
|
|
@@ -10385,21 +10970,16 @@ function registerImportCommand(program2) {
|
|
|
10385
10970
|
}
|
|
10386
10971
|
});
|
|
10387
10972
|
}
|
|
10973
|
+
var init_import = __esm({
|
|
10974
|
+
"src/commands/import.ts"() {
|
|
10975
|
+
"use strict";
|
|
10976
|
+
init_dist();
|
|
10977
|
+
}
|
|
10978
|
+
});
|
|
10388
10979
|
|
|
10389
10980
|
// src/commands/inspect.ts
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
in: "agent-to-editor"
|
|
10393
|
-
};
|
|
10394
|
-
var KIND_FROM_CODE = {
|
|
10395
|
-
req: "request",
|
|
10396
|
-
rsp: "response",
|
|
10397
|
-
ntf: "notification",
|
|
10398
|
-
err: "error",
|
|
10399
|
-
unk: "unknown"
|
|
10400
|
-
};
|
|
10401
|
-
function registerInspectCommand(program2) {
|
|
10402
|
-
program2.command("inspect").description(
|
|
10981
|
+
function registerInspectCommand(program) {
|
|
10982
|
+
program.command("inspect").description(
|
|
10403
10983
|
"Print messages of a saved session to stdout \u2014 the terminal equivalent of the inspector timeline. Combine with grep / jq for shell-side analysis."
|
|
10404
10984
|
).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(
|
|
10405
10985
|
"--dir <codes>",
|
|
@@ -10543,13 +11123,6 @@ function passes(m, f) {
|
|
|
10543
11123
|
if (f.grep && !m.raw.toLowerCase().includes(f.grep)) return false;
|
|
10544
11124
|
return true;
|
|
10545
11125
|
}
|
|
10546
|
-
var KIND_LABEL2 = {
|
|
10547
|
-
request: "REQ",
|
|
10548
|
-
response: "RSP",
|
|
10549
|
-
notification: "NTF",
|
|
10550
|
-
error: "ERR",
|
|
10551
|
-
unknown: "UNK"
|
|
10552
|
-
};
|
|
10553
11126
|
function toTableRow(m, pair, specCell = "") {
|
|
10554
11127
|
let paired = "\u2014";
|
|
10555
11128
|
let latency = "\u2014";
|
|
@@ -10609,9 +11182,6 @@ function formatBytes2(n) {
|
|
|
10609
11182
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
10610
11183
|
return `${(n / 1024 / 1024).toFixed(1)}MB`;
|
|
10611
11184
|
}
|
|
10612
|
-
var PREVIEW_MAX = 50;
|
|
10613
|
-
var METHOD_MAX = 40;
|
|
10614
|
-
var RPC_MAX = 10;
|
|
10615
11185
|
function colorSpec(s, cell) {
|
|
10616
11186
|
if (cell === "\u2713") return s.green(cell);
|
|
10617
11187
|
if (cell.startsWith("\u26A0")) return s.red(cell);
|
|
@@ -10656,10 +11226,41 @@ function truncate(s, max) {
|
|
|
10656
11226
|
if (s.length <= max) return s;
|
|
10657
11227
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
10658
11228
|
}
|
|
11229
|
+
var DIR_FROM_CODE, KIND_FROM_CODE, KIND_LABEL2, PREVIEW_MAX, METHOD_MAX, RPC_MAX;
|
|
11230
|
+
var init_inspect = __esm({
|
|
11231
|
+
"src/commands/inspect.ts"() {
|
|
11232
|
+
"use strict";
|
|
11233
|
+
init_dist();
|
|
11234
|
+
init_style();
|
|
11235
|
+
init_palette();
|
|
11236
|
+
init_table();
|
|
11237
|
+
DIR_FROM_CODE = {
|
|
11238
|
+
out: "editor-to-agent",
|
|
11239
|
+
in: "agent-to-editor"
|
|
11240
|
+
};
|
|
11241
|
+
KIND_FROM_CODE = {
|
|
11242
|
+
req: "request",
|
|
11243
|
+
rsp: "response",
|
|
11244
|
+
ntf: "notification",
|
|
11245
|
+
err: "error",
|
|
11246
|
+
unk: "unknown"
|
|
11247
|
+
};
|
|
11248
|
+
KIND_LABEL2 = {
|
|
11249
|
+
request: "REQ",
|
|
11250
|
+
response: "RSP",
|
|
11251
|
+
notification: "NTF",
|
|
11252
|
+
error: "ERR",
|
|
11253
|
+
unknown: "UNK"
|
|
11254
|
+
};
|
|
11255
|
+
PREVIEW_MAX = 50;
|
|
11256
|
+
METHOD_MAX = 40;
|
|
11257
|
+
RPC_MAX = 10;
|
|
11258
|
+
}
|
|
11259
|
+
});
|
|
10659
11260
|
|
|
10660
11261
|
// src/commands/list.ts
|
|
10661
|
-
function registerListCommand(
|
|
10662
|
-
|
|
11262
|
+
function registerListCommand(program) {
|
|
11263
|
+
program.command("list").description(
|
|
10663
11264
|
"List saved sessions in the captures database, newest first. Use `acp-devtools doctor` for live captures (those are not in the database)."
|
|
10664
11265
|
).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(
|
|
10665
11266
|
"--client <substring>",
|
|
@@ -10671,18 +11272,17 @@ function registerListCommand(program2) {
|
|
|
10671
11272
|
`);
|
|
10672
11273
|
process.exit(2);
|
|
10673
11274
|
}
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
rows = rows.filter((r) => {
|
|
10680
|
-
const fields = [r.client_name, r.client_version, r.client_platform].filter(
|
|
10681
|
-
(v) => typeof v === "string"
|
|
10682
|
-
);
|
|
10683
|
-
return fields.some((f) => f.toLowerCase().includes(needle));
|
|
10684
|
-
});
|
|
11275
|
+
if (opts.imported && opts.saved) {
|
|
11276
|
+
process.stderr.write(
|
|
11277
|
+
"acp-devtools: --imported and --saved are mutually exclusive\n"
|
|
11278
|
+
);
|
|
11279
|
+
process.exit(2);
|
|
10685
11280
|
}
|
|
11281
|
+
const filters = {};
|
|
11282
|
+
if (opts.imported) filters.imported = true;
|
|
11283
|
+
if (opts.saved) filters.imported = false;
|
|
11284
|
+
if (opts.client) filters.client = opts.client;
|
|
11285
|
+
const rows = listSessionsSummary(opts.db, limit, filters);
|
|
10686
11286
|
if (opts.json) {
|
|
10687
11287
|
process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
|
|
10688
11288
|
return;
|
|
@@ -10709,13 +11309,6 @@ function formatAge(deltaMs) {
|
|
|
10709
11309
|
if (h < 24) return `${h}h${m % 60 > 0 ? `${m % 60}m` : ""}`;
|
|
10710
11310
|
return `${Math.floor(h / 24)}d`;
|
|
10711
11311
|
}
|
|
10712
|
-
var COLUMNS = [
|
|
10713
|
-
{ title: "ID", align: "left" },
|
|
10714
|
-
{ title: "AGE", align: "right" },
|
|
10715
|
-
{ title: "KIND", align: "left" },
|
|
10716
|
-
{ title: "MSGS", align: "right" },
|
|
10717
|
-
{ title: "SESSION", align: "left" }
|
|
10718
|
-
];
|
|
10719
11312
|
function formatTable(rows) {
|
|
10720
11313
|
const s = createStyler(colorEnabled(process.stdout));
|
|
10721
11314
|
const now = Date.now();
|
|
@@ -10731,70 +11324,28 @@ function formatTable(rows) {
|
|
|
10731
11324
|
});
|
|
10732
11325
|
return renderTable(s, COLUMNS, body);
|
|
10733
11326
|
}
|
|
11327
|
+
var COLUMNS;
|
|
11328
|
+
var init_list = __esm({
|
|
11329
|
+
"src/commands/list.ts"() {
|
|
11330
|
+
"use strict";
|
|
11331
|
+
init_dist();
|
|
11332
|
+
init_style();
|
|
11333
|
+
init_palette();
|
|
11334
|
+
init_table();
|
|
11335
|
+
COLUMNS = [
|
|
11336
|
+
{ title: "ID", align: "left" },
|
|
11337
|
+
{ title: "AGE", align: "right" },
|
|
11338
|
+
{ title: "KIND", align: "left" },
|
|
11339
|
+
{ title: "MSGS", align: "right" },
|
|
11340
|
+
{ title: "SESSION", align: "left" }
|
|
11341
|
+
];
|
|
11342
|
+
}
|
|
11343
|
+
});
|
|
10734
11344
|
|
|
10735
11345
|
// src/commands/mcp.ts
|
|
10736
11346
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10737
11347
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10738
11348
|
import { z } from "zod";
|
|
10739
|
-
var SERVER_INSTRUCTIONS = `acp-devtools exposes a local SQLite store of captured
|
|
10740
|
-
Agent Client Protocol (ACP) sessions. ACP is the editor\u2194agent JSON-RPC
|
|
10741
|
-
protocol used by Claude Code, Goose, Codex, and other IDE agents.
|
|
10742
|
-
|
|
10743
|
-
Every tool here is **read-only** \u2014 nothing in this server writes to the
|
|
10744
|
-
database. Use it to investigate:
|
|
10745
|
-
|
|
10746
|
-
- a slow session (\`get_latency_stats\` returns per-method p50/p99/max
|
|
10747
|
-
plus auto-detected insights: hotspot, long-tail, outlier, busiest,
|
|
10748
|
-
errors)
|
|
10749
|
-
- a misbehaving agent (\`find_spec_violations\` validates every frame
|
|
10750
|
-
against the ACP JSON schema)
|
|
10751
|
-
- specific events (\`get_session_messages\` + \`get_message\` for the
|
|
10752
|
-
raw JSON-RPC payload)
|
|
10753
|
-
- capture inventory (\`list_sessions\`, \`find_sessions_by_client\`)
|
|
10754
|
-
- one-shot digest of a session (\`get_session_summary\` bundles
|
|
10755
|
-
metadata + latency stats + insights in one call \u2014 preferred over
|
|
10756
|
-
three separate calls when you just need an overview)
|
|
10757
|
-
- a regression or A/B comparison between two sessions
|
|
10758
|
-
(\`diff_sessions\` aligns two captures and reports added / removed
|
|
10759
|
-
frames and field-level payload changes)
|
|
10760
|
-
|
|
10761
|
-
A typical investigation:
|
|
10762
|
-
1. \`list_sessions\` to see what is captured
|
|
10763
|
-
2. \`get_session_summary({session_id})\` for the headline numbers and
|
|
10764
|
-
auto-detected insights \u2014 start here when you don't know what's
|
|
10765
|
-
wrong yet
|
|
10766
|
-
3. drill into a specific method or message with
|
|
10767
|
-
\`get_session_messages\` / \`get_message\` / \`get_paired\`
|
|
10768
|
-
|
|
10769
|
-
For common investigations the server also exposes canned prompts via
|
|
10770
|
-
\`prompts/list\` \u2014 \`triage_slow_session\`, \`audit_spec_violations\`,
|
|
10771
|
-
\`compare_clients\`. Each prompt seeds the right tool sequence.
|
|
10772
|
-
|
|
10773
|
-
Every tool declares an \`outputSchema\`, so results include both
|
|
10774
|
-
\`content\` (text JSON) and \`structuredContent\` (typed object matching
|
|
10775
|
-
the schema). Prefer \`structuredContent\` if your client supports it \u2014
|
|
10776
|
-
it avoids re-parsing the text payload.
|
|
10777
|
-
|
|
10778
|
-
Schema notes: sessions have a structured-metadata layer populated by the
|
|
10779
|
-
\`backfill-metadata\` CLI; \`client_name\` / \`client_version\` /
|
|
10780
|
-
\`client_platform\` may be null on freshly imported captures until that
|
|
10781
|
-
command runs.
|
|
10782
|
-
|
|
10783
|
-
Redaction: every tool that returns frame contents OR derived views
|
|
10784
|
-
of them (\`get_message\`, \`get_session_messages\`, \`search_messages\`,
|
|
10785
|
-
\`get_session_metadata\`, \`get_session_summary\`, \`diff_sessions\`)
|
|
10786
|
-
redacts auth headers and proxy tokens (\`Authorization\`,
|
|
10787
|
-
\`X-Api-Key\`, JetBrains \`_meta.proxyConfig.proxies[*].proxy.headers.*\`,
|
|
10788
|
-
etc). Sensitive fields appear as \`<REDACTED>\`. This is
|
|
10789
|
-
unconditional; there is no opt-out flag, because the human owns the
|
|
10790
|
-
share decision (use \`acp-devtools export <id> --raw\` on the CLI when
|
|
10791
|
-
the export stays on your machine). \`search_messages\` indexes the
|
|
10792
|
-
pre-redaction bytes so you can still find a frame by a token fragment,
|
|
10793
|
-
but the returned \`raw\` is the redacted copy \u2014 do not quote the live
|
|
10794
|
-
secret back to the user. \`diff_sessions\` operates on already-redacted
|
|
10795
|
-
frames, so a token that rotated between two sessions shows up as equal
|
|
10796
|
-
("<REDACTED>" on both sides) rather than as a change \u2014 this is
|
|
10797
|
-
intentional, treating "token rotated" as a leaked side channel.`;
|
|
10798
11349
|
function annotations(meta) {
|
|
10799
11350
|
return {
|
|
10800
11351
|
title: meta.title,
|
|
@@ -10809,60 +11360,8 @@ function structuredResult(payload) {
|
|
|
10809
11360
|
structuredContent: payload
|
|
10810
11361
|
};
|
|
10811
11362
|
}
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
name: z.string().nullable(),
|
|
10815
|
-
agent_command: z.string().nullable(),
|
|
10816
|
-
started_at: z.number(),
|
|
10817
|
-
ended_at: z.number().nullable(),
|
|
10818
|
-
message_count: z.number(),
|
|
10819
|
-
client_name: z.string().nullable(),
|
|
10820
|
-
imported_at: z.number().nullable(),
|
|
10821
|
-
client_version: z.string().nullable(),
|
|
10822
|
-
client_platform: z.string().nullable(),
|
|
10823
|
-
agent_name: z.string().nullable(),
|
|
10824
|
-
agent_version: z.string().nullable(),
|
|
10825
|
-
protocol_version: z.number().nullable(),
|
|
10826
|
-
current_mode: z.string().nullable(),
|
|
10827
|
-
current_model: z.string().nullable()
|
|
10828
|
-
});
|
|
10829
|
-
var insightSchema = z.object({
|
|
10830
|
-
kind: z.enum(["hotspot", "long-tail", "outlier", "busiest", "errors"]),
|
|
10831
|
-
summary: z.string(),
|
|
10832
|
-
detail: z.string().optional(),
|
|
10833
|
-
methods: z.array(z.string())
|
|
10834
|
-
});
|
|
10835
|
-
var perMethodSchema = z.object({
|
|
10836
|
-
method: z.string(),
|
|
10837
|
-
kind: z.enum(["request", "notification"]),
|
|
10838
|
-
count: z.number(),
|
|
10839
|
-
sampleSize: z.number(),
|
|
10840
|
-
p50: z.number().nullable(),
|
|
10841
|
-
p99: z.number().nullable(),
|
|
10842
|
-
max: z.number().nullable(),
|
|
10843
|
-
totalLatencyMs: z.number().nullable(),
|
|
10844
|
-
latencies: z.array(z.number())
|
|
10845
|
-
});
|
|
10846
|
-
var messageSchema = z.object({
|
|
10847
|
-
seq: z.number(),
|
|
10848
|
-
timestamp: z.number(),
|
|
10849
|
-
direction: z.enum(["editor-to-agent", "agent-to-editor"]),
|
|
10850
|
-
kind: z.enum(["request", "response", "notification", "error", "unknown"]),
|
|
10851
|
-
method: z.string().optional(),
|
|
10852
|
-
rpcId: z.union([z.string(), z.number()]).optional(),
|
|
10853
|
-
raw: z.string(),
|
|
10854
|
-
payload: z.unknown().nullable(),
|
|
10855
|
-
parseError: z.string().optional()
|
|
10856
|
-
});
|
|
10857
|
-
var latencySchema = z.object({
|
|
10858
|
-
sampleSize: z.number(),
|
|
10859
|
-
p50: z.number().nullable(),
|
|
10860
|
-
p90: z.number().nullable(),
|
|
10861
|
-
p99: z.number().nullable(),
|
|
10862
|
-
max: z.number().nullable()
|
|
10863
|
-
});
|
|
10864
|
-
function registerMcpCommand(program2) {
|
|
10865
|
-
program2.command("mcp").description(
|
|
11363
|
+
function registerMcpCommand(program) {
|
|
11364
|
+
program.command("mcp").description(
|
|
10866
11365
|
'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".'
|
|
10867
11366
|
).option("--db <path>", "captures database", defaultCapturesDbPath()).option("--name <name>", "server name advertised in MCP handshake", "acp-devtools").action(async (opts) => {
|
|
10868
11367
|
const server = buildMcpServer(opts);
|
|
@@ -11426,17 +11925,132 @@ function errorResult(message) {
|
|
|
11426
11925
|
content: [{ type: "text", text: message }]
|
|
11427
11926
|
};
|
|
11428
11927
|
}
|
|
11928
|
+
var SERVER_INSTRUCTIONS, sessionSummaryRowSchema, insightSchema, perMethodSchema, messageSchema, latencySchema;
|
|
11929
|
+
var init_mcp = __esm({
|
|
11930
|
+
"src/commands/mcp.ts"() {
|
|
11931
|
+
"use strict";
|
|
11932
|
+
init_dist();
|
|
11933
|
+
init_version();
|
|
11934
|
+
SERVER_INSTRUCTIONS = `acp-devtools exposes a local SQLite store of captured
|
|
11935
|
+
Agent Client Protocol (ACP) sessions. ACP is the editor\u2194agent JSON-RPC
|
|
11936
|
+
protocol used by Claude Code, Goose, Codex, and other IDE agents.
|
|
11429
11937
|
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11938
|
+
Every tool here is **read-only** \u2014 nothing in this server writes to the
|
|
11939
|
+
database. Use it to investigate:
|
|
11940
|
+
|
|
11941
|
+
- a slow session (\`get_latency_stats\` returns per-method p50/p99/max
|
|
11942
|
+
plus auto-detected insights: hotspot, long-tail, outlier, busiest,
|
|
11943
|
+
errors)
|
|
11944
|
+
- a misbehaving agent (\`find_spec_violations\` validates every frame
|
|
11945
|
+
against the ACP JSON schema)
|
|
11946
|
+
- specific events (\`get_session_messages\` + \`get_message\` for the
|
|
11947
|
+
raw JSON-RPC payload)
|
|
11948
|
+
- capture inventory (\`list_sessions\`, \`find_sessions_by_client\`)
|
|
11949
|
+
- one-shot digest of a session (\`get_session_summary\` bundles
|
|
11950
|
+
metadata + latency stats + insights in one call \u2014 preferred over
|
|
11951
|
+
three separate calls when you just need an overview)
|
|
11952
|
+
- a regression or A/B comparison between two sessions
|
|
11953
|
+
(\`diff_sessions\` aligns two captures and reports added / removed
|
|
11954
|
+
frames and field-level payload changes)
|
|
11955
|
+
|
|
11956
|
+
A typical investigation:
|
|
11957
|
+
1. \`list_sessions\` to see what is captured
|
|
11958
|
+
2. \`get_session_summary({session_id})\` for the headline numbers and
|
|
11959
|
+
auto-detected insights \u2014 start here when you don't know what's
|
|
11960
|
+
wrong yet
|
|
11961
|
+
3. drill into a specific method or message with
|
|
11962
|
+
\`get_session_messages\` / \`get_message\` / \`get_paired\`
|
|
11963
|
+
|
|
11964
|
+
For common investigations the server also exposes canned prompts via
|
|
11965
|
+
\`prompts/list\` \u2014 \`triage_slow_session\`, \`audit_spec_violations\`,
|
|
11966
|
+
\`compare_clients\`. Each prompt seeds the right tool sequence.
|
|
11967
|
+
|
|
11968
|
+
Every tool declares an \`outputSchema\`, so results include both
|
|
11969
|
+
\`content\` (text JSON) and \`structuredContent\` (typed object matching
|
|
11970
|
+
the schema). Prefer \`structuredContent\` if your client supports it \u2014
|
|
11971
|
+
it avoids re-parsing the text payload.
|
|
11972
|
+
|
|
11973
|
+
Schema notes: sessions have a structured-metadata layer populated by the
|
|
11974
|
+
\`backfill-metadata\` CLI; \`client_name\` / \`client_version\` /
|
|
11975
|
+
\`client_platform\` may be null on freshly imported captures until that
|
|
11976
|
+
command runs.
|
|
11977
|
+
|
|
11978
|
+
Redaction: every tool that returns frame contents OR derived views
|
|
11979
|
+
of them (\`get_message\`, \`get_session_messages\`, \`search_messages\`,
|
|
11980
|
+
\`get_session_metadata\`, \`get_session_summary\`, \`diff_sessions\`)
|
|
11981
|
+
redacts auth headers and proxy tokens (\`Authorization\`,
|
|
11982
|
+
\`X-Api-Key\`, JetBrains \`_meta.proxyConfig.proxies[*].proxy.headers.*\`,
|
|
11983
|
+
etc). Sensitive fields appear as \`<REDACTED>\`. This is
|
|
11984
|
+
unconditional; there is no opt-out flag, because the human owns the
|
|
11985
|
+
share decision (use \`acp-devtools export <id> --raw\` on the CLI when
|
|
11986
|
+
the export stays on your machine). \`search_messages\` indexes the
|
|
11987
|
+
pre-redaction bytes so you can still find a frame by a token fragment,
|
|
11988
|
+
but the returned \`raw\` is the redacted copy \u2014 do not quote the live
|
|
11989
|
+
secret back to the user. \`diff_sessions\` operates on already-redacted
|
|
11990
|
+
frames, so a token that rotated between two sessions shows up as equal
|
|
11991
|
+
("<REDACTED>" on both sides) rather than as a change \u2014 this is
|
|
11992
|
+
intentional, treating "token rotated" as a leaked side channel.`;
|
|
11993
|
+
sessionSummaryRowSchema = z.object({
|
|
11994
|
+
id: z.number(),
|
|
11995
|
+
name: z.string().nullable(),
|
|
11996
|
+
agent_command: z.string().nullable(),
|
|
11997
|
+
started_at: z.number(),
|
|
11998
|
+
ended_at: z.number().nullable(),
|
|
11999
|
+
message_count: z.number(),
|
|
12000
|
+
client_name: z.string().nullable(),
|
|
12001
|
+
imported_at: z.number().nullable(),
|
|
12002
|
+
client_version: z.string().nullable(),
|
|
12003
|
+
client_platform: z.string().nullable(),
|
|
12004
|
+
agent_name: z.string().nullable(),
|
|
12005
|
+
agent_version: z.string().nullable(),
|
|
12006
|
+
protocol_version: z.number().nullable(),
|
|
12007
|
+
current_mode: z.string().nullable(),
|
|
12008
|
+
current_model: z.string().nullable()
|
|
12009
|
+
});
|
|
12010
|
+
insightSchema = z.object({
|
|
12011
|
+
kind: z.enum(["hotspot", "long-tail", "outlier", "busiest", "errors"]),
|
|
12012
|
+
summary: z.string(),
|
|
12013
|
+
detail: z.string().optional(),
|
|
12014
|
+
methods: z.array(z.string())
|
|
12015
|
+
});
|
|
12016
|
+
perMethodSchema = z.object({
|
|
12017
|
+
method: z.string(),
|
|
12018
|
+
kind: z.enum(["request", "notification"]),
|
|
12019
|
+
count: z.number(),
|
|
12020
|
+
sampleSize: z.number(),
|
|
12021
|
+
p50: z.number().nullable(),
|
|
12022
|
+
p99: z.number().nullable(),
|
|
12023
|
+
max: z.number().nullable(),
|
|
12024
|
+
totalLatencyMs: z.number().nullable(),
|
|
12025
|
+
latencies: z.array(z.number())
|
|
12026
|
+
});
|
|
12027
|
+
messageSchema = z.object({
|
|
12028
|
+
seq: z.number(),
|
|
12029
|
+
timestamp: z.number(),
|
|
12030
|
+
direction: z.enum(["editor-to-agent", "agent-to-editor"]),
|
|
12031
|
+
kind: z.enum(["request", "response", "notification", "error", "unknown"]),
|
|
12032
|
+
method: z.string().optional(),
|
|
12033
|
+
rpcId: z.union([z.string(), z.number()]).optional(),
|
|
12034
|
+
raw: z.string(),
|
|
12035
|
+
payload: z.unknown().nullable(),
|
|
12036
|
+
parseError: z.string().optional()
|
|
12037
|
+
});
|
|
12038
|
+
latencySchema = z.object({
|
|
12039
|
+
sampleSize: z.number(),
|
|
12040
|
+
p50: z.number().nullable(),
|
|
12041
|
+
p90: z.number().nullable(),
|
|
12042
|
+
p99: z.number().nullable(),
|
|
12043
|
+
max: z.number().nullable()
|
|
12044
|
+
});
|
|
12045
|
+
}
|
|
12046
|
+
});
|
|
11433
12047
|
|
|
11434
12048
|
// src/commands/playback-source.ts
|
|
11435
12049
|
import { readFileSync as readFileSync3 } from "fs";
|
|
11436
12050
|
import { basename as basename2 } from "path";
|
|
11437
12051
|
function loadPlaybackScript(opts) {
|
|
11438
12052
|
if (opts.script && opts.session !== void 0) {
|
|
11439
|
-
throw new
|
|
12053
|
+
throw new PlaybackUsageError("--script and --session are mutually exclusive");
|
|
11440
12054
|
}
|
|
11441
12055
|
if (opts.script) {
|
|
11442
12056
|
let text;
|
|
@@ -11455,7 +12069,7 @@ function loadPlaybackScript(opts) {
|
|
|
11455
12069
|
if (opts.session !== void 0) {
|
|
11456
12070
|
const id = Number(opts.session);
|
|
11457
12071
|
if (!Number.isInteger(id) || id <= 0) {
|
|
11458
|
-
throw new
|
|
12072
|
+
throw new PlaybackUsageError(`invalid --session "${opts.session}"`);
|
|
11459
12073
|
}
|
|
11460
12074
|
session = Session.load(db, id);
|
|
11461
12075
|
} else {
|
|
@@ -11475,10 +12089,25 @@ function loadPlaybackScript(opts) {
|
|
|
11475
12089
|
db.close();
|
|
11476
12090
|
}
|
|
11477
12091
|
}
|
|
12092
|
+
var PlaybackUsageError;
|
|
12093
|
+
var init_playback_source = __esm({
|
|
12094
|
+
"src/commands/playback-source.ts"() {
|
|
12095
|
+
"use strict";
|
|
12096
|
+
init_dist();
|
|
12097
|
+
PlaybackUsageError = class extends Error {
|
|
12098
|
+
constructor(message) {
|
|
12099
|
+
super(message);
|
|
12100
|
+
this.name = "PlaybackUsageError";
|
|
12101
|
+
}
|
|
12102
|
+
};
|
|
12103
|
+
}
|
|
12104
|
+
});
|
|
11478
12105
|
|
|
11479
12106
|
// src/commands/mock-agent.ts
|
|
11480
|
-
|
|
11481
|
-
|
|
12107
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
12108
|
+
import { dirname as dirname2 } from "path";
|
|
12109
|
+
function registerMockAgentCommand(program) {
|
|
12110
|
+
program.command("mock-agent").description(
|
|
11482
12111
|
"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."
|
|
11483
12112
|
).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(
|
|
11484
12113
|
"--realtime",
|
|
@@ -11500,7 +12129,7 @@ function registerMockAgentCommand(program2) {
|
|
|
11500
12129
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11501
12130
|
process.stderr.write(`acp-devtools: ${msg}
|
|
11502
12131
|
`);
|
|
11503
|
-
process.exit(1);
|
|
12132
|
+
process.exit(err instanceof PlaybackUsageError ? 2 : 1);
|
|
11504
12133
|
}
|
|
11505
12134
|
const engine = new PlaybackEngine(loaded.messages, "agent");
|
|
11506
12135
|
process.stderr.write(
|
|
@@ -11560,11 +12189,18 @@ function registerMockAgentCommand(program2) {
|
|
|
11560
12189
|
}
|
|
11561
12190
|
};
|
|
11562
12191
|
let pendingTimer = null;
|
|
12192
|
+
let stdinEnded = false;
|
|
11563
12193
|
const emit = (line) => {
|
|
11564
12194
|
process.stdout.write(line + "\n");
|
|
11565
12195
|
log("\u2190", line);
|
|
11566
12196
|
capture(line, "agent-to-editor");
|
|
11567
12197
|
};
|
|
12198
|
+
const finishIfDrained = () => {
|
|
12199
|
+
if (stdinEnded && !pendingTimer) {
|
|
12200
|
+
closeSession();
|
|
12201
|
+
process.exit(0);
|
|
12202
|
+
}
|
|
12203
|
+
};
|
|
11568
12204
|
const drain = () => {
|
|
11569
12205
|
if (pendingTimer) return;
|
|
11570
12206
|
while (true) {
|
|
@@ -11583,9 +12219,7 @@ function registerMockAgentCommand(program2) {
|
|
|
11583
12219
|
emit(step.line);
|
|
11584
12220
|
continue;
|
|
11585
12221
|
}
|
|
11586
|
-
|
|
11587
|
-
return;
|
|
11588
|
-
}
|
|
12222
|
+
finishIfDrained();
|
|
11589
12223
|
return;
|
|
11590
12224
|
}
|
|
11591
12225
|
};
|
|
@@ -11605,19 +12239,25 @@ function registerMockAgentCommand(program2) {
|
|
|
11605
12239
|
}
|
|
11606
12240
|
});
|
|
11607
12241
|
process.stdin.on("end", () => {
|
|
12242
|
+
stdinEnded = true;
|
|
11608
12243
|
drain();
|
|
11609
|
-
closeSession();
|
|
11610
|
-
process.exit(0);
|
|
11611
12244
|
});
|
|
11612
12245
|
drain();
|
|
11613
12246
|
});
|
|
11614
12247
|
}
|
|
12248
|
+
var init_mock_agent = __esm({
|
|
12249
|
+
"src/commands/mock-agent.ts"() {
|
|
12250
|
+
"use strict";
|
|
12251
|
+
init_dist();
|
|
12252
|
+
init_playback_source();
|
|
12253
|
+
}
|
|
12254
|
+
});
|
|
11615
12255
|
|
|
11616
12256
|
// src/commands/mock-editor.ts
|
|
11617
12257
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
11618
12258
|
import { dirname as dirname3 } from "path";
|
|
11619
|
-
function registerMockEditorCommand(
|
|
11620
|
-
|
|
12259
|
+
function registerMockEditorCommand(program) {
|
|
12260
|
+
program.command("mock-editor").description(
|
|
11621
12261
|
"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."
|
|
11622
12262
|
).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(
|
|
11623
12263
|
"--realtime",
|
|
@@ -11639,7 +12279,7 @@ function registerMockEditorCommand(program2) {
|
|
|
11639
12279
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11640
12280
|
process.stderr.write(`acp-devtools: ${msg}
|
|
11641
12281
|
`);
|
|
11642
|
-
process.exit(1);
|
|
12282
|
+
process.exit(err instanceof PlaybackUsageError ? 2 : 1);
|
|
11643
12283
|
}
|
|
11644
12284
|
const engine = new PlaybackEngine(loaded.messages, "editor");
|
|
11645
12285
|
process.stderr.write(
|
|
@@ -11780,14 +12420,17 @@ function registerMockEditorCommand(program2) {
|
|
|
11780
12420
|
}
|
|
11781
12421
|
});
|
|
11782
12422
|
}
|
|
12423
|
+
var init_mock_editor = __esm({
|
|
12424
|
+
"src/commands/mock-editor.ts"() {
|
|
12425
|
+
"use strict";
|
|
12426
|
+
init_dist();
|
|
12427
|
+
init_playback_source();
|
|
12428
|
+
}
|
|
12429
|
+
});
|
|
11783
12430
|
|
|
11784
12431
|
// src/commands/proxy.ts
|
|
11785
12432
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11786
12433
|
import { dirname as dirname4 } from "path";
|
|
11787
|
-
var DIRECTION_LABEL = {
|
|
11788
|
-
"editor-to-agent": "\u2192 AGENT",
|
|
11789
|
-
"agent-to-editor": "\u2190 AGENT"
|
|
11790
|
-
};
|
|
11791
12434
|
function formatPretty(msg) {
|
|
11792
12435
|
const time = new Date(msg.timestamp).toISOString().slice(11, 23);
|
|
11793
12436
|
const dir = DIRECTION_LABEL[msg.direction];
|
|
@@ -11797,9 +12440,9 @@ function formatPretty(msg) {
|
|
|
11797
12440
|
return `${head}${id}${err}
|
|
11798
12441
|
${msg.raw}`;
|
|
11799
12442
|
}
|
|
11800
|
-
function registerProxyCommand(
|
|
12443
|
+
function registerProxyCommand(program) {
|
|
11801
12444
|
const knownAgentList = listAgents().map((a) => a.shortName).join(", ");
|
|
11802
|
-
|
|
12445
|
+
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(
|
|
11803
12446
|
"--agent <name>",
|
|
11804
12447
|
`preset for a known ACP agent (${knownAgentList})`
|
|
11805
12448
|
).option("--log <mode>", "message log format: json | pretty | none", "none").option("--cwd <dir>", "working directory for the agent").option(
|
|
@@ -11834,7 +12477,7 @@ function registerProxyCommand(program2) {
|
|
|
11834
12477
|
} else {
|
|
11835
12478
|
process.stderr.write(
|
|
11836
12479
|
`acp-devtools: no agent specified.
|
|
11837
|
-
Pass an agent command (\`acp-devtools proxy npx -y @
|
|
12480
|
+
Pass an agent command (\`acp-devtools proxy npx -y @agentclientprotocol/claude-agent-acp\`)
|
|
11838
12481
|
or use --agent <name> with one of: ${knownAgentList}.
|
|
11839
12482
|
`
|
|
11840
12483
|
);
|
|
@@ -12025,6 +12668,17 @@ function registerProxyCommand(program2) {
|
|
|
12025
12668
|
}
|
|
12026
12669
|
});
|
|
12027
12670
|
}
|
|
12671
|
+
var DIRECTION_LABEL;
|
|
12672
|
+
var init_proxy3 = __esm({
|
|
12673
|
+
"src/commands/proxy.ts"() {
|
|
12674
|
+
"use strict";
|
|
12675
|
+
init_dist();
|
|
12676
|
+
DIRECTION_LABEL = {
|
|
12677
|
+
"editor-to-agent": "\u2192 AGENT",
|
|
12678
|
+
"agent-to-editor": "\u2190 AGENT"
|
|
12679
|
+
};
|
|
12680
|
+
}
|
|
12681
|
+
});
|
|
12028
12682
|
|
|
12029
12683
|
// src/commands/replay.ts
|
|
12030
12684
|
import { readFileSync as readFileSync4 } from "fs";
|
|
@@ -12080,8 +12734,8 @@ function loadFromSqlite(dbPath, idArg) {
|
|
|
12080
12734
|
cleanup: () => db.close()
|
|
12081
12735
|
};
|
|
12082
12736
|
}
|
|
12083
|
-
function registerReplayCommand(
|
|
12084
|
-
|
|
12737
|
+
function registerReplayCommand(program) {
|
|
12738
|
+
program.command("replay").description(
|
|
12085
12739
|
"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."
|
|
12086
12740
|
).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) => {
|
|
12087
12741
|
const wsPort = Number(opts.wsPort);
|
|
@@ -12138,10 +12792,16 @@ function registerReplayCommand(program2) {
|
|
|
12138
12792
|
source.cleanup();
|
|
12139
12793
|
});
|
|
12140
12794
|
}
|
|
12795
|
+
var init_replay2 = __esm({
|
|
12796
|
+
"src/commands/replay.ts"() {
|
|
12797
|
+
"use strict";
|
|
12798
|
+
init_dist();
|
|
12799
|
+
}
|
|
12800
|
+
});
|
|
12141
12801
|
|
|
12142
12802
|
// src/commands/search.ts
|
|
12143
|
-
function registerSearchCommand(
|
|
12144
|
-
|
|
12803
|
+
function registerSearchCommand(program) {
|
|
12804
|
+
program.command("search").description(
|
|
12145
12805
|
"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."
|
|
12146
12806
|
).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) => {
|
|
12147
12807
|
const limit = Number(opts.limit);
|
|
@@ -12213,7 +12873,6 @@ function registerSearchCommand(program2) {
|
|
|
12213
12873
|
process.stdout.write(renderHits(s, hits, needle));
|
|
12214
12874
|
});
|
|
12215
12875
|
}
|
|
12216
|
-
var SNIPPET_LEN = 80;
|
|
12217
12876
|
function makeSnippet(raw, needle) {
|
|
12218
12877
|
const idx = raw.toLowerCase().indexOf(needle);
|
|
12219
12878
|
if (idx < 0) return raw.slice(0, SNIPPET_LEN);
|
|
@@ -12223,12 +12882,6 @@ function makeSnippet(raw, needle) {
|
|
|
12223
12882
|
const suffix = end < raw.length ? "\u2026" : "";
|
|
12224
12883
|
return prefix + raw.slice(start, end).replace(/\s+/g, " ") + suffix;
|
|
12225
12884
|
}
|
|
12226
|
-
var METHOD_MAX2 = 32;
|
|
12227
|
-
var COLUMNS2 = [
|
|
12228
|
-
{ title: "LOC", align: "left" },
|
|
12229
|
-
{ title: "METHOD", align: "left" },
|
|
12230
|
-
{ title: "MATCH", align: "left" }
|
|
12231
|
-
];
|
|
12232
12885
|
function renderHits(s, hits, needle) {
|
|
12233
12886
|
const body = hits.map((h) => [
|
|
12234
12887
|
s.cyan(`#${h.sessionId}/${h.seq}`),
|
|
@@ -12257,12 +12910,32 @@ function truncate2(s, max) {
|
|
|
12257
12910
|
if (s.length <= max) return s;
|
|
12258
12911
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
12259
12912
|
}
|
|
12913
|
+
var SNIPPET_LEN, METHOD_MAX2, COLUMNS2;
|
|
12914
|
+
var init_search = __esm({
|
|
12915
|
+
"src/commands/search.ts"() {
|
|
12916
|
+
"use strict";
|
|
12917
|
+
init_dist();
|
|
12918
|
+
init_style();
|
|
12919
|
+
init_table();
|
|
12920
|
+
SNIPPET_LEN = 80;
|
|
12921
|
+
METHOD_MAX2 = 32;
|
|
12922
|
+
COLUMNS2 = [
|
|
12923
|
+
{ title: "LOC", align: "left" },
|
|
12924
|
+
{ title: "METHOD", align: "left" },
|
|
12925
|
+
{ title: "MATCH", align: "left" }
|
|
12926
|
+
];
|
|
12927
|
+
}
|
|
12928
|
+
});
|
|
12260
12929
|
|
|
12261
12930
|
// src/commands/session-info.ts
|
|
12262
|
-
function registerSessionInfoCommand(
|
|
12263
|
-
|
|
12931
|
+
function registerSessionInfoCommand(program) {
|
|
12932
|
+
program.command("session-info").description(
|
|
12264
12933
|
"Print derived client/agent metadata for a saved session \u2014 the terminal equivalent of the inspector's session info panel."
|
|
12265
|
-
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--json", "machine-readable JSON instead of human-readable text").
|
|
12934
|
+
).argument("<id>", "session id (see `acp-devtools list`)").option("--db <path>", "captures database", defaultCapturesDbPath()).option("--json", "machine-readable JSON instead of human-readable text").option(
|
|
12935
|
+
"--raw",
|
|
12936
|
+
"skip default redaction of auth headers / proxy tokens (use only when the output stays on YOUR machine)",
|
|
12937
|
+
false
|
|
12938
|
+
).action((rawId, opts) => {
|
|
12266
12939
|
const id = Number(rawId);
|
|
12267
12940
|
if (!Number.isInteger(id) || id <= 0) {
|
|
12268
12941
|
process.stderr.write(`acp-devtools: invalid id "${rawId}"
|
|
@@ -12292,8 +12965,30 @@ function registerSessionInfoCommand(program2) {
|
|
|
12292
12965
|
}
|
|
12293
12966
|
const messages = [];
|
|
12294
12967
|
for (const m of session.messages()) messages.push(m);
|
|
12295
|
-
const meta = extractSessionMetadata(messages);
|
|
12296
12968
|
db.close();
|
|
12969
|
+
let fieldsRedacted = 0;
|
|
12970
|
+
let messagesAffected = 0;
|
|
12971
|
+
const display = opts.raw ? messages : messages.map((m) => {
|
|
12972
|
+
const r = redactMessage(m);
|
|
12973
|
+
if (r.count > 0) {
|
|
12974
|
+
messagesAffected += 1;
|
|
12975
|
+
fieldsRedacted += r.count;
|
|
12976
|
+
}
|
|
12977
|
+
return r.redacted;
|
|
12978
|
+
});
|
|
12979
|
+
const meta = extractSessionMetadata(display);
|
|
12980
|
+
const writeRedactionNote = () => {
|
|
12981
|
+
if (opts.raw) {
|
|
12982
|
+
process.stderr.write(
|
|
12983
|
+
"acp-devtools: --raw was set; output may contain auth headers / proxy tokens \u2014 do not share publicly\n"
|
|
12984
|
+
);
|
|
12985
|
+
} else if (fieldsRedacted > 0) {
|
|
12986
|
+
process.stderr.write(
|
|
12987
|
+
`acp-devtools: redacted ${fieldsRedacted} field(s) across ${messagesAffected} message(s) \u2014 re-run with --raw to print them
|
|
12988
|
+
`
|
|
12989
|
+
);
|
|
12990
|
+
}
|
|
12991
|
+
};
|
|
12297
12992
|
if (opts.json) {
|
|
12298
12993
|
process.stdout.write(
|
|
12299
12994
|
JSON.stringify(
|
|
@@ -12313,9 +13008,11 @@ function registerSessionInfoCommand(program2) {
|
|
|
12313
13008
|
2
|
|
12314
13009
|
) + "\n"
|
|
12315
13010
|
);
|
|
13011
|
+
writeRedactionNote();
|
|
12316
13012
|
return;
|
|
12317
13013
|
}
|
|
12318
13014
|
renderText(session, meta, messages.length);
|
|
13015
|
+
writeRedactionNote();
|
|
12319
13016
|
});
|
|
12320
13017
|
}
|
|
12321
13018
|
function renderText(session, meta, msgCount) {
|
|
@@ -12325,12 +13022,10 @@ function renderText(session, meta, msgCount) {
|
|
|
12325
13022
|
`);
|
|
12326
13023
|
w.write(s.dim("\u2500".repeat(48)) + "\n");
|
|
12327
13024
|
const clientLabel = meta.client.title ?? meta.client.name ?? session.info.clientName ?? "\u2014";
|
|
12328
|
-
const clientVer = meta.client.version ? ` v${meta.client.version}` : "";
|
|
12329
13025
|
const clientPlatform = meta.client.platform ? ` (${meta.client.platform})` : "";
|
|
12330
|
-
w.write(pad(s, "Client", `${clientLabel}${
|
|
13026
|
+
w.write(pad(s, "Client", `${withVersion(clientLabel, meta.client.version)}${clientPlatform}`));
|
|
12331
13027
|
const agentLabel = meta.agent.name ?? shortAgent(session.info.agentCommand) ?? "\u2014";
|
|
12332
|
-
|
|
12333
|
-
w.write(pad(s, "Agent", `${agentLabel}${agentVer}`));
|
|
13028
|
+
w.write(pad(s, "Agent", withVersion(agentLabel, meta.agent.version)));
|
|
12334
13029
|
w.write(pad(s, "Protocol", meta.protocolVersion !== null ? `ACP v${meta.protocolVersion}` : "\u2014"));
|
|
12335
13030
|
w.write(
|
|
12336
13031
|
pad(
|
|
@@ -12378,6 +13073,10 @@ ${heading2(s, "JETBRAINS EXTENSIONS")}
|
|
|
12378
13073
|
);
|
|
12379
13074
|
}
|
|
12380
13075
|
}
|
|
13076
|
+
function withVersion(label, version) {
|
|
13077
|
+
if (!version || label.includes(version)) return label;
|
|
13078
|
+
return `${label} v${version}`;
|
|
13079
|
+
}
|
|
12381
13080
|
function heading2(s, label) {
|
|
12382
13081
|
return s.bold(s.yellow(label));
|
|
12383
13082
|
}
|
|
@@ -12400,11 +13099,17 @@ function shortAgent(agentCommand) {
|
|
|
12400
13099
|
}
|
|
12401
13100
|
return parts[0] ?? null;
|
|
12402
13101
|
}
|
|
13102
|
+
var init_session_info = __esm({
|
|
13103
|
+
"src/commands/session-info.ts"() {
|
|
13104
|
+
"use strict";
|
|
13105
|
+
init_dist();
|
|
13106
|
+
init_style();
|
|
13107
|
+
}
|
|
13108
|
+
});
|
|
12403
13109
|
|
|
12404
13110
|
// src/commands/stats.ts
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
program2.command("stats").description(
|
|
13111
|
+
function registerStatsCommand(program) {
|
|
13112
|
+
program.command("stats").description(
|
|
12408
13113
|
"Aggregate counts and latency percentiles for one saved session \u2014 the equivalent of the inspector's footer StatsBar."
|
|
12409
13114
|
).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) => {
|
|
12410
13115
|
const id = Number(rawId);
|
|
@@ -12512,13 +13217,13 @@ function computeStats(info, messages) {
|
|
|
12512
13217
|
p90: latencies.length ? percentile2(latencies, 90) : null,
|
|
12513
13218
|
p99: latencies.length ? percentile2(latencies, 99) : null,
|
|
12514
13219
|
max: latencies.length ? latencies[latencies.length - 1] : null,
|
|
12515
|
-
mean: latencies.length ? Math.round(
|
|
13220
|
+
mean: latencies.length ? Math.round(mean2(latencies)) : null
|
|
12516
13221
|
},
|
|
12517
13222
|
perMethod,
|
|
12518
13223
|
insights
|
|
12519
13224
|
};
|
|
12520
13225
|
}
|
|
12521
|
-
function
|
|
13226
|
+
function mean2(xs) {
|
|
12522
13227
|
return xs.reduce((acc, x) => acc + x, 0) / xs.length;
|
|
12523
13228
|
}
|
|
12524
13229
|
function formatLatency3(ms) {
|
|
@@ -12623,27 +13328,6 @@ function renderStats(s, stats, byMethod) {
|
|
|
12623
13328
|
}
|
|
12624
13329
|
return lines.join("");
|
|
12625
13330
|
}
|
|
12626
|
-
var INSIGHT_GLYPH = {
|
|
12627
|
-
hotspot: "!",
|
|
12628
|
-
"long-tail": "~",
|
|
12629
|
-
outlier: "*",
|
|
12630
|
-
busiest: "#",
|
|
12631
|
-
errors: "x"
|
|
12632
|
-
};
|
|
12633
|
-
var INSIGHT_LABEL = {
|
|
12634
|
-
hotspot: "HOTSPOT ",
|
|
12635
|
-
"long-tail": "LONG TAIL",
|
|
12636
|
-
outlier: "OUTLIER ",
|
|
12637
|
-
busiest: "BUSIEST ",
|
|
12638
|
-
errors: "ERRORS "
|
|
12639
|
-
};
|
|
12640
|
-
var INSIGHT_COLOR = {
|
|
12641
|
-
hotspot: "red",
|
|
12642
|
-
"long-tail": "yellow",
|
|
12643
|
-
outlier: "magenta",
|
|
12644
|
-
busiest: "cyan",
|
|
12645
|
-
errors: "red"
|
|
12646
|
-
};
|
|
12647
13331
|
function renderInsights(s, insights) {
|
|
12648
13332
|
const lines = [heading3(s, "INSIGHTS") + "\n"];
|
|
12649
13333
|
for (const i of insights) {
|
|
@@ -12656,7 +13340,6 @@ function renderInsights(s, insights) {
|
|
|
12656
13340
|
}
|
|
12657
13341
|
return lines.join("");
|
|
12658
13342
|
}
|
|
12659
|
-
var METHOD_MAX3 = 40;
|
|
12660
13343
|
function renderPerMethod(s, rows) {
|
|
12661
13344
|
const columns = [
|
|
12662
13345
|
{ title: "METHOD", align: "left" },
|
|
@@ -12681,6 +13364,39 @@ function renderPerMethod(s, rows) {
|
|
|
12681
13364
|
});
|
|
12682
13365
|
return renderTable(s, columns, body);
|
|
12683
13366
|
}
|
|
13367
|
+
var percentile2, INSIGHT_GLYPH, INSIGHT_LABEL, INSIGHT_COLOR, METHOD_MAX3;
|
|
13368
|
+
var init_stats = __esm({
|
|
13369
|
+
"src/commands/stats.ts"() {
|
|
13370
|
+
"use strict";
|
|
13371
|
+
init_dist();
|
|
13372
|
+
init_style();
|
|
13373
|
+
init_palette();
|
|
13374
|
+
init_table();
|
|
13375
|
+
percentile2 = percentile;
|
|
13376
|
+
INSIGHT_GLYPH = {
|
|
13377
|
+
hotspot: "!",
|
|
13378
|
+
"long-tail": "~",
|
|
13379
|
+
outlier: "*",
|
|
13380
|
+
busiest: "#",
|
|
13381
|
+
errors: "x"
|
|
13382
|
+
};
|
|
13383
|
+
INSIGHT_LABEL = {
|
|
13384
|
+
hotspot: "HOTSPOT ",
|
|
13385
|
+
"long-tail": "LONG TAIL",
|
|
13386
|
+
outlier: "OUTLIER ",
|
|
13387
|
+
busiest: "BUSIEST ",
|
|
13388
|
+
errors: "ERRORS "
|
|
13389
|
+
};
|
|
13390
|
+
INSIGHT_COLOR = {
|
|
13391
|
+
hotspot: "red",
|
|
13392
|
+
"long-tail": "yellow",
|
|
13393
|
+
outlier: "magenta",
|
|
13394
|
+
busiest: "cyan",
|
|
13395
|
+
errors: "red"
|
|
13396
|
+
};
|
|
13397
|
+
METHOD_MAX3 = 40;
|
|
13398
|
+
}
|
|
13399
|
+
});
|
|
12684
13400
|
|
|
12685
13401
|
// src/commands/ui.ts
|
|
12686
13402
|
import { createReadStream, existsSync as existsSync7, statSync as statSync3 } from "fs";
|
|
@@ -12688,26 +13404,6 @@ import { createServer } from "http";
|
|
|
12688
13404
|
import { dirname as dirname5, extname, join as join4, normalize, resolve, sep } from "path";
|
|
12689
13405
|
import { fileURLToPath } from "url";
|
|
12690
13406
|
import { spawn as spawn2 } from "child_process";
|
|
12691
|
-
var MIME_TYPES = {
|
|
12692
|
-
".html": "text/html; charset=utf-8",
|
|
12693
|
-
".js": "application/javascript; charset=utf-8",
|
|
12694
|
-
".mjs": "application/javascript; charset=utf-8",
|
|
12695
|
-
".css": "text/css; charset=utf-8",
|
|
12696
|
-
".json": "application/json; charset=utf-8",
|
|
12697
|
-
".map": "application/json; charset=utf-8",
|
|
12698
|
-
".svg": "image/svg+xml",
|
|
12699
|
-
".png": "image/png",
|
|
12700
|
-
".jpg": "image/jpeg",
|
|
12701
|
-
".jpeg": "image/jpeg",
|
|
12702
|
-
".gif": "image/gif",
|
|
12703
|
-
".webp": "image/webp",
|
|
12704
|
-
".ico": "image/x-icon",
|
|
12705
|
-
".woff": "font/woff",
|
|
12706
|
-
".woff2": "font/woff2",
|
|
12707
|
-
".ttf": "font/ttf",
|
|
12708
|
-
".otf": "font/otf",
|
|
12709
|
-
".txt": "text/plain; charset=utf-8"
|
|
12710
|
-
};
|
|
12711
13407
|
function uiDistDir() {
|
|
12712
13408
|
const here = dirname5(fileURLToPath(import.meta.url));
|
|
12713
13409
|
return resolve(here, "ui");
|
|
@@ -12762,8 +13458,8 @@ function openBrowser(url) {
|
|
|
12762
13458
|
} catch {
|
|
12763
13459
|
}
|
|
12764
13460
|
}
|
|
12765
|
-
function registerUiCommand(
|
|
12766
|
-
|
|
13461
|
+
function registerUiCommand(program) {
|
|
13462
|
+
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(
|
|
12767
13463
|
"--captures-db <file>",
|
|
12768
13464
|
"path to the captures SQLite database (default: ~/.acp-devtools/captures.db)"
|
|
12769
13465
|
).action(async (opts) => {
|
|
@@ -12783,15 +13479,17 @@ function registerUiCommand(program2) {
|
|
|
12783
13479
|
process.exit(1);
|
|
12784
13480
|
}
|
|
12785
13481
|
const capturesDbPath = opts.capturesDb ?? defaultCapturesDbPath();
|
|
13482
|
+
const allowedHosts = [opts.host];
|
|
12786
13483
|
const apiHandler = createApiHandler({
|
|
12787
13484
|
capturesDbPath,
|
|
12788
|
-
binaryPath: process.argv[1] ?? null
|
|
13485
|
+
binaryPath: process.argv[1] ?? null,
|
|
13486
|
+
allowedHosts
|
|
12789
13487
|
});
|
|
12790
13488
|
const server = createServer((req, res) => {
|
|
12791
13489
|
if (apiHandler(req, res)) return;
|
|
12792
13490
|
serveStatic(req, res, rootDir);
|
|
12793
13491
|
});
|
|
12794
|
-
attachReplayUpgrade(server, { capturesDbPath });
|
|
13492
|
+
attachReplayUpgrade(server, { capturesDbPath, allowedHosts });
|
|
12795
13493
|
await new Promise((resolveListen, rejectListen) => {
|
|
12796
13494
|
server.once("error", rejectListen);
|
|
12797
13495
|
server.listen(port, opts.host, () => {
|
|
@@ -12826,10 +13524,37 @@ function registerUiCommand(program2) {
|
|
|
12826
13524
|
});
|
|
12827
13525
|
});
|
|
12828
13526
|
}
|
|
13527
|
+
var MIME_TYPES;
|
|
13528
|
+
var init_ui = __esm({
|
|
13529
|
+
"src/commands/ui.ts"() {
|
|
13530
|
+
"use strict";
|
|
13531
|
+
init_dist();
|
|
13532
|
+
MIME_TYPES = {
|
|
13533
|
+
".html": "text/html; charset=utf-8",
|
|
13534
|
+
".js": "application/javascript; charset=utf-8",
|
|
13535
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
13536
|
+
".css": "text/css; charset=utf-8",
|
|
13537
|
+
".json": "application/json; charset=utf-8",
|
|
13538
|
+
".map": "application/json; charset=utf-8",
|
|
13539
|
+
".svg": "image/svg+xml",
|
|
13540
|
+
".png": "image/png",
|
|
13541
|
+
".jpg": "image/jpeg",
|
|
13542
|
+
".jpeg": "image/jpeg",
|
|
13543
|
+
".gif": "image/gif",
|
|
13544
|
+
".webp": "image/webp",
|
|
13545
|
+
".ico": "image/x-icon",
|
|
13546
|
+
".woff": "font/woff",
|
|
13547
|
+
".woff2": "font/woff2",
|
|
13548
|
+
".ttf": "font/ttf",
|
|
13549
|
+
".otf": "font/otf",
|
|
13550
|
+
".txt": "text/plain; charset=utf-8"
|
|
13551
|
+
};
|
|
13552
|
+
}
|
|
13553
|
+
});
|
|
12829
13554
|
|
|
12830
13555
|
// src/commands/validate.ts
|
|
12831
|
-
function registerValidateCommand(
|
|
12832
|
-
|
|
13556
|
+
function registerValidateCommand(program) {
|
|
13557
|
+
program.command("validate").description(
|
|
12833
13558
|
"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."
|
|
12834
13559
|
).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) => {
|
|
12835
13560
|
const id = Number(rawId);
|
|
@@ -12933,16 +13658,6 @@ function registerValidateCommand(program2) {
|
|
|
12933
13658
|
process.exit(1);
|
|
12934
13659
|
});
|
|
12935
13660
|
}
|
|
12936
|
-
var METHOD_MAX4 = 28;
|
|
12937
|
-
var SCHEMA_MAX = 28;
|
|
12938
|
-
var PATH_MAX = 38;
|
|
12939
|
-
var COLUMNS3 = [
|
|
12940
|
-
{ title: "SEQ", align: "right" },
|
|
12941
|
-
{ title: "METHOD", align: "left" },
|
|
12942
|
-
{ title: "SCHEMA", align: "left" },
|
|
12943
|
-
{ title: "PATH", align: "left" },
|
|
12944
|
-
{ title: "ERROR", align: "left" }
|
|
12945
|
-
];
|
|
12946
13661
|
function renderViolations(s, violations) {
|
|
12947
13662
|
const body = violations.map((v) => [
|
|
12948
13663
|
s.dim(`#${v.seq}`),
|
|
@@ -12957,6 +13672,26 @@ function truncate3(s, max) {
|
|
|
12957
13672
|
if (s.length <= max) return s;
|
|
12958
13673
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
12959
13674
|
}
|
|
13675
|
+
var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS3;
|
|
13676
|
+
var init_validate2 = __esm({
|
|
13677
|
+
"src/commands/validate.ts"() {
|
|
13678
|
+
"use strict";
|
|
13679
|
+
init_dist();
|
|
13680
|
+
init_inspect();
|
|
13681
|
+
init_style();
|
|
13682
|
+
init_table();
|
|
13683
|
+
METHOD_MAX4 = 28;
|
|
13684
|
+
SCHEMA_MAX = 28;
|
|
13685
|
+
PATH_MAX = 38;
|
|
13686
|
+
COLUMNS3 = [
|
|
13687
|
+
{ title: "SEQ", align: "right" },
|
|
13688
|
+
{ title: "METHOD", align: "left" },
|
|
13689
|
+
{ title: "SCHEMA", align: "left" },
|
|
13690
|
+
{ title: "PATH", align: "left" },
|
|
13691
|
+
{ title: "ERROR", align: "left" }
|
|
13692
|
+
];
|
|
13693
|
+
}
|
|
13694
|
+
});
|
|
12960
13695
|
|
|
12961
13696
|
// src/lib/help.ts
|
|
12962
13697
|
import { homedir as homedir4 } from "os";
|
|
@@ -12966,102 +13701,6 @@ function tidyHome(text, home = homedir4()) {
|
|
|
12966
13701
|
const collapsed = text.split(escapedHome).join("~").split(home).join("~");
|
|
12967
13702
|
return collapsed.replace(/~[^"\s)]*/g, (segment) => segment.replace(/\\+/g, "/"));
|
|
12968
13703
|
}
|
|
12969
|
-
var GROUPS = [
|
|
12970
|
-
{ title: "Capture", commands: ["proxy", "mock-agent", "mock-editor"] },
|
|
12971
|
-
{
|
|
12972
|
-
title: "Inspect",
|
|
12973
|
-
commands: ["list", "inspect", "search", "stats", "diff", "session-info", "validate"]
|
|
12974
|
-
},
|
|
12975
|
-
{ title: "View", commands: ["ui", "replay"] },
|
|
12976
|
-
{ title: "Manage", commands: ["export", "import", "delete", "backfill-metadata"] },
|
|
12977
|
-
{ title: "Setup", commands: ["doctor", "mcp"] }
|
|
12978
|
-
];
|
|
12979
|
-
var SUMMARIES = {
|
|
12980
|
-
proxy: "Capture an agent's traffic through a proxy",
|
|
12981
|
-
"mock-agent": "Replay a session as a fake ACP agent",
|
|
12982
|
-
"mock-editor": "Replay a session as a fake editor",
|
|
12983
|
-
list: "List saved sessions, newest first",
|
|
12984
|
-
inspect: "Print a session's frames to stdout",
|
|
12985
|
-
search: "Search frame payloads across sessions",
|
|
12986
|
-
stats: "Latency percentiles for one session",
|
|
12987
|
-
diff: "Compare two sessions frame by frame",
|
|
12988
|
-
"session-info": "Show a session's client/agent metadata",
|
|
12989
|
-
validate: "Check frames against the ACP spec",
|
|
12990
|
-
ui: "Open the web inspector",
|
|
12991
|
-
replay: "Stream a session to the UI over WebSocket",
|
|
12992
|
-
export: "Export a session to self-contained JSON",
|
|
12993
|
-
import: "Import a JSON export into the store",
|
|
12994
|
-
delete: "Delete saved sessions permanently",
|
|
12995
|
-
"backfill-metadata": "Recompute session metadata columns",
|
|
12996
|
-
doctor: "Diagnose setup and report issues",
|
|
12997
|
-
mcp: "Expose captures to Claude over MCP"
|
|
12998
|
-
};
|
|
12999
|
-
var ROOT_EXAMPLES = [
|
|
13000
|
-
"# Capture a Claude Code session (Zed adapter)",
|
|
13001
|
-
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
13002
|
-
"",
|
|
13003
|
-
"# Browse captured sessions in the web inspector",
|
|
13004
|
-
"acp-devtools ui",
|
|
13005
|
-
"",
|
|
13006
|
-
"# List sessions, then print one to the terminal",
|
|
13007
|
-
"acp-devtools list",
|
|
13008
|
-
"acp-devtools inspect 12",
|
|
13009
|
-
"",
|
|
13010
|
-
"# Diagnose your setup when nothing's being captured",
|
|
13011
|
-
"acp-devtools doctor"
|
|
13012
|
-
];
|
|
13013
|
-
var EXAMPLES = {
|
|
13014
|
-
proxy: [
|
|
13015
|
-
"# Wrap the Zed Claude Code adapter and capture every frame",
|
|
13016
|
-
"acp-devtools proxy npx -y @zed-industries/claude-code-acp",
|
|
13017
|
-
"",
|
|
13018
|
-
"# Capture headless, into a specific database",
|
|
13019
|
-
"acp-devtools proxy --no-ws --save-to /tmp/run.db node ./my-agent.js"
|
|
13020
|
-
],
|
|
13021
|
-
ui: [
|
|
13022
|
-
"# Serve the inspector on :3737 and open a browser",
|
|
13023
|
-
"acp-devtools ui",
|
|
13024
|
-
"",
|
|
13025
|
-
"# Pick a port and stay headless",
|
|
13026
|
-
"acp-devtools ui --port 4000 --no-open"
|
|
13027
|
-
],
|
|
13028
|
-
list: [
|
|
13029
|
-
"acp-devtools list",
|
|
13030
|
-
"",
|
|
13031
|
-
"# Machine-readable JSON",
|
|
13032
|
-
"acp-devtools list --json"
|
|
13033
|
-
],
|
|
13034
|
-
inspect: [
|
|
13035
|
-
"acp-devtools inspect 12",
|
|
13036
|
-
"",
|
|
13037
|
-
"# Only request/response frames for one method",
|
|
13038
|
-
"acp-devtools inspect 12 --method session/prompt --paired"
|
|
13039
|
-
],
|
|
13040
|
-
search: ["acp-devtools search session/prompt", "acp-devtools search rate_limit --in-payload"],
|
|
13041
|
-
stats: ["acp-devtools stats 12", "acp-devtools stats 12 --by-method"],
|
|
13042
|
-
diff: ["# Worked yesterday, broke today?", "acp-devtools diff 41 42"],
|
|
13043
|
-
export: [
|
|
13044
|
-
"acp-devtools export 12 -o session-12.json",
|
|
13045
|
-
"",
|
|
13046
|
-
"# Straight to stdout for sharing",
|
|
13047
|
-
"acp-devtools export 12 > session-12.json"
|
|
13048
|
-
],
|
|
13049
|
-
import: ["acp-devtools import session-12.json"],
|
|
13050
|
-
delete: ["acp-devtools delete 12", "acp-devtools delete 12 13 14"],
|
|
13051
|
-
replay: ["acp-devtools replay 12", "acp-devtools replay --file session-12.json"],
|
|
13052
|
-
validate: ["acp-devtools validate 12"],
|
|
13053
|
-
"session-info": ["acp-devtools session-info 12"],
|
|
13054
|
-
doctor: ["acp-devtools doctor"],
|
|
13055
|
-
mcp: ["# Read-only MCP server over stdio (wire into .claude/mcp_servers.json)", "acp-devtools mcp"],
|
|
13056
|
-
"backfill-metadata": [
|
|
13057
|
-
"acp-devtools backfill-metadata",
|
|
13058
|
-
"",
|
|
13059
|
-
"# Recompute a single session",
|
|
13060
|
-
"acp-devtools backfill-metadata 12"
|
|
13061
|
-
]
|
|
13062
|
-
};
|
|
13063
|
-
var INDENT = " ";
|
|
13064
|
-
var GAP = 3;
|
|
13065
13704
|
function heading4(s, title) {
|
|
13066
13705
|
return s.bold(s.yellow(title.toUpperCase()));
|
|
13067
13706
|
}
|
|
@@ -13189,8 +13828,8 @@ function formatCommandHelp(cmd, helper, s, width) {
|
|
|
13189
13828
|
}
|
|
13190
13829
|
return out.join("\n");
|
|
13191
13830
|
}
|
|
13192
|
-
function configureCliHelp(
|
|
13193
|
-
|
|
13831
|
+
function configureCliHelp(program) {
|
|
13832
|
+
program.configureHelp({
|
|
13194
13833
|
helpWidth: process.stdout.columns ?? 80,
|
|
13195
13834
|
formatHelp(cmd, helper) {
|
|
13196
13835
|
const s = createStyler(colorEnabled(process.stdout));
|
|
@@ -13199,37 +13838,252 @@ function configureCliHelp(program2) {
|
|
|
13199
13838
|
}
|
|
13200
13839
|
});
|
|
13201
13840
|
const err = createStyler(colorEnabled(process.stderr));
|
|
13202
|
-
|
|
13841
|
+
program.configureOutput({
|
|
13203
13842
|
outputError(str, write) {
|
|
13204
13843
|
write(err.red(str));
|
|
13205
13844
|
}
|
|
13206
13845
|
});
|
|
13207
13846
|
}
|
|
13847
|
+
var GROUPS, SUMMARIES, ROOT_EXAMPLES, EXAMPLES, INDENT, GAP;
|
|
13848
|
+
var init_help = __esm({
|
|
13849
|
+
"src/lib/help.ts"() {
|
|
13850
|
+
"use strict";
|
|
13851
|
+
init_style();
|
|
13852
|
+
GROUPS = [
|
|
13853
|
+
{ title: "Capture", commands: ["proxy", "mock-agent", "mock-editor"] },
|
|
13854
|
+
{
|
|
13855
|
+
title: "Inspect",
|
|
13856
|
+
commands: ["list", "inspect", "search", "stats", "diff", "session-info", "validate"]
|
|
13857
|
+
},
|
|
13858
|
+
{ title: "View", commands: ["ui", "replay"] },
|
|
13859
|
+
{ title: "Manage", commands: ["export", "import", "delete", "backfill-metadata"] },
|
|
13860
|
+
{ title: "Setup", commands: ["doctor", "mcp"] }
|
|
13861
|
+
];
|
|
13862
|
+
SUMMARIES = {
|
|
13863
|
+
proxy: "Capture an agent's traffic through a proxy",
|
|
13864
|
+
"mock-agent": "Replay a session as a fake ACP agent",
|
|
13865
|
+
"mock-editor": "Replay a session as a fake editor",
|
|
13866
|
+
list: "List saved sessions, newest first",
|
|
13867
|
+
inspect: "Print a session's frames to stdout",
|
|
13868
|
+
search: "Search frame payloads across sessions",
|
|
13869
|
+
stats: "Latency percentiles for one session",
|
|
13870
|
+
diff: "Compare two sessions frame by frame",
|
|
13871
|
+
"session-info": "Show a session's client/agent metadata",
|
|
13872
|
+
validate: "Check frames against the ACP spec",
|
|
13873
|
+
ui: "Open the web inspector",
|
|
13874
|
+
replay: "Stream a session to the UI over WebSocket",
|
|
13875
|
+
export: "Export a session to self-contained JSON",
|
|
13876
|
+
import: "Import a JSON export into the store",
|
|
13877
|
+
delete: "Delete saved sessions permanently",
|
|
13878
|
+
"backfill-metadata": "Recompute session metadata columns",
|
|
13879
|
+
doctor: "Diagnose setup and report issues",
|
|
13880
|
+
mcp: "Expose captures to Claude over MCP"
|
|
13881
|
+
};
|
|
13882
|
+
ROOT_EXAMPLES = [
|
|
13883
|
+
"# Capture a Claude Code session (Zed adapter)",
|
|
13884
|
+
"acp-devtools proxy npx -y @agentclientprotocol/claude-agent-acp",
|
|
13885
|
+
"",
|
|
13886
|
+
"# Browse captured sessions in the web inspector",
|
|
13887
|
+
"acp-devtools ui",
|
|
13888
|
+
"",
|
|
13889
|
+
"# List sessions, then print one to the terminal",
|
|
13890
|
+
"acp-devtools list",
|
|
13891
|
+
"acp-devtools inspect 12",
|
|
13892
|
+
"",
|
|
13893
|
+
"# Diagnose your setup when nothing's being captured",
|
|
13894
|
+
"acp-devtools doctor"
|
|
13895
|
+
];
|
|
13896
|
+
EXAMPLES = {
|
|
13897
|
+
proxy: [
|
|
13898
|
+
"# Wrap the Zed Claude Code adapter and capture every frame",
|
|
13899
|
+
"acp-devtools proxy npx -y @agentclientprotocol/claude-agent-acp",
|
|
13900
|
+
"",
|
|
13901
|
+
"# Capture headless, into a specific database",
|
|
13902
|
+
"acp-devtools proxy --no-ws --save-to /tmp/run.db node ./my-agent.js"
|
|
13903
|
+
],
|
|
13904
|
+
ui: [
|
|
13905
|
+
"# Serve the inspector on :3737 and open a browser",
|
|
13906
|
+
"acp-devtools ui",
|
|
13907
|
+
"",
|
|
13908
|
+
"# Pick a port and stay headless",
|
|
13909
|
+
"acp-devtools ui --port 4000 --no-open"
|
|
13910
|
+
],
|
|
13911
|
+
list: [
|
|
13912
|
+
"acp-devtools list",
|
|
13913
|
+
"",
|
|
13914
|
+
"# Machine-readable JSON",
|
|
13915
|
+
"acp-devtools list --json"
|
|
13916
|
+
],
|
|
13917
|
+
inspect: [
|
|
13918
|
+
"acp-devtools inspect 12",
|
|
13919
|
+
"",
|
|
13920
|
+
"# Only request/response frames for one method",
|
|
13921
|
+
"acp-devtools inspect 12 --method session/prompt --paired"
|
|
13922
|
+
],
|
|
13923
|
+
search: ["acp-devtools search session/prompt", "acp-devtools search rate_limit --in-payload"],
|
|
13924
|
+
stats: ["acp-devtools stats 12", "acp-devtools stats 12 --by-method"],
|
|
13925
|
+
diff: ["# Worked yesterday, broke today?", "acp-devtools diff 41 42"],
|
|
13926
|
+
export: [
|
|
13927
|
+
"acp-devtools export 12 -o session-12.json",
|
|
13928
|
+
"",
|
|
13929
|
+
"# Straight to stdout for sharing",
|
|
13930
|
+
"acp-devtools export 12 > session-12.json"
|
|
13931
|
+
],
|
|
13932
|
+
import: ["acp-devtools import session-12.json"],
|
|
13933
|
+
delete: ["acp-devtools delete 12", "acp-devtools delete 12 13 14"],
|
|
13934
|
+
replay: ["acp-devtools replay 12", "acp-devtools replay --file session-12.json"],
|
|
13935
|
+
validate: ["acp-devtools validate 12"],
|
|
13936
|
+
"session-info": ["acp-devtools session-info 12"],
|
|
13937
|
+
doctor: ["acp-devtools doctor"],
|
|
13938
|
+
mcp: ["# Read-only MCP server over stdio (wire into .claude/mcp_servers.json)", "acp-devtools mcp"],
|
|
13939
|
+
"backfill-metadata": [
|
|
13940
|
+
"acp-devtools backfill-metadata",
|
|
13941
|
+
"",
|
|
13942
|
+
"# Recompute a single session",
|
|
13943
|
+
"acp-devtools backfill-metadata 12"
|
|
13944
|
+
]
|
|
13945
|
+
};
|
|
13946
|
+
INDENT = " ";
|
|
13947
|
+
GAP = 3;
|
|
13948
|
+
}
|
|
13949
|
+
});
|
|
13208
13950
|
|
|
13209
13951
|
// src/program.ts
|
|
13952
|
+
var program_exports = {};
|
|
13953
|
+
__export(program_exports, {
|
|
13954
|
+
buildProgram: () => buildProgram
|
|
13955
|
+
});
|
|
13956
|
+
import { Command } from "commander";
|
|
13210
13957
|
function buildProgram() {
|
|
13211
|
-
const
|
|
13212
|
-
|
|
13213
|
-
configureCliHelp(
|
|
13214
|
-
registerProxyCommand(
|
|
13215
|
-
registerReplayCommand(
|
|
13216
|
-
registerUiCommand(
|
|
13217
|
-
registerDoctorCommand(
|
|
13218
|
-
registerExportCommand(
|
|
13219
|
-
registerImportCommand(
|
|
13220
|
-
registerDeleteCommand(
|
|
13221
|
-
registerDiffCommand(
|
|
13222
|
-
registerListCommand(
|
|
13223
|
-
registerInspectCommand(
|
|
13224
|
-
registerSearchCommand(
|
|
13225
|
-
registerSessionInfoCommand(
|
|
13226
|
-
registerStatsCommand(
|
|
13227
|
-
registerBackfillMetadataCommand(
|
|
13228
|
-
registerMcpCommand(
|
|
13229
|
-
registerMockAgentCommand(
|
|
13230
|
-
registerMockEditorCommand(
|
|
13231
|
-
registerValidateCommand(
|
|
13232
|
-
return
|
|
13958
|
+
const program = new Command();
|
|
13959
|
+
program.name("acp-devtools").description("Visual debugger / inspector for the Agent Client Protocol (ACP)").version(CLI_VERSION).enablePositionalOptions();
|
|
13960
|
+
configureCliHelp(program);
|
|
13961
|
+
registerProxyCommand(program);
|
|
13962
|
+
registerReplayCommand(program);
|
|
13963
|
+
registerUiCommand(program);
|
|
13964
|
+
registerDoctorCommand(program);
|
|
13965
|
+
registerExportCommand(program);
|
|
13966
|
+
registerImportCommand(program);
|
|
13967
|
+
registerDeleteCommand(program);
|
|
13968
|
+
registerDiffCommand(program);
|
|
13969
|
+
registerListCommand(program);
|
|
13970
|
+
registerInspectCommand(program);
|
|
13971
|
+
registerSearchCommand(program);
|
|
13972
|
+
registerSessionInfoCommand(program);
|
|
13973
|
+
registerStatsCommand(program);
|
|
13974
|
+
registerBackfillMetadataCommand(program);
|
|
13975
|
+
registerMcpCommand(program);
|
|
13976
|
+
registerMockAgentCommand(program);
|
|
13977
|
+
registerMockEditorCommand(program);
|
|
13978
|
+
registerValidateCommand(program);
|
|
13979
|
+
return program;
|
|
13980
|
+
}
|
|
13981
|
+
var init_program = __esm({
|
|
13982
|
+
"src/program.ts"() {
|
|
13983
|
+
"use strict";
|
|
13984
|
+
init_backfill_metadata();
|
|
13985
|
+
init_delete();
|
|
13986
|
+
init_diff();
|
|
13987
|
+
init_doctor();
|
|
13988
|
+
init_export2();
|
|
13989
|
+
init_import();
|
|
13990
|
+
init_inspect();
|
|
13991
|
+
init_list();
|
|
13992
|
+
init_mcp();
|
|
13993
|
+
init_mock_agent();
|
|
13994
|
+
init_mock_editor();
|
|
13995
|
+
init_proxy3();
|
|
13996
|
+
init_replay2();
|
|
13997
|
+
init_search();
|
|
13998
|
+
init_session_info();
|
|
13999
|
+
init_stats();
|
|
14000
|
+
init_ui();
|
|
14001
|
+
init_validate2();
|
|
14002
|
+
init_help();
|
|
14003
|
+
init_version();
|
|
14004
|
+
}
|
|
14005
|
+
});
|
|
14006
|
+
|
|
14007
|
+
// src/lib/native-error.ts
|
|
14008
|
+
var BINDING_PATTERNS = [
|
|
14009
|
+
/Could not locate the bindings file/i,
|
|
14010
|
+
/better[_-]sqlite3/i,
|
|
14011
|
+
/node[_-]gyp/i,
|
|
14012
|
+
/prebuild[-_]install/i,
|
|
14013
|
+
/\.node['"]?\)?:?\s/i,
|
|
14014
|
+
/Cannot find module .*\.node/i,
|
|
14015
|
+
/NODE_MODULE_VERSION/i
|
|
14016
|
+
];
|
|
14017
|
+
function isNativeBindingError(err) {
|
|
14018
|
+
if (!err) return false;
|
|
14019
|
+
const msg = err instanceof Error ? `${err.message}
|
|
14020
|
+
${err.stack ?? ""}` : String(err);
|
|
14021
|
+
return BINDING_PATTERNS.some((re) => re.test(msg));
|
|
14022
|
+
}
|
|
14023
|
+
function detectInstallSource(ctx) {
|
|
14024
|
+
const p = (ctx.binaryPath ?? "").toLowerCase();
|
|
14025
|
+
if (!p) return "unknown";
|
|
14026
|
+
if (p.includes("/cellar/acp-devtools/") || p.includes("/homebrew/")) {
|
|
14027
|
+
return "homebrew";
|
|
14028
|
+
}
|
|
14029
|
+
if (p.includes("linuxbrew")) return "homebrew";
|
|
14030
|
+
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")) {
|
|
14031
|
+
return "npm-global";
|
|
14032
|
+
}
|
|
14033
|
+
return "unknown";
|
|
14034
|
+
}
|
|
14035
|
+
function formatNativeBindingMessage(err, ctx) {
|
|
14036
|
+
const source = detectInstallSource(ctx);
|
|
14037
|
+
const detail = err instanceof Error ? err.message : String(err ?? "unknown error");
|
|
14038
|
+
const lines = [
|
|
14039
|
+
"acp-devtools: native module `better-sqlite3` failed to load.",
|
|
14040
|
+
"",
|
|
14041
|
+
"The SQLite binding (`build/Release/better_sqlite3.node`) is missing or",
|
|
14042
|
+
"incompatible with this Node version. acp-devtools needs it to read and",
|
|
14043
|
+
"write the captures database \u2014 every command depends on it.",
|
|
14044
|
+
""
|
|
14045
|
+
];
|
|
14046
|
+
if (source === "homebrew") {
|
|
14047
|
+
lines.push(
|
|
14048
|
+
"You installed via Homebrew. Rebuild the binding in place:",
|
|
14049
|
+
"",
|
|
14050
|
+
' PREFIX="$(brew --prefix acp-devtools)/libexec/lib/node_modules/acp-devtools"',
|
|
14051
|
+
' npm rebuild --prefix "$PREFIX" better-sqlite3',
|
|
14052
|
+
"",
|
|
14053
|
+
"Or reinstall once the formula fix lands:",
|
|
14054
|
+
"",
|
|
14055
|
+
" brew update && brew reinstall acp-devtools",
|
|
14056
|
+
""
|
|
14057
|
+
);
|
|
14058
|
+
} else if (source === "npm-global") {
|
|
14059
|
+
lines.push(
|
|
14060
|
+
"You installed via `npm i -g`. Rebuild the binding:",
|
|
14061
|
+
"",
|
|
14062
|
+
" npm rebuild -g better-sqlite3",
|
|
14063
|
+
"",
|
|
14064
|
+
"If that fails the prebuild lookup probably has no match for your Node",
|
|
14065
|
+
"version, and source build needs Python 3 + a C++ toolchain (Xcode CLT",
|
|
14066
|
+
"on macOS, build-essential on Linux, Visual Studio Build Tools on",
|
|
14067
|
+
"Windows). Install those and retry, or pin to an older Node major.",
|
|
14068
|
+
""
|
|
14069
|
+
);
|
|
14070
|
+
} else {
|
|
14071
|
+
lines.push(
|
|
14072
|
+
"Try rebuilding the native dependency:",
|
|
14073
|
+
"",
|
|
14074
|
+
" npm rebuild better-sqlite3",
|
|
14075
|
+
"",
|
|
14076
|
+
"If you installed globally via npm, add `-g`. If via Homebrew, run",
|
|
14077
|
+
"`brew reinstall acp-devtools` once the formula is patched.",
|
|
14078
|
+
""
|
|
14079
|
+
);
|
|
14080
|
+
}
|
|
14081
|
+
lines.push(
|
|
14082
|
+
`Underlying error: ${detail}`,
|
|
14083
|
+
`Node: ${process.version} \xB7 platform: ${process.platform}/${process.arch}`,
|
|
14084
|
+
"Report unresolved cases at https://github.com/maksugr/acp-devtools/issues"
|
|
14085
|
+
);
|
|
14086
|
+
return lines.join("\n") + "\n";
|
|
13233
14087
|
}
|
|
13234
14088
|
|
|
13235
14089
|
// src/index.ts
|
|
@@ -13258,10 +14112,10 @@ var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
13258
14112
|
"-V",
|
|
13259
14113
|
"--version"
|
|
13260
14114
|
]);
|
|
13261
|
-
function expandArgv(rawArgs) {
|
|
14115
|
+
function expandArgv(rawArgs, defaultAgent, isAgentShortcut2) {
|
|
13262
14116
|
if (rawArgs.length === 0) {
|
|
13263
14117
|
if (!process.stdin.isTTY) {
|
|
13264
|
-
return ["proxy", "--agent",
|
|
14118
|
+
return ["proxy", "--agent", defaultAgent];
|
|
13265
14119
|
}
|
|
13266
14120
|
return rawArgs;
|
|
13267
14121
|
}
|
|
@@ -13269,16 +14123,53 @@ function expandArgv(rawArgs) {
|
|
|
13269
14123
|
if (KNOWN_SUBCOMMANDS.has(first)) {
|
|
13270
14124
|
return rawArgs;
|
|
13271
14125
|
}
|
|
13272
|
-
if (
|
|
14126
|
+
if (isAgentShortcut2(first)) {
|
|
13273
14127
|
return ["proxy", "--agent", first, ...rawArgs.slice(1)];
|
|
13274
14128
|
}
|
|
13275
|
-
return
|
|
14129
|
+
return rawArgs;
|
|
13276
14130
|
}
|
|
13277
|
-
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
|
|
13281
|
-
|
|
14131
|
+
function reportFatal(err, prefix = "acp-devtools") {
|
|
14132
|
+
if (isNativeBindingError(err)) {
|
|
14133
|
+
process.stderr.write(
|
|
14134
|
+
formatNativeBindingMessage(err, {
|
|
14135
|
+
binaryPath: process.argv[1] ?? ""
|
|
14136
|
+
})
|
|
14137
|
+
);
|
|
14138
|
+
process.exit(1);
|
|
14139
|
+
}
|
|
14140
|
+
process.stderr.write(
|
|
14141
|
+
`${prefix}: ${err instanceof Error ? err.message : String(err)}
|
|
14142
|
+
`
|
|
14143
|
+
);
|
|
13282
14144
|
process.exit(1);
|
|
13283
|
-
}
|
|
14145
|
+
}
|
|
14146
|
+
async function main() {
|
|
14147
|
+
let core;
|
|
14148
|
+
let buildProgram2;
|
|
14149
|
+
try {
|
|
14150
|
+
core = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
14151
|
+
({ buildProgram: buildProgram2 } = await Promise.resolve().then(() => (init_program(), program_exports)));
|
|
14152
|
+
} catch (err) {
|
|
14153
|
+
reportFatal(err, "acp-devtools: failed to start");
|
|
14154
|
+
}
|
|
14155
|
+
const expanded = expandArgv(
|
|
14156
|
+
process.argv.slice(2),
|
|
14157
|
+
core.DEFAULT_AGENT,
|
|
14158
|
+
core.isAgentShortcut
|
|
14159
|
+
);
|
|
14160
|
+
const program = buildProgram2();
|
|
14161
|
+
try {
|
|
14162
|
+
await program.parseAsync([
|
|
14163
|
+
process.argv[0],
|
|
14164
|
+
process.argv[1],
|
|
14165
|
+
...expanded
|
|
14166
|
+
]);
|
|
14167
|
+
} catch (err) {
|
|
14168
|
+
reportFatal(err);
|
|
14169
|
+
}
|
|
14170
|
+
}
|
|
14171
|
+
main();
|
|
14172
|
+
export {
|
|
14173
|
+
expandArgv
|
|
14174
|
+
};
|
|
13284
14175
|
//# sourceMappingURL=index.js.map
|