@ait-co/devtools 0.1.79 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devtools-opener-BbUXBzgA.js.map +1 -1
- package/dist/devtools-opener-Bp671YXu.cjs.map +1 -1
- package/dist/devtools-opener-D84kZFtR.js.map +1 -1
- package/dist/devtools-opener-h6A-UjzC.cjs.map +1 -1
- package/dist/mcp/cli.js +214 -55
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/panel/index.js +6 -2
- package/dist/panel/index.js.map +1 -1
- package/dist/{qr-http-server-D2d44bv7.js → qr-http-server-DJ5K3Odk.js} +34 -1
- package/dist/qr-http-server-DJ5K3Odk.js.map +1 -0
- package/dist/{qr-http-server-Dx7KnQtg.js → qr-http-server-DkOFfZsR.js} +34 -1
- package/dist/qr-http-server-DkOFfZsR.js.map +1 -0
- package/dist/{qr-http-server-DOOLghY0.cjs → qr-http-server-Dkx2-pKF.cjs} +34 -1
- package/dist/qr-http-server-Dkx2-pKF.cjs.map +1 -0
- package/dist/{qr-http-server-oENyLvn9.cjs → qr-http-server-MIUHaiYw.cjs} +34 -1
- package/dist/qr-http-server-MIUHaiYw.cjs.map +1 -0
- package/dist/{relay-url-store-Cx_SqWtl.cjs → relay-url-store-BiEK9BN1.cjs} +3 -1
- package/dist/relay-url-store-BiEK9BN1.cjs.map +1 -0
- package/dist/{relay-url-store-CV8nScsn.js → relay-url-store-DH8-VUFc.js} +3 -1
- package/dist/relay-url-store-DH8-VUFc.js.map +1 -0
- package/dist/{relay-url-store-B_wrNe5A.js → relay-url-store-RKcao_yG.js} +6 -1
- package/dist/relay-url-store-RKcao_yG.js.map +1 -0
- package/dist/{tunnel-8h2r-ouK.js → tunnel-BTlq1mmH.js} +2 -2
- package/dist/{tunnel-8h2r-ouK.js.map → tunnel-BTlq1mmH.js.map} +1 -1
- package/dist/{tunnel-CInRDnKE.cjs → tunnel-C-AFdAVL.cjs} +2 -2
- package/dist/{tunnel-CInRDnKE.cjs.map → tunnel-C-AFdAVL.cjs.map} +1 -1
- package/dist/unplugin/index.cjs +6 -3
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +6 -3
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/tunnel.cjs +1 -1
- package/dist/unplugin/tunnel.js +1 -1
- package/package.json +1 -1
- package/dist/qr-http-server-D2d44bv7.js.map +0 -1
- package/dist/qr-http-server-DOOLghY0.cjs.map +0 -1
- package/dist/qr-http-server-Dx7KnQtg.js.map +0 -1
- package/dist/qr-http-server-oENyLvn9.cjs.map +0 -1
- package/dist/relay-url-store-B_wrNe5A.js.map +0 -1
- package/dist/relay-url-store-CV8nScsn.js.map +0 -1
- package/dist/relay-url-store-Cx_SqWtl.cjs.map +0 -1
package/dist/mcp/cli.js
CHANGED
|
@@ -1289,51 +1289,67 @@ function openUrlInBrowser(url) {
|
|
|
1289
1289
|
return false;
|
|
1290
1290
|
}
|
|
1291
1291
|
/**
|
|
1292
|
-
* Manages auto-opening Chrome DevTools
|
|
1292
|
+
* Manages auto-opening Chrome DevTools on every NEW target attach (issue #530).
|
|
1293
1293
|
*
|
|
1294
1294
|
* Create one instance per `runDebugServer` call and pass its `open()` method
|
|
1295
|
-
* as the `
|
|
1295
|
+
* as the `onAttach` callback to the attach watcher (via `DualConnectionRouter`).
|
|
1296
|
+
*
|
|
1297
|
+
* The open fires for each NEW `targetId` — subsequent notifications for the
|
|
1298
|
+
* same target are de-duplicated. Re-attach with a fresh targetId (e.g. after
|
|
1299
|
+
* page reload on the phone) fires a new open. The URL opened is the stable
|
|
1300
|
+
* `/inspector` endpoint (issue #530) when `inspectorStableUrl` is provided —
|
|
1301
|
+
* it mints a fresh TOTP at click time so there is no expiry race. Falls back to
|
|
1302
|
+
* building a direct `front_end/chii_app.html?wss=…` URL when
|
|
1303
|
+
* `inspectorStableUrl` is absent.
|
|
1296
1304
|
*
|
|
1297
|
-
* The open fires at most once. Subsequent `open()` calls are no-ops.
|
|
1298
1305
|
* Opt-out and mock-environment guard are checked at call time.
|
|
1299
1306
|
*/
|
|
1300
1307
|
var AutoDevtoolsOpener = class {
|
|
1301
|
-
|
|
1308
|
+
/** Per-target de-dupe set (issue #530 — target-unit guard replaces once-per-daemon). */
|
|
1309
|
+
_openedTargets = /* @__PURE__ */ new Set();
|
|
1302
1310
|
/**
|
|
1303
1311
|
* Attempts to auto-open Chii DevTools in the developer's browser.
|
|
1304
1312
|
*
|
|
1305
|
-
*
|
|
1306
|
-
*
|
|
1307
|
-
* relay's WebSocket upgrade gate accepts the connection.
|
|
1313
|
+
* Opens when:
|
|
1314
|
+
* - `options.targetId` is a NEW target (not yet in `_openedTargets`).
|
|
1308
1315
|
*
|
|
1309
1316
|
* No-op when any of the following conditions hold:
|
|
1310
|
-
* 1.
|
|
1317
|
+
* 1. `targetId` has already been opened (`_openedTargets` has it).
|
|
1311
1318
|
* 2. `AIT_AUTO_DEVTOOLS=0` opt-out is set.
|
|
1312
1319
|
* 3. `options.env` is `mock` (env 1 — F12 is already available).
|
|
1313
|
-
* 4. `options.
|
|
1314
|
-
* 5. `
|
|
1320
|
+
* 4. `options.targetId` is null/undefined/empty (no page attached yet).
|
|
1321
|
+
* 5. Neither `inspectorStableUrl` nor `relayHttpBaseUrl` is available.
|
|
1315
1322
|
*
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1323
|
+
* When `inspectorStableUrl` is provided (issue #530 stable URL): opens
|
|
1324
|
+
* `http://127.0.0.1:<port>/inspector` directly and writes it to stderr.
|
|
1325
|
+
* The URL contains no tunnel host or TOTP code — safe to log anywhere.
|
|
1318
1326
|
*
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
* the page or re-run `open()` (though the once-per-session guard prevents
|
|
1323
|
-
* that — restart the MCP server if needed).
|
|
1327
|
+
* Legacy path (no `inspectorStableUrl`): builds a direct
|
|
1328
|
+
* `<relay-base>/front_end/chii_app.html?wss=…` URL from `relayHttpBaseUrl`
|
|
1329
|
+
* + `mintTotp`, writes to stderr. TOTP expiry caveat applies (~3 min window).
|
|
1324
1330
|
*
|
|
1325
|
-
* SECRET-HANDLING:
|
|
1326
|
-
* host and
|
|
1327
|
-
* persistent
|
|
1331
|
+
* SECRET-HANDLING: direct inspector URL (written to stderr) may contain relay
|
|
1332
|
+
* host and TOTP code. Stable URL is secret-free. Neither must go to stdout or
|
|
1333
|
+
* persistent logs.
|
|
1328
1334
|
*/
|
|
1329
1335
|
open(options) {
|
|
1330
|
-
if (this._opened) return;
|
|
1331
1336
|
if (isAutoDevtoolsDisabled()) return;
|
|
1332
1337
|
if (options.env === "mock") return;
|
|
1333
|
-
if (!options.relayHttpBaseUrl) return;
|
|
1334
1338
|
if (!options.targetId) return;
|
|
1335
|
-
|
|
1336
|
-
|
|
1339
|
+
const targetId = options.targetId;
|
|
1340
|
+
if (this._openedTargets.has(targetId)) return;
|
|
1341
|
+
if (options.inspectorStableUrl) {
|
|
1342
|
+
this._openedTargets.add(targetId);
|
|
1343
|
+
const stableUrl = options.inspectorStableUrl;
|
|
1344
|
+
process.stderr.write(`[ait-debug] 기기가 연결됐습니다 — Chii DevTools를 자동으로 엽니다.
|
|
1345
|
+
[ait-debug] 인스펙터 URL: ${stableUrl}\n[ait-debug] (AIT_AUTO_DEVTOOLS=0 으로 자동 열기를 끌 수 있습니다)
|
|
1346
|
+
`);
|
|
1347
|
+
if (!openUrlInBrowser(stableUrl)) process.stderr.write(`[ait-debug] 브라우저 자동 열기 실패 — ${stableUrl} 을 브라우저에서 직접 여세요.\n`);
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (!options.relayHttpBaseUrl) return;
|
|
1351
|
+
this._openedTargets.add(targetId);
|
|
1352
|
+
const inspectorUrl = buildChiiInspectorUrl(options.relayHttpBaseUrl, targetId, options.mintTotp);
|
|
1337
1353
|
if (inspectorUrl === null) {
|
|
1338
1354
|
process.stderr.write("[ait-debug] 기기가 연결됐습니다 — TOTP secret 미설정으로 인스펙터 URL을 생성할 수 없습니다.\n[ait-debug] relay 세션은 AIT_DEBUG_TOTP_SECRET 설정이 필요합니다.\n");
|
|
1339
1355
|
return;
|
|
@@ -1344,9 +1360,17 @@ var AutoDevtoolsOpener = class {
|
|
|
1344
1360
|
`);
|
|
1345
1361
|
if (!openUrlInBrowser(inspectorUrl)) process.stderr.write("[ait-debug] 브라우저 자동 열기 실패 — 위 URL을 브라우저에서 직접 여세요.\n");
|
|
1346
1362
|
}
|
|
1347
|
-
/**
|
|
1363
|
+
/**
|
|
1364
|
+
* Returns `true` if `open()` has been called for at least one target.
|
|
1365
|
+
* (Replaces the old once-per-session `_opened` flag; kept for interface
|
|
1366
|
+
* compatibility with tests that read `opener.opened`.)
|
|
1367
|
+
*/
|
|
1348
1368
|
get opened() {
|
|
1349
|
-
return this.
|
|
1369
|
+
return this._openedTargets.size > 0;
|
|
1370
|
+
}
|
|
1371
|
+
/** Returns the set of target IDs that have already been auto-opened. */
|
|
1372
|
+
get openedTargets() {
|
|
1373
|
+
return this._openedTargets;
|
|
1350
1374
|
}
|
|
1351
1375
|
};
|
|
1352
1376
|
//#endregion
|
|
@@ -2112,6 +2136,8 @@ const en = {
|
|
|
2112
2136
|
"dashboard.inspector.section": "Inspector",
|
|
2113
2137
|
"dashboard.inspector.open": "Open inspector",
|
|
2114
2138
|
"dashboard.inspector.waiting": "Inspector URL pending — appears after a page attaches",
|
|
2139
|
+
"inspector.error.noTarget": "No page attached. Attach a device and try again.",
|
|
2140
|
+
"inspector.error.relayDown": "Relay is not active. Start a relay session first.",
|
|
2115
2141
|
"attach.title": "AIT Debug Session — QR Scan",
|
|
2116
2142
|
"attach.deployment": "deployment: {label}",
|
|
2117
2143
|
"attach.steps.section": "How to scan",
|
|
@@ -2364,6 +2390,8 @@ const tables = {
|
|
|
2364
2390
|
"dashboard.inspector.section": "인스펙터",
|
|
2365
2391
|
"dashboard.inspector.open": "인스펙터 열기",
|
|
2366
2392
|
"dashboard.inspector.waiting": "인스펙터 URL 대기 중 (페이지 attach 후 표시됩니다)",
|
|
2393
|
+
"inspector.error.noTarget": "연결된 페이지가 없습니다. 기기를 attach한 후 다시 시도하세요.",
|
|
2394
|
+
"inspector.error.relayDown": "relay가 활성화되지 않았습니다. start_debug로 relay를 기동하세요.",
|
|
2367
2395
|
"attach.title": "AIT 디버그 세션 — QR 스캔",
|
|
2368
2396
|
"attach.deployment": "deployment: {label}",
|
|
2369
2397
|
"attach.steps.section": "스캔 절차",
|
|
@@ -3116,6 +3144,7 @@ function buildAttachHtml(qrDataUrl, safeLabel, safeAttachUrl, locale, path = "/a
|
|
|
3116
3144
|
* @param getDashboardState - dashboard 상태를 반환하는 클로저. 주입 시 `GET /` dashboard와
|
|
3117
3145
|
* `GET /events` SSE 스트림이 활성화된다. 미주입 시 두 라우트는 204/서비스 없음으로 응답.
|
|
3118
3146
|
* @param options - 서버 옵션. `sseRefreshIntervalMs`로 idle 탭 TOTP 만료 방지 주기를 조정.
|
|
3147
|
+
* `getDirectInspectorUrl`로 /inspector 라우트에서 직접 조립 URL을 제공해 redirect 루프를 방지.
|
|
3119
3148
|
*/
|
|
3120
3149
|
async function startQrHttpServer(getDashboardState, options) {
|
|
3121
3150
|
const { default: QRCode } = await import("qrcode");
|
|
@@ -3213,6 +3242,31 @@ async function startQrHttpServer(getDashboardState, options) {
|
|
|
3213
3242
|
});
|
|
3214
3243
|
return;
|
|
3215
3244
|
}
|
|
3245
|
+
if (path === "/inspector") {
|
|
3246
|
+
const getDirectInspectorUrl = options?.getDirectInspectorUrl;
|
|
3247
|
+
if (!getDirectInspectorUrl) {
|
|
3248
|
+
res.writeHead(503, { "Content-Type": "text/plain; charset=utf-8" });
|
|
3249
|
+
res.end("Inspector endpoint is not available in this server mode.");
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
const result = getDirectInspectorUrl();
|
|
3253
|
+
const s = resolveLocaleStrings(locale);
|
|
3254
|
+
if (!result.ok) {
|
|
3255
|
+
const body = `<!DOCTYPE html><html lang="${locale}"><head><meta charset="utf-8"><title>Inspector</title></head><body><p>${escapeHtml(s(result.reason === "noTarget" ? "inspector.error.noTarget" : "inspector.error.relayDown"))}</p><p style="font-size:0.9em;color:#666">` + (locale === "ko" ? "(<a href=\"/\">대시보드로 돌아가기</a>)" : "(<a href=\"/\">Back to dashboard</a>)") + `</p></body></html>`;
|
|
3256
|
+
res.writeHead(502, {
|
|
3257
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
3258
|
+
"Cache-Control": "no-store"
|
|
3259
|
+
});
|
|
3260
|
+
res.end(body);
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
res.writeHead(302, {
|
|
3264
|
+
Location: result.url,
|
|
3265
|
+
"Cache-Control": "no-store"
|
|
3266
|
+
});
|
|
3267
|
+
res.end();
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3216
3270
|
if (path === "/qr.png") {
|
|
3217
3271
|
const encodedU = params.get("u") ?? "";
|
|
3218
3272
|
let attachUrl;
|
|
@@ -3267,6 +3321,9 @@ async function startQrHttpServer(getDashboardState, options) {
|
|
|
3267
3321
|
buildAttachPageUrl(attachUrl) {
|
|
3268
3322
|
return `http://127.0.0.1:${port}/attach?u=${encodeURIComponent(attachUrl)}`;
|
|
3269
3323
|
},
|
|
3324
|
+
get inspectorStableUrl() {
|
|
3325
|
+
return `http://127.0.0.1:${port}/inspector`;
|
|
3326
|
+
},
|
|
3270
3327
|
notifyStateChange() {
|
|
3271
3328
|
notifyStateChangeInternal();
|
|
3272
3329
|
},
|
|
@@ -4660,7 +4717,7 @@ async function readMcpSdkVersion() {
|
|
|
4660
4717
|
* some test environments that skip the build step).
|
|
4661
4718
|
*/
|
|
4662
4719
|
function readDevtoolsVersion() {
|
|
4663
|
-
return "0.1.
|
|
4720
|
+
return "0.1.80";
|
|
4664
4721
|
}
|
|
4665
4722
|
/**
|
|
4666
4723
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -5164,7 +5221,7 @@ function createDebugServer(deps) {
|
|
|
5164
5221
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5165
5222
|
const server = new Server({
|
|
5166
5223
|
name: "ait-debug",
|
|
5167
|
-
version: "0.1.
|
|
5224
|
+
version: "0.1.80"
|
|
5168
5225
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5169
5226
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5170
5227
|
const conn = router.active;
|
|
@@ -5243,7 +5300,7 @@ function createDebugServer(deps) {
|
|
|
5243
5300
|
const buildProjectRoot = typeof rawBuildProjectRoot === "string" ? rawBuildProjectRoot : void 0;
|
|
5244
5301
|
let tunnelHttpUrl = process.env.AIT_TUNNEL_BASE_URL?.trim() ?? "";
|
|
5245
5302
|
if (tunnelHttpUrl === "" && buildProjectRoot !== void 0) {
|
|
5246
|
-
const { readRelayUrls } = await import("../relay-url-store-
|
|
5303
|
+
const { readRelayUrls } = await import("../relay-url-store-RKcao_yG.js");
|
|
5247
5304
|
tunnelHttpUrl = (await readRelayUrls({ projectRoot: buildProjectRoot }))?.tunnelBaseUrl ?? "";
|
|
5248
5305
|
}
|
|
5249
5306
|
if (tunnelHttpUrl === "") return mcpError("build_attach_url(mobile): AIT_TUNNEL_BASE_URL이 설정되지 않았습니다. dev 서버가 tunnel:{cdp:true}로 기동 중이면 .ait_urls 파일이 자동 생성돼 있어야 합니다. 자동 발견이 되지 않을 경우 앱 HTTP 터널 URL을 AIT_TUNNEL_BASE_URL 환경변수로 직접 전달하세요.");
|
|
@@ -5974,7 +6031,26 @@ async function bootRelayFamily(options = {}) {
|
|
|
5974
6031
|
* wss URL) — it is NEVER logged here. The caller validates presence and passes
|
|
5975
6032
|
* the value straight to the CDP client.
|
|
5976
6033
|
*/
|
|
5977
|
-
|
|
6034
|
+
/**
|
|
6035
|
+
* Attempts to read the local loopback HTTP base URL of the env-2 Chii relay
|
|
6036
|
+
* (issue #530). Resolution order:
|
|
6037
|
+
* 1. `AIT_RELAY_LOCAL_URL` env var, if set and non-empty.
|
|
6038
|
+
* 2. `relayLocalUrl` from the `.ait_urls` file, if `projectRoot` is given.
|
|
6039
|
+
* 3. `undefined` — caller falls back to the tunnel base (existing behavior).
|
|
6040
|
+
*
|
|
6041
|
+
* This is a best-effort read — never throws. The returned value is a plain
|
|
6042
|
+
* `http://127.0.0.1:<port>` loopback URL; no secret exposure.
|
|
6043
|
+
*/
|
|
6044
|
+
async function readRelayLocalUrl(env = process.env, projectRoot) {
|
|
6045
|
+
const envValue = (env.AIT_RELAY_LOCAL_URL ?? "").trim();
|
|
6046
|
+
if (envValue !== "") return envValue;
|
|
6047
|
+
if (projectRoot !== void 0) try {
|
|
6048
|
+
const { readRelayUrls } = await import("../relay-url-store-RKcao_yG.js");
|
|
6049
|
+
const stored = await readRelayUrls({ projectRoot });
|
|
6050
|
+
if (stored?.relayLocalUrl) return stored.relayLocalUrl;
|
|
6051
|
+
} catch {}
|
|
6052
|
+
}
|
|
6053
|
+
async function bootExternalRelayFamily(relayBaseUrl, relayLocalUrl) {
|
|
5978
6054
|
assertRelayAuthConfigured();
|
|
5979
6055
|
const connection = createRelayConnection(relayBaseUrl);
|
|
5980
6056
|
const tunnelStatus = makeTunnelStatus(true, relayBaseUrl.replace(/^http/, "ws"));
|
|
@@ -5982,6 +6058,7 @@ async function bootExternalRelayFamily(relayBaseUrl) {
|
|
|
5982
6058
|
connection,
|
|
5983
6059
|
relayOrigin: "external-pwa",
|
|
5984
6060
|
relayHttpUrl: relayBaseUrl,
|
|
6061
|
+
relayLocalHttpUrl: relayLocalUrl,
|
|
5985
6062
|
getTunnelStatus: () => tunnelStatus,
|
|
5986
6063
|
stop() {
|
|
5987
6064
|
connection.close();
|
|
@@ -6022,7 +6099,7 @@ async function readMobileRelayBaseUrl(env = process.env, projectRoot) {
|
|
|
6022
6099
|
const envValue = typeof raw === "string" ? raw.trim() : "";
|
|
6023
6100
|
if (envValue !== "") return envValue;
|
|
6024
6101
|
if (projectRoot !== void 0) {
|
|
6025
|
-
const { readRelayUrls } = await import("../relay-url-store-
|
|
6102
|
+
const { readRelayUrls } = await import("../relay-url-store-RKcao_yG.js");
|
|
6026
6103
|
const stored = await readRelayUrls({ projectRoot });
|
|
6027
6104
|
if (stored?.relayBaseUrl !== void 0) return stored.relayBaseUrl;
|
|
6028
6105
|
}
|
|
@@ -6106,13 +6183,18 @@ var DualConnectionRouter = class {
|
|
|
6106
6183
|
return this.activeFamily?.relayOrigin;
|
|
6107
6184
|
}
|
|
6108
6185
|
/**
|
|
6109
|
-
*
|
|
6110
|
-
*
|
|
6111
|
-
*
|
|
6112
|
-
*
|
|
6186
|
+
* HTTP base URL of the Chii relay to use for inspector URL assembly (#503,
|
|
6187
|
+
* #530). Prefers the LOCAL loopback base (`relayLocalHttpUrl`) when available
|
|
6188
|
+
* so front_end page load + client WS do not traverse a cloudflare tunnel —
|
|
6189
|
+
* falls back to `relayHttpUrl` (the tunnel base for env-2, loopback for env-3/4)
|
|
6190
|
+
* when not set. Returns `undefined` when no relay family is active.
|
|
6191
|
+
*
|
|
6192
|
+
* SECRET-HANDLING: when relayLocalHttpUrl is absent this falls back to
|
|
6193
|
+
* relayHttpUrl which may carry the tunnel host — callers must not log it.
|
|
6113
6194
|
*/
|
|
6114
6195
|
get activeRelayHttpUrl() {
|
|
6115
|
-
|
|
6196
|
+
if (!this.activeFamily) return void 0;
|
|
6197
|
+
return this.activeFamily.relayLocalHttpUrl ?? this.activeFamily.relayHttpUrl;
|
|
6116
6198
|
}
|
|
6117
6199
|
/** Every booted family (for unified shutdown). All families are lazy (#396). */
|
|
6118
6200
|
bootedFamilies() {
|
|
@@ -6160,7 +6242,9 @@ var DualConnectionRouter = class {
|
|
|
6160
6242
|
if (activeFamily.connection.kind === "relay") {
|
|
6161
6243
|
const firstTarget = activeFamily.connection.listTargets()[0];
|
|
6162
6244
|
const env = deriveEnvironment(activeFamily.connection.kind, getLiveIntent(), activeFamily.relayOrigin);
|
|
6245
|
+
const inspectorStableUrl = this.deps.getInspectorStableUrl?.() ?? null;
|
|
6163
6246
|
this.deps.devtoolsOpener.open({
|
|
6247
|
+
inspectorStableUrl,
|
|
6164
6248
|
relayHttpBaseUrl: activeFamily.relayHttpUrl,
|
|
6165
6249
|
targetId: firstTarget?.id,
|
|
6166
6250
|
mintTotp: process.env.AIT_DEBUG_TOTP_SECRET ? () => generateTotp(process.env.AIT_DEBUG_TOTP_SECRET) : void 0,
|
|
@@ -6224,7 +6308,7 @@ async function runDebugServer(options = {}) {
|
|
|
6224
6308
|
const devtoolsOpener = new AutoDevtoolsOpener();
|
|
6225
6309
|
const diagnosticsCollector = new InMemoryDiagnosticsCollector();
|
|
6226
6310
|
const router = new DualConnectionRouter({
|
|
6227
|
-
bootLazyFor: async (key, projectRoot) => key === "relay-sandbox" ? bootExternalRelayFamily(await readMobileRelayBaseUrl(process.env, projectRoot)) : key === "local-browser" ? bootLocalFamily() : bootRelayFamily({
|
|
6311
|
+
bootLazyFor: async (key, projectRoot) => key === "relay-sandbox" ? bootExternalRelayFamily(await readMobileRelayBaseUrl(process.env, projectRoot), await readRelayLocalUrl(process.env, projectRoot)) : key === "local-browser" ? bootLocalFamily() : bootRelayFamily({
|
|
6228
6312
|
relayPort: options.relayPort,
|
|
6229
6313
|
verifyAuth: buildRelayVerifyAuth(),
|
|
6230
6314
|
onWssUrl: (wssUrl) => {
|
|
@@ -6235,7 +6319,8 @@ async function runDebugServer(options = {}) {
|
|
|
6235
6319
|
}),
|
|
6236
6320
|
diagnosticsCollector,
|
|
6237
6321
|
devtoolsOpener,
|
|
6238
|
-
onPageAttach: () => qrServer?.notifyStateChange()
|
|
6322
|
+
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6323
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
6239
6324
|
});
|
|
6240
6325
|
const aitSource = new RoutingAitSource(() => {
|
|
6241
6326
|
return router.active;
|
|
@@ -6243,9 +6328,7 @@ async function runDebugServer(options = {}) {
|
|
|
6243
6328
|
let lastAttachParts = null;
|
|
6244
6329
|
const getDashboardState = () => {
|
|
6245
6330
|
const targets = router.active.listTargets();
|
|
6246
|
-
const
|
|
6247
|
-
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6248
|
-
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6331
|
+
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
6249
6332
|
return {
|
|
6250
6333
|
tunnel: {
|
|
6251
6334
|
up: router.relayTunnelStatus().up,
|
|
@@ -6260,9 +6343,35 @@ async function runDebugServer(options = {}) {
|
|
|
6260
6343
|
mode: deriveEnvironment(router.active.kind, getLiveIntent(), router.activeRelayOrigin)
|
|
6261
6344
|
};
|
|
6262
6345
|
};
|
|
6346
|
+
const getDirectInspectorUrl = () => {
|
|
6347
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6348
|
+
if (!relayHttpUrl) return {
|
|
6349
|
+
ok: false,
|
|
6350
|
+
reason: "relayDown"
|
|
6351
|
+
};
|
|
6352
|
+
const targets = router.active.listTargets();
|
|
6353
|
+
if (targets.length === 0) return {
|
|
6354
|
+
ok: false,
|
|
6355
|
+
reason: "noTarget"
|
|
6356
|
+
};
|
|
6357
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6358
|
+
if (!totpSecret) return {
|
|
6359
|
+
ok: false,
|
|
6360
|
+
reason: "totpUnavailable"
|
|
6361
|
+
};
|
|
6362
|
+
const url = buildChiiInspectorUrl(relayHttpUrl, targets[0].id, () => generateTotp(totpSecret, Date.now()));
|
|
6363
|
+
if (url === null) return {
|
|
6364
|
+
ok: false,
|
|
6365
|
+
reason: "totpUnavailable"
|
|
6366
|
+
};
|
|
6367
|
+
return {
|
|
6368
|
+
ok: true,
|
|
6369
|
+
url
|
|
6370
|
+
};
|
|
6371
|
+
};
|
|
6263
6372
|
let qrServer;
|
|
6264
6373
|
try {
|
|
6265
|
-
qrServer = await startQrHttpServer(getDashboardState);
|
|
6374
|
+
qrServer = await startQrHttpServer(getDashboardState, { getDirectInspectorUrl });
|
|
6266
6375
|
} catch (err) {
|
|
6267
6376
|
logWarn("server.start", { msg: `QR HTTP 서버 시작 실패 (text QR fallback 사용): ${err instanceof Error ? err.message : String(err)}` });
|
|
6268
6377
|
}
|
|
@@ -6398,7 +6507,7 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6398
6507
|
const devtoolsOpener = new AutoDevtoolsOpener();
|
|
6399
6508
|
const diagnosticsCollector = new InMemoryDiagnosticsCollector();
|
|
6400
6509
|
const router = new DualConnectionRouter({
|
|
6401
|
-
bootLazyFor: async (key, projectRoot) => key === "relay-sandbox" ? bootExternalRelayFamily(await readMobileRelayBaseUrl(process.env, projectRoot)) : key === "local-browser" ? bootLocalFamilyForEntry() : bootRelayFamily({
|
|
6510
|
+
bootLazyFor: async (key, projectRoot) => key === "relay-sandbox" ? bootExternalRelayFamily(await readMobileRelayBaseUrl(process.env, projectRoot), await readRelayLocalUrl(process.env, projectRoot)) : key === "local-browser" ? bootLocalFamilyForEntry() : bootRelayFamily({
|
|
6402
6511
|
verifyAuth: buildRelayVerifyAuth(),
|
|
6403
6512
|
onWssUrl: (wssUrl) => {
|
|
6404
6513
|
lockHandle.updateWssUrl(wssUrl);
|
|
@@ -6408,7 +6517,8 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6408
6517
|
}),
|
|
6409
6518
|
diagnosticsCollector,
|
|
6410
6519
|
devtoolsOpener,
|
|
6411
|
-
onPageAttach: () => qrServer?.notifyStateChange()
|
|
6520
|
+
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6521
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
6412
6522
|
});
|
|
6413
6523
|
const aitSource = new RoutingAitSource(() => {
|
|
6414
6524
|
return router.active;
|
|
@@ -6416,9 +6526,7 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6416
6526
|
let lastAttachParts = null;
|
|
6417
6527
|
const getDashboardState = () => {
|
|
6418
6528
|
const targets = router.active.listTargets();
|
|
6419
|
-
const
|
|
6420
|
-
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6421
|
-
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6529
|
+
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
6422
6530
|
return {
|
|
6423
6531
|
tunnel: {
|
|
6424
6532
|
up: router.relayTunnelStatus().up,
|
|
@@ -6432,9 +6540,35 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6432
6540
|
inspectorUrl
|
|
6433
6541
|
};
|
|
6434
6542
|
};
|
|
6543
|
+
const getDirectInspectorUrl = () => {
|
|
6544
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6545
|
+
if (!relayHttpUrl) return {
|
|
6546
|
+
ok: false,
|
|
6547
|
+
reason: "relayDown"
|
|
6548
|
+
};
|
|
6549
|
+
const targets = router.active.listTargets();
|
|
6550
|
+
if (targets.length === 0) return {
|
|
6551
|
+
ok: false,
|
|
6552
|
+
reason: "noTarget"
|
|
6553
|
+
};
|
|
6554
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6555
|
+
if (!totpSecret) return {
|
|
6556
|
+
ok: false,
|
|
6557
|
+
reason: "totpUnavailable"
|
|
6558
|
+
};
|
|
6559
|
+
const url = buildChiiInspectorUrl(relayHttpUrl, targets[0].id, () => generateTotp(totpSecret, Date.now()));
|
|
6560
|
+
if (url === null) return {
|
|
6561
|
+
ok: false,
|
|
6562
|
+
reason: "totpUnavailable"
|
|
6563
|
+
};
|
|
6564
|
+
return {
|
|
6565
|
+
ok: true,
|
|
6566
|
+
url
|
|
6567
|
+
};
|
|
6568
|
+
};
|
|
6435
6569
|
let qrServer;
|
|
6436
6570
|
try {
|
|
6437
|
-
qrServer = await startQrHttpServer(getDashboardState);
|
|
6571
|
+
qrServer = await startQrHttpServer(getDashboardState, { getDirectInspectorUrl });
|
|
6438
6572
|
} catch (err) {
|
|
6439
6573
|
logWarn("server.start", { msg: `QR HTTP 서버 시작 실패 (text QR fallback 사용): ${err instanceof Error ? err.message : String(err)}` });
|
|
6440
6574
|
}
|
|
@@ -6554,7 +6688,7 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6554
6688
|
const devtoolsOpener = new AutoDevtoolsOpener();
|
|
6555
6689
|
const diagnosticsCollector = new InMemoryDiagnosticsCollector();
|
|
6556
6690
|
const router = new DualConnectionRouter({
|
|
6557
|
-
bootLazyFor: async (key) => key === "relay-sandbox" ? bootExternalRelayFamily(relayBaseUrl) : key === "local-browser" ? bootLocalFamily() : bootRelayFamily({
|
|
6691
|
+
bootLazyFor: async (key) => key === "relay-sandbox" ? bootExternalRelayFamily(relayBaseUrl, await readRelayLocalUrl(process.env, options.projectRoot ?? process.cwd())) : key === "local-browser" ? bootLocalFamily() : bootRelayFamily({
|
|
6558
6692
|
verifyAuth: buildRelayVerifyAuth(),
|
|
6559
6693
|
onWssUrl: (wssUrl) => {
|
|
6560
6694
|
lockHandle.updateWssUrl(wssUrl);
|
|
@@ -6564,7 +6698,8 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6564
6698
|
}),
|
|
6565
6699
|
diagnosticsCollector,
|
|
6566
6700
|
devtoolsOpener,
|
|
6567
|
-
onPageAttach: () => qrServer?.notifyStateChange()
|
|
6701
|
+
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6702
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
6568
6703
|
});
|
|
6569
6704
|
const aitSource = new RoutingAitSource(() => {
|
|
6570
6705
|
return router.active;
|
|
@@ -6572,9 +6707,7 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6572
6707
|
let lastAttachParts = null;
|
|
6573
6708
|
const getDashboardState = () => {
|
|
6574
6709
|
const targets = router.active.listTargets();
|
|
6575
|
-
const
|
|
6576
|
-
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6577
|
-
const inspectorUrl = relayHttpUrl && targets.length > 0 ? buildChiiInspectorUrl(relayHttpUrl, targets[0].id, totpSecret ? () => generateTotp(totpSecret, Date.now()) : void 0) : null;
|
|
6710
|
+
const inspectorUrl = qrServer?.inspectorStableUrl ?? null;
|
|
6578
6711
|
return {
|
|
6579
6712
|
tunnel: {
|
|
6580
6713
|
up: router.relayTunnelStatus().up,
|
|
@@ -6588,9 +6721,35 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6588
6721
|
inspectorUrl
|
|
6589
6722
|
};
|
|
6590
6723
|
};
|
|
6724
|
+
const getDirectInspectorUrl = () => {
|
|
6725
|
+
const relayHttpUrl = router.activeRelayHttpUrl;
|
|
6726
|
+
if (!relayHttpUrl) return {
|
|
6727
|
+
ok: false,
|
|
6728
|
+
reason: "relayDown"
|
|
6729
|
+
};
|
|
6730
|
+
const targets = router.active.listTargets();
|
|
6731
|
+
if (targets.length === 0) return {
|
|
6732
|
+
ok: false,
|
|
6733
|
+
reason: "noTarget"
|
|
6734
|
+
};
|
|
6735
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
6736
|
+
if (!totpSecret) return {
|
|
6737
|
+
ok: false,
|
|
6738
|
+
reason: "totpUnavailable"
|
|
6739
|
+
};
|
|
6740
|
+
const url = buildChiiInspectorUrl(relayHttpUrl, targets[0].id, () => generateTotp(totpSecret, Date.now()));
|
|
6741
|
+
if (url === null) return {
|
|
6742
|
+
ok: false,
|
|
6743
|
+
reason: "totpUnavailable"
|
|
6744
|
+
};
|
|
6745
|
+
return {
|
|
6746
|
+
ok: true,
|
|
6747
|
+
url
|
|
6748
|
+
};
|
|
6749
|
+
};
|
|
6591
6750
|
let qrServer;
|
|
6592
6751
|
try {
|
|
6593
|
-
qrServer = await startQrHttpServer(getDashboardState);
|
|
6752
|
+
qrServer = await startQrHttpServer(getDashboardState, { getDirectInspectorUrl });
|
|
6594
6753
|
} catch (err) {
|
|
6595
6754
|
logWarn("server.start", { msg: `QR HTTP 서버 시작 실패 (text QR fallback 사용): ${err instanceof Error ? err.message : String(err)}` });
|
|
6596
6755
|
}
|
|
@@ -7105,7 +7264,7 @@ function createDevServer(deps = {}) {
|
|
|
7105
7264
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
7106
7265
|
const server = new Server({
|
|
7107
7266
|
name: "ait-devtools",
|
|
7108
|
-
version: "0.1.
|
|
7267
|
+
version: "0.1.80"
|
|
7109
7268
|
}, { capabilities: { tools: {} } });
|
|
7110
7269
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
7111
7270
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|