@ait-co/devtools 0.1.100 → 0.1.101
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/mcp/cli.js +90 -9
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/panel/index.js +2 -2
- package/package.json +1 -1
package/dist/mcp/cli.js
CHANGED
|
@@ -4858,7 +4858,7 @@ async function readMcpSdkVersion() {
|
|
|
4858
4858
|
* some test environments that skip the build step).
|
|
4859
4859
|
*/
|
|
4860
4860
|
function readDevtoolsVersion() {
|
|
4861
|
-
return "0.1.
|
|
4861
|
+
return "0.1.101";
|
|
4862
4862
|
}
|
|
4863
4863
|
/**
|
|
4864
4864
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -5313,6 +5313,52 @@ function makeTunnelStatus(up, wssUrl, droppedAt = null, reissueAttempts = 0) {
|
|
|
5313
5313
|
* Node-only.
|
|
5314
5314
|
*/
|
|
5315
5315
|
/**
|
|
5316
|
+
* Maximum age (ms) of a page's `lastSeenAt` before it is treated as a ghost
|
|
5317
|
+
* and excluded from the `wait_for_attach` short-circuit in `build_attach_url`
|
|
5318
|
+
* (issue #610).
|
|
5319
|
+
*
|
|
5320
|
+
* Rationale: the env-2 relay is owned by the dev server (unplugin), so every
|
|
5321
|
+
* `dev:phone:cdp` restart produces a new quick-tunnel. The old relay goes
|
|
5322
|
+
* offline immediately, but the daemon's warm `ChiiCdpConnection` still lists
|
|
5323
|
+
* the last-seen target — its `lastSeenAt` freezes at the moment the old relay
|
|
5324
|
+
* died. A 5-minute threshold is large enough to be invisible in normal usage
|
|
5325
|
+
* (active CDP sessions see a message every few seconds) while being small
|
|
5326
|
+
* enough to catch a relay that went down before the daemon was re-entered.
|
|
5327
|
+
*
|
|
5328
|
+
* Injectable for tests via {@link DebugServerDeps.stalePageThresholdMs}.
|
|
5329
|
+
*/
|
|
5330
|
+
const RELAY_SANDBOX_STALE_PAGE_MS = 300 * 1e3;
|
|
5331
|
+
/**
|
|
5332
|
+
* Predicate used by `build_attach_url`'s `wait_for_attach` loop to decide
|
|
5333
|
+
* whether the relay-sandbox connection has a genuinely fresh page attached.
|
|
5334
|
+
*
|
|
5335
|
+
* Stale-ghost gating (issue #610): when the dev server restarts with a new
|
|
5336
|
+
* quick-tunnel, the warm `ChiiCdpConnection` still lists the last-seen target
|
|
5337
|
+
* but its `lastSeenAt` is frozen. A page whose `lastSeenAt` exceeds
|
|
5338
|
+
* `stalePageThresholdMs` is a ghost from the dead relay — it must NOT
|
|
5339
|
+
* short-circuit `wait_for_attach`.
|
|
5340
|
+
*
|
|
5341
|
+
* Rules:
|
|
5342
|
+
* - `pages.length === 0` → false (nothing attached).
|
|
5343
|
+
* - Connection has no `getLastSeenAt` (test fakes, local-browser) → falls back
|
|
5344
|
+
* to `pages.length > 0` (regression-safe).
|
|
5345
|
+
* - `seenMs === null` → treat as fresh (no CDP message received yet, first
|
|
5346
|
+
* message pending — the connection is alive).
|
|
5347
|
+
* - Otherwise: at least one page must satisfy `nowMs - seenMs <=
|
|
5348
|
+
* stalePageThresholdMs`.
|
|
5349
|
+
*
|
|
5350
|
+
* Exported for unit testing.
|
|
5351
|
+
*/
|
|
5352
|
+
function isSandboxPageFresh(pages, getLastSeenAt, nowMs, stalePageThresholdMs) {
|
|
5353
|
+
if (pages.length === 0) return false;
|
|
5354
|
+
if (getLastSeenAt === null) return true;
|
|
5355
|
+
return pages.some((p) => {
|
|
5356
|
+
const seenMs = getLastSeenAt(p.id);
|
|
5357
|
+
if (seenMs === null) return true;
|
|
5358
|
+
return nowMs - seenMs <= stalePageThresholdMs;
|
|
5359
|
+
});
|
|
5360
|
+
}
|
|
5361
|
+
/**
|
|
5316
5362
|
* Parses `_deploymentId` from the query string of a scheme URL.
|
|
5317
5363
|
*
|
|
5318
5364
|
* Returns `null` when the param is absent or empty — callers treat that as
|
|
@@ -5395,7 +5441,7 @@ function waitForAttachWithEvents(connection, filterFn, timeoutMs, pollIntervalMs
|
|
|
5395
5441
|
* naturally via `enableDomains`). The tier only controls visibility.
|
|
5396
5442
|
*/
|
|
5397
5443
|
function createDebugServer(deps) {
|
|
5398
|
-
const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 6e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt, getTunnelChildPid, readLock: readLockDep } = deps;
|
|
5444
|
+
const { connection, router: routerDep, aitSource, getTunnelStatus, waitForAttachTimeoutMs = 6e4, qrHttpServer, getEnvironment: getEnvDep, getEnvironmentReason: getEnvReasonDep, diagnosticsCollector: collectorDep, totpSecret, onAttachUrlBuilt, getTunnelChildPid, readLock: readLockDep, stalePageThresholdMs = RELAY_SANDBOX_STALE_PAGE_MS, nowMs = () => Date.now() } = deps;
|
|
5399
5445
|
const getTotpSecret = deps.getTotpSecret ?? (() => totpSecret);
|
|
5400
5446
|
const readLockFn = readLockDep ?? readServerLock;
|
|
5401
5447
|
const router = routerDep ?? makeSingleConnectionRouter(connection);
|
|
@@ -5404,7 +5450,7 @@ function createDebugServer(deps) {
|
|
|
5404
5450
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5405
5451
|
const server = new Server({
|
|
5406
5452
|
name: "ait-debug",
|
|
5407
|
-
version: "0.1.
|
|
5453
|
+
version: "0.1.101"
|
|
5408
5454
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5409
5455
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5410
5456
|
const conn = router.active;
|
|
@@ -5533,7 +5579,10 @@ function createDebugServer(deps) {
|
|
|
5533
5579
|
});
|
|
5534
5580
|
const relayUrl = tunnelStatus.wssUrl;
|
|
5535
5581
|
const totp = totpMeta;
|
|
5536
|
-
const
|
|
5582
|
+
const connAsAny = conn;
|
|
5583
|
+
const getLastSeenAt = typeof connAsAny.getTargetLastSeenAt === "function" ? (id) => connAsAny.getTargetLastSeenAt(id) : null;
|
|
5584
|
+
const callNow = nowMs();
|
|
5585
|
+
const isMatchingPage = (pages) => isSandboxPageFresh(pages, getLastSeenAt, callNow, stalePageThresholdMs);
|
|
5537
5586
|
const buildTimeoutError = (baseText, timeoutSec, observed) => {
|
|
5538
5587
|
const observedUrls = observed.slice(0, 3).map((p) => p.url.slice(0, 80)).join(", ");
|
|
5539
5588
|
return `${baseText}\n\nNo page attached within ${timeoutSec}s${observed.length > 0 ? ` — previously attached pages: [${observedUrls}]` : ""} — launcher QR을 폰 카메라로 스캔한 뒤 call list_pages를 다시 호출하세요.`;
|
|
@@ -6459,10 +6508,39 @@ var DualConnectionRouter = class {
|
|
|
6459
6508
|
* `projectRoot` is forwarded to `bootLazyFor` so `relay-sandbox` boot can
|
|
6460
6509
|
* fall back to `.ait_urls` file discovery (#424) when `AIT_RELAY_BASE_URL` is
|
|
6461
6510
|
* not set in the environment.
|
|
6511
|
+
*
|
|
6512
|
+
* **Relay-sandbox stale-URL rebuild (issue #610):** when the `relay-sandbox`
|
|
6513
|
+
* family is already warm, reads the current relay URL via
|
|
6514
|
+
* `deps.readSandboxRelayUrl` and compares it against the cached
|
|
6515
|
+
* `relayHttpUrl`. If they differ (dev server was restarted → new tunnel),
|
|
6516
|
+
* the stale family is torn down, evicted from the map, and a fresh one is
|
|
6517
|
+
* booted. If they match, or if the URL cannot be read, the warm family is
|
|
6518
|
+
* reused (fail-open — no unnecessary teardown on transient read errors).
|
|
6519
|
+
*
|
|
6520
|
+
* SECRET-HANDLING: fresh and cached relay URLs carry the tunnel host. The
|
|
6521
|
+
* comparison result (same/different) is the only thing surfaced — URLs are
|
|
6522
|
+
* never logged.
|
|
6462
6523
|
*/
|
|
6463
6524
|
async familyFor(key, projectRoot) {
|
|
6464
6525
|
const warm = this.lazyFamilies.get(key);
|
|
6465
|
-
if (warm)
|
|
6526
|
+
if (warm) {
|
|
6527
|
+
if (key === "relay-sandbox" && this.deps.readSandboxRelayUrl !== void 0) {
|
|
6528
|
+
let freshUrl = null;
|
|
6529
|
+
try {
|
|
6530
|
+
freshUrl = await this.deps.readSandboxRelayUrl(projectRoot);
|
|
6531
|
+
} catch {
|
|
6532
|
+
freshUrl = null;
|
|
6533
|
+
}
|
|
6534
|
+
if (freshUrl !== null && freshUrl !== warm.relayHttpUrl) {
|
|
6535
|
+
warm.stop();
|
|
6536
|
+
this.lazyFamilies.delete(key);
|
|
6537
|
+
const booted = await this.deps.bootLazyFor(key, projectRoot);
|
|
6538
|
+
this.lazyFamilies.set(key, booted);
|
|
6539
|
+
return booted;
|
|
6540
|
+
}
|
|
6541
|
+
}
|
|
6542
|
+
return warm;
|
|
6543
|
+
}
|
|
6466
6544
|
const booted = await this.deps.bootLazyFor(key, projectRoot);
|
|
6467
6545
|
this.lazyFamilies.set(key, booted);
|
|
6468
6546
|
return booted;
|
|
@@ -6522,7 +6600,8 @@ async function runDebugServer(options = {}) {
|
|
|
6522
6600
|
diagnosticsCollector,
|
|
6523
6601
|
devtoolsOpener,
|
|
6524
6602
|
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6525
|
-
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
6603
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null,
|
|
6604
|
+
readSandboxRelayUrl: (pr) => readMobileRelayBaseUrl(process.env, pr).catch(() => null)
|
|
6526
6605
|
});
|
|
6527
6606
|
const aitSource = new RoutingAitSource(() => {
|
|
6528
6607
|
return router.active;
|
|
@@ -6734,7 +6813,8 @@ async function runLocalDebugServer(options = {}) {
|
|
|
6734
6813
|
diagnosticsCollector,
|
|
6735
6814
|
devtoolsOpener,
|
|
6736
6815
|
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6737
|
-
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
6816
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null,
|
|
6817
|
+
readSandboxRelayUrl: (pr) => readMobileRelayBaseUrl(process.env, pr).catch(() => null)
|
|
6738
6818
|
});
|
|
6739
6819
|
const aitSource = new RoutingAitSource(() => {
|
|
6740
6820
|
return router.active;
|
|
@@ -6929,7 +7009,8 @@ async function runMobileDebugServer(options = {}) {
|
|
|
6929
7009
|
diagnosticsCollector,
|
|
6930
7010
|
devtoolsOpener,
|
|
6931
7011
|
onPageAttach: () => qrServer?.notifyStateChange(),
|
|
6932
|
-
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null
|
|
7012
|
+
getInspectorStableUrl: () => qrServer?.inspectorStableUrl ?? null,
|
|
7013
|
+
readSandboxRelayUrl: (pr) => readMobileRelayBaseUrl(process.env, pr ?? options.projectRoot ?? process.cwd()).catch(() => null)
|
|
6933
7014
|
});
|
|
6934
7015
|
const aitSource = new RoutingAitSource(() => {
|
|
6935
7016
|
return router.active;
|
|
@@ -7503,7 +7584,7 @@ function createDevServer(deps = {}) {
|
|
|
7503
7584
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
7504
7585
|
const server = new Server({
|
|
7505
7586
|
name: "ait-devtools",
|
|
7506
|
-
version: "0.1.
|
|
7587
|
+
version: "0.1.101"
|
|
7507
7588
|
}, { capabilities: { tools: {} } });
|
|
7508
7589
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
7509
7590
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|