@btraut/browser-bridge 0.7.3 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/README.md +49 -4
- package/dist/api.js +3922 -3239
- package/dist/api.js.map +4 -4
- package/dist/index.js +1027 -142
- package/dist/index.js.map +4 -4
- package/extension/dist/background.js +853 -23
- package/extension/dist/background.js.map +3 -3
- package/extension/dist/content.js +84 -2
- package/extension/dist/content.js.map +2 -2
- package/extension/dist/options-ui.js +59 -1
- package/extension/dist/options-ui.js.map +2 -2
- package/extension/manifest.json +17 -5
- package/package.json +1 -1
- package/skills/browser-bridge/skill.json +1 -1
package/dist/index.js
CHANGED
|
@@ -76,6 +76,498 @@ var successEnvelopeSchema = (result) => import_zod.z.object({
|
|
|
76
76
|
result
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
+
// packages/shared/src/logging.ts
|
|
80
|
+
var import_node_fs2 = require("node:fs");
|
|
81
|
+
var import_node_path2 = require("node:path");
|
|
82
|
+
|
|
83
|
+
// packages/shared/src/runtime-config.ts
|
|
84
|
+
var import_node_fs = require("node:fs");
|
|
85
|
+
var import_node_path = require("node:path");
|
|
86
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
87
|
+
var LEGACY_DEFAULT_PORT = 3210;
|
|
88
|
+
var DETERMINISTIC_PORT_WINDOW = 2e3;
|
|
89
|
+
var ENV_CORE_HOST = "BROWSER_BRIDGE_CORE_HOST";
|
|
90
|
+
var ENV_VISION_HOST = "BROWSER_VISION_CORE_HOST";
|
|
91
|
+
var ENV_CORE_PORT = "BROWSER_BRIDGE_CORE_PORT";
|
|
92
|
+
var ENV_VISION_PORT = "BROWSER_VISION_CORE_PORT";
|
|
93
|
+
var RUNTIME_METADATA_RELATIVE_PATH = ".context/browser-bridge/dev.json";
|
|
94
|
+
var DEFAULT_LOG_DIRECTORY_RELATIVE_PATH = ".context/logs/browser-bridge";
|
|
95
|
+
var resolveCwd = (cwd) => (0, import_node_path.resolve)(cwd ?? process.cwd());
|
|
96
|
+
var resolveOptionalPath = (cwd, value) => {
|
|
97
|
+
if (!value) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
return (0, import_node_path.isAbsolute)(value) ? value : (0, import_node_path.resolve)(cwd, value);
|
|
101
|
+
};
|
|
102
|
+
var normalizeHost = (value) => {
|
|
103
|
+
if (typeof value !== "string") {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
const trimmed = value.trim();
|
|
107
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
108
|
+
};
|
|
109
|
+
var parsePort = (value, label, invalidPolicy) => {
|
|
110
|
+
if (value === void 0 || value === null) {
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
if (typeof value === "string" && value.trim().length === 0) {
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value), 10);
|
|
117
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
118
|
+
return Math.floor(parsed);
|
|
119
|
+
}
|
|
120
|
+
if (invalidPolicy === "throw") {
|
|
121
|
+
throw new Error(`Invalid ${label}: ${String(value)}`);
|
|
122
|
+
}
|
|
123
|
+
return void 0;
|
|
124
|
+
};
|
|
125
|
+
var hashString = (value) => {
|
|
126
|
+
let hash = 2166136261;
|
|
127
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
128
|
+
hash ^= value.charCodeAt(index);
|
|
129
|
+
hash = Math.imul(hash, 16777619);
|
|
130
|
+
}
|
|
131
|
+
return hash >>> 0;
|
|
132
|
+
};
|
|
133
|
+
var normalizePathForHash = (value) => value.replace(/\\/g, "/").toLowerCase();
|
|
134
|
+
var sanitizeToken = (value) => value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
135
|
+
var fallbackWorktreeId = (gitRoot) => {
|
|
136
|
+
const hash = hashString(normalizePathForHash(gitRoot)).toString(16).padStart(8, "0");
|
|
137
|
+
return `wt-${hash}`;
|
|
138
|
+
};
|
|
139
|
+
var extractWorktreeIdFromGitDir = (gitDir) => {
|
|
140
|
+
const normalized = gitDir.replace(/\\/g, "/");
|
|
141
|
+
const marker = "/.git/worktrees/";
|
|
142
|
+
const markerIndex = normalized.lastIndexOf(marker);
|
|
143
|
+
if (markerIndex < 0) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const remainder = normalized.slice(markerIndex + marker.length);
|
|
147
|
+
const rawId = remainder.split("/")[0];
|
|
148
|
+
if (!rawId) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const sanitized = sanitizeToken(rawId);
|
|
152
|
+
return sanitized.length > 0 ? sanitized : null;
|
|
153
|
+
};
|
|
154
|
+
var readWorktreeGitDir = (gitRoot) => {
|
|
155
|
+
const gitPath = (0, import_node_path.join)(gitRoot, ".git");
|
|
156
|
+
try {
|
|
157
|
+
const stats = (0, import_node_fs.statSync)(gitPath);
|
|
158
|
+
if (stats.isDirectory()) {
|
|
159
|
+
return gitPath;
|
|
160
|
+
}
|
|
161
|
+
if (!stats.isFile()) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const raw = (0, import_node_fs.readFileSync)(gitPath, "utf8");
|
|
169
|
+
const match = raw.match(/^gitdir:\s*(.+)$/m);
|
|
170
|
+
if (!match?.[1]) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const candidate = match[1].trim();
|
|
174
|
+
return (0, import_node_path.isAbsolute)(candidate) ? candidate : (0, import_node_path.resolve)(gitRoot, candidate);
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var resolveEnvHost = (env) => {
|
|
180
|
+
const bridgeHost = normalizeHost(env[ENV_CORE_HOST]);
|
|
181
|
+
if (bridgeHost) {
|
|
182
|
+
return bridgeHost;
|
|
183
|
+
}
|
|
184
|
+
return normalizeHost(env[ENV_VISION_HOST]);
|
|
185
|
+
};
|
|
186
|
+
var resolveEnvPortRaw = (env) => {
|
|
187
|
+
if (env[ENV_CORE_PORT] !== void 0) {
|
|
188
|
+
return env[ENV_CORE_PORT];
|
|
189
|
+
}
|
|
190
|
+
return env[ENV_VISION_PORT];
|
|
191
|
+
};
|
|
192
|
+
var sanitizeMetadata = (raw) => {
|
|
193
|
+
if (!raw || typeof raw !== "object") {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const candidate = raw;
|
|
197
|
+
const host = normalizeHost(candidate.host);
|
|
198
|
+
const port = parsePort(candidate.port, "port", "ignore");
|
|
199
|
+
const gitRoot = normalizeHost(candidate.git_root);
|
|
200
|
+
const worktreeId = normalizeHost(candidate.worktree_id);
|
|
201
|
+
const extensionId = normalizeHost(candidate.extension_id);
|
|
202
|
+
const updatedAt = normalizeHost(candidate.updated_at);
|
|
203
|
+
if (!host && port === void 0 && !gitRoot && !worktreeId && !extensionId && !updatedAt) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
host,
|
|
208
|
+
port,
|
|
209
|
+
git_root: gitRoot,
|
|
210
|
+
worktree_id: worktreeId,
|
|
211
|
+
extension_id: extensionId,
|
|
212
|
+
updated_at: updatedAt
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
var findGitRoot = (cwd = process.cwd()) => {
|
|
216
|
+
let current = (0, import_node_path.resolve)(cwd);
|
|
217
|
+
while (true) {
|
|
218
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(current, ".git"))) {
|
|
219
|
+
return current;
|
|
220
|
+
}
|
|
221
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
222
|
+
if (parent === current) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
current = parent;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var resolveWorktreeId = ({
|
|
229
|
+
cwd,
|
|
230
|
+
gitRoot
|
|
231
|
+
} = {}) => {
|
|
232
|
+
const resolvedGitRoot = gitRoot ?? findGitRoot(resolveCwd(cwd));
|
|
233
|
+
if (!resolvedGitRoot) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const gitDir = readWorktreeGitDir(resolvedGitRoot);
|
|
237
|
+
const parsedId = gitDir ? extractWorktreeIdFromGitDir(gitDir) : null;
|
|
238
|
+
if (parsedId) {
|
|
239
|
+
return parsedId;
|
|
240
|
+
}
|
|
241
|
+
return fallbackWorktreeId(resolvedGitRoot);
|
|
242
|
+
};
|
|
243
|
+
var resolveDeterministicCorePort = ({
|
|
244
|
+
cwd,
|
|
245
|
+
gitRoot
|
|
246
|
+
} = {}) => {
|
|
247
|
+
const resolvedGitRoot = gitRoot ?? findGitRoot(resolveCwd(cwd));
|
|
248
|
+
if (!resolvedGitRoot) {
|
|
249
|
+
return LEGACY_DEFAULT_PORT;
|
|
250
|
+
}
|
|
251
|
+
const seed = normalizePathForHash(resolvedGitRoot);
|
|
252
|
+
return LEGACY_DEFAULT_PORT + hashString(seed) % DETERMINISTIC_PORT_WINDOW;
|
|
253
|
+
};
|
|
254
|
+
var resolveRuntimeMetadataPath = ({
|
|
255
|
+
cwd,
|
|
256
|
+
gitRoot,
|
|
257
|
+
metadataPath
|
|
258
|
+
} = {}) => {
|
|
259
|
+
const resolvedCwd = resolveCwd(cwd);
|
|
260
|
+
const providedPath = resolveOptionalPath(resolvedCwd, metadataPath);
|
|
261
|
+
if (providedPath) {
|
|
262
|
+
return providedPath;
|
|
263
|
+
}
|
|
264
|
+
const root = gitRoot ?? findGitRoot(resolvedCwd) ?? resolvedCwd;
|
|
265
|
+
return (0, import_node_path.join)(root, RUNTIME_METADATA_RELATIVE_PATH);
|
|
266
|
+
};
|
|
267
|
+
var resolveLogDirectory = ({
|
|
268
|
+
cwd,
|
|
269
|
+
gitRoot,
|
|
270
|
+
logDir
|
|
271
|
+
} = {}) => {
|
|
272
|
+
const resolvedCwd = resolveCwd(cwd);
|
|
273
|
+
const providedPath = resolveOptionalPath(resolvedCwd, logDir);
|
|
274
|
+
if (providedPath) {
|
|
275
|
+
return providedPath;
|
|
276
|
+
}
|
|
277
|
+
const resolvedGitRoot = gitRoot === void 0 ? findGitRoot(resolvedCwd) : gitRoot ? (0, import_node_path.resolve)(gitRoot) : null;
|
|
278
|
+
return (0, import_node_path.join)(
|
|
279
|
+
resolvedGitRoot ?? resolvedCwd,
|
|
280
|
+
DEFAULT_LOG_DIRECTORY_RELATIVE_PATH
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
var readRuntimeMetadata = ({
|
|
284
|
+
cwd,
|
|
285
|
+
gitRoot,
|
|
286
|
+
metadataPath
|
|
287
|
+
} = {}) => {
|
|
288
|
+
const path9 = resolveRuntimeMetadataPath({ cwd, gitRoot, metadataPath });
|
|
289
|
+
if (!(0, import_node_fs.existsSync)(path9)) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const raw = (0, import_node_fs.readFileSync)(path9, "utf8");
|
|
294
|
+
const parsed = JSON.parse(raw);
|
|
295
|
+
return sanitizeMetadata(parsed);
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var writeRuntimeMetadata = (metadata, {
|
|
301
|
+
cwd,
|
|
302
|
+
gitRoot,
|
|
303
|
+
metadataPath
|
|
304
|
+
} = {}) => {
|
|
305
|
+
const path9 = resolveRuntimeMetadataPath({ cwd, gitRoot, metadataPath });
|
|
306
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path9), { recursive: true });
|
|
307
|
+
(0, import_node_fs.writeFileSync)(path9, `${JSON.stringify(metadata, null, 2)}
|
|
308
|
+
`, "utf8");
|
|
309
|
+
return path9;
|
|
310
|
+
};
|
|
311
|
+
var resolveCoreRuntime = (options = {}) => {
|
|
312
|
+
const env = options.env ?? process.env;
|
|
313
|
+
const resolvedCwd = resolveCwd(options.cwd);
|
|
314
|
+
const gitRoot = options.gitRoot === void 0 ? findGitRoot(resolvedCwd) : options.gitRoot ? (0, import_node_path.resolve)(options.gitRoot) : null;
|
|
315
|
+
const metadataPath = resolveRuntimeMetadataPath({
|
|
316
|
+
cwd: resolvedCwd,
|
|
317
|
+
gitRoot,
|
|
318
|
+
metadataPath: options.metadataPath
|
|
319
|
+
});
|
|
320
|
+
const metadata = options.metadata === void 0 ? readRuntimeMetadata({ metadataPath }) : sanitizeMetadata(options.metadata);
|
|
321
|
+
const deterministicPort = resolveDeterministicCorePort({
|
|
322
|
+
cwd: resolvedCwd,
|
|
323
|
+
gitRoot
|
|
324
|
+
});
|
|
325
|
+
const optionHost = normalizeHost(options.host);
|
|
326
|
+
const envHost = resolveEnvHost(env);
|
|
327
|
+
const metadataHost = normalizeHost(metadata?.host);
|
|
328
|
+
const host = optionHost ?? envHost ?? metadataHost ?? DEFAULT_HOST;
|
|
329
|
+
const hostSource = optionHost ? "option" : envHost ? "env" : metadataHost ? "metadata" : "default";
|
|
330
|
+
const optionPort = parsePort(options.port, "port", "throw");
|
|
331
|
+
const envPort = parsePort(
|
|
332
|
+
resolveEnvPortRaw(env),
|
|
333
|
+
"port",
|
|
334
|
+
options.strictEnvPort ? "throw" : "ignore"
|
|
335
|
+
);
|
|
336
|
+
const metadataPort = parsePort(metadata?.port, "port", "ignore");
|
|
337
|
+
let port;
|
|
338
|
+
let portSource;
|
|
339
|
+
if (optionPort !== void 0) {
|
|
340
|
+
port = optionPort;
|
|
341
|
+
portSource = "option";
|
|
342
|
+
} else if (envPort !== void 0) {
|
|
343
|
+
port = envPort;
|
|
344
|
+
portSource = "env";
|
|
345
|
+
} else if (metadataPort !== void 0) {
|
|
346
|
+
port = metadataPort;
|
|
347
|
+
portSource = "metadata";
|
|
348
|
+
} else {
|
|
349
|
+
port = deterministicPort;
|
|
350
|
+
portSource = "deterministic";
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
host,
|
|
354
|
+
port,
|
|
355
|
+
hostSource,
|
|
356
|
+
portSource,
|
|
357
|
+
metadataPath,
|
|
358
|
+
metadata,
|
|
359
|
+
gitRoot,
|
|
360
|
+
worktreeId: resolveWorktreeId({ cwd: resolvedCwd, gitRoot }),
|
|
361
|
+
deterministicPort
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// packages/shared/src/logging.ts
|
|
366
|
+
var DEFAULT_LOG_ROTATION_MAX_BYTES = 10 * 1024 * 1024;
|
|
367
|
+
var DEFAULT_LOG_RETENTION = 20;
|
|
368
|
+
var DEFAULT_REDACT_KEYS = [
|
|
369
|
+
"authorization",
|
|
370
|
+
"cookie",
|
|
371
|
+
"setcookie",
|
|
372
|
+
"password",
|
|
373
|
+
"passwd",
|
|
374
|
+
"secret",
|
|
375
|
+
"token",
|
|
376
|
+
"accesstoken",
|
|
377
|
+
"refreshtoken",
|
|
378
|
+
"apikey",
|
|
379
|
+
"apikeyid",
|
|
380
|
+
"privatekey",
|
|
381
|
+
"clientsecret"
|
|
382
|
+
];
|
|
383
|
+
var REDACTED_VALUE = "[REDACTED]";
|
|
384
|
+
var MAX_NORMALIZE_DEPTH = 8;
|
|
385
|
+
var LOG_LEVEL_PRIORITY = {
|
|
386
|
+
debug: 10,
|
|
387
|
+
info: 20,
|
|
388
|
+
warn: 30,
|
|
389
|
+
error: 40
|
|
390
|
+
};
|
|
391
|
+
var normalizeRedactionKey = (value) => value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
|
392
|
+
var sanitizeStreamName = (stream) => {
|
|
393
|
+
const sanitized = stream.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
394
|
+
if (sanitized.length === 0) {
|
|
395
|
+
throw new Error("Logger stream name must contain visible characters.");
|
|
396
|
+
}
|
|
397
|
+
return sanitized;
|
|
398
|
+
};
|
|
399
|
+
var normalizeForJson = (value, seen, depth = 0) => {
|
|
400
|
+
if (value === null) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
if (depth >= MAX_NORMALIZE_DEPTH) {
|
|
404
|
+
return "[MaxDepth]";
|
|
405
|
+
}
|
|
406
|
+
const valueType = typeof value;
|
|
407
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") {
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
410
|
+
if (valueType === "bigint") {
|
|
411
|
+
return String(value);
|
|
412
|
+
}
|
|
413
|
+
if (valueType === "undefined" || valueType === "symbol" || valueType === "function") {
|
|
414
|
+
return String(value);
|
|
415
|
+
}
|
|
416
|
+
if (value instanceof Date) {
|
|
417
|
+
return value.toISOString();
|
|
418
|
+
}
|
|
419
|
+
if (value instanceof Error) {
|
|
420
|
+
return {
|
|
421
|
+
name: value.name,
|
|
422
|
+
message: value.message,
|
|
423
|
+
stack: value.stack ?? null
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (Array.isArray(value)) {
|
|
427
|
+
return value.map((item) => normalizeForJson(item, seen, depth + 1));
|
|
428
|
+
}
|
|
429
|
+
if (value instanceof Set) {
|
|
430
|
+
return Array.from(value.values()).map(
|
|
431
|
+
(item) => normalizeForJson(item, seen, depth + 1)
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
if (value instanceof Map) {
|
|
435
|
+
const mapped = {};
|
|
436
|
+
for (const [entryKey, entryValue] of value.entries()) {
|
|
437
|
+
mapped[String(entryKey)] = normalizeForJson(entryValue, seen, depth + 1);
|
|
438
|
+
}
|
|
439
|
+
return mapped;
|
|
440
|
+
}
|
|
441
|
+
if (typeof value === "object") {
|
|
442
|
+
const objectValue = value;
|
|
443
|
+
if (seen.has(objectValue)) {
|
|
444
|
+
return "[Circular]";
|
|
445
|
+
}
|
|
446
|
+
seen.add(objectValue);
|
|
447
|
+
const normalized = {};
|
|
448
|
+
for (const [key, nestedValue] of Object.entries(objectValue)) {
|
|
449
|
+
normalized[key] = normalizeForJson(nestedValue, seen, depth + 1);
|
|
450
|
+
}
|
|
451
|
+
seen.delete(objectValue);
|
|
452
|
+
return normalized;
|
|
453
|
+
}
|
|
454
|
+
return String(value);
|
|
455
|
+
};
|
|
456
|
+
var redactJson = (value, redactKeys) => {
|
|
457
|
+
if (Array.isArray(value)) {
|
|
458
|
+
return value.map((item) => redactJson(item, redactKeys));
|
|
459
|
+
}
|
|
460
|
+
if (!value || typeof value !== "object") {
|
|
461
|
+
return value;
|
|
462
|
+
}
|
|
463
|
+
const record = value;
|
|
464
|
+
const redacted = {};
|
|
465
|
+
for (const [key, nestedValue] of Object.entries(record)) {
|
|
466
|
+
if (redactKeys.has(normalizeRedactionKey(key))) {
|
|
467
|
+
redacted[key] = REDACTED_VALUE;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
redacted[key] = redactJson(nestedValue, redactKeys);
|
|
471
|
+
}
|
|
472
|
+
return redacted;
|
|
473
|
+
};
|
|
474
|
+
var streamFilePath = (logDir, stream) => (0, import_node_path2.join)(logDir, `${stream}.jsonl`);
|
|
475
|
+
var rotationFilePath = (logDir, stream, index) => (0, import_node_path2.join)(logDir, `${stream}.${index}.jsonl`);
|
|
476
|
+
var rotateStreamFiles = (logDir, stream, retention) => {
|
|
477
|
+
if (retention <= 1) {
|
|
478
|
+
(0, import_node_fs2.rmSync)(streamFilePath(logDir, stream), { force: true });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const maxArchiveIndex = retention - 1;
|
|
482
|
+
(0, import_node_fs2.rmSync)(rotationFilePath(logDir, stream, maxArchiveIndex), { force: true });
|
|
483
|
+
for (let index = maxArchiveIndex - 1; index >= 1; index -= 1) {
|
|
484
|
+
const source = rotationFilePath(logDir, stream, index);
|
|
485
|
+
if (!(0, import_node_fs2.existsSync)(source)) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const target = rotationFilePath(logDir, stream, index + 1);
|
|
489
|
+
(0, import_node_fs2.renameSync)(source, target);
|
|
490
|
+
}
|
|
491
|
+
const current = streamFilePath(logDir, stream);
|
|
492
|
+
if ((0, import_node_fs2.existsSync)(current)) {
|
|
493
|
+
(0, import_node_fs2.renameSync)(current, rotationFilePath(logDir, stream, 1));
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var rotateIfNeeded = (state, pendingLine) => {
|
|
497
|
+
if (!(0, import_node_fs2.existsSync)(state.filePath)) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const pendingBytes = Buffer.byteLength(pendingLine, "utf8");
|
|
501
|
+
const currentSize = (0, import_node_fs2.statSync)(state.filePath).size;
|
|
502
|
+
if (currentSize + pendingBytes <= state.maxBytes) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
rotateStreamFiles(state.logDir, state.stream, state.retention);
|
|
506
|
+
};
|
|
507
|
+
var buildLogger = (state, bindings) => {
|
|
508
|
+
const log = (level, event, fields = {}) => {
|
|
509
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[state.level]) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const payload = {
|
|
513
|
+
ts: state.now().toISOString(),
|
|
514
|
+
level,
|
|
515
|
+
stream: state.stream,
|
|
516
|
+
event,
|
|
517
|
+
...bindings,
|
|
518
|
+
...fields
|
|
519
|
+
};
|
|
520
|
+
const normalized = normalizeForJson(payload, /* @__PURE__ */ new WeakSet());
|
|
521
|
+
const redacted = redactJson(normalized, state.redactKeys);
|
|
522
|
+
const line = `${JSON.stringify(redacted)}
|
|
523
|
+
`;
|
|
524
|
+
try {
|
|
525
|
+
rotateIfNeeded(state, line);
|
|
526
|
+
(0, import_node_fs2.appendFileSync)(state.filePath, line, "utf8");
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
stream: state.stream,
|
|
532
|
+
level: state.level,
|
|
533
|
+
logDir: state.logDir,
|
|
534
|
+
filePath: state.filePath,
|
|
535
|
+
child: (childBindings) => buildLogger(state, {
|
|
536
|
+
...bindings,
|
|
537
|
+
...childBindings
|
|
538
|
+
}),
|
|
539
|
+
log,
|
|
540
|
+
debug: (event, fields) => log("debug", event, fields),
|
|
541
|
+
info: (event, fields) => log("info", event, fields),
|
|
542
|
+
warn: (event, fields) => log("warn", event, fields),
|
|
543
|
+
error: (event, fields) => log("error", event, fields)
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
var createJsonlLogger = (options) => {
|
|
547
|
+
const stream = sanitizeStreamName(options.stream);
|
|
548
|
+
const logDir = resolveLogDirectory({
|
|
549
|
+
cwd: options.cwd,
|
|
550
|
+
gitRoot: options.gitRoot,
|
|
551
|
+
logDir: options.logDir
|
|
552
|
+
});
|
|
553
|
+
(0, import_node_fs2.mkdirSync)(logDir, { recursive: true });
|
|
554
|
+
const maxBytes = typeof options.maxBytes === "number" && options.maxBytes > 0 ? Math.floor(options.maxBytes) : DEFAULT_LOG_ROTATION_MAX_BYTES;
|
|
555
|
+
const retention = typeof options.retention === "number" && options.retention > 0 ? Math.max(1, Math.floor(options.retention)) : DEFAULT_LOG_RETENTION;
|
|
556
|
+
const state = {
|
|
557
|
+
stream,
|
|
558
|
+
level: options.level ?? "debug",
|
|
559
|
+
logDir,
|
|
560
|
+
filePath: streamFilePath(logDir, stream),
|
|
561
|
+
maxBytes,
|
|
562
|
+
retention,
|
|
563
|
+
redactKeys: new Set(
|
|
564
|
+
(options.redactKeys ?? DEFAULT_REDACT_KEYS).map(normalizeRedactionKey)
|
|
565
|
+
),
|
|
566
|
+
now: options.now ?? (() => /* @__PURE__ */ new Date())
|
|
567
|
+
};
|
|
568
|
+
return buildLogger(state, options.bindings ?? {});
|
|
569
|
+
};
|
|
570
|
+
|
|
79
571
|
// packages/shared/src/schemas.ts
|
|
80
572
|
var import_zod2 = require("zod");
|
|
81
573
|
var LocatorRoleSchema = import_zod2.z.object({
|
|
@@ -589,7 +1081,7 @@ var import_zod3 = require("zod");
|
|
|
589
1081
|
|
|
590
1082
|
// packages/cli/src/core-client.ts
|
|
591
1083
|
var import_node_child_process = require("node:child_process");
|
|
592
|
-
var
|
|
1084
|
+
var import_node_path3 = require("node:path");
|
|
593
1085
|
var import_promises = require("node:timers/promises");
|
|
594
1086
|
var CoreClientError = class extends Error {
|
|
595
1087
|
constructor(info) {
|
|
@@ -598,29 +1090,9 @@ var CoreClientError = class extends Error {
|
|
|
598
1090
|
this.info = info;
|
|
599
1091
|
}
|
|
600
1092
|
};
|
|
601
|
-
var DEFAULT_HOST = "127.0.0.1";
|
|
602
|
-
var DEFAULT_PORT = 3210;
|
|
603
1093
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
604
1094
|
var HEALTH_RETRY_MS = 250;
|
|
605
1095
|
var HEALTH_ATTEMPTS = 20;
|
|
606
|
-
var resolveHost = (host) => {
|
|
607
|
-
const candidate = host?.trim() || process.env.BROWSER_BRIDGE_CORE_HOST || process.env.BROWSER_VISION_CORE_HOST;
|
|
608
|
-
if (candidate && candidate.length > 0) {
|
|
609
|
-
return candidate;
|
|
610
|
-
}
|
|
611
|
-
return DEFAULT_HOST;
|
|
612
|
-
};
|
|
613
|
-
var resolvePort = (port) => {
|
|
614
|
-
const candidate = port ?? (process.env.BROWSER_BRIDGE_CORE_PORT ? Number.parseInt(process.env.BROWSER_BRIDGE_CORE_PORT, 10) : process.env.BROWSER_VISION_CORE_PORT ? Number.parseInt(process.env.BROWSER_VISION_CORE_PORT, 10) : void 0);
|
|
615
|
-
if (candidate === void 0 || candidate === null) {
|
|
616
|
-
return DEFAULT_PORT;
|
|
617
|
-
}
|
|
618
|
-
const parsed = typeof candidate === "number" ? candidate : Number.parseInt(candidate, 10);
|
|
619
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
620
|
-
throw new Error(`Invalid port: ${String(candidate)}`);
|
|
621
|
-
}
|
|
622
|
-
return parsed;
|
|
623
|
-
};
|
|
624
1096
|
var resolveTimeoutMs = (timeoutMs) => {
|
|
625
1097
|
const candidate = timeoutMs ?? (process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS, 10) : process.env.BROWSER_VISION_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_VISION_CORE_TIMEOUT_MS, 10) : void 0);
|
|
626
1098
|
if (candidate === void 0 || candidate === null) {
|
|
@@ -633,21 +1105,48 @@ var resolveTimeoutMs = (timeoutMs) => {
|
|
|
633
1105
|
return Math.floor(parsed);
|
|
634
1106
|
};
|
|
635
1107
|
var normalizePath = (path9) => path9.startsWith("/") ? path9 : `/${path9}`;
|
|
1108
|
+
var durationMs = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
|
|
636
1109
|
var createCoreClient = (options = {}) => {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
1110
|
+
const logger = options.logger ?? createJsonlLogger({
|
|
1111
|
+
stream: "cli",
|
|
1112
|
+
cwd: options.cwd
|
|
1113
|
+
}).child({ scope: "core-client" });
|
|
1114
|
+
let runtime = resolveCoreRuntime({
|
|
1115
|
+
host: options.host,
|
|
1116
|
+
port: options.port,
|
|
1117
|
+
cwd: options.cwd,
|
|
1118
|
+
strictEnvPort: true
|
|
1119
|
+
});
|
|
1120
|
+
let baseUrl = `http://${runtime.host}:${runtime.port}`;
|
|
640
1121
|
const timeoutMs = resolveTimeoutMs(options.timeoutMs);
|
|
641
1122
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
642
1123
|
const spawnImpl = options.spawnImpl ?? import_node_child_process.spawn;
|
|
643
1124
|
const ensureDaemon = options.ensureDaemon ?? true;
|
|
1125
|
+
const allowRuntimeRefresh = options.host === void 0 && options.port === void 0 && process.env.BROWSER_BRIDGE_CORE_HOST === void 0 && process.env.BROWSER_VISION_CORE_HOST === void 0 && process.env.BROWSER_BRIDGE_CORE_PORT === void 0 && process.env.BROWSER_VISION_CORE_PORT === void 0;
|
|
1126
|
+
const refreshRuntime = () => {
|
|
1127
|
+
if (!allowRuntimeRefresh) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
runtime = resolveCoreRuntime({
|
|
1131
|
+
cwd: options.cwd,
|
|
1132
|
+
strictEnvPort: true
|
|
1133
|
+
});
|
|
1134
|
+
baseUrl = `http://${runtime.host}:${runtime.port}`;
|
|
1135
|
+
};
|
|
644
1136
|
const requestJson = async (method, path9, body) => {
|
|
1137
|
+
const requestPath = normalizePath(path9);
|
|
1138
|
+
const startedAt = process.hrtime.bigint();
|
|
1139
|
+
logger.debug("cli.core.request.start", {
|
|
1140
|
+
method,
|
|
1141
|
+
path: requestPath,
|
|
1142
|
+
base_url: baseUrl
|
|
1143
|
+
});
|
|
645
1144
|
const controller = new AbortController();
|
|
646
1145
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
647
1146
|
try {
|
|
648
1147
|
let response;
|
|
649
1148
|
try {
|
|
650
|
-
response = await fetchImpl(`${baseUrl}${
|
|
1149
|
+
response = await fetchImpl(`${baseUrl}${requestPath}`, {
|
|
651
1150
|
method,
|
|
652
1151
|
headers: {
|
|
653
1152
|
"content-type": "application/json"
|
|
@@ -657,6 +1156,13 @@ var createCoreClient = (options = {}) => {
|
|
|
657
1156
|
});
|
|
658
1157
|
} catch (error) {
|
|
659
1158
|
if (controller.signal.aborted || error instanceof Error && error.name === "AbortError") {
|
|
1159
|
+
logger.warn("cli.core.request.timeout", {
|
|
1160
|
+
method,
|
|
1161
|
+
path: requestPath,
|
|
1162
|
+
base_url: baseUrl,
|
|
1163
|
+
timeout_ms: timeoutMs,
|
|
1164
|
+
duration_ms: durationMs(startedAt)
|
|
1165
|
+
});
|
|
660
1166
|
throw new CoreClientError({
|
|
661
1167
|
code: "TIMEOUT",
|
|
662
1168
|
message: `Core request timed out after ${timeoutMs}ms.`,
|
|
@@ -664,20 +1170,50 @@ var createCoreClient = (options = {}) => {
|
|
|
664
1170
|
details: {
|
|
665
1171
|
timeout_ms: timeoutMs,
|
|
666
1172
|
base_url: baseUrl,
|
|
667
|
-
path:
|
|
1173
|
+
path: requestPath
|
|
668
1174
|
}
|
|
669
1175
|
});
|
|
670
1176
|
}
|
|
1177
|
+
logger.error("cli.core.request.failed", {
|
|
1178
|
+
method,
|
|
1179
|
+
path: requestPath,
|
|
1180
|
+
base_url: baseUrl,
|
|
1181
|
+
duration_ms: durationMs(startedAt),
|
|
1182
|
+
error
|
|
1183
|
+
});
|
|
671
1184
|
throw error;
|
|
672
1185
|
}
|
|
673
1186
|
const raw = await response.text();
|
|
674
1187
|
if (!raw) {
|
|
1188
|
+
logger.warn("cli.core.request.empty_response", {
|
|
1189
|
+
method,
|
|
1190
|
+
path: requestPath,
|
|
1191
|
+
base_url: baseUrl,
|
|
1192
|
+
status: response.status,
|
|
1193
|
+
duration_ms: durationMs(startedAt)
|
|
1194
|
+
});
|
|
675
1195
|
throw new Error(`Empty response from Core (${response.status}).`);
|
|
676
1196
|
}
|
|
677
1197
|
try {
|
|
678
|
-
|
|
1198
|
+
const parsed = JSON.parse(raw);
|
|
1199
|
+
logger.debug("cli.core.request.end", {
|
|
1200
|
+
method,
|
|
1201
|
+
path: requestPath,
|
|
1202
|
+
base_url: baseUrl,
|
|
1203
|
+
status: response.status,
|
|
1204
|
+
duration_ms: durationMs(startedAt)
|
|
1205
|
+
});
|
|
1206
|
+
return parsed;
|
|
679
1207
|
} catch (error) {
|
|
680
1208
|
const message = error instanceof Error ? error.message : "Unknown JSON parse error";
|
|
1209
|
+
logger.error("cli.core.request.invalid_json", {
|
|
1210
|
+
method,
|
|
1211
|
+
path: requestPath,
|
|
1212
|
+
base_url: baseUrl,
|
|
1213
|
+
status: response.status,
|
|
1214
|
+
duration_ms: durationMs(startedAt),
|
|
1215
|
+
error
|
|
1216
|
+
});
|
|
681
1217
|
throw new Error(`Failed to parse Core response: ${message}`);
|
|
682
1218
|
}
|
|
683
1219
|
} finally {
|
|
@@ -697,56 +1233,108 @@ var createCoreClient = (options = {}) => {
|
|
|
697
1233
|
});
|
|
698
1234
|
} catch (error) {
|
|
699
1235
|
if (controller.signal.aborted || error instanceof Error && error.name === "AbortError") {
|
|
1236
|
+
logger.warn("cli.core.health.timeout", {
|
|
1237
|
+
base_url: baseUrl,
|
|
1238
|
+
timeout_ms: timeoutMs
|
|
1239
|
+
});
|
|
700
1240
|
return false;
|
|
701
1241
|
}
|
|
1242
|
+
logger.warn("cli.core.health.fetch_failed", {
|
|
1243
|
+
base_url: baseUrl,
|
|
1244
|
+
error
|
|
1245
|
+
});
|
|
702
1246
|
throw error;
|
|
703
1247
|
}
|
|
704
1248
|
if (!response.ok) {
|
|
1249
|
+
logger.warn("cli.core.health.non_ok", {
|
|
1250
|
+
base_url: baseUrl,
|
|
1251
|
+
status: response.status
|
|
1252
|
+
});
|
|
705
1253
|
return false;
|
|
706
1254
|
}
|
|
707
1255
|
const data = await response.json().catch(() => null);
|
|
708
|
-
|
|
1256
|
+
const ok = Boolean(data?.ok);
|
|
1257
|
+
if (!ok) {
|
|
1258
|
+
logger.warn("cli.core.health.not_ready", {
|
|
1259
|
+
base_url: baseUrl
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
return ok;
|
|
709
1263
|
} finally {
|
|
710
1264
|
clearTimeout(timeout);
|
|
711
1265
|
}
|
|
712
|
-
} catch {
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
logger.warn("cli.core.health.error", {
|
|
1268
|
+
base_url: baseUrl,
|
|
1269
|
+
error
|
|
1270
|
+
});
|
|
713
1271
|
return false;
|
|
714
1272
|
}
|
|
715
1273
|
};
|
|
716
1274
|
const spawnDaemon = () => {
|
|
717
|
-
const coreEntry = (0,
|
|
1275
|
+
const coreEntry = (0, import_node_path3.resolve)(__dirname, "api.js");
|
|
1276
|
+
const startOptions = [];
|
|
1277
|
+
if (runtime.hostSource === "option" || runtime.hostSource === "env") {
|
|
1278
|
+
startOptions.push(`host: ${JSON.stringify(runtime.host)}`);
|
|
1279
|
+
}
|
|
1280
|
+
if (runtime.portSource === "option" || runtime.portSource === "env") {
|
|
1281
|
+
startOptions.push(`port: ${runtime.port}`);
|
|
1282
|
+
}
|
|
718
1283
|
const script = `const { startCoreServer } = require(${JSON.stringify(
|
|
719
1284
|
coreEntry
|
|
720
1285
|
)});
|
|
721
|
-
startCoreServer({
|
|
722
|
-
|
|
723
|
-
)}
|
|
1286
|
+
startCoreServer({ ${startOptions.join(
|
|
1287
|
+
", "
|
|
1288
|
+
)} })
|
|
724
1289
|
.catch((err) => { console.error(err); process.exit(1); });`;
|
|
1290
|
+
logger.info("cli.core.spawn.start", {
|
|
1291
|
+
host: runtime.host,
|
|
1292
|
+
port: runtime.port,
|
|
1293
|
+
host_source: runtime.hostSource,
|
|
1294
|
+
port_source: runtime.portSource
|
|
1295
|
+
});
|
|
725
1296
|
const child = spawnImpl(process.execPath, ["-e", script], {
|
|
726
1297
|
detached: true,
|
|
727
1298
|
stdio: "ignore",
|
|
728
|
-
env: {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1299
|
+
env: { ...process.env }
|
|
1300
|
+
});
|
|
1301
|
+
child.on("error", (error) => {
|
|
1302
|
+
logger.error("cli.core.spawn.error", {
|
|
1303
|
+
host: runtime.host,
|
|
1304
|
+
port: runtime.port,
|
|
1305
|
+
error
|
|
1306
|
+
});
|
|
735
1307
|
});
|
|
736
1308
|
child.unref();
|
|
737
1309
|
};
|
|
738
1310
|
const ensureCoreRunning = async () => {
|
|
1311
|
+
refreshRuntime();
|
|
739
1312
|
if (await checkHealth()) {
|
|
1313
|
+
logger.debug("cli.core.ensure_ready.already_running", {
|
|
1314
|
+
base_url: baseUrl
|
|
1315
|
+
});
|
|
740
1316
|
return;
|
|
741
1317
|
}
|
|
742
1318
|
spawnDaemon();
|
|
743
1319
|
for (let attempt = 0; attempt < HEALTH_ATTEMPTS; attempt += 1) {
|
|
744
1320
|
await (0, import_promises.setTimeout)(HEALTH_RETRY_MS);
|
|
1321
|
+
refreshRuntime();
|
|
745
1322
|
if (await checkHealth()) {
|
|
1323
|
+
logger.info("cli.core.ensure_ready.ready", {
|
|
1324
|
+
base_url: baseUrl,
|
|
1325
|
+
attempts: attempt + 1
|
|
1326
|
+
});
|
|
746
1327
|
return;
|
|
747
1328
|
}
|
|
748
1329
|
}
|
|
749
|
-
|
|
1330
|
+
logger.error("cli.core.ensure_ready.failed", {
|
|
1331
|
+
host: runtime.host,
|
|
1332
|
+
port: runtime.port,
|
|
1333
|
+
attempts: HEALTH_ATTEMPTS
|
|
1334
|
+
});
|
|
1335
|
+
throw new Error(
|
|
1336
|
+
`Core daemon failed to start on ${runtime.host}:${runtime.port}.`
|
|
1337
|
+
);
|
|
750
1338
|
};
|
|
751
1339
|
let ensurePromise = null;
|
|
752
1340
|
const ensureReady = async () => {
|
|
@@ -760,9 +1348,16 @@ startCoreServer({ host: ${JSON.stringify(
|
|
|
760
1348
|
};
|
|
761
1349
|
const post = async (path9, body) => {
|
|
762
1350
|
await ensureReady();
|
|
1351
|
+
refreshRuntime();
|
|
763
1352
|
return requestJson("POST", path9, body);
|
|
764
1353
|
};
|
|
765
|
-
return {
|
|
1354
|
+
return {
|
|
1355
|
+
get baseUrl() {
|
|
1356
|
+
return baseUrl;
|
|
1357
|
+
},
|
|
1358
|
+
ensureReady,
|
|
1359
|
+
post
|
|
1360
|
+
};
|
|
766
1361
|
};
|
|
767
1362
|
|
|
768
1363
|
// packages/cli/src/cli-output.ts
|
|
@@ -857,37 +1452,76 @@ var getGlobalOptions = (command) => {
|
|
|
857
1452
|
const root = getRootCommand(command);
|
|
858
1453
|
return root.opts();
|
|
859
1454
|
};
|
|
860
|
-
var
|
|
1455
|
+
var durationMs2 = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
|
|
1456
|
+
var commandLabel = (command) => {
|
|
1457
|
+
const pieces = command.name().split(" ").filter((piece) => piece.length > 0);
|
|
1458
|
+
return pieces.length > 0 ? pieces.join(".") : "unknown";
|
|
1459
|
+
};
|
|
1460
|
+
var createCliLogger = (command) => createJsonlLogger({ stream: "cli" }).child({
|
|
1461
|
+
scope: "cli-runtime",
|
|
1462
|
+
command: commandLabel(command)
|
|
1463
|
+
});
|
|
1464
|
+
var createClientFromCommand = (command, logger) => {
|
|
861
1465
|
const options = getGlobalOptions(command);
|
|
862
1466
|
return createCoreClient({
|
|
863
1467
|
host: options.host,
|
|
864
1468
|
port: options.port,
|
|
865
|
-
ensureDaemon: options.daemon !== false
|
|
1469
|
+
ensureDaemon: options.daemon !== false,
|
|
1470
|
+
logger: logger?.child({ scope: "core-client" })
|
|
866
1471
|
});
|
|
867
1472
|
};
|
|
868
1473
|
var runCommand = async (command, work) => {
|
|
869
1474
|
const options = getGlobalOptions(command);
|
|
870
|
-
const
|
|
1475
|
+
const logger = createCliLogger(command);
|
|
1476
|
+
const startedAt = process.hrtime.bigint();
|
|
1477
|
+
logger.info("cli.command.start", {
|
|
1478
|
+
host: options.host ?? null,
|
|
1479
|
+
port: options.port ?? null,
|
|
1480
|
+
daemon: options.daemon !== false,
|
|
1481
|
+
json: Boolean(options.json)
|
|
1482
|
+
});
|
|
1483
|
+
const client = createClientFromCommand(command, logger);
|
|
871
1484
|
try {
|
|
872
1485
|
const envelope2 = await work(client, options);
|
|
873
1486
|
outputEnvelope(envelope2, { json: Boolean(options.json) });
|
|
1487
|
+
logger.info("cli.command.end", {
|
|
1488
|
+
ok: envelope2.ok,
|
|
1489
|
+
duration_ms: durationMs2(startedAt)
|
|
1490
|
+
});
|
|
874
1491
|
if (!envelope2.ok) {
|
|
875
1492
|
process.exitCode = 1;
|
|
876
1493
|
}
|
|
877
1494
|
} catch (error) {
|
|
1495
|
+
logger.error("cli.command.error", {
|
|
1496
|
+
duration_ms: durationMs2(startedAt),
|
|
1497
|
+
error
|
|
1498
|
+
});
|
|
878
1499
|
outputError(error, { json: Boolean(options.json) });
|
|
879
1500
|
process.exitCode = 1;
|
|
880
1501
|
}
|
|
881
1502
|
};
|
|
882
1503
|
var runLocal = async (command, work) => {
|
|
883
1504
|
const options = getGlobalOptions(command);
|
|
1505
|
+
const logger = createCliLogger(command).child({ mode: "local" });
|
|
1506
|
+
const startedAt = process.hrtime.bigint();
|
|
1507
|
+
logger.info("cli.command.start", {
|
|
1508
|
+
json: Boolean(options.json)
|
|
1509
|
+
});
|
|
884
1510
|
try {
|
|
885
1511
|
const envelope2 = await work(options);
|
|
886
1512
|
outputEnvelope(envelope2, { json: Boolean(options.json) });
|
|
1513
|
+
logger.info("cli.command.end", {
|
|
1514
|
+
ok: envelope2.ok,
|
|
1515
|
+
duration_ms: durationMs2(startedAt)
|
|
1516
|
+
});
|
|
887
1517
|
if (!envelope2.ok) {
|
|
888
1518
|
process.exitCode = 1;
|
|
889
1519
|
}
|
|
890
1520
|
} catch (error) {
|
|
1521
|
+
logger.error("cli.command.error", {
|
|
1522
|
+
duration_ms: durationMs2(startedAt),
|
|
1523
|
+
error
|
|
1524
|
+
});
|
|
891
1525
|
outputError(error, { json: Boolean(options.json) });
|
|
892
1526
|
process.exitCode = 1;
|
|
893
1527
|
}
|
|
@@ -971,6 +1605,149 @@ var registerDiagnosticsCommands = (program2) => {
|
|
|
971
1605
|
});
|
|
972
1606
|
};
|
|
973
1607
|
|
|
1608
|
+
// packages/cli/src/open-path.ts
|
|
1609
|
+
var import_node_child_process2 = require("node:child_process");
|
|
1610
|
+
var openPath = async (target) => {
|
|
1611
|
+
const platform = process.platform;
|
|
1612
|
+
if (platform === "darwin") {
|
|
1613
|
+
const child2 = (0, import_node_child_process2.spawn)("open", [target], { detached: true, stdio: "ignore" });
|
|
1614
|
+
child2.unref();
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
if (platform === "win32") {
|
|
1618
|
+
const child2 = (0, import_node_child_process2.spawn)("cmd", ["/c", "start", "", target], {
|
|
1619
|
+
detached: true,
|
|
1620
|
+
stdio: "ignore"
|
|
1621
|
+
});
|
|
1622
|
+
child2.unref();
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const child = (0, import_node_child_process2.spawn)("xdg-open", [target], {
|
|
1626
|
+
detached: true,
|
|
1627
|
+
stdio: "ignore"
|
|
1628
|
+
});
|
|
1629
|
+
child.unref();
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
// packages/cli/src/commands/dev.ts
|
|
1633
|
+
var ENV_EXTENSION_ID = "BROWSER_BRIDGE_EXTENSION_ID";
|
|
1634
|
+
var ACTIVATION_FLAG_PARAM = "bb_activate";
|
|
1635
|
+
var ACTIVATION_PORT_PARAM = "corePort";
|
|
1636
|
+
var ACTIVATION_WORKTREE_PARAM = "worktreeId";
|
|
1637
|
+
var normalizeToken = (value) => {
|
|
1638
|
+
if (typeof value !== "string") {
|
|
1639
|
+
return void 0;
|
|
1640
|
+
}
|
|
1641
|
+
const trimmed = value.trim();
|
|
1642
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1643
|
+
};
|
|
1644
|
+
var resolveActivationExtensionId = (options) => {
|
|
1645
|
+
const optionId = normalizeToken(options.optionExtensionId);
|
|
1646
|
+
if (optionId) {
|
|
1647
|
+
return { extensionId: optionId, source: "flag" };
|
|
1648
|
+
}
|
|
1649
|
+
const envId = normalizeToken(options.envExtensionId);
|
|
1650
|
+
if (envId) {
|
|
1651
|
+
return { extensionId: envId, source: "env" };
|
|
1652
|
+
}
|
|
1653
|
+
const metadataId = normalizeToken(options.metadataExtensionId);
|
|
1654
|
+
if (metadataId) {
|
|
1655
|
+
return { extensionId: metadataId, source: "metadata" };
|
|
1656
|
+
}
|
|
1657
|
+
return null;
|
|
1658
|
+
};
|
|
1659
|
+
var buildActivationOptionsUrl = (options) => {
|
|
1660
|
+
const search = new URLSearchParams();
|
|
1661
|
+
search.set(ACTIVATION_FLAG_PARAM, "1");
|
|
1662
|
+
search.set(ACTIVATION_PORT_PARAM, String(options.corePort));
|
|
1663
|
+
if (options.worktreeId) {
|
|
1664
|
+
search.set(ACTIVATION_WORKTREE_PARAM, options.worktreeId);
|
|
1665
|
+
}
|
|
1666
|
+
return `chrome-extension://${options.extensionId}/options.html?${search.toString()}`;
|
|
1667
|
+
};
|
|
1668
|
+
var buildPersistedRuntimeMetadata = (runtime, extensionId) => ({
|
|
1669
|
+
host: runtime.host,
|
|
1670
|
+
port: runtime.port,
|
|
1671
|
+
git_root: runtime.gitRoot ?? runtime.metadata?.git_root,
|
|
1672
|
+
worktree_id: runtime.worktreeId ?? runtime.metadata?.worktree_id,
|
|
1673
|
+
extension_id: extensionId,
|
|
1674
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1675
|
+
});
|
|
1676
|
+
var resolveRuntimeForCommand = (options) => resolveCoreRuntime({
|
|
1677
|
+
host: options.host,
|
|
1678
|
+
port: options.port,
|
|
1679
|
+
strictEnvPort: true
|
|
1680
|
+
});
|
|
1681
|
+
var registerDevCommands = (program2) => {
|
|
1682
|
+
const dev = program2.command("dev").description("Development commands");
|
|
1683
|
+
dev.command("info").description("Print resolved runtime details for the current worktree").action(async (_options, command) => {
|
|
1684
|
+
await runLocal(command, async (globalOptions) => {
|
|
1685
|
+
const runtime = resolveRuntimeForCommand(globalOptions);
|
|
1686
|
+
const logDir = resolveLogDirectory({ gitRoot: runtime.gitRoot });
|
|
1687
|
+
return {
|
|
1688
|
+
ok: true,
|
|
1689
|
+
result: {
|
|
1690
|
+
host: runtime.host,
|
|
1691
|
+
hostSource: runtime.hostSource,
|
|
1692
|
+
port: runtime.port,
|
|
1693
|
+
portSource: runtime.portSource,
|
|
1694
|
+
deterministicPort: runtime.deterministicPort,
|
|
1695
|
+
worktreeId: runtime.worktreeId,
|
|
1696
|
+
metadataPath: runtime.metadataPath,
|
|
1697
|
+
logDir,
|
|
1698
|
+
metadataSnapshot: runtime.metadata
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
});
|
|
1702
|
+
});
|
|
1703
|
+
dev.command("activate").description(
|
|
1704
|
+
"Persist worktree runtime metadata and open extension options for activation"
|
|
1705
|
+
).option(
|
|
1706
|
+
"--extension-id <id>",
|
|
1707
|
+
"Chrome extension id to activate for this worktree"
|
|
1708
|
+
).action(async (options, command) => {
|
|
1709
|
+
await runLocal(command, async (globalOptions) => {
|
|
1710
|
+
const runtime = resolveRuntimeForCommand(globalOptions);
|
|
1711
|
+
const extension = resolveActivationExtensionId({
|
|
1712
|
+
optionExtensionId: options.extensionId,
|
|
1713
|
+
envExtensionId: process.env[ENV_EXTENSION_ID],
|
|
1714
|
+
metadataExtensionId: runtime.metadata?.extension_id
|
|
1715
|
+
});
|
|
1716
|
+
if (!extension) {
|
|
1717
|
+
throw new CliError({
|
|
1718
|
+
code: "INVALID_ARGUMENT",
|
|
1719
|
+
message: "Missing extension id. Provide --extension-id <id>, set BROWSER_BRIDGE_EXTENSION_ID, or persist extension_id in metadata by running dev activate with --extension-id once.",
|
|
1720
|
+
retryable: false,
|
|
1721
|
+
details: {
|
|
1722
|
+
metadataPath: runtime.metadataPath
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
const metadataPath = writeRuntimeMetadata(
|
|
1727
|
+
buildPersistedRuntimeMetadata(runtime, extension.extensionId),
|
|
1728
|
+
{ metadataPath: runtime.metadataPath }
|
|
1729
|
+
);
|
|
1730
|
+
const activationUrl = buildActivationOptionsUrl({
|
|
1731
|
+
extensionId: extension.extensionId,
|
|
1732
|
+
corePort: runtime.port,
|
|
1733
|
+
worktreeId: runtime.worktreeId
|
|
1734
|
+
});
|
|
1735
|
+
await openPath(activationUrl);
|
|
1736
|
+
return {
|
|
1737
|
+
ok: true,
|
|
1738
|
+
result: {
|
|
1739
|
+
extensionId: extension.extensionId,
|
|
1740
|
+
extensionIdSource: extension.source,
|
|
1741
|
+
host: runtime.host,
|
|
1742
|
+
port: runtime.port,
|
|
1743
|
+
metadataPath,
|
|
1744
|
+
activationUrl
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
});
|
|
1748
|
+
});
|
|
1749
|
+
};
|
|
1750
|
+
|
|
974
1751
|
// packages/cli/src/locator.ts
|
|
975
1752
|
var buildLocator = (options) => {
|
|
976
1753
|
const locator = {};
|
|
@@ -1418,39 +2195,36 @@ var registerInspectCommands = (program2) => {
|
|
|
1418
2195
|
};
|
|
1419
2196
|
|
|
1420
2197
|
// packages/mcp-adapter/src/core-client.ts
|
|
1421
|
-
var DEFAULT_HOST2 = "127.0.0.1";
|
|
1422
|
-
var DEFAULT_PORT2 = 3210;
|
|
1423
2198
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
1424
|
-
var resolveHost2 = (host) => {
|
|
1425
|
-
const candidate = host?.trim() || process.env.BROWSER_BRIDGE_CORE_HOST || process.env.BROWSER_VISION_CORE_HOST;
|
|
1426
|
-
if (candidate && candidate.length > 0) {
|
|
1427
|
-
return candidate;
|
|
1428
|
-
}
|
|
1429
|
-
return DEFAULT_HOST2;
|
|
1430
|
-
};
|
|
1431
|
-
var resolvePort2 = (port) => {
|
|
1432
|
-
const candidate = port ?? (process.env.BROWSER_BRIDGE_CORE_PORT ? Number.parseInt(process.env.BROWSER_BRIDGE_CORE_PORT, 10) : process.env.BROWSER_VISION_CORE_PORT ? Number.parseInt(process.env.BROWSER_VISION_CORE_PORT, 10) : void 0);
|
|
1433
|
-
if (candidate === void 0 || candidate === null) {
|
|
1434
|
-
return DEFAULT_PORT2;
|
|
1435
|
-
}
|
|
1436
|
-
const parsed = typeof candidate === "number" ? candidate : Number.parseInt(candidate, 10);
|
|
1437
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
1438
|
-
throw new Error(`Invalid port: ${String(candidate)}`);
|
|
1439
|
-
}
|
|
1440
|
-
return parsed;
|
|
1441
|
-
};
|
|
1442
2199
|
var normalizePath2 = (path9) => path9.startsWith("/") ? path9 : `/${path9}`;
|
|
2200
|
+
var durationMs3 = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
|
|
1443
2201
|
var createCoreClient2 = (options = {}) => {
|
|
1444
|
-
const
|
|
1445
|
-
|
|
2202
|
+
const logger = options.logger ?? createJsonlLogger({
|
|
2203
|
+
stream: "mcp-adapter",
|
|
2204
|
+
cwd: options.cwd
|
|
2205
|
+
}).child({ scope: "core-client" });
|
|
2206
|
+
const runtime = resolveCoreRuntime({
|
|
2207
|
+
host: options.host,
|
|
2208
|
+
port: options.port,
|
|
2209
|
+
cwd: options.cwd,
|
|
2210
|
+
strictEnvPort: true
|
|
2211
|
+
});
|
|
2212
|
+
const host = runtime.host;
|
|
2213
|
+
const port = runtime.port;
|
|
1446
2214
|
const baseUrl = `http://${host}:${port}`;
|
|
1447
2215
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
1448
2216
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1449
2217
|
const requestJson = async (path9, body) => {
|
|
2218
|
+
const requestPath = normalizePath2(path9);
|
|
2219
|
+
const startedAt = process.hrtime.bigint();
|
|
2220
|
+
logger.debug("mcp.core.request.start", {
|
|
2221
|
+
path: requestPath,
|
|
2222
|
+
base_url: baseUrl
|
|
2223
|
+
});
|
|
1450
2224
|
const controller = new AbortController();
|
|
1451
2225
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1452
2226
|
try {
|
|
1453
|
-
const response = await fetchImpl(`${baseUrl}${
|
|
2227
|
+
const response = await fetchImpl(`${baseUrl}${requestPath}`, {
|
|
1454
2228
|
method: "POST",
|
|
1455
2229
|
headers: {
|
|
1456
2230
|
"content-type": "application/json"
|
|
@@ -1460,14 +2234,42 @@ var createCoreClient2 = (options = {}) => {
|
|
|
1460
2234
|
});
|
|
1461
2235
|
const raw = await response.text();
|
|
1462
2236
|
if (!raw) {
|
|
2237
|
+
logger.warn("mcp.core.request.empty_response", {
|
|
2238
|
+
path: requestPath,
|
|
2239
|
+
base_url: baseUrl,
|
|
2240
|
+
status: response.status,
|
|
2241
|
+
duration_ms: durationMs3(startedAt)
|
|
2242
|
+
});
|
|
1463
2243
|
throw new Error(`Empty response from Core (${response.status}).`);
|
|
1464
2244
|
}
|
|
1465
2245
|
try {
|
|
1466
|
-
|
|
2246
|
+
const parsed = JSON.parse(raw);
|
|
2247
|
+
logger.debug("mcp.core.request.end", {
|
|
2248
|
+
path: requestPath,
|
|
2249
|
+
base_url: baseUrl,
|
|
2250
|
+
status: response.status,
|
|
2251
|
+
duration_ms: durationMs3(startedAt)
|
|
2252
|
+
});
|
|
2253
|
+
return parsed;
|
|
1467
2254
|
} catch (error) {
|
|
1468
2255
|
const message = error instanceof Error ? error.message : "Unknown JSON parse error";
|
|
2256
|
+
logger.error("mcp.core.request.invalid_json", {
|
|
2257
|
+
path: requestPath,
|
|
2258
|
+
base_url: baseUrl,
|
|
2259
|
+
status: response.status,
|
|
2260
|
+
duration_ms: durationMs3(startedAt),
|
|
2261
|
+
error
|
|
2262
|
+
});
|
|
1469
2263
|
throw new Error(`Failed to parse Core response: ${message}`);
|
|
1470
2264
|
}
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
logger.error("mcp.core.request.failed", {
|
|
2267
|
+
path: requestPath,
|
|
2268
|
+
base_url: baseUrl,
|
|
2269
|
+
duration_ms: durationMs3(startedAt),
|
|
2270
|
+
error
|
|
2271
|
+
});
|
|
2272
|
+
throw error;
|
|
1471
2273
|
} finally {
|
|
1472
2274
|
clearTimeout(timeout);
|
|
1473
2275
|
}
|
|
@@ -1913,25 +2715,57 @@ var DEFAULT_SERVER_NAME = "browser-bridge";
|
|
|
1913
2715
|
var DEFAULT_SERVER_VERSION = "0.0.0";
|
|
1914
2716
|
var DEFAULT_HTTP_HOST = "127.0.0.1";
|
|
1915
2717
|
var DEFAULT_HTTP_PATH = "/mcp";
|
|
2718
|
+
var durationMs4 = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
|
|
2719
|
+
var resolveAdapterLogger = (options) => options.logger ?? createJsonlLogger({
|
|
2720
|
+
stream: "mcp-adapter",
|
|
2721
|
+
cwd: options.cwd
|
|
2722
|
+
});
|
|
1916
2723
|
var createMcpServer = (options = {}) => {
|
|
2724
|
+
const logger = resolveAdapterLogger(options);
|
|
1917
2725
|
const server = new import_mcp.McpServer({
|
|
1918
2726
|
name: options.name ?? DEFAULT_SERVER_NAME,
|
|
1919
2727
|
version: options.version ?? DEFAULT_SERVER_VERSION
|
|
1920
2728
|
});
|
|
1921
|
-
const client = options.coreClient ?? createCoreClient2(
|
|
2729
|
+
const client = options.coreClient ?? createCoreClient2({
|
|
2730
|
+
...options,
|
|
2731
|
+
logger: logger.child({ scope: "core-client" })
|
|
2732
|
+
});
|
|
1922
2733
|
registerBrowserBridgeTools(server, client);
|
|
2734
|
+
logger.info("mcp.server.created", {
|
|
2735
|
+
name: options.name ?? DEFAULT_SERVER_NAME,
|
|
2736
|
+
version: options.version ?? DEFAULT_SERVER_VERSION,
|
|
2737
|
+
core_base_url: client.baseUrl
|
|
2738
|
+
});
|
|
1923
2739
|
return { server, client };
|
|
1924
2740
|
};
|
|
1925
2741
|
var startMcpServer = async (options = {}) => {
|
|
1926
|
-
const
|
|
2742
|
+
const logger = resolveAdapterLogger(options);
|
|
2743
|
+
logger.info("mcp.stdio.start.begin", {
|
|
2744
|
+
name: options.name ?? DEFAULT_SERVER_NAME,
|
|
2745
|
+
version: options.version ?? DEFAULT_SERVER_VERSION
|
|
2746
|
+
});
|
|
2747
|
+
const handle = createMcpServer({
|
|
2748
|
+
...options,
|
|
2749
|
+
logger
|
|
2750
|
+
});
|
|
1927
2751
|
const transport = new import_stdio.StdioServerTransport();
|
|
1928
|
-
|
|
2752
|
+
try {
|
|
2753
|
+
await handle.server.connect(transport);
|
|
2754
|
+
logger.info("mcp.stdio.start.ready", {
|
|
2755
|
+
core_base_url: handle.client.baseUrl
|
|
2756
|
+
});
|
|
2757
|
+
} catch (error) {
|
|
2758
|
+
logger.error("mcp.stdio.start.failed", {
|
|
2759
|
+
error
|
|
2760
|
+
});
|
|
2761
|
+
throw error;
|
|
2762
|
+
}
|
|
1929
2763
|
return { ...handle, transport };
|
|
1930
2764
|
};
|
|
1931
2765
|
var readJsonBody = async (req, maxBytes = 5 * 1024 * 1024) => {
|
|
1932
2766
|
const chunks = [];
|
|
1933
2767
|
let total = 0;
|
|
1934
|
-
await new Promise((
|
|
2768
|
+
await new Promise((resolve3, reject) => {
|
|
1935
2769
|
req.on("data", (chunk) => {
|
|
1936
2770
|
total += chunk.length;
|
|
1937
2771
|
if (total > maxBytes) {
|
|
@@ -1941,7 +2775,7 @@ var readJsonBody = async (req, maxBytes = 5 * 1024 * 1024) => {
|
|
|
1941
2775
|
}
|
|
1942
2776
|
chunks.push(chunk);
|
|
1943
2777
|
});
|
|
1944
|
-
req.on("end", () =>
|
|
2778
|
+
req.on("end", () => resolve3());
|
|
1945
2779
|
req.on("error", (err) => reject(err));
|
|
1946
2780
|
});
|
|
1947
2781
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
@@ -1960,10 +2794,19 @@ var getHeaderValue = (value) => {
|
|
|
1960
2794
|
return void 0;
|
|
1961
2795
|
};
|
|
1962
2796
|
var startMcpHttpServer = async (options = {}) => {
|
|
2797
|
+
const logger = resolveAdapterLogger(options).child({ scope: "http-server" });
|
|
1963
2798
|
const host = options.host ?? DEFAULT_HTTP_HOST;
|
|
1964
2799
|
const port = typeof options.port === "number" ? options.port : 0;
|
|
1965
2800
|
const path9 = options.path ?? DEFAULT_HTTP_PATH;
|
|
1966
|
-
|
|
2801
|
+
logger.info("mcp.http.start.begin", {
|
|
2802
|
+
host,
|
|
2803
|
+
port,
|
|
2804
|
+
path: path9
|
|
2805
|
+
});
|
|
2806
|
+
const client = options.coreClient ?? createCoreClient2({
|
|
2807
|
+
...options,
|
|
2808
|
+
logger: logger.child({ scope: "core-client" })
|
|
2809
|
+
});
|
|
1967
2810
|
const sessions = /* @__PURE__ */ new Map();
|
|
1968
2811
|
const closeAllSessions = async () => {
|
|
1969
2812
|
const entries = Array.from(sessions.values());
|
|
@@ -1982,23 +2825,52 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
1982
2825
|
);
|
|
1983
2826
|
};
|
|
1984
2827
|
const httpServer = (0, import_node_http.createServer)(async (req, res) => {
|
|
2828
|
+
const startedAt = process.hrtime.bigint();
|
|
2829
|
+
const requestLogger = logger.child({
|
|
2830
|
+
scope: "http-request",
|
|
2831
|
+
request_id: (0, import_node_crypto.randomUUID)()
|
|
2832
|
+
});
|
|
2833
|
+
requestLogger.debug("mcp.http.request.start", {
|
|
2834
|
+
method: req.method,
|
|
2835
|
+
url: req.url ?? ""
|
|
2836
|
+
});
|
|
1985
2837
|
try {
|
|
1986
2838
|
const url = new URL(req.url ?? "", `http://${host}`);
|
|
1987
2839
|
if (url.pathname !== path9) {
|
|
1988
2840
|
res.writeHead(404, { "content-type": "application/json" });
|
|
1989
2841
|
res.end(JSON.stringify({ error: "Not Found" }));
|
|
2842
|
+
requestLogger.warn("mcp.http.request.not_found", {
|
|
2843
|
+
method: req.method,
|
|
2844
|
+
pathname: url.pathname,
|
|
2845
|
+
expected_path: path9,
|
|
2846
|
+
status: 404,
|
|
2847
|
+
duration_ms: durationMs4(startedAt)
|
|
2848
|
+
});
|
|
1990
2849
|
return;
|
|
1991
2850
|
}
|
|
1992
2851
|
const sessionId = getHeaderValue(req.headers["mcp-session-id"]);
|
|
2852
|
+
requestLogger.debug("mcp.http.request.session", {
|
|
2853
|
+
session_id: sessionId ?? null
|
|
2854
|
+
});
|
|
1993
2855
|
const parsedBody = req.method === "POST" ? await readJsonBody(req) : void 0;
|
|
1994
2856
|
if (sessionId) {
|
|
1995
2857
|
const entry = sessions.get(sessionId);
|
|
1996
2858
|
if (!entry) {
|
|
1997
2859
|
res.writeHead(404, { "content-type": "application/json" });
|
|
1998
2860
|
res.end(JSON.stringify({ error: "Unknown session." }));
|
|
2861
|
+
requestLogger.warn("mcp.http.request.unknown_session", {
|
|
2862
|
+
session_id: sessionId,
|
|
2863
|
+
status: 404,
|
|
2864
|
+
duration_ms: durationMs4(startedAt)
|
|
2865
|
+
});
|
|
1999
2866
|
return;
|
|
2000
2867
|
}
|
|
2001
2868
|
await entry.transport.handleRequest(req, res, parsedBody);
|
|
2869
|
+
requestLogger.info("mcp.http.request.forwarded", {
|
|
2870
|
+
session_id: sessionId,
|
|
2871
|
+
status: res.statusCode,
|
|
2872
|
+
duration_ms: durationMs4(startedAt)
|
|
2873
|
+
});
|
|
2002
2874
|
return;
|
|
2003
2875
|
}
|
|
2004
2876
|
if (req.method !== "POST" || !(0, import_types.isInitializeRequest)(parsedBody)) {
|
|
@@ -2008,6 +2880,11 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
2008
2880
|
error: "Missing mcp-session-id header. First request must be initialize."
|
|
2009
2881
|
})
|
|
2010
2882
|
);
|
|
2883
|
+
requestLogger.warn("mcp.http.request.invalid_initialize", {
|
|
2884
|
+
method: req.method,
|
|
2885
|
+
status: 400,
|
|
2886
|
+
duration_ms: durationMs4(startedAt)
|
|
2887
|
+
});
|
|
2011
2888
|
return;
|
|
2012
2889
|
}
|
|
2013
2890
|
let sessionEntry;
|
|
@@ -2016,6 +2893,9 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
2016
2893
|
onsessioninitialized: (sid) => {
|
|
2017
2894
|
if (sessionEntry) {
|
|
2018
2895
|
sessions.set(sid, sessionEntry);
|
|
2896
|
+
logger.info("mcp.http.session.opened", {
|
|
2897
|
+
session_id: sid
|
|
2898
|
+
});
|
|
2019
2899
|
}
|
|
2020
2900
|
},
|
|
2021
2901
|
onsessionclosed: async (sid) => {
|
|
@@ -2028,6 +2908,9 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
2028
2908
|
entry.transport.close(),
|
|
2029
2909
|
entry.server.close()
|
|
2030
2910
|
]);
|
|
2911
|
+
logger.info("mcp.http.session.closed", {
|
|
2912
|
+
session_id: sid
|
|
2913
|
+
});
|
|
2031
2914
|
}
|
|
2032
2915
|
});
|
|
2033
2916
|
const sessionServer = new import_mcp.McpServer({
|
|
@@ -2038,7 +2921,18 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
2038
2921
|
await sessionServer.connect(transport);
|
|
2039
2922
|
sessionEntry = { transport, server: sessionServer };
|
|
2040
2923
|
await transport.handleRequest(req, res, parsedBody);
|
|
2924
|
+
requestLogger.info("mcp.http.request.initialized", {
|
|
2925
|
+
status: res.statusCode,
|
|
2926
|
+
duration_ms: durationMs4(startedAt)
|
|
2927
|
+
});
|
|
2041
2928
|
} catch (error) {
|
|
2929
|
+
requestLogger.error("mcp.http.request.error", {
|
|
2930
|
+
method: req.method,
|
|
2931
|
+
url: req.url ?? "",
|
|
2932
|
+
status: 500,
|
|
2933
|
+
duration_ms: durationMs4(startedAt),
|
|
2934
|
+
error
|
|
2935
|
+
});
|
|
2042
2936
|
res.writeHead(500, { "content-type": "application/json" });
|
|
2043
2937
|
res.end(
|
|
2044
2938
|
JSON.stringify({
|
|
@@ -2047,29 +2941,43 @@ var startMcpHttpServer = async (options = {}) => {
|
|
|
2047
2941
|
);
|
|
2048
2942
|
}
|
|
2049
2943
|
});
|
|
2050
|
-
const resolvedPort = await new Promise((
|
|
2944
|
+
const resolvedPort = await new Promise((resolve3, reject) => {
|
|
2051
2945
|
httpServer.listen(port, host, () => {
|
|
2052
2946
|
const address = httpServer.address();
|
|
2053
|
-
|
|
2947
|
+
resolve3(typeof address === "object" && address ? address.port : port);
|
|
2054
2948
|
});
|
|
2055
2949
|
httpServer.on("error", reject);
|
|
2056
2950
|
});
|
|
2951
|
+
logger.info("mcp.http.start.ready", {
|
|
2952
|
+
host,
|
|
2953
|
+
port: resolvedPort,
|
|
2954
|
+
path: path9,
|
|
2955
|
+
core_base_url: client.baseUrl
|
|
2956
|
+
});
|
|
2057
2957
|
return {
|
|
2058
2958
|
client,
|
|
2059
2959
|
host,
|
|
2060
2960
|
port: resolvedPort,
|
|
2061
2961
|
path: path9,
|
|
2062
2962
|
close: async () => {
|
|
2063
|
-
|
|
2963
|
+
logger.info("mcp.http.stop.begin", {
|
|
2964
|
+
host,
|
|
2965
|
+
port: resolvedPort
|
|
2966
|
+
});
|
|
2967
|
+
await new Promise((resolve3, reject) => {
|
|
2064
2968
|
httpServer.close((err) => {
|
|
2065
2969
|
if (err) {
|
|
2066
2970
|
reject(err);
|
|
2067
2971
|
} else {
|
|
2068
|
-
|
|
2972
|
+
resolve3();
|
|
2069
2973
|
}
|
|
2070
2974
|
});
|
|
2071
2975
|
});
|
|
2072
2976
|
await closeAllSessions();
|
|
2977
|
+
logger.info("mcp.http.stop.complete", {
|
|
2978
|
+
host,
|
|
2979
|
+
port: resolvedPort
|
|
2980
|
+
});
|
|
2073
2981
|
}
|
|
2074
2982
|
};
|
|
2075
2983
|
};
|
|
@@ -2098,19 +3006,19 @@ var checkboxPrompt = async (options) => {
|
|
|
2098
3006
|
};
|
|
2099
3007
|
|
|
2100
3008
|
// packages/cli/src/installer/mcp-install.ts
|
|
2101
|
-
var
|
|
3009
|
+
var import_node_child_process3 = require("node:child_process");
|
|
2102
3010
|
|
|
2103
3011
|
// packages/cli/src/installer/cursor-mcp.ts
|
|
2104
3012
|
var import_promises2 = __toESM(require("node:fs/promises"));
|
|
2105
3013
|
var import_node_os = __toESM(require("node:os"));
|
|
2106
|
-
var
|
|
3014
|
+
var import_node_path4 = __toESM(require("node:path"));
|
|
2107
3015
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2108
3016
|
var resolveCursorUserSettingsPath = (options) => {
|
|
2109
3017
|
const platform = options?.platform ?? process.platform;
|
|
2110
3018
|
const homeDir = options?.homeDir ?? import_node_os.default.homedir();
|
|
2111
3019
|
const env = options?.env ?? process.env;
|
|
2112
3020
|
if (platform === "darwin") {
|
|
2113
|
-
return
|
|
3021
|
+
return import_node_path4.default.join(
|
|
2114
3022
|
homeDir,
|
|
2115
3023
|
"Library",
|
|
2116
3024
|
"Application Support",
|
|
@@ -2126,13 +3034,13 @@ var resolveCursorUserSettingsPath = (options) => {
|
|
|
2126
3034
|
"APPDATA is not set; cannot resolve Cursor settings path."
|
|
2127
3035
|
);
|
|
2128
3036
|
}
|
|
2129
|
-
return
|
|
3037
|
+
return import_node_path4.default.join(appData, "Cursor", "User", "settings.json");
|
|
2130
3038
|
}
|
|
2131
|
-
return
|
|
3039
|
+
return import_node_path4.default.join(homeDir, ".config", "Cursor", "User", "settings.json");
|
|
2132
3040
|
};
|
|
2133
3041
|
var installCursorMcp = async (settingsPath) => {
|
|
2134
3042
|
const filePath = settingsPath ?? resolveCursorUserSettingsPath();
|
|
2135
|
-
const dir =
|
|
3043
|
+
const dir = import_node_path4.default.dirname(filePath);
|
|
2136
3044
|
await import_promises2.default.mkdir(dir, { recursive: true });
|
|
2137
3045
|
let settings = {};
|
|
2138
3046
|
try {
|
|
@@ -2168,15 +3076,15 @@ var installCursorMcp = async (settingsPath) => {
|
|
|
2168
3076
|
|
|
2169
3077
|
// packages/cli/src/installer/mcp-install.ts
|
|
2170
3078
|
var runQuiet = async (cmd, args) => {
|
|
2171
|
-
await new Promise((
|
|
2172
|
-
const child = (0,
|
|
3079
|
+
await new Promise((resolve3, reject) => {
|
|
3080
|
+
const child = (0, import_node_child_process3.spawn)(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2173
3081
|
let stderr = "";
|
|
2174
3082
|
child.stderr?.on("data", (chunk) => {
|
|
2175
3083
|
stderr += String(chunk);
|
|
2176
3084
|
});
|
|
2177
3085
|
child.on("error", reject);
|
|
2178
3086
|
child.on("exit", (code) => {
|
|
2179
|
-
if (code === 0)
|
|
3087
|
+
if (code === 0) resolve3();
|
|
2180
3088
|
else {
|
|
2181
3089
|
const suffix = stderr.trim() ? `: ${stderr.trim()}` : "";
|
|
2182
3090
|
reject(new Error(`${cmd} exited with ${code ?? "unknown"}${suffix}`));
|
|
@@ -2344,10 +3252,10 @@ var import_turndown = __toESM(require("turndown"));
|
|
|
2344
3252
|
// packages/core/src/artifacts.ts
|
|
2345
3253
|
var import_promises3 = require("node:fs/promises");
|
|
2346
3254
|
var import_node_os2 = __toESM(require("node:os"));
|
|
2347
|
-
var
|
|
3255
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
2348
3256
|
var ARTIFACTS_DIR_NAME = "browser-agent";
|
|
2349
3257
|
var resolveTempRoot = () => process.env.TMPDIR || process.env.TEMP || process.env.TMP || import_node_os2.default.tmpdir();
|
|
2350
|
-
var getArtifactRootDir = (sessionId) =>
|
|
3258
|
+
var getArtifactRootDir = (sessionId) => import_node_path5.default.join(resolveTempRoot(), ARTIFACTS_DIR_NAME, sessionId);
|
|
2351
3259
|
var ensureArtifactRootDir = async (sessionId) => {
|
|
2352
3260
|
const rootDir = getArtifactRootDir(sessionId);
|
|
2353
3261
|
await (0, import_promises3.mkdir)(rootDir, { recursive: true });
|
|
@@ -2482,30 +3390,6 @@ var PROCESS_STARTED_AT = new Date(
|
|
|
2482
3390
|
Date.now() - Math.floor(process.uptime() * 1e3)
|
|
2483
3391
|
).toISOString();
|
|
2484
3392
|
|
|
2485
|
-
// packages/cli/src/open-path.ts
|
|
2486
|
-
var import_node_child_process3 = require("node:child_process");
|
|
2487
|
-
var openPath = async (target) => {
|
|
2488
|
-
const platform = process.platform;
|
|
2489
|
-
if (platform === "darwin") {
|
|
2490
|
-
const child2 = (0, import_node_child_process3.spawn)("open", [target], { detached: true, stdio: "ignore" });
|
|
2491
|
-
child2.unref();
|
|
2492
|
-
return;
|
|
2493
|
-
}
|
|
2494
|
-
if (platform === "win32") {
|
|
2495
|
-
const child2 = (0, import_node_child_process3.spawn)("cmd", ["/c", "start", "", target], {
|
|
2496
|
-
detached: true,
|
|
2497
|
-
stdio: "ignore"
|
|
2498
|
-
});
|
|
2499
|
-
child2.unref();
|
|
2500
|
-
return;
|
|
2501
|
-
}
|
|
2502
|
-
const child = (0, import_node_child_process3.spawn)("xdg-open", [target], {
|
|
2503
|
-
detached: true,
|
|
2504
|
-
stdio: "ignore"
|
|
2505
|
-
});
|
|
2506
|
-
child.unref();
|
|
2507
|
-
};
|
|
2508
|
-
|
|
2509
3393
|
// packages/cli/src/commands/open-artifacts.ts
|
|
2510
3394
|
var registerOpenArtifactsCommand = (program2) => {
|
|
2511
3395
|
program2.command("open-artifacts").description("Open the artifact folder for a session").requiredOption("--session-id <id>", "Session identifier").action(async (options, command) => {
|
|
@@ -2558,60 +3442,60 @@ var registerSessionCommands = (program2) => {
|
|
|
2558
3442
|
// packages/cli/src/commands/skill.ts
|
|
2559
3443
|
var import_promises7 = __toESM(require("node:fs/promises"));
|
|
2560
3444
|
var import_node_os4 = __toESM(require("node:os"));
|
|
2561
|
-
var
|
|
3445
|
+
var import_node_path10 = __toESM(require("node:path"));
|
|
2562
3446
|
|
|
2563
3447
|
// packages/cli/src/installer/harness-targets.ts
|
|
2564
3448
|
var import_node_os3 = __toESM(require("node:os"));
|
|
2565
|
-
var
|
|
3449
|
+
var import_node_path6 = __toESM(require("node:path"));
|
|
2566
3450
|
var getDefaultHarnessTargets = (homeDir) => {
|
|
2567
3451
|
const home = homeDir ?? import_node_os3.default.homedir();
|
|
2568
3452
|
return [
|
|
2569
3453
|
{
|
|
2570
3454
|
id: "codex",
|
|
2571
3455
|
label: "Codex",
|
|
2572
|
-
skillsDir:
|
|
3456
|
+
skillsDir: import_node_path6.default.join(home, ".agents", "skills"),
|
|
2573
3457
|
supportsMcpInstall: true
|
|
2574
3458
|
},
|
|
2575
3459
|
{
|
|
2576
3460
|
id: "claude",
|
|
2577
3461
|
label: "Claude",
|
|
2578
|
-
skillsDir:
|
|
3462
|
+
skillsDir: import_node_path6.default.join(home, ".claude", "skills"),
|
|
2579
3463
|
supportsMcpInstall: true
|
|
2580
3464
|
},
|
|
2581
3465
|
{
|
|
2582
3466
|
id: "cursor",
|
|
2583
3467
|
label: "Cursor",
|
|
2584
|
-
skillsDir:
|
|
3468
|
+
skillsDir: import_node_path6.default.join(home, ".cursor", "skills"),
|
|
2585
3469
|
supportsMcpInstall: true
|
|
2586
3470
|
},
|
|
2587
3471
|
{
|
|
2588
3472
|
id: "factory",
|
|
2589
3473
|
label: "Factory",
|
|
2590
|
-
skillsDir:
|
|
3474
|
+
skillsDir: import_node_path6.default.join(home, ".factory", "skills"),
|
|
2591
3475
|
supportsMcpInstall: false
|
|
2592
3476
|
},
|
|
2593
3477
|
{
|
|
2594
3478
|
id: "opencode",
|
|
2595
3479
|
label: "OpenCode",
|
|
2596
|
-
skillsDir:
|
|
3480
|
+
skillsDir: import_node_path6.default.join(home, ".opencode", "skills"),
|
|
2597
3481
|
supportsMcpInstall: false
|
|
2598
3482
|
},
|
|
2599
3483
|
{
|
|
2600
3484
|
id: "gemini",
|
|
2601
3485
|
label: "Gemini",
|
|
2602
|
-
skillsDir:
|
|
3486
|
+
skillsDir: import_node_path6.default.join(home, ".gemini", "skills"),
|
|
2603
3487
|
supportsMcpInstall: false
|
|
2604
3488
|
},
|
|
2605
3489
|
{
|
|
2606
3490
|
id: "github",
|
|
2607
3491
|
label: "GitHub",
|
|
2608
|
-
skillsDir:
|
|
3492
|
+
skillsDir: import_node_path6.default.join(home, ".github", "skills"),
|
|
2609
3493
|
supportsMcpInstall: false
|
|
2610
3494
|
},
|
|
2611
3495
|
{
|
|
2612
3496
|
id: "ampcode",
|
|
2613
3497
|
label: "Ampcode",
|
|
2614
|
-
skillsDir:
|
|
3498
|
+
skillsDir: import_node_path6.default.join(home, ".ampcode", "skills"),
|
|
2615
3499
|
supportsMcpInstall: false
|
|
2616
3500
|
}
|
|
2617
3501
|
];
|
|
@@ -2619,7 +3503,7 @@ var getDefaultHarnessTargets = (homeDir) => {
|
|
|
2619
3503
|
|
|
2620
3504
|
// packages/cli/src/installer/package-info.ts
|
|
2621
3505
|
var import_promises4 = __toESM(require("node:fs/promises"));
|
|
2622
|
-
var
|
|
3506
|
+
var import_node_path7 = __toESM(require("node:path"));
|
|
2623
3507
|
var PACKAGE_NAME = "@btraut/browser-bridge";
|
|
2624
3508
|
var tryReadJson = async (filePath) => {
|
|
2625
3509
|
try {
|
|
@@ -2632,12 +3516,12 @@ var tryReadJson = async (filePath) => {
|
|
|
2632
3516
|
var resolveCliPackageRootDir = async () => {
|
|
2633
3517
|
let dir = __dirname;
|
|
2634
3518
|
for (let i = 0; i < 12; i++) {
|
|
2635
|
-
const candidate =
|
|
3519
|
+
const candidate = import_node_path7.default.join(dir, "package.json");
|
|
2636
3520
|
const parsed = await tryReadJson(candidate);
|
|
2637
3521
|
if (parsed && parsed.name === PACKAGE_NAME) {
|
|
2638
3522
|
return dir;
|
|
2639
3523
|
}
|
|
2640
|
-
const parent =
|
|
3524
|
+
const parent = import_node_path7.default.dirname(dir);
|
|
2641
3525
|
if (parent === dir) {
|
|
2642
3526
|
break;
|
|
2643
3527
|
}
|
|
@@ -2649,7 +3533,7 @@ var resolveCliPackageRootDir = async () => {
|
|
|
2649
3533
|
};
|
|
2650
3534
|
var readCliPackageVersion = async () => {
|
|
2651
3535
|
const rootDir = await resolveCliPackageRootDir();
|
|
2652
|
-
const pkgPath =
|
|
3536
|
+
const pkgPath = import_node_path7.default.join(rootDir, "package.json");
|
|
2653
3537
|
const parsed = await tryReadJson(pkgPath);
|
|
2654
3538
|
if (!parsed || typeof parsed.version !== "string" || !parsed.version) {
|
|
2655
3539
|
throw new Error(`Unable to read version from ${pkgPath}`);
|
|
@@ -2658,14 +3542,14 @@ var readCliPackageVersion = async () => {
|
|
|
2658
3542
|
};
|
|
2659
3543
|
var resolveSkillSourceDir = async () => {
|
|
2660
3544
|
const rootDir = await resolveCliPackageRootDir();
|
|
2661
|
-
const packaged =
|
|
3545
|
+
const packaged = import_node_path7.default.join(rootDir, "skills", "browser-bridge");
|
|
2662
3546
|
try {
|
|
2663
3547
|
await import_promises4.default.stat(packaged);
|
|
2664
3548
|
return packaged;
|
|
2665
3549
|
} catch {
|
|
2666
3550
|
}
|
|
2667
|
-
const repoRoot =
|
|
2668
|
-
const docsSkill =
|
|
3551
|
+
const repoRoot = import_node_path7.default.resolve(rootDir, "..", "..");
|
|
3552
|
+
const docsSkill = import_node_path7.default.join(repoRoot, "docs", "skills", "browser-bridge");
|
|
2669
3553
|
try {
|
|
2670
3554
|
await import_promises4.default.stat(docsSkill);
|
|
2671
3555
|
return docsSkill;
|
|
@@ -2678,16 +3562,16 @@ var resolveSkillSourceDir = async () => {
|
|
|
2678
3562
|
|
|
2679
3563
|
// packages/cli/src/installer/skill-install.ts
|
|
2680
3564
|
var import_promises6 = __toESM(require("node:fs/promises"));
|
|
2681
|
-
var
|
|
3565
|
+
var import_node_path9 = __toESM(require("node:path"));
|
|
2682
3566
|
|
|
2683
3567
|
// packages/cli/src/installer/skill-manifest.ts
|
|
2684
3568
|
var import_promises5 = __toESM(require("node:fs/promises"));
|
|
2685
|
-
var
|
|
3569
|
+
var import_node_path8 = __toESM(require("node:path"));
|
|
2686
3570
|
var SKILL_MANIFEST_FILENAME = "skill.json";
|
|
2687
3571
|
var readSkillManifest = async (skillDir) => {
|
|
2688
3572
|
try {
|
|
2689
3573
|
const raw = await import_promises5.default.readFile(
|
|
2690
|
-
|
|
3574
|
+
import_node_path8.default.join(skillDir, SKILL_MANIFEST_FILENAME),
|
|
2691
3575
|
"utf8"
|
|
2692
3576
|
);
|
|
2693
3577
|
const parsed = JSON.parse(raw);
|
|
@@ -2702,7 +3586,7 @@ var readSkillManifest = async (skillDir) => {
|
|
|
2702
3586
|
var writeSkillManifest = async (skillDir, version) => {
|
|
2703
3587
|
const payload = { name: "browser-bridge", version };
|
|
2704
3588
|
await import_promises5.default.writeFile(
|
|
2705
|
-
|
|
3589
|
+
import_node_path8.default.join(skillDir, SKILL_MANIFEST_FILENAME),
|
|
2706
3590
|
JSON.stringify(payload, null, 2) + "\n",
|
|
2707
3591
|
"utf8"
|
|
2708
3592
|
);
|
|
@@ -2710,7 +3594,7 @@ var writeSkillManifest = async (skillDir, version) => {
|
|
|
2710
3594
|
|
|
2711
3595
|
// packages/cli/src/installer/skill-install.ts
|
|
2712
3596
|
var installBrowserBridgeSkill = async (options) => {
|
|
2713
|
-
const destDir =
|
|
3597
|
+
const destDir = import_node_path9.default.join(options.destSkillsDir, "browser-bridge");
|
|
2714
3598
|
await import_promises6.default.mkdir(options.destSkillsDir, { recursive: true });
|
|
2715
3599
|
await import_promises6.default.rm(destDir, { recursive: true, force: true });
|
|
2716
3600
|
await import_promises6.default.cp(options.srcSkillDir, destDir, { recursive: true });
|
|
@@ -2722,9 +3606,9 @@ var installBrowserBridgeSkill = async (options) => {
|
|
|
2722
3606
|
var getHarnessMarkerDir = (homeDir, harness) => {
|
|
2723
3607
|
switch (harness) {
|
|
2724
3608
|
case "codex":
|
|
2725
|
-
return
|
|
3609
|
+
return import_node_path10.default.join(homeDir, ".agents");
|
|
2726
3610
|
default:
|
|
2727
|
-
return
|
|
3611
|
+
return import_node_path10.default.join(homeDir, `.${harness}`);
|
|
2728
3612
|
}
|
|
2729
3613
|
};
|
|
2730
3614
|
var registerSkillCommands = (program2) => {
|
|
@@ -2802,7 +3686,7 @@ var registerSkillCommands = (program2) => {
|
|
|
2802
3686
|
const targets = getDefaultHarnessTargets();
|
|
2803
3687
|
const rows = [];
|
|
2804
3688
|
for (const t of targets) {
|
|
2805
|
-
const skillDir =
|
|
3689
|
+
const skillDir = import_node_path10.default.join(t.skillsDir, "browser-bridge");
|
|
2806
3690
|
let installed = false;
|
|
2807
3691
|
try {
|
|
2808
3692
|
await import_promises7.default.stat(skillDir);
|
|
@@ -2830,7 +3714,7 @@ var registerSkillCommands = (program2) => {
|
|
|
2830
3714
|
// packages/cli/src/commands/install.ts
|
|
2831
3715
|
var import_promises8 = __toESM(require("node:fs/promises"));
|
|
2832
3716
|
var import_node_os5 = __toESM(require("node:os"));
|
|
2833
|
-
var
|
|
3717
|
+
var import_node_path11 = __toESM(require("node:path"));
|
|
2834
3718
|
var formatInstallSummary = (options) => {
|
|
2835
3719
|
const wantsSkill = options.setup.includes("skill");
|
|
2836
3720
|
const wantsMcp = options.setup.includes("mcp");
|
|
@@ -2865,9 +3749,9 @@ var getHarnessMarkerDir2 = (harness) => {
|
|
|
2865
3749
|
const homeDir = import_node_os5.default.homedir();
|
|
2866
3750
|
switch (harness) {
|
|
2867
3751
|
case "codex":
|
|
2868
|
-
return
|
|
3752
|
+
return import_node_path11.default.join(homeDir, ".agents");
|
|
2869
3753
|
default:
|
|
2870
|
-
return
|
|
3754
|
+
return import_node_path11.default.join(homeDir, `.${harness}`);
|
|
2871
3755
|
}
|
|
2872
3756
|
};
|
|
2873
3757
|
var registerInstallCommand = (program2) => {
|
|
@@ -2962,6 +3846,7 @@ registerInspectCommands(program);
|
|
|
2962
3846
|
registerArtifactsCommands(program);
|
|
2963
3847
|
registerDiagnosticsCommands(program);
|
|
2964
3848
|
registerDialogCommands(program);
|
|
3849
|
+
registerDevCommands(program);
|
|
2965
3850
|
registerOpenArtifactsCommand(program);
|
|
2966
3851
|
registerMcpCommand(program);
|
|
2967
3852
|
registerSkillCommands(program);
|