codeam-cli 2.67.7 → 2.69.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/CHANGELOG.md +13 -0
- package/dist/index.js +440 -55
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.68.0] — 2026-08-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Inspector de preview — proxy inyector, script y E2E en navegador (#665)
|
|
12
|
+
|
|
13
|
+
## [2.67.6] — 2026-08-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Ordena los POST del ciclo de vida del preview
|
|
18
|
+
- **cli:** Re-afirma el preview en el heartbeat para que su snapshot no caduque
|
|
19
|
+
|
|
7
20
|
## [2.67.5] — 2026-08-27
|
|
8
21
|
|
|
9
22
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -8081,7 +8081,7 @@ function readAnonId() {
|
|
|
8081
8081
|
}
|
|
8082
8082
|
function superProperties() {
|
|
8083
8083
|
return {
|
|
8084
|
-
cliVersion: true ? "2.
|
|
8084
|
+
cliVersion: true ? "2.69.0" : "0.0.0-dev",
|
|
8085
8085
|
nodeVersion: process.version,
|
|
8086
8086
|
platform: process.platform,
|
|
8087
8087
|
arch: process.arch,
|
|
@@ -8323,7 +8323,7 @@ var http = __toESM(require("http"));
|
|
|
8323
8323
|
// package.json
|
|
8324
8324
|
var package_default = {
|
|
8325
8325
|
name: "codeam-cli",
|
|
8326
|
-
version: "2.
|
|
8326
|
+
version: "2.69.0",
|
|
8327
8327
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
8328
8328
|
type: "commonjs",
|
|
8329
8329
|
main: "dist/index.js",
|
|
@@ -8345,7 +8345,8 @@ var package_default = {
|
|
|
8345
8345
|
"test:coverage": "vitest run --coverage",
|
|
8346
8346
|
typecheck: "tsc --noEmit",
|
|
8347
8347
|
postinstall: "node dist/postinstall.js || true",
|
|
8348
|
-
prepublishOnly: "npm run build"
|
|
8348
|
+
prepublishOnly: "npm run build",
|
|
8349
|
+
e2e: "playwright test"
|
|
8349
8350
|
},
|
|
8350
8351
|
keywords: [
|
|
8351
8352
|
"claude",
|
|
@@ -8413,11 +8414,14 @@ var package_default = {
|
|
|
8413
8414
|
},
|
|
8414
8415
|
devDependencies: {
|
|
8415
8416
|
"@codeam/shared": "*",
|
|
8417
|
+
"@playwright/test": "^1.62.1",
|
|
8418
|
+
"@types/jsdom": "^30.0.0",
|
|
8416
8419
|
"@types/node": "^22.0.0",
|
|
8417
8420
|
"@types/qrcode-terminal": "^0.12.0",
|
|
8418
8421
|
"@types/which": "^3.0.4",
|
|
8419
8422
|
"@types/ws": "^8.5.0",
|
|
8420
8423
|
"@vitest/coverage-v8": "^4.1.7",
|
|
8424
|
+
jsdom: "^29.1.1",
|
|
8421
8425
|
"node-pty": "^1.1.0",
|
|
8422
8426
|
tsup: "^8.0.0",
|
|
8423
8427
|
typescript: "^7.0.2",
|
|
@@ -9855,7 +9859,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9855
9859
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9856
9860
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9857
9861
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9858
|
-
..."2.
|
|
9862
|
+
..."2.69.0" ? { ideVersion: "2.69.0" } : {}
|
|
9859
9863
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9860
9864
|
}
|
|
9861
9865
|
/**
|
|
@@ -14058,7 +14062,7 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
|
|
|
14058
14062
|
onPaired(info);
|
|
14059
14063
|
};
|
|
14060
14064
|
dispatchers.add(dispatch);
|
|
14061
|
-
const
|
|
14065
|
+
const connect3 = () => {
|
|
14062
14066
|
if (stopped) return;
|
|
14063
14067
|
const url2 = new URL(`${API_BASE5}/api/commands/pending/stream`);
|
|
14064
14068
|
url2.searchParams.set("pluginId", pluginId);
|
|
@@ -14084,7 +14088,7 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
|
|
|
14084
14088
|
`sse status=${res.statusCode}; will reconnect in 1s`
|
|
14085
14089
|
);
|
|
14086
14090
|
res.resume();
|
|
14087
|
-
setTimeout(
|
|
14091
|
+
setTimeout(connect3, 1e3);
|
|
14088
14092
|
return;
|
|
14089
14093
|
}
|
|
14090
14094
|
let buf = "";
|
|
@@ -14099,23 +14103,23 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
|
|
|
14099
14103
|
}
|
|
14100
14104
|
});
|
|
14101
14105
|
res.on("end", () => {
|
|
14102
|
-
if (!stopped) setTimeout(
|
|
14106
|
+
if (!stopped) setTimeout(connect3, 250);
|
|
14103
14107
|
});
|
|
14104
14108
|
res.on("error", () => {
|
|
14105
|
-
if (!stopped) setTimeout(
|
|
14109
|
+
if (!stopped) setTimeout(connect3, 1e3);
|
|
14106
14110
|
});
|
|
14107
14111
|
}
|
|
14108
14112
|
);
|
|
14109
14113
|
req.on("error", () => {
|
|
14110
|
-
if (!stopped) setTimeout(
|
|
14114
|
+
if (!stopped) setTimeout(connect3, 1e3);
|
|
14111
14115
|
});
|
|
14112
14116
|
req.on("timeout", () => {
|
|
14113
14117
|
req?.destroy();
|
|
14114
|
-
if (!stopped) setTimeout(
|
|
14118
|
+
if (!stopped) setTimeout(connect3, 250);
|
|
14115
14119
|
});
|
|
14116
14120
|
req.end();
|
|
14117
14121
|
};
|
|
14118
|
-
|
|
14122
|
+
connect3();
|
|
14119
14123
|
timeoutTimer = setTimeout(() => {
|
|
14120
14124
|
stop();
|
|
14121
14125
|
onTimeout();
|
|
@@ -22114,7 +22118,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22114
22118
|
if (process.env.NODE_ENV === "test") return;
|
|
22115
22119
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22116
22120
|
if (process.env.CI) return;
|
|
22117
|
-
const current2 = true ? "2.
|
|
22121
|
+
const current2 = true ? "2.69.0" : null;
|
|
22118
22122
|
if (!current2) return;
|
|
22119
22123
|
const cache = readCache();
|
|
22120
22124
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22131,7 +22135,7 @@ function checkForUpdates() {
|
|
|
22131
22135
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22132
22136
|
if (process.env.CI) return;
|
|
22133
22137
|
if (!process.stdout.isTTY) return;
|
|
22134
|
-
const current2 = true ? "2.
|
|
22138
|
+
const current2 = true ? "2.69.0" : null;
|
|
22135
22139
|
if (!current2) return;
|
|
22136
22140
|
const cache = readCache();
|
|
22137
22141
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22154,7 +22158,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22154
22158
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22155
22159
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22156
22160
|
function currentCliVersion() {
|
|
22157
|
-
return true ? "2.
|
|
22161
|
+
return true ? "2.69.0" : null;
|
|
22158
22162
|
}
|
|
22159
22163
|
async function installedVersion(deps) {
|
|
22160
22164
|
const ls = await deps.run(
|
|
@@ -25103,7 +25107,7 @@ function registerPreview(sessionId, preview) {
|
|
|
25103
25107
|
}
|
|
25104
25108
|
function killProcessTree(child, signal = "SIGTERM") {
|
|
25105
25109
|
const pid = child.pid;
|
|
25106
|
-
if (pid
|
|
25110
|
+
if (!pid) return;
|
|
25107
25111
|
if (process.platform !== "win32") {
|
|
25108
25112
|
try {
|
|
25109
25113
|
process.kill(-pid, signal);
|
|
@@ -25125,6 +25129,10 @@ async function killPreview(sessionId) {
|
|
|
25125
25129
|
killProcessTree(preview.tunnel, "SIGTERM");
|
|
25126
25130
|
}
|
|
25127
25131
|
await new Promise((r) => setTimeout(r, 100));
|
|
25132
|
+
try {
|
|
25133
|
+
await preview.inspector?.close();
|
|
25134
|
+
} catch {
|
|
25135
|
+
}
|
|
25128
25136
|
killProcessTree(preview.devServer, "SIGTERM");
|
|
25129
25137
|
const sigkillTimer = setTimeout(() => {
|
|
25130
25138
|
killProcessTree(preview.devServer, "SIGKILL");
|
|
@@ -25309,6 +25317,378 @@ async function resolveNamedTunnel(ctx, fetchTunnel) {
|
|
|
25309
25317
|
}
|
|
25310
25318
|
}
|
|
25311
25319
|
|
|
25320
|
+
// src/services/preview/inspector-proxy.ts
|
|
25321
|
+
var http6 = __toESM(require("http"));
|
|
25322
|
+
var net3 = __toESM(require("net"));
|
|
25323
|
+
var INJECTION_SCAN_BUDGET_BYTES = 64 * 1024;
|
|
25324
|
+
var INJECTION_ANCHORS = ["</head>", "<body>", "</body>"];
|
|
25325
|
+
function wantsHtml(accept) {
|
|
25326
|
+
return typeof accept === "string" && accept.includes("text/html");
|
|
25327
|
+
}
|
|
25328
|
+
function isHtmlResponse(contentType) {
|
|
25329
|
+
return typeof contentType === "string" && contentType.toLowerCase().includes("text/html");
|
|
25330
|
+
}
|
|
25331
|
+
function injectAt(html, script) {
|
|
25332
|
+
for (const anchor of INJECTION_ANCHORS) {
|
|
25333
|
+
const at3 = html.indexOf(anchor);
|
|
25334
|
+
if (at3 === -1) continue;
|
|
25335
|
+
const insertAt = anchor.startsWith("</") ? at3 : at3 + anchor.length;
|
|
25336
|
+
return html.slice(0, insertAt) + script + html.slice(insertAt);
|
|
25337
|
+
}
|
|
25338
|
+
return null;
|
|
25339
|
+
}
|
|
25340
|
+
async function startInspectorProxy(opts) {
|
|
25341
|
+
const targetHost = opts.targetHost ?? "127.0.0.1";
|
|
25342
|
+
const upgraded = /* @__PURE__ */ new Set();
|
|
25343
|
+
const server = http6.createServer((req, res) => {
|
|
25344
|
+
const inject = wantsHtml(req.headers.accept);
|
|
25345
|
+
const headers = { ...req.headers };
|
|
25346
|
+
if (inject) delete headers["accept-encoding"];
|
|
25347
|
+
const upstream = http6.request(
|
|
25348
|
+
{ host: targetHost, port: opts.targetPort, method: req.method, path: req.url, headers },
|
|
25349
|
+
(originRes) => {
|
|
25350
|
+
const outHeaders = { ...originRes.headers };
|
|
25351
|
+
const willInject = inject && isHtmlResponse(originRes.headers["content-type"]);
|
|
25352
|
+
if (willInject) delete outHeaders["content-length"];
|
|
25353
|
+
res.writeHead(originRes.statusCode ?? 502, outHeaders);
|
|
25354
|
+
if (!willInject) {
|
|
25355
|
+
originRes.pipe(res);
|
|
25356
|
+
return;
|
|
25357
|
+
}
|
|
25358
|
+
let buffered = "";
|
|
25359
|
+
let done = false;
|
|
25360
|
+
originRes.setEncoding("utf8");
|
|
25361
|
+
originRes.on("data", (chunk) => {
|
|
25362
|
+
if (done) {
|
|
25363
|
+
res.write(chunk);
|
|
25364
|
+
return;
|
|
25365
|
+
}
|
|
25366
|
+
buffered += chunk;
|
|
25367
|
+
const injected = injectAt(buffered, opts.script);
|
|
25368
|
+
if (injected !== null) {
|
|
25369
|
+
done = true;
|
|
25370
|
+
res.write(injected);
|
|
25371
|
+
buffered = "";
|
|
25372
|
+
return;
|
|
25373
|
+
}
|
|
25374
|
+
if (buffered.length >= INJECTION_SCAN_BUDGET_BYTES) {
|
|
25375
|
+
done = true;
|
|
25376
|
+
res.write(buffered);
|
|
25377
|
+
buffered = "";
|
|
25378
|
+
}
|
|
25379
|
+
});
|
|
25380
|
+
originRes.on("end", () => {
|
|
25381
|
+
if (!done && buffered.length > 0) res.write(buffered);
|
|
25382
|
+
res.end();
|
|
25383
|
+
});
|
|
25384
|
+
}
|
|
25385
|
+
);
|
|
25386
|
+
upstream.on("error", () => {
|
|
25387
|
+
if (!res.headersSent) res.writeHead(502, { "content-type": "text/plain" });
|
|
25388
|
+
res.end("preview origin unreachable");
|
|
25389
|
+
});
|
|
25390
|
+
req.pipe(upstream);
|
|
25391
|
+
});
|
|
25392
|
+
server.on("upgrade", (req, clientSocket, head) => {
|
|
25393
|
+
upgraded.add(clientSocket);
|
|
25394
|
+
const upstream = net3.connect(opts.targetPort, targetHost, () => {
|
|
25395
|
+
const lines = [`${req.method} ${req.url} HTTP/1.1`];
|
|
25396
|
+
for (const [k2, v] of Object.entries(req.headers)) {
|
|
25397
|
+
for (const one of Array.isArray(v) ? v : [v]) {
|
|
25398
|
+
if (one !== void 0) lines.push(`${k2}: ${one}`);
|
|
25399
|
+
}
|
|
25400
|
+
}
|
|
25401
|
+
upstream.write(`${lines.join("\r\n")}\r
|
|
25402
|
+
\r
|
|
25403
|
+
`);
|
|
25404
|
+
if (head?.length) upstream.write(head);
|
|
25405
|
+
upstream.pipe(clientSocket);
|
|
25406
|
+
clientSocket.pipe(upstream);
|
|
25407
|
+
});
|
|
25408
|
+
const drop = () => {
|
|
25409
|
+
upgraded.delete(clientSocket);
|
|
25410
|
+
upstream.destroy();
|
|
25411
|
+
clientSocket.destroy();
|
|
25412
|
+
};
|
|
25413
|
+
upstream.on("error", drop);
|
|
25414
|
+
upstream.on("close", drop);
|
|
25415
|
+
clientSocket.on("error", drop);
|
|
25416
|
+
clientSocket.on("close", drop);
|
|
25417
|
+
});
|
|
25418
|
+
await new Promise((resolve10, reject) => {
|
|
25419
|
+
server.once("error", reject);
|
|
25420
|
+
server.listen(0, "127.0.0.1", () => resolve10());
|
|
25421
|
+
});
|
|
25422
|
+
const port = server.address().port;
|
|
25423
|
+
return {
|
|
25424
|
+
port,
|
|
25425
|
+
close: () => new Promise((resolve10) => {
|
|
25426
|
+
for (const socket of upgraded) socket.destroy();
|
|
25427
|
+
upgraded.clear();
|
|
25428
|
+
server.closeAllConnections?.();
|
|
25429
|
+
server.close(() => resolve10());
|
|
25430
|
+
})
|
|
25431
|
+
};
|
|
25432
|
+
}
|
|
25433
|
+
|
|
25434
|
+
// src/services/preview/inspector-client.ts
|
|
25435
|
+
var INSPECTOR_CHANNEL = "codeam-inspector";
|
|
25436
|
+
function inspectorClientSource(opts) {
|
|
25437
|
+
const origins = JSON.stringify(opts.allowedOrigins);
|
|
25438
|
+
const channel = JSON.stringify(INSPECTOR_CHANNEL);
|
|
25439
|
+
return `<script id="codeam-inspector" data-codeam="inspector">
|
|
25440
|
+
(function () {
|
|
25441
|
+
'use strict';
|
|
25442
|
+
try {
|
|
25443
|
+
var CHANNEL = ${channel};
|
|
25444
|
+
var ALLOWED = ${origins};
|
|
25445
|
+
|
|
25446
|
+
var enabled = false;
|
|
25447
|
+
var driver = null; // el origen que nos encendio
|
|
25448
|
+
var overlay = null;
|
|
25449
|
+
var label = null;
|
|
25450
|
+
var current = null;
|
|
25451
|
+
|
|
25452
|
+
function allowed(origin) {
|
|
25453
|
+
return ALLOWED.indexOf(origin) !== -1;
|
|
25454
|
+
}
|
|
25455
|
+
|
|
25456
|
+
// \xBFEstamos dentro del WebView de la app movil?
|
|
25457
|
+
//
|
|
25458
|
+
// \u26A0\uFE0F En un WebView el documento es de NIVEL SUPERIOR, asi que
|
|
25459
|
+
// \`window.parent === window\` y la via del iframe no existe: el script
|
|
25460
|
+
// hablaba solo, y en movil el inspector no devolvia nada. React Native
|
|
25461
|
+
// expone su propio canal, que es el unico que sale de ahi.
|
|
25462
|
+
function rnBridge() {
|
|
25463
|
+
return typeof window.ReactNativeWebView !== 'undefined' &&
|
|
25464
|
+
window.ReactNativeWebView &&
|
|
25465
|
+
typeof window.ReactNativeWebView.postMessage === 'function'
|
|
25466
|
+
? window.ReactNativeWebView
|
|
25467
|
+
: null;
|
|
25468
|
+
}
|
|
25469
|
+
|
|
25470
|
+
function send(type, payload) {
|
|
25471
|
+
if (!driver) return;
|
|
25472
|
+
var msg = { source: CHANNEL, type: type };
|
|
25473
|
+
for (var k in payload) if (Object.prototype.hasOwnProperty.call(payload, k)) msg[k] = payload[k];
|
|
25474
|
+
|
|
25475
|
+
var rn = rnBridge();
|
|
25476
|
+
if (rn) {
|
|
25477
|
+
// El canal de RN transporta CADENAS, no objetos: pasarle el objeto
|
|
25478
|
+
// llega al otro lado como "[object Object]".
|
|
25479
|
+
rn.postMessage(JSON.stringify(msg));
|
|
25480
|
+
return;
|
|
25481
|
+
}
|
|
25482
|
+
if (window.parent === window) return;
|
|
25483
|
+
window.parent.postMessage(msg, driver);
|
|
25484
|
+
}
|
|
25485
|
+
|
|
25486
|
+
// El resaltado. Un div fijo por encima de todo que NUNCA captura el
|
|
25487
|
+
// puntero: si lo capturara, el elemento de debajo dejaria de recibir el
|
|
25488
|
+
// mousemove y el resaltado se quedaria pegado al primero que tocara.
|
|
25489
|
+
function ensureOverlay() {
|
|
25490
|
+
if (overlay) return;
|
|
25491
|
+
overlay = document.createElement('div');
|
|
25492
|
+
overlay.setAttribute('data-codeam', 'inspector-overlay');
|
|
25493
|
+
overlay.style.cssText = [
|
|
25494
|
+
'position:fixed', 'pointer-events:none', 'z-index:2147483647',
|
|
25495
|
+
'border:1px solid #a855f7', 'background:rgba(168,85,247,0.14)',
|
|
25496
|
+
'border-radius:2px', 'transition:none', 'display:none'
|
|
25497
|
+
].join(';');
|
|
25498
|
+
label = document.createElement('div');
|
|
25499
|
+
label.setAttribute('data-codeam', 'inspector-label');
|
|
25500
|
+
label.style.cssText = [
|
|
25501
|
+
'position:fixed', 'pointer-events:none', 'z-index:2147483647',
|
|
25502
|
+
'background:#a855f7', 'color:#000', 'font:600 11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace',
|
|
25503
|
+
'padding:2px 6px', 'border-radius:3px', 'white-space:nowrap', 'display:none'
|
|
25504
|
+
].join(';');
|
|
25505
|
+
document.body.appendChild(overlay);
|
|
25506
|
+
document.body.appendChild(label);
|
|
25507
|
+
}
|
|
25508
|
+
|
|
25509
|
+
function removeOverlay() {
|
|
25510
|
+
if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
25511
|
+
if (label && label.parentNode) label.parentNode.removeChild(label);
|
|
25512
|
+
overlay = null;
|
|
25513
|
+
label = null;
|
|
25514
|
+
}
|
|
25515
|
+
|
|
25516
|
+
// Fichero y linea del componente de React, cuando el dev server los
|
|
25517
|
+
// expone. Es lo que hace que el agente reciba
|
|
25518
|
+
// "components/whatsapp-button.tsx:42" en vez de "x:0.62 y:0.81".
|
|
25519
|
+
// Solo existe en builds de desarrollo; en produccion devuelve null y se
|
|
25520
|
+
// cae al selector CSS.
|
|
25521
|
+
function reactSource(el) {
|
|
25522
|
+
try {
|
|
25523
|
+
for (var key in el) {
|
|
25524
|
+
if (key.indexOf('__reactFiber$') !== 0) continue;
|
|
25525
|
+
var fiber = el[key];
|
|
25526
|
+
while (fiber) {
|
|
25527
|
+
var src = fiber._debugSource;
|
|
25528
|
+
if (src && src.fileName) {
|
|
25529
|
+
return { file: String(src.fileName), line: Number(src.lineNumber) || null };
|
|
25530
|
+
}
|
|
25531
|
+
fiber = fiber.return;
|
|
25532
|
+
}
|
|
25533
|
+
}
|
|
25534
|
+
} catch (e) { /* nunca romper la pagina por esto */ }
|
|
25535
|
+
return null;
|
|
25536
|
+
}
|
|
25537
|
+
|
|
25538
|
+
// Un selector corto y legible, el respaldo cuando no hay React.
|
|
25539
|
+
function selectorFor(el) {
|
|
25540
|
+
if (el.id) return '#' + el.id;
|
|
25541
|
+
var name = el.tagName ? el.tagName.toLowerCase() : 'node';
|
|
25542
|
+
var cls = (el.className && typeof el.className === 'string')
|
|
25543
|
+
? el.className.trim().split(/\\s+/).slice(0, 3).join('.')
|
|
25544
|
+
: '';
|
|
25545
|
+
return cls ? name + '.' + cls : name;
|
|
25546
|
+
}
|
|
25547
|
+
|
|
25548
|
+
function describe(el) {
|
|
25549
|
+
var r = el.getBoundingClientRect();
|
|
25550
|
+
var text = (el.textContent || '').replace(/\\s+/g, ' ').trim();
|
|
25551
|
+
return {
|
|
25552
|
+
tag: el.tagName ? el.tagName.toLowerCase() : 'node',
|
|
25553
|
+
id: el.id || null,
|
|
25554
|
+
classes: (el.className && typeof el.className === 'string') ? el.className.trim() : '',
|
|
25555
|
+
selector: selectorFor(el),
|
|
25556
|
+
// Un recorte, no el contenido: describir no es extraer.
|
|
25557
|
+
text: text.length > 120 ? text.slice(0, 120) + '\u2026' : text,
|
|
25558
|
+
rect: { x: r.left, y: r.top, w: r.width, h: r.height },
|
|
25559
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
25560
|
+
source: reactSource(el)
|
|
25561
|
+
};
|
|
25562
|
+
}
|
|
25563
|
+
|
|
25564
|
+
function paint(el) {
|
|
25565
|
+
ensureOverlay();
|
|
25566
|
+
var r = el.getBoundingClientRect();
|
|
25567
|
+
overlay.style.display = 'block';
|
|
25568
|
+
overlay.style.left = r.left + 'px';
|
|
25569
|
+
overlay.style.top = r.top + 'px';
|
|
25570
|
+
overlay.style.width = r.width + 'px';
|
|
25571
|
+
overlay.style.height = r.height + 'px';
|
|
25572
|
+
label.style.display = 'block';
|
|
25573
|
+
label.textContent = selectorFor(el) + ' ' + Math.round(r.width) + '\xD7' + Math.round(r.height);
|
|
25574
|
+
// Encima si cabe; si no, dentro. Una etiqueta cortada por el borde
|
|
25575
|
+
// superior no se lee.
|
|
25576
|
+
var above = r.top >= 20;
|
|
25577
|
+
label.style.left = r.left + 'px';
|
|
25578
|
+
label.style.top = (above ? r.top - 20 : r.top + 2) + 'px';
|
|
25579
|
+
}
|
|
25580
|
+
|
|
25581
|
+
function onMove(e) {
|
|
25582
|
+
if (!enabled) return;
|
|
25583
|
+
var el = e.target;
|
|
25584
|
+
if (!el || el === current || !el.getBoundingClientRect) return;
|
|
25585
|
+
if (el.getAttribute && el.getAttribute('data-codeam')) return;
|
|
25586
|
+
current = el;
|
|
25587
|
+
paint(el);
|
|
25588
|
+
send('hover', { element: describe(el) });
|
|
25589
|
+
}
|
|
25590
|
+
|
|
25591
|
+
// Captura, y se traga el evento: mientras el modo esta activo un clic
|
|
25592
|
+
// SELECCIONA, no navega. Sin esto, pulsar un enlace se llevaria el
|
|
25593
|
+
// preview a otra pagina en mitad del gesto.
|
|
25594
|
+
function onClick(e) {
|
|
25595
|
+
if (!enabled) return;
|
|
25596
|
+
var el = e.target;
|
|
25597
|
+
if (el && el.getAttribute && el.getAttribute('data-codeam')) return;
|
|
25598
|
+
e.preventDefault();
|
|
25599
|
+
e.stopPropagation();
|
|
25600
|
+
if (el) send('pick', { element: describe(el) });
|
|
25601
|
+
}
|
|
25602
|
+
|
|
25603
|
+
function enable(origin) {
|
|
25604
|
+
if (enabled) return;
|
|
25605
|
+
enabled = true;
|
|
25606
|
+
driver = origin;
|
|
25607
|
+
document.addEventListener('mousemove', onMove, true);
|
|
25608
|
+
document.addEventListener('click', onClick, true);
|
|
25609
|
+
send('ready', {});
|
|
25610
|
+
}
|
|
25611
|
+
|
|
25612
|
+
function disable() {
|
|
25613
|
+
if (!enabled) return;
|
|
25614
|
+
enabled = false;
|
|
25615
|
+
current = null;
|
|
25616
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
25617
|
+
document.removeEventListener('click', onClick, true);
|
|
25618
|
+
removeOverlay();
|
|
25619
|
+
driver = null;
|
|
25620
|
+
}
|
|
25621
|
+
|
|
25622
|
+
window.addEventListener('message', function (e) {
|
|
25623
|
+
if (!allowed(e.origin)) return;
|
|
25624
|
+
var data = e.data;
|
|
25625
|
+
if (!data || data.source !== CHANNEL) return;
|
|
25626
|
+
if (data.type === 'enable') enable(e.origin);
|
|
25627
|
+
else if (data.type === 'disable') disable();
|
|
25628
|
+
});
|
|
25629
|
+
|
|
25630
|
+
/**
|
|
25631
|
+
* La puerta para React Native, que no puede usar \`postMessage\`.
|
|
25632
|
+
*
|
|
25633
|
+
* \u26A0\uFE0F Aqui NO hay lista blanca de origenes, y no es un descuido: quien
|
|
25634
|
+
* llama esto es la app anfitriona sobre SU PROPIO WebView, via
|
|
25635
|
+
* \`injectJavaScript\` \u2014 no hay un tercero al que filtrar. La lista blanca
|
|
25636
|
+
* protege el caso del iframe, donde cualquier pagina puede embeber el
|
|
25637
|
+
* preview y mandar mensajes; ese caso sigue exactamente igual de cerrado.
|
|
25638
|
+
*
|
|
25639
|
+
* Solo se expone donde ese puente existe, para no dejar en una pagina
|
|
25640
|
+
* normal una funcion global que encienda el inspector sin permiso.
|
|
25641
|
+
*/
|
|
25642
|
+
if (rnBridge()) {
|
|
25643
|
+
window.__codeamInspector = {
|
|
25644
|
+
enable: function () { enable('react-native'); },
|
|
25645
|
+
disable: disable,
|
|
25646
|
+
};
|
|
25647
|
+
}
|
|
25648
|
+
} catch (e) {
|
|
25649
|
+
// Pase lo que pase, la app del usuario sigue funcionando.
|
|
25650
|
+
}
|
|
25651
|
+
})();
|
|
25652
|
+
</script>`;
|
|
25653
|
+
}
|
|
25654
|
+
|
|
25655
|
+
// src/services/preview/inspector-bringup.ts
|
|
25656
|
+
var DEFAULT_INSPECTOR_ORIGINS = [
|
|
25657
|
+
"https://www.codeagent-mobile.com",
|
|
25658
|
+
"https://codeagent-mobile.com",
|
|
25659
|
+
"https://dev.codeagent-mobile.com"
|
|
25660
|
+
];
|
|
25661
|
+
function inspectorEnabled(env) {
|
|
25662
|
+
const raw = env.CODEAM_PREVIEW_INSPECTOR;
|
|
25663
|
+
return raw !== "0" && raw !== "false";
|
|
25664
|
+
}
|
|
25665
|
+
function resolveInspectorOrigins(env) {
|
|
25666
|
+
const raw = env.CODEAM_INSPECTOR_ORIGINS;
|
|
25667
|
+
if (!raw) return DEFAULT_INSPECTOR_ORIGINS;
|
|
25668
|
+
const extra = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
25669
|
+
return [...DEFAULT_INSPECTOR_ORIGINS, ...extra];
|
|
25670
|
+
}
|
|
25671
|
+
async function bringUpInspector(targetPort, deps = {}) {
|
|
25672
|
+
const env = deps.env ?? process.env;
|
|
25673
|
+
const log2 = deps.log ?? (() => void 0);
|
|
25674
|
+
if (!inspectorEnabled(env)) {
|
|
25675
|
+
log2("preview inspector disabled by CODEAM_PREVIEW_INSPECTOR");
|
|
25676
|
+
return { port: targetPort, proxy: null };
|
|
25677
|
+
}
|
|
25678
|
+
try {
|
|
25679
|
+
const start2 = deps.start ?? startInspectorProxy;
|
|
25680
|
+
const proxy = await start2({
|
|
25681
|
+
targetPort,
|
|
25682
|
+
script: inspectorClientSource({ allowedOrigins: resolveInspectorOrigins(env) })
|
|
25683
|
+
});
|
|
25684
|
+
log2(`preview inspector proxy on :${proxy.port} \u2192 :${targetPort}`);
|
|
25685
|
+
return { port: proxy.port, proxy };
|
|
25686
|
+
} catch (e) {
|
|
25687
|
+
log2(`preview inspector unavailable (${e.message}) \u2014 going direct`);
|
|
25688
|
+
return { port: targetPort, proxy: null };
|
|
25689
|
+
}
|
|
25690
|
+
}
|
|
25691
|
+
|
|
25312
25692
|
// src/services/preview/start-orchestrator.ts
|
|
25313
25693
|
var INSTALL_TIMEOUT_MS2 = 5 * 6e4;
|
|
25314
25694
|
var SETUP_TIMEOUT_MS = 2 * 6e4;
|
|
@@ -25420,6 +25800,7 @@ async function runPreviewStart(args2) {
|
|
|
25420
25800
|
sessionId,
|
|
25421
25801
|
devServer: dev.devServer,
|
|
25422
25802
|
tunnel: tun.tunnel,
|
|
25803
|
+
inspector: tun.inspector,
|
|
25423
25804
|
url: tun.url,
|
|
25424
25805
|
framework: detection.framework,
|
|
25425
25806
|
detection,
|
|
@@ -25665,8 +26046,12 @@ async function establishTunnel(ctx, dev) {
|
|
|
25665
26046
|
});
|
|
25666
26047
|
return null;
|
|
25667
26048
|
}
|
|
25668
|
-
return { url: expoUrl, tunnel: null };
|
|
26049
|
+
return { url: expoUrl, tunnel: null, inspector: null };
|
|
25669
26050
|
}
|
|
26051
|
+
const inspection = await bringUpInspector(detection.port, {
|
|
26052
|
+
log: (m) => log.info("preview", m)
|
|
26053
|
+
});
|
|
26054
|
+
const tunnelPort = inspection.port;
|
|
25670
26055
|
let bin;
|
|
25671
26056
|
try {
|
|
25672
26057
|
bin = await resolveCloudflared();
|
|
@@ -25694,7 +26079,7 @@ async function establishTunnel(ctx, dev) {
|
|
|
25694
26079
|
if (namedToken && namedHostname) {
|
|
25695
26080
|
try {
|
|
25696
26081
|
emitProgress("TUNNEL_STARTING", `named tunnel ${namedHostname}`);
|
|
25697
|
-
const candidate = await spawnNamedTunnel(bin, namedToken,
|
|
26082
|
+
const candidate = await spawnNamedTunnel(bin, namedToken, tunnelPort);
|
|
25698
26083
|
const outcome = await awaitTunnelRegistered(
|
|
25699
26084
|
candidate,
|
|
25700
26085
|
namedHostname,
|
|
@@ -25727,7 +26112,7 @@ async function establishTunnel(ctx, dev) {
|
|
|
25727
26112
|
}
|
|
25728
26113
|
const candidate = (0, import_child_process22.spawn)(
|
|
25729
26114
|
bin,
|
|
25730
|
-
["tunnel", "--url", `http://localhost:${
|
|
26115
|
+
["tunnel", "--url", `http://localhost:${tunnelPort}`],
|
|
25731
26116
|
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
25732
26117
|
);
|
|
25733
26118
|
const outcome = await awaitTunnelRegistered(
|
|
@@ -25752,7 +26137,7 @@ async function establishTunnel(ctx, dev) {
|
|
|
25752
26137
|
});
|
|
25753
26138
|
return null;
|
|
25754
26139
|
}
|
|
25755
|
-
return { url: parsedUrl, tunnel };
|
|
26140
|
+
return { url: parsedUrl, tunnel, inspector: inspection.proxy };
|
|
25756
26141
|
}
|
|
25757
26142
|
|
|
25758
26143
|
// src/beads/bd-adapter.ts
|
|
@@ -29842,7 +30227,7 @@ var fs67 = __toESM(require("fs"));
|
|
|
29842
30227
|
var path74 = __toESM(require("path"));
|
|
29843
30228
|
var os56 = __toESM(require("os"));
|
|
29844
30229
|
var https7 = __toESM(require("https"));
|
|
29845
|
-
var
|
|
30230
|
+
var http7 = __toESM(require("http"));
|
|
29846
30231
|
var import_zod2 = require("zod");
|
|
29847
30232
|
var historyRecordSchema = import_zod2.z.object({
|
|
29848
30233
|
type: import_zod2.z.string().optional(),
|
|
@@ -29925,7 +30310,7 @@ function post(endpoint, body, pluginAuthToken) {
|
|
|
29925
30310
|
return new Promise((resolve10) => {
|
|
29926
30311
|
const payload = JSON.stringify(body);
|
|
29927
30312
|
const u2 = new URL(`${API_BASE8}${endpoint}`);
|
|
29928
|
-
const transport = u2.protocol === "https:" ? https7 :
|
|
30313
|
+
const transport = u2.protocol === "https:" ? https7 : http7;
|
|
29929
30314
|
const req = transport.request(
|
|
29930
30315
|
{
|
|
29931
30316
|
hostname: u2.hostname,
|
|
@@ -32822,8 +33207,8 @@ var ConnectionBuilder = class {
|
|
|
32822
33207
|
if (message.kind !== "request" || message.method !== method) {
|
|
32823
33208
|
return Handled.no(message);
|
|
32824
33209
|
}
|
|
32825
|
-
const
|
|
32826
|
-
return await handler(
|
|
33210
|
+
const request2 = parse(message.params);
|
|
33211
|
+
return await handler(request2, message.responder, cx) ?? Handled.yes();
|
|
32827
33212
|
},
|
|
32828
33213
|
describe: () => `${this.connectionName ?? "request"}:${method}`
|
|
32829
33214
|
});
|
|
@@ -33384,23 +33769,23 @@ var AsyncQueue = class {
|
|
|
33384
33769
|
});
|
|
33385
33770
|
}
|
|
33386
33771
|
};
|
|
33387
|
-
function cloneNewSessionRequest(
|
|
33772
|
+
function cloneNewSessionRequest(request2) {
|
|
33388
33773
|
return {
|
|
33389
|
-
...
|
|
33390
|
-
additionalDirectories:
|
|
33391
|
-
mcpServers: [...
|
|
33774
|
+
...request2,
|
|
33775
|
+
additionalDirectories: request2.additionalDirectories ? [...request2.additionalDirectories] : void 0,
|
|
33776
|
+
mcpServers: [...request2.mcpServers]
|
|
33392
33777
|
};
|
|
33393
33778
|
}
|
|
33394
33779
|
var SessionBuilder = class _SessionBuilder {
|
|
33395
33780
|
cx;
|
|
33396
33781
|
request;
|
|
33397
|
-
constructor(cx,
|
|
33782
|
+
constructor(cx, request2) {
|
|
33398
33783
|
this.cx = cx;
|
|
33399
|
-
this.request = cloneNewSessionRequest(
|
|
33784
|
+
this.request = cloneNewSessionRequest(request2);
|
|
33400
33785
|
}
|
|
33401
33786
|
/** @internal */
|
|
33402
|
-
static create(cx,
|
|
33403
|
-
return new _SessionBuilder(cx,
|
|
33787
|
+
static create(cx, request2) {
|
|
33788
|
+
return new _SessionBuilder(cx, request2);
|
|
33404
33789
|
}
|
|
33405
33790
|
/**
|
|
33406
33791
|
* Returns the `session/new` request that will be sent.
|
|
@@ -34528,9 +34913,9 @@ function toolCallReferencesInternal(call, homeDir2) {
|
|
|
34528
34913
|
}
|
|
34529
34914
|
return false;
|
|
34530
34915
|
}
|
|
34531
|
-
function internalPathPermissionOutcome(
|
|
34532
|
-
if (!toolCallReferencesInternal(
|
|
34533
|
-
const reject =
|
|
34916
|
+
function internalPathPermissionOutcome(request2, homeDir2) {
|
|
34917
|
+
if (!toolCallReferencesInternal(request2.toolCall, homeDir2)) return null;
|
|
34918
|
+
const reject = request2.options.find((o) => o.kind === "reject_once") ?? request2.options.find((o) => o.kind === "reject_always");
|
|
34534
34919
|
if (reject) return { outcome: { outcome: "selected", optionId: reject.optionId } };
|
|
34535
34920
|
return { outcome: { outcome: "cancelled" } };
|
|
34536
34921
|
}
|
|
@@ -34589,9 +34974,9 @@ function toolPathIsSecret(p2) {
|
|
|
34589
34974
|
return !!p2 && SECRET_RE.test(p2);
|
|
34590
34975
|
}
|
|
34591
34976
|
var GUARDRAIL_SECRET_READ_BLOCK_REASON = "Reading this secret file is blocked by this session's guardrails (Reading secrets = Deny). Adjust it in the session Guardrails settings if intended.";
|
|
34592
|
-
function guardrailDecision(
|
|
34593
|
-
if (isProseOnlyToolKind(
|
|
34594
|
-
const hay = haystack(
|
|
34977
|
+
function guardrailDecision(request2, policy) {
|
|
34978
|
+
if (isProseOnlyToolKind(request2.toolCall.kind)) return null;
|
|
34979
|
+
const hay = haystack(request2.toolCall);
|
|
34595
34980
|
if (!hay) return null;
|
|
34596
34981
|
const matched = matchedCategories(hay);
|
|
34597
34982
|
if (matched.size === 0) return null;
|
|
@@ -34611,7 +34996,7 @@ function guardrailDecision(request, policy) {
|
|
|
34611
34996
|
kind: "deny",
|
|
34612
34997
|
category: denyCat,
|
|
34613
34998
|
reason: GUARDRAIL_CATEGORY_META[denyCat].description,
|
|
34614
|
-
outcome: rejectOutcome(
|
|
34999
|
+
outcome: rejectOutcome(request2.options)
|
|
34615
35000
|
};
|
|
34616
35001
|
}
|
|
34617
35002
|
if (confirmCat) {
|
|
@@ -35876,7 +36261,7 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
|
|
|
35876
36261
|
}
|
|
35877
36262
|
|
|
35878
36263
|
// src/services/streaming/transport.ts
|
|
35879
|
-
var
|
|
36264
|
+
var http8 = __toESM(require("http"));
|
|
35880
36265
|
var https8 = __toESM(require("https"));
|
|
35881
36266
|
var _transport4 = {
|
|
35882
36267
|
post: _post3,
|
|
@@ -35886,7 +36271,7 @@ function _post3(url2, headers, payload) {
|
|
|
35886
36271
|
return new Promise((resolve10, reject) => {
|
|
35887
36272
|
let settled = false;
|
|
35888
36273
|
const u2 = new URL(url2);
|
|
35889
|
-
const lib = u2.protocol === "https:" ? https8 :
|
|
36274
|
+
const lib = u2.protocol === "https:" ? https8 : http8;
|
|
35890
36275
|
const req = lib.request(
|
|
35891
36276
|
{
|
|
35892
36277
|
hostname: u2.hostname,
|
|
@@ -35928,7 +36313,7 @@ function _get(url2, headers) {
|
|
|
35928
36313
|
return new Promise((resolve10, reject) => {
|
|
35929
36314
|
let settled = false;
|
|
35930
36315
|
const u2 = new URL(url2);
|
|
35931
|
-
const lib = u2.protocol === "https:" ? https8 :
|
|
36316
|
+
const lib = u2.protocol === "https:" ? https8 : http8;
|
|
35932
36317
|
const req = lib.request(
|
|
35933
36318
|
{
|
|
35934
36319
|
hostname: u2.hostname,
|
|
@@ -36733,12 +37118,12 @@ function mapSessionUpdate(notification) {
|
|
|
36733
37118
|
return [];
|
|
36734
37119
|
}
|
|
36735
37120
|
}
|
|
36736
|
-
function mapPermissionRequest(
|
|
36737
|
-
const prompt = describeToolCall(
|
|
37121
|
+
function mapPermissionRequest(request2) {
|
|
37122
|
+
const prompt = describeToolCall(request2.toolCall) ?? "The agent requested permission to continue.";
|
|
36738
37123
|
const optionIdByLabel = {};
|
|
36739
37124
|
const kindByLabel = {};
|
|
36740
37125
|
const labels = [];
|
|
36741
|
-
for (const opt of
|
|
37126
|
+
for (const opt of request2.options) {
|
|
36742
37127
|
const label = opt.name?.trim() || humanizeKind(opt.kind);
|
|
36743
37128
|
if (label in optionIdByLabel) continue;
|
|
36744
37129
|
optionIdByLabel[label] = opt.optionId;
|
|
@@ -36834,10 +37219,10 @@ function guardrailBlockNotice(decision, toolCall) {
|
|
|
36834
37219
|
return `\u26D4 Guardrail auto-blocked ${describeToolCallForNotice(toolCall)} \u2014 ${decision.reason} This session's "${decision.category}" guardrail is set to Deny (adjust it in the session's Guardrails settings if intended). This was an automatic block, not a rejection by you.`;
|
|
36835
37220
|
}
|
|
36836
37221
|
function createOnRequestPermission(deps) {
|
|
36837
|
-
return async (
|
|
37222
|
+
return async (request2) => {
|
|
36838
37223
|
let guardrailConfirm = false;
|
|
36839
37224
|
if (!deps.isLocal()) {
|
|
36840
|
-
const denied = internalPathPermissionOutcome(
|
|
37225
|
+
const denied = internalPathPermissionOutcome(request2);
|
|
36841
37226
|
if (denied) {
|
|
36842
37227
|
log.warn(
|
|
36843
37228
|
"acpRunner",
|
|
@@ -36845,17 +37230,17 @@ function createOnRequestPermission(deps) {
|
|
|
36845
37230
|
);
|
|
36846
37231
|
void deps.publisher.publishOutput({
|
|
36847
37232
|
type: "text",
|
|
36848
|
-
content: internalBlockNotice(
|
|
37233
|
+
content: internalBlockNotice(request2.toolCall),
|
|
36849
37234
|
done: true
|
|
36850
37235
|
});
|
|
36851
37236
|
return denied;
|
|
36852
37237
|
}
|
|
36853
|
-
const g = guardrailDecision(
|
|
37238
|
+
const g = guardrailDecision(request2, deps.getPolicy());
|
|
36854
37239
|
if (g?.kind === "deny") {
|
|
36855
37240
|
log.warn("acpRunner", `guardrail [${g.category}] \u2014 denying tool call`);
|
|
36856
37241
|
void deps.publisher.publishOutput({
|
|
36857
37242
|
type: "text",
|
|
36858
|
-
content: guardrailBlockNotice(g,
|
|
37243
|
+
content: guardrailBlockNotice(g, request2.toolCall),
|
|
36859
37244
|
done: true
|
|
36860
37245
|
});
|
|
36861
37246
|
return g.outcome;
|
|
@@ -36866,7 +37251,7 @@ function createOnRequestPermission(deps) {
|
|
|
36866
37251
|
}
|
|
36867
37252
|
}
|
|
36868
37253
|
if (deps.autoApprovePermissions && !guardrailConfirm) {
|
|
36869
|
-
const allow = pickAllowOption(
|
|
37254
|
+
const allow = pickAllowOption(request2.options);
|
|
36870
37255
|
if (allow) {
|
|
36871
37256
|
log.info(
|
|
36872
37257
|
"acpRunner",
|
|
@@ -36876,7 +37261,7 @@ function createOnRequestPermission(deps) {
|
|
|
36876
37261
|
}
|
|
36877
37262
|
log.warn("acpRunner", "AUTO mode \u2014 no allow option offered; falling back to interactive");
|
|
36878
37263
|
}
|
|
36879
|
-
const { event, optionIdByLabel } = mapPermissionRequest(
|
|
37264
|
+
const { event, optionIdByLabel } = mapPermissionRequest(request2);
|
|
36880
37265
|
await deps.publisher.publishAwaitingAnswer(event);
|
|
36881
37266
|
return deps.registerPermission({
|
|
36882
37267
|
questionId: event.questionId,
|
|
@@ -42787,8 +43172,8 @@ async function runBatonSession(opts) {
|
|
|
42787
43172
|
// instance the baton's own AcpDriver.start bracketing uses.
|
|
42788
43173
|
beginLoadReplay: () => streaming.beginLoadReplay(),
|
|
42789
43174
|
endLoadReplay: () => streaming.endLoadReplay(),
|
|
42790
|
-
onRequestPermission: async (
|
|
42791
|
-
const { event, optionIdByLabel } = mapPermissionRequest(
|
|
43175
|
+
onRequestPermission: async (request2) => {
|
|
43176
|
+
const { event, optionIdByLabel } = mapPermissionRequest(request2);
|
|
42792
43177
|
await publisher.publishAwaitingAnswer(event);
|
|
42793
43178
|
return streaming.registerPermission({
|
|
42794
43179
|
questionId: event.questionId,
|
|
@@ -45990,7 +46375,7 @@ function checkChokidar() {
|
|
|
45990
46375
|
}
|
|
45991
46376
|
async function doctor(args2 = []) {
|
|
45992
46377
|
const json = args2.includes("--json");
|
|
45993
|
-
const cliVersion = true ? "2.
|
|
46378
|
+
const cliVersion = true ? "2.69.0" : "0.0.0-dev";
|
|
45994
46379
|
const apiBase2 = resolveApiBaseUrl();
|
|
45995
46380
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
45996
46381
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -46381,7 +46766,7 @@ async function mcpRun(args2) {
|
|
|
46381
46766
|
// src/commands/version.ts
|
|
46382
46767
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
46383
46768
|
function version2() {
|
|
46384
|
-
const v = true ? "2.
|
|
46769
|
+
const v = true ? "2.69.0" : "unknown";
|
|
46385
46770
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
46386
46771
|
}
|
|
46387
46772
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.69.0",
|
|
4
4
|
"description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"test:coverage": "vitest run --coverage",
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
24
|
"postinstall": "node dist/postinstall.js || true",
|
|
25
|
-
"prepublishOnly": "npm run build"
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"e2e": "playwright test"
|
|
26
27
|
},
|
|
27
28
|
"keywords": [
|
|
28
29
|
"claude",
|
|
@@ -90,11 +91,14 @@
|
|
|
90
91
|
},
|
|
91
92
|
"devDependencies": {
|
|
92
93
|
"@codeam/shared": "*",
|
|
94
|
+
"@playwright/test": "^1.62.1",
|
|
95
|
+
"@types/jsdom": "^30.0.0",
|
|
93
96
|
"@types/node": "^22.0.0",
|
|
94
97
|
"@types/qrcode-terminal": "^0.12.0",
|
|
95
98
|
"@types/which": "^3.0.4",
|
|
96
99
|
"@types/ws": "^8.5.0",
|
|
97
100
|
"@vitest/coverage-v8": "^4.1.7",
|
|
101
|
+
"jsdom": "^29.1.1",
|
|
98
102
|
"node-pty": "^1.1.0",
|
|
99
103
|
"tsup": "^8.0.0",
|
|
100
104
|
"typescript": "^7.0.2",
|