@tonyclaw/agent-inspector 3.0.29 → 3.0.30
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/.output/cli.js +791 -191
- package/.output/nitro.json +1 -1
- package/.output/public/assets/CompareDrawer-B6cXJohL.js +1 -0
- package/.output/public/assets/InspectorPet-mAzeGmEE.js +4108 -0
- package/.output/public/assets/ProxyViewerContainer-CkFWv_Nm.js +126 -0
- package/.output/public/assets/{ReplayDialog-CctnITbt.js → ReplayDialog-DodiTL6E.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-BrN0RBbL.js → RequestAnatomy-B3IauToI.js} +1 -1
- package/.output/public/assets/ResponseView-Hn-5ordK.js +3 -0
- package/.output/public/assets/StreamingChunkSequence-vLDOVQM_.js +1 -0
- package/.output/public/assets/_sessionId-BFH4TOaI.js +1 -0
- package/.output/public/assets/{_sessionId-DKM91udC.js → _sessionId-Cj_HIFZx.js} +1 -1
- package/.output/public/assets/{index-Bq-nqolG.js → index-Bmj2lcWE.js} +1 -1
- package/.output/public/assets/index-CgJj-HBC.css +1 -0
- package/.output/public/assets/index-Ch7uqnCT.js +1 -0
- package/.output/public/assets/index-DkDdKvLl.js +76 -0
- package/.output/public/assets/{index-DPiESBo2.js → index-ZlhqCrSg.js} +6 -6
- package/.output/public/assets/{json-viewer-CfaDJwx8.js → json-viewer-BdrnVQu-.js} +1 -1
- package/.output/public/assets/jszip.min-CS_nt_o1.js +2 -0
- package/.output/public/assets/qwen-mMn3f5ul.webp +0 -0
- package/.output/server/_libs/jszip.mjs +35 -9
- package/.output/server/_libs/lucide-react.mjs +86 -86
- package/.output/server/_libs/radix-ui__react-dialog.mjs +3 -3
- package/.output/server/_libs/tanstack__react-router.mjs +1 -1
- package/.output/server/{_sessionId-CwGXPD4C.mjs → _sessionId-C5S3pGZd.mjs} +15 -15
- package/.output/server/_sessionId-g6D69lKq.mjs +81 -0
- package/.output/server/_ssr/{CompareDrawer-R_P1-VNr.mjs → CompareDrawer-CqUmGHal.mjs} +138 -103
- package/.output/server/_ssr/InspectorPet-DI0UJd01.mjs +925 -0
- package/.output/server/_ssr/{ProxyViewerContainer-DUgxRNbj.mjs → ProxyViewerContainer-CvsUmbJ3.mjs} +1157 -1613
- package/.output/server/_ssr/{ReplayDialog-De9wKKzS.mjs → ReplayDialog-DS48dpFG.mjs} +18 -18
- package/.output/server/_ssr/{RequestAnatomy-Db4MxUYo.mjs → RequestAnatomy-DxYVQU1C.mjs} +58 -58
- package/.output/server/_ssr/{ResponseView-8rDXOiNB.mjs → ResponseView-BLXW73eq.mjs} +60 -60
- package/.output/server/_ssr/{StreamingChunkSequence-N9teZHkA.mjs → StreamingChunkSequence-DD0iFuy0.mjs} +16 -16
- package/.output/server/_ssr/{index-D4DvByuW.mjs → index-B10LIaK0.mjs} +15 -15
- package/.output/server/_ssr/index-RHVJnU2p.mjs +81 -0
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-BjoHRbjS.mjs → json-viewer-LDu2XBaK.mjs} +58 -58
- package/.output/server/_ssr/{router-BYlePDj8.mjs → router-GKvrwifJ.mjs} +9005 -6488
- package/.output/server/_tanstack-start-manifest_v-Df2n4mbT.mjs +4 -0
- package/.output/server/index.mjs +103 -75
- package/.output/workers/logFinalizer.worker.js +16908 -0
- package/.output/workers/sessionWorkerEntry.js +16904 -0
- package/README.md +21 -5
- package/package.json +6 -2
- package/src/assets/logos/qwen.webp +0 -0
- package/src/cli.ts +85 -10
- package/src/components/OnboardingBanner.tsx +41 -21
- package/src/components/ProxyViewer.tsx +198 -269
- package/src/components/ProxyViewerContainer.tsx +128 -17
- package/src/components/ecosystem/AgentLabDialog.tsx +166 -19
- package/src/components/errors/SafeErrorBoundary.tsx +201 -0
- package/src/components/pi-agent/PiAgentPanel.tsx +4 -1
- package/src/components/providers/ImportWizardDialog.tsx +18 -42
- package/src/components/providers/ProviderLogo.tsx +1 -1
- package/src/components/providers/ProvidersPanel.tsx +8 -5
- package/src/components/providers/SettingsDialog.tsx +34 -11
- package/src/components/proxy-viewer/ApplicationBar.tsx +63 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +92 -49
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +186 -0
- package/src/components/proxy-viewer/LogEntry.tsx +283 -275
- package/src/components/proxy-viewer/RawExportWarning.tsx +77 -0
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +61 -0
- package/src/components/proxy-viewer/ThreadConnector.tsx +16 -1
- package/src/components/proxy-viewer/TurnGroup.tsx +13 -2
- package/src/components/proxy-viewer/accessibility.ts +8 -0
- package/src/components/proxy-viewer/lazy.ts +4 -0
- package/src/components/proxy-viewer/liveConnectionState.ts +158 -0
- package/src/components/proxy-viewer/proxyViewerLogic.ts +10 -0
- package/src/components/proxy-viewer/viewerState.ts +2 -6
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/tabs.tsx +3 -3
- package/src/components/ui/transient-toast.tsx +1 -1
- package/src/lib/apiClient.ts +17 -2
- package/src/lib/ecosystemContract.ts +34 -0
- package/src/lib/export-logs.ts +1 -1
- package/src/lib/providerContract.ts +70 -4
- package/src/lib/providerImportContract.ts +27 -0
- package/src/lib/providerModelMetadata.ts +16 -7
- package/src/lib/resourceLimits.ts +152 -0
- package/src/lib/safeDiagnostic.ts +38 -0
- package/src/lib/useProviders.ts +4 -4
- package/src/mcp/server.ts +39 -6
- package/src/mcp/toolHandlers.ts +131 -4
- package/src/proxy/chunkStorage.ts +20 -6
- package/src/proxy/config.ts +20 -6
- package/src/proxy/dataDir.ts +3 -0
- package/src/proxy/ecosystemExecutionConfirmation.ts +114 -0
- package/src/proxy/ecosystemExecutionRoute.ts +116 -0
- package/src/proxy/ecosystemTasks.ts +48 -0
- package/src/proxy/evidenceExporter.ts +23 -9
- package/src/proxy/groupEvidenceExporter.ts +26 -5
- package/src/proxy/groupStore.ts +13 -3
- package/src/proxy/handler.ts +103 -20
- package/src/proxy/identityProxy.ts +333 -14
- package/src/proxy/logFinalizer.ts +73 -6
- package/src/proxy/logImportUpload.ts +128 -0
- package/src/proxy/logImporter.ts +321 -70
- package/src/proxy/logIndex.ts +16 -6
- package/src/proxy/logger.ts +294 -41
- package/src/proxy/privateDataPath.ts +183 -0
- package/src/proxy/providerScanStore.ts +87 -0
- package/src/proxy/providerSecretStore.ts +58 -19
- package/src/proxy/providers.ts +175 -58
- package/src/proxy/rawStreamCapture.ts +66 -5
- package/src/proxy/runStore.ts +13 -3
- package/src/proxy/runtimeAdmission.ts +52 -0
- package/src/proxy/runtimeHealth.ts +206 -0
- package/src/proxy/runtimeShutdown.ts +75 -0
- package/src/proxy/sessionArchive.ts +15 -2
- package/src/proxy/sessionProcess.ts +19 -0
- package/src/proxy/sessionRuntime.ts +7 -0
- package/src/proxy/shutdownCoordinator.ts +90 -0
- package/src/proxy/sqliteLogIndex.ts +18 -2
- package/src/proxy/store.ts +18 -2
- package/src/routes/__root.tsx +18 -1
- package/src/routes/api/alerts.summary.ts +12 -8
- package/src/routes/api/alerts.ts +27 -9
- package/src/routes/api/ecosystem.packages.$packageId.help.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.install.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +3 -11
- package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +3 -8
- package/src/routes/api/ecosystem.recipes.$recipeId.run.ts +3 -8
- package/src/routes/api/health.ts +9 -2
- package/src/routes/api/knowledge.project-context.ts +11 -0
- package/src/routes/api/knowledge.search.ts +33 -2
- package/src/routes/api/logs.$id.body.ts +16 -2
- package/src/routes/api/logs.import.ts +7 -18
- package/src/routes/api/logs.stream.ts +147 -69
- package/src/routes/api/logs.ts +55 -14
- package/src/routes/api/providers.$providerId.model-metadata.ts +22 -4
- package/src/routes/api/providers.$providerId.ts +37 -6
- package/src/routes/api/providers.export.ts +34 -16
- package/src/routes/api/providers.import.ts +42 -8
- package/src/routes/api/providers.scan.ts +13 -8
- package/src/routes/api/providers.ts +36 -5
- package/src/routes/api/runs.ts +12 -12
- package/src/routes/api/sessions.ts +15 -8
- package/src/routes/index.tsx +6 -0
- package/src/routes/livez.ts +13 -0
- package/src/routes/readyz.ts +18 -0
- package/src/routes/session/$sessionId.tsx +6 -0
- package/styles/globals.css +19 -3
- package/.output/public/assets/CompareDrawer-Cb5ccPeX.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-COBe5GTT.js +0 -4234
- package/.output/public/assets/ResponseView-Cqq87lX_.js +0 -3
- package/.output/public/assets/StreamingChunkSequence-CnTa9mCr.js +0 -1
- package/.output/public/assets/index-DTLNVcgc.js +0 -76
- package/.output/public/assets/index-DliqmwUw.css +0 -1
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/server/_tanstack-start-manifest_v-DMCA8KK3.mjs +0 -4
- package/src/assets/logos/mcp.png +0 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/components/ui/mcp-logo.tsx +0 -20
package/README.md
CHANGED
|
@@ -33,11 +33,27 @@ agent-inspector-mcp doctor
|
|
|
33
33
|
|
|
34
34
|
Agent Inspector requires Node.js 22 or newer. Node.js 24 LTS is recommended for production use.
|
|
35
35
|
|
|
36
|
-
By default, Agent Inspector binds to `127.0.0.1
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
`
|
|
40
|
-
|
|
36
|
+
By default, Agent Inspector binds to `127.0.0.1` and local tools need no Inspector credential.
|
|
37
|
+
With `--host 0.0.0.0`, non-loopback coding tools must authenticate `/proxy` with a dedicated
|
|
38
|
+
`AGENT_INSPECTOR_PROXY_TOKEN`, supplied as `Proxy-Authorization: Bearer <token>` or
|
|
39
|
+
`X-Agent-Inspector-Proxy-Token: <token>`. Provider `Authorization` remains separate and is never
|
|
40
|
+
accepted as the Inspector proxy credential. The UI, REST API, and MCP control plane remain
|
|
41
|
+
local-only unless `AGENT_INSPECTOR_CONTROL_TOKEN` is configured for authenticated remote API/MCP
|
|
42
|
+
clients. `AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY=1` and
|
|
43
|
+
`AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL=1` are temporary compatibility overrides intended only for a
|
|
44
|
+
trusted firewall or loopback-only port mapping.
|
|
45
|
+
|
|
46
|
+
The packaged CLI keeps the protected identity ingress enabled even on loopback because it also owns
|
|
47
|
+
Host validation, CSRF enforcement, credential scoping, and first-hop byte budgets. Disabling it with
|
|
48
|
+
`--no-identity-proxy` requires the explicit diagnostic-only
|
|
49
|
+
`AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS=1` override.
|
|
50
|
+
|
|
51
|
+
When exposing Inspector through a named reverse proxy, add its public hostname to the comma-separated
|
|
52
|
+
`AGENT_INSPECTOR_TRUSTED_HOSTS` allowlist. Browser mutations require same-origin CSRF proof; the
|
|
53
|
+
built-in UI supplies it automatically. The public ingress streams request bodies with backpressure
|
|
54
|
+
and defaults to a 64 MiB hard limit and a 120 second body deadline; override these with positive
|
|
55
|
+
integer byte/millisecond values in `AGENT_INSPECTOR_MAX_REQUEST_BYTES` and
|
|
56
|
+
`AGENT_INSPECTOR_REQUEST_TIMEOUT_MS` when a trusted workload requires different bounds.
|
|
41
57
|
|
|
42
58
|
`@tonyclaw/agent-inspector` runs the local UI, proxy, REST API, storage, and built-in
|
|
43
59
|
`/api/mcp` endpoint. `@tonyclaw/agent-inspector-mcp` is the enhancement package for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonyclaw/agent-inspector",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent observability and knowledge capture layer for AI coding tools.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,11 @@
|
|
|
54
54
|
"scripts": {
|
|
55
55
|
"dev": "vite dev",
|
|
56
56
|
"start": "node .output/cli.js",
|
|
57
|
-
"build": "vite build && bun build:cli && bun build:mcp",
|
|
57
|
+
"build": "vite build && bun build:workers && bun build:cli && bun build:mcp && bun check:bundle",
|
|
58
|
+
"build:workers": "npx esbuild src/proxy/logFinalizer.worker.ts src/proxy/sessionWorkerEntry.ts --bundle --platform=node --target=node22 --format=esm --outdir=.output/workers --external:better-sqlite3",
|
|
59
|
+
"smoke:workers": "node scripts/smoke-production-workers.mjs",
|
|
60
|
+
"smoke:packed-workers": "node scripts/smoke-packed-workers.mjs",
|
|
61
|
+
"check:bundle": "node scripts/check-client-bundle.mjs",
|
|
58
62
|
"build:cli": "npx esbuild src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=.output/cli.js",
|
|
59
63
|
"build:mcp": "npx esbuild packages/agent-inspector-mcp/src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=packages/agent-inspector-mcp/dist/cli.js",
|
|
60
64
|
"postinstall": "node scripts/setup-windows-runtime.mjs && node scripts/setup-agent-skills.mjs",
|
|
Binary file
|
package/src/cli.ts
CHANGED
|
@@ -19,7 +19,12 @@ import {
|
|
|
19
19
|
shouldSuppressServerOutputLine,
|
|
20
20
|
type ServerOutputFilterContext,
|
|
21
21
|
} from "./cli/startupOutput.js";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
closeIdentityProxyGracefully,
|
|
24
|
+
forceCloseIdentityProxy,
|
|
25
|
+
startIdentityProxy,
|
|
26
|
+
stopIdentityProxyAdmission,
|
|
27
|
+
} from "./proxy/identityProxy.js";
|
|
23
28
|
import { findPidsByPort, killPid, openUrlCommand } from "./proxy/platformCommands.js";
|
|
24
29
|
|
|
25
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -36,6 +41,8 @@ const DEFAULT_BIND_HOST = "127.0.0.1";
|
|
|
36
41
|
const LOCAL_PROBE_TIMEOUT_MS = 2000;
|
|
37
42
|
const BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
|
|
38
43
|
const DEFAULT_CAPTURE_MODE = "simple";
|
|
44
|
+
const DEFAULT_SHUTDOWN_DEADLINE_MS = 15_000;
|
|
45
|
+
const SUPERVISOR_SHUTDOWN_GRACE_MS = 2_000;
|
|
39
46
|
|
|
40
47
|
type CaptureMode = "simple" | "full";
|
|
41
48
|
|
|
@@ -232,10 +239,20 @@ function printAccessHints(port: number, host?: string): void {
|
|
|
232
239
|
console.log(line);
|
|
233
240
|
}
|
|
234
241
|
if (!isLoopbackHost(host)) {
|
|
242
|
+
const proxyTokenConfigured = (process.env["AGENT_INSPECTOR_PROXY_TOKEN"] ?? "") !== "";
|
|
243
|
+
const legacyProxyEnabled =
|
|
244
|
+
process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1";
|
|
245
|
+
if (proxyTokenConfigured) {
|
|
246
|
+
console.log(" Remote proxy: dedicated token authentication enabled");
|
|
247
|
+
} else if (legacyProxyEnabled) {
|
|
248
|
+
console.warn(" Remote proxy: UNAUTHENTICATED compatibility mode enabled");
|
|
249
|
+
} else {
|
|
250
|
+
console.warn(" Remote proxy: blocked until AGENT_INSPECTOR_PROXY_TOKEN is configured");
|
|
251
|
+
}
|
|
235
252
|
if (process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1") {
|
|
236
253
|
console.warn(" Control plane: REMOTE ACCESS ENABLED by administrator override");
|
|
237
254
|
} else {
|
|
238
|
-
console.log(" Control plane: local only
|
|
255
|
+
console.log(" Control plane: local only by default");
|
|
239
256
|
if ((process.env["AGENT_INSPECTOR_CONTROL_TOKEN"] ?? "") !== "") {
|
|
240
257
|
console.log(" Remote API: Bearer token authentication enabled");
|
|
241
258
|
}
|
|
@@ -243,6 +260,15 @@ function printAccessHints(port: number, host?: string): void {
|
|
|
243
260
|
}
|
|
244
261
|
}
|
|
245
262
|
|
|
263
|
+
function readPositiveIntegerEnv(name: string): number | undefined {
|
|
264
|
+
const raw = process.env[name];
|
|
265
|
+
if (raw === undefined || raw.trim().length === 0) return undefined;
|
|
266
|
+
const parsed = Number(raw);
|
|
267
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
|
|
268
|
+
console.warn(`Ignoring invalid ${name}=${raw}; expected a positive integer.`);
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
246
272
|
type BackgroundSupervisorOptions = {
|
|
247
273
|
port: number;
|
|
248
274
|
host: string | undefined;
|
|
@@ -313,6 +339,15 @@ async function tryStartIdentityProxy(
|
|
|
313
339
|
restrictRemoteControl: !isLoopbackHost(listenHost),
|
|
314
340
|
allowRemoteControl: process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1",
|
|
315
341
|
controlToken: process.env["AGENT_INSPECTOR_CONTROL_TOKEN"],
|
|
342
|
+
proxyToken: process.env["AGENT_INSPECTOR_PROXY_TOKEN"],
|
|
343
|
+
allowUnauthenticatedRemoteProxy:
|
|
344
|
+
process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1",
|
|
345
|
+
trustedHosts: (process.env["AGENT_INSPECTOR_TRUSTED_HOSTS"] ?? "")
|
|
346
|
+
.split(",")
|
|
347
|
+
.map((host) => host.trim())
|
|
348
|
+
.filter((host) => host.length > 0),
|
|
349
|
+
maxRequestBytes: readPositiveIntegerEnv("AGENT_INSPECTOR_MAX_REQUEST_BYTES"),
|
|
350
|
+
requestTimeoutMs: readPositiveIntegerEnv("AGENT_INSPECTOR_REQUEST_TIMEOUT_MS"),
|
|
316
351
|
});
|
|
317
352
|
return server;
|
|
318
353
|
} catch (err) {
|
|
@@ -475,13 +510,9 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
475
510
|
return;
|
|
476
511
|
}
|
|
477
512
|
|
|
478
|
-
if (
|
|
479
|
-
!enableIdentityProxy &&
|
|
480
|
-
!isLoopbackHost(host) &&
|
|
481
|
-
process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] !== "1"
|
|
482
|
-
) {
|
|
513
|
+
if (!enableIdentityProxy && process.env["AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS"] !== "1") {
|
|
483
514
|
console.error(
|
|
484
|
-
"Refusing
|
|
515
|
+
"Refusing --no-identity-proxy because it also disables Host, CSRF, credential-scope, and first-hop budget enforcement. Keep the protected ingress enabled or set AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS=1 explicitly for temporary diagnostics.",
|
|
485
516
|
);
|
|
486
517
|
process.exitCode = 1;
|
|
487
518
|
return;
|
|
@@ -663,6 +694,10 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
663
694
|
serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
|
|
664
695
|
serverEnv["PROXY_PORT"] = String(port);
|
|
665
696
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
697
|
+
const workerDir = join(__dirname, "workers");
|
|
698
|
+
if (existsSync(workerDir)) {
|
|
699
|
+
serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
|
|
700
|
+
}
|
|
666
701
|
const serverProcess = spawn(serverCommand.command, serverCommand.args, {
|
|
667
702
|
stdio: background ? ["ignore", "ignore", "ignore"] : ["ignore", "pipe", "pipe"],
|
|
668
703
|
detached: background,
|
|
@@ -692,6 +727,41 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
692
727
|
port: number;
|
|
693
728
|
server: import("node:http").Server;
|
|
694
729
|
}> = [];
|
|
730
|
+
let supervisorShutdownPromise: Promise<void> | null = null;
|
|
731
|
+
const requestSupervisorShutdown = (signal: "SIGINT" | "SIGTERM"): void => {
|
|
732
|
+
if (supervisorShutdownPromise !== null) return;
|
|
733
|
+
for (const proxy of identityProxies) stopIdentityProxyAdmission(proxy.server);
|
|
734
|
+
serverProcess.kill(signal);
|
|
735
|
+
|
|
736
|
+
const childDeadline =
|
|
737
|
+
readPositiveIntegerEnv("AGENT_INSPECTOR_SHUTDOWN_TIMEOUT_MS") ?? DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
738
|
+
let deadline: ReturnType<typeof setTimeout> | null = null;
|
|
739
|
+
let forced = false;
|
|
740
|
+
const graceful = Promise.all([
|
|
741
|
+
serverExit.then(() => undefined),
|
|
742
|
+
...identityProxies.map(async (proxy) => await closeIdentityProxyGracefully(proxy.server)),
|
|
743
|
+
]).then(() => undefined);
|
|
744
|
+
const timedOut = new Promise<void>((resolve) => {
|
|
745
|
+
deadline = setTimeout(() => {
|
|
746
|
+
forced = true;
|
|
747
|
+
for (const proxy of identityProxies) forceCloseIdentityProxy(proxy.server);
|
|
748
|
+
if (serverProcess.exitCode === null) serverProcess.kill("SIGKILL");
|
|
749
|
+
resolve();
|
|
750
|
+
}, childDeadline + SUPERVISOR_SHUTDOWN_GRACE_MS);
|
|
751
|
+
});
|
|
752
|
+
supervisorShutdownPromise = Promise.race([graceful, timedOut])
|
|
753
|
+
.finally(() => {
|
|
754
|
+
if (deadline !== null) clearTimeout(deadline);
|
|
755
|
+
})
|
|
756
|
+
.then(() => {
|
|
757
|
+
if (forced) process.exitCode = 1;
|
|
758
|
+
});
|
|
759
|
+
};
|
|
760
|
+
const handleSigint = (): void => requestSupervisorShutdown("SIGINT");
|
|
761
|
+
const handleSigterm = (): void => requestSupervisorShutdown("SIGTERM");
|
|
762
|
+
const currentSupervisorShutdown = (): Promise<void> | null => supervisorShutdownPromise;
|
|
763
|
+
process.on("SIGINT", handleSigint);
|
|
764
|
+
process.on("SIGTERM", handleSigterm);
|
|
695
765
|
if (enableIdentityProxy) {
|
|
696
766
|
const primaryProxy = await tryStartIdentityProxy(port, upstreamPort, host, upstreamHost);
|
|
697
767
|
if (primaryProxy !== null) {
|
|
@@ -759,7 +829,12 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
759
829
|
|
|
760
830
|
process.exitCode = await serverExit;
|
|
761
831
|
|
|
762
|
-
|
|
763
|
-
|
|
832
|
+
const activeSupervisorShutdown = currentSupervisorShutdown();
|
|
833
|
+
if (activeSupervisorShutdown !== null) {
|
|
834
|
+
await activeSupervisorShutdown;
|
|
835
|
+
} else {
|
|
836
|
+
await Promise.all(identityProxies.map((proxy) => closeIdentityProxyGracefully(proxy.server)));
|
|
764
837
|
}
|
|
838
|
+
process.off("SIGINT", handleSigint);
|
|
839
|
+
process.off("SIGTERM", handleSigterm);
|
|
765
840
|
}
|
|
@@ -3,28 +3,31 @@ import { useState, type JSX } from "react";
|
|
|
3
3
|
import { useOnboarding } from "../lib/useOnboarding";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Presentational onboarding surface, exported so responsive and accessibility
|
|
7
|
+
* behavior can be verified without runtime configuration state.
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export function OnboardingTip({
|
|
10
|
+
expanded,
|
|
11
|
+
onToggle,
|
|
12
|
+
onDismiss,
|
|
13
|
+
}: {
|
|
14
|
+
expanded: boolean;
|
|
15
|
+
onToggle: () => void;
|
|
16
|
+
onDismiss: () => void;
|
|
17
|
+
}): JSX.Element {
|
|
15
18
|
const detailClass = expanded ? "grid" : "hidden sm:grid";
|
|
16
19
|
|
|
17
20
|
return (
|
|
18
21
|
<div
|
|
19
22
|
role="note"
|
|
20
23
|
aria-label="Onboarding tip"
|
|
21
|
-
className="mx-
|
|
24
|
+
className="mx-3 mt-2 mb-1 rounded-md border border-warning/30 bg-warning/10 px-3 py-2 text-sm shadow-sm sm:mx-4"
|
|
22
25
|
>
|
|
23
26
|
<div className="flex items-start gap-3">
|
|
24
|
-
<Info className="mt-0.5 size-4 shrink-0 text-
|
|
27
|
+
<Info className="mt-0.5 size-4 shrink-0 text-warning" aria-hidden="true" />
|
|
25
28
|
<div className="min-w-0 flex-1">
|
|
26
29
|
<div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
27
|
-
<span className="font-medium text-
|
|
30
|
+
<span className="font-medium text-foreground">Inspector ready</span>
|
|
28
31
|
<span className="hidden truncate text-xs text-muted-foreground sm:inline">
|
|
29
32
|
Request, response, tool, timing, token, replay, and memory signals are active.
|
|
30
33
|
</span>
|
|
@@ -45,8 +48,8 @@ export function OnboardingBanner(): JSX.Element | null {
|
|
|
45
48
|
</div>
|
|
46
49
|
<button
|
|
47
50
|
type="button"
|
|
48
|
-
onClick={
|
|
49
|
-
className="inline-flex h-8 shrink-0 items-center gap-1 rounded-md border border-
|
|
51
|
+
onClick={onToggle}
|
|
52
|
+
className="inline-flex h-8 shrink-0 items-center gap-1 rounded-md border border-warning/35 px-2 text-xs font-medium text-foreground transition-colors hover:bg-warning/15 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none sm:hidden"
|
|
50
53
|
aria-expanded={expanded}
|
|
51
54
|
aria-label={expanded ? "Hide onboarding details" : "Show onboarding details"}
|
|
52
55
|
>
|
|
@@ -56,10 +59,8 @@ export function OnboardingBanner(): JSX.Element | null {
|
|
|
56
59
|
</button>
|
|
57
60
|
<button
|
|
58
61
|
type="button"
|
|
59
|
-
onClick={
|
|
60
|
-
|
|
61
|
-
}}
|
|
62
|
-
className="hidden h-8 shrink-0 items-center gap-1.5 rounded-md border border-amber-500/40 px-3 text-xs text-amber-200 transition-colors hover:bg-amber-500/10 sm:inline-flex"
|
|
62
|
+
onClick={onDismiss}
|
|
63
|
+
className="hidden h-8 shrink-0 items-center gap-1.5 rounded-md border border-warning/40 px-3 text-xs font-medium text-foreground transition-colors hover:bg-warning/15 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none sm:inline-flex"
|
|
63
64
|
aria-label="Dismiss onboarding tip"
|
|
64
65
|
>
|
|
65
66
|
<Check className="size-3.5" />
|
|
@@ -67,10 +68,8 @@ export function OnboardingBanner(): JSX.Element | null {
|
|
|
67
68
|
</button>
|
|
68
69
|
<button
|
|
69
70
|
type="button"
|
|
70
|
-
onClick={
|
|
71
|
-
|
|
72
|
-
}}
|
|
73
|
-
className="-m-1 shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
71
|
+
onClick={onDismiss}
|
|
72
|
+
className="-m-1 shrink-0 rounded-sm p-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
|
74
73
|
aria-label="Dismiss"
|
|
75
74
|
>
|
|
76
75
|
<X className="size-3.5" />
|
|
@@ -79,3 +78,24 @@ export function OnboardingBanner(): JSX.Element | null {
|
|
|
79
78
|
</div>
|
|
80
79
|
);
|
|
81
80
|
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* First-launch onboarding banner. Shows once on a fresh install and persists
|
|
84
|
+
* dismissal through the server runtime config.
|
|
85
|
+
*/
|
|
86
|
+
export function OnboardingBanner(): JSX.Element | null {
|
|
87
|
+
const { hasSeenOnboarding, isLoading, markSeen } = useOnboarding();
|
|
88
|
+
const [expanded, setExpanded] = useState(false);
|
|
89
|
+
|
|
90
|
+
if (isLoading || hasSeenOnboarding) return null;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<OnboardingTip
|
|
94
|
+
expanded={expanded}
|
|
95
|
+
onToggle={() => setExpanded((value) => !value)}
|
|
96
|
+
onDismiss={() => {
|
|
97
|
+
void markSeen();
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|