@tonyclaw/agent-inspector 3.0.47 → 3.1.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/.output/backend/nitro.json +1 -1
- package/.output/cli.js +1855 -1758
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-BwZEsYfS.mjs → router-CPZp1dgy.mjs} +304 -2
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-V-8lPASC.js → CompareDrawer-CkABWY5h.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D4CVnfdf.js → InspectorPet-BvsFl3jv.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-CO3ZVHLX.js → ProxyViewerContainer-D6O6tY5h.js} +23 -23
- package/.output/ui/assets/{ReplayDialog-CpGyTLtt.js → ReplayDialog-uJnW9FgF.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-BLuJL9dZ.js → RequestAnatomy-CJ1IFwXf.js} +1 -1
- package/.output/ui/assets/{ResponseView-BuSC-CH7.js → ResponseView-Da8fKfh6.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DeA-JJKa.js → StreamingChunkSequence-zIZ1fbEj.js} +1 -1
- package/.output/ui/assets/{_sessionId-Chp67th6.js → _sessionId-CY9mZjlC.js} +1 -1
- package/.output/ui/assets/{_sessionId-8HuHFhjo.js → _sessionId-WYeJ04Ke.js} +1 -1
- package/.output/ui/assets/{index-atoQrlb8.js → index-Bzm_5L4l.js} +1 -1
- package/.output/ui/assets/{index-nY3E1vxV.js → index-C2AvphEC.js} +2 -2
- package/.output/ui/assets/{index-DPmKK_5d.js → index-CwOAPU17.js} +1 -1
- package/.output/ui/assets/{index-nl9-dpZn.js → index-DWI-2j8i.js} +1 -1
- package/.output/ui/assets/{json-viewer-B9kIzqeX.js → json-viewer-C9EpwpoT.js} +1 -1
- package/.output/ui/assets/{jszip.min-B7tfyE-I.js → jszip.min-CEk40IyV.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/README.md +46 -0
- package/package.json +4 -4
- package/src/cli/instance.ts +12 -1
- package/src/cli/instanceArgs.ts +1 -1
- package/src/cli/rustBackendPackage.ts +8 -0
- package/src/cli.ts +108 -9
- package/src/components/ProxyViewer.tsx +4 -13
- package/src/components/proxy-viewer/proxyViewerLogic.ts +9 -0
package/.output/cli.js
CHANGED
|
@@ -14,6 +14,206 @@ var __export = (target, all) => {
|
|
|
14
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// package.json
|
|
18
|
+
var package_default;
|
|
19
|
+
var init_package = __esm({
|
|
20
|
+
"package.json"() {
|
|
21
|
+
package_default = {
|
|
22
|
+
name: "@tonyclaw/agent-inspector",
|
|
23
|
+
version: "3.1.0",
|
|
24
|
+
type: "module",
|
|
25
|
+
description: "Agent observability and knowledge capture layer for AI coding tools.",
|
|
26
|
+
license: "MIT",
|
|
27
|
+
engines: {
|
|
28
|
+
node: ">=22.0.0"
|
|
29
|
+
},
|
|
30
|
+
author: "TonyClaw",
|
|
31
|
+
repository: {
|
|
32
|
+
type: "git",
|
|
33
|
+
url: "git+https://gitcode.com/TonyClaw/agent-inspector.git"
|
|
34
|
+
},
|
|
35
|
+
homepage: "https://gitcode.com/TonyClaw/agent-inspector#readme",
|
|
36
|
+
workspaces: [
|
|
37
|
+
"packages/*"
|
|
38
|
+
],
|
|
39
|
+
bugs: {
|
|
40
|
+
url: "https://gitcode.com/TonyClaw/agent-inspector/issues"
|
|
41
|
+
},
|
|
42
|
+
publishConfig: {
|
|
43
|
+
access: "public",
|
|
44
|
+
registry: "https://registry.npmjs.org/"
|
|
45
|
+
},
|
|
46
|
+
keywords: [
|
|
47
|
+
"claude",
|
|
48
|
+
"anthropic",
|
|
49
|
+
"openai",
|
|
50
|
+
"proxy",
|
|
51
|
+
"api-inspector",
|
|
52
|
+
"agent-observability",
|
|
53
|
+
"agent-memory",
|
|
54
|
+
"debugging",
|
|
55
|
+
"llm",
|
|
56
|
+
"ai-coding-tools"
|
|
57
|
+
],
|
|
58
|
+
bin: {
|
|
59
|
+
"agent-inspector": "bin/agent-inspector.js"
|
|
60
|
+
},
|
|
61
|
+
files: [
|
|
62
|
+
"bin",
|
|
63
|
+
"src",
|
|
64
|
+
"!src/**/*.test.ts",
|
|
65
|
+
"!src/**/*.stories.tsx",
|
|
66
|
+
"!src/**/__fixtures__",
|
|
67
|
+
"!src/ui/routeTree.gen.ts",
|
|
68
|
+
"!src/backend/routeTree.gen.ts",
|
|
69
|
+
"scripts/setup-windows-runtime.mjs",
|
|
70
|
+
"scripts/setup-agent-skills.mjs",
|
|
71
|
+
"scripts/generate-theme-scales.mjs",
|
|
72
|
+
"docs/instance-control.openapi.yaml",
|
|
73
|
+
"styles",
|
|
74
|
+
".output",
|
|
75
|
+
"!.output/*.exe"
|
|
76
|
+
],
|
|
77
|
+
scripts: {
|
|
78
|
+
dev: "node scripts/dev-decoupled.mjs",
|
|
79
|
+
"dev:ui": "vite dev --config vite.ui.config.ts",
|
|
80
|
+
"dev:backend": "node scripts/dev-backend.mjs",
|
|
81
|
+
"test:smoke": "bun scripts/run-test-suite.mjs smoke",
|
|
82
|
+
"test:fast": "bun scripts/run-test-suite.mjs smoke",
|
|
83
|
+
"test:unit": "bun scripts/run-test-suite.mjs unit",
|
|
84
|
+
"test:integration": "bun scripts/run-test-suite.mjs integration",
|
|
85
|
+
"test:changed": "bun scripts/run-test-suite.mjs changed",
|
|
86
|
+
"test:all": "bun scripts/run-test-suite.mjs all",
|
|
87
|
+
"test:compatibility-tooling": "node --test scripts/compatibility/*.test.mjs",
|
|
88
|
+
start: "node .output/cli.js",
|
|
89
|
+
"start:backend": "node .output/cli.js --backend-only --no-open",
|
|
90
|
+
"preview:ui": "vite preview --config vite.ui.config.ts",
|
|
91
|
+
"clean:output": "node scripts/clean-output.mjs",
|
|
92
|
+
build: "bun clean:output && bun build:backend && bun build:ui && bun smoke:ui-preview && bun build:workers && bun build:cli && bun build:mcp && bun check:bundle && bun check:boundaries",
|
|
93
|
+
"build:backend": "vite build --config vite.backend.config.ts",
|
|
94
|
+
"build:ui": "vite build --config vite.ui.config.ts",
|
|
95
|
+
"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",
|
|
96
|
+
"smoke:workers": "node scripts/smoke-production-workers.mjs",
|
|
97
|
+
"smoke:packed-workers": "node scripts/smoke-packed-workers.mjs",
|
|
98
|
+
"smoke:instances": "node scripts/smoke-windows-instances.mjs",
|
|
99
|
+
"smoke:coding-agent-instances": "node scripts/smoke-coding-agent-instances.mjs",
|
|
100
|
+
"smoke:ui-preview": "node scripts/smoke-decoupled-ui-preview.mjs",
|
|
101
|
+
"check:bundle": "node scripts/check-client-bundle.mjs",
|
|
102
|
+
"check:boundaries": "node scripts/check-runtime-boundaries.mjs",
|
|
103
|
+
"compatibility:bdd": "node scripts/lint-bdd-catalog.mjs",
|
|
104
|
+
"compatibility:compare": "node scripts/compatibility/cli.mjs compare",
|
|
105
|
+
"compatibility:drift": "node scripts/check-backend-compatibility.mjs",
|
|
106
|
+
"compatibility:fixtures": "bun scripts/validate-compatibility-fixtures.ts",
|
|
107
|
+
"compatibility:freeze": "node scripts/freeze-backend-baseline.mjs",
|
|
108
|
+
"compatibility:run": "node scripts/compatibility/cli.mjs run",
|
|
109
|
+
"compatibility:performance": "node scripts/compatibility/performance-cli.mjs",
|
|
110
|
+
"compatibility:performance:verify": "node scripts/compatibility/performance-cli.mjs verify",
|
|
111
|
+
"build:cli": "npx esbuild src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=.output/cli.js",
|
|
112
|
+
"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",
|
|
113
|
+
"rust:build-package": "node scripts/rust/build-platform.mjs",
|
|
114
|
+
"rust:stage-package": "node scripts/rust/stage-platform.mjs",
|
|
115
|
+
"rust:verify-package": "node scripts/rust/verify-platform.mjs",
|
|
116
|
+
"rust:pack-package": "node scripts/rust/pack-platform.mjs",
|
|
117
|
+
"rust:native-artifact": "node scripts/rust/create-native-artifact.mjs",
|
|
118
|
+
"rust:native:aggregate": "node scripts/rust/aggregate-native-artifacts.mjs",
|
|
119
|
+
"test:rust-packaging": "bun test scripts/rust/npmPackaging.test.ts scripts/rust/nativeReleaseContract.test.ts scripts/rust/nativeStartupSmoke.test.ts scripts/releaseCore.test.ts",
|
|
120
|
+
"rust:packages:check": "node scripts/rust/sync-package-versions.mjs",
|
|
121
|
+
"rust:packages:sync": "node scripts/rust/sync-package-versions.mjs --write",
|
|
122
|
+
"themes:generate": "node scripts/generate-theme-scales.mjs",
|
|
123
|
+
postinstall: "node scripts/setup-windows-runtime.mjs && node scripts/setup-agent-skills.mjs",
|
|
124
|
+
prepublishOnly: "node scripts/rust/require-release-orchestrator.mjs",
|
|
125
|
+
typecheck: "tsc --noEmit --incremental --tsBuildInfoFile .cache/tsconfig.tsbuildinfo",
|
|
126
|
+
lint: "eslint . --cache --cache-location .cache/eslintcache",
|
|
127
|
+
format: "biome format --write .",
|
|
128
|
+
"format:check": "biome format .",
|
|
129
|
+
"verify:fast": "bun scripts/run-static-checks.mjs --format && bun test:changed",
|
|
130
|
+
"extension:zip": "node scripts/pack-chrome-extension.mjs",
|
|
131
|
+
"extension:release": "node scripts/release-chrome-extension.mjs",
|
|
132
|
+
release: "node scripts/release.mjs",
|
|
133
|
+
knip: "knip",
|
|
134
|
+
check: "bun scripts/run-static-checks.mjs --format && bun check:boundaries && bun compatibility:bdd && bun rust:packages:check",
|
|
135
|
+
prepare: "husky",
|
|
136
|
+
ladle: "ladle serve",
|
|
137
|
+
"ladle:build": "ladle build"
|
|
138
|
+
},
|
|
139
|
+
dependencies: {
|
|
140
|
+
"@mariozechner/pi-agent-core": "^0.73.1",
|
|
141
|
+
"@mariozechner/pi-ai": "^0.73.1",
|
|
142
|
+
"@modelcontextprotocol/server": "^2.0.0-beta.4",
|
|
143
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
144
|
+
"@tanstack/react-router": "^1.170.18",
|
|
145
|
+
"@tanstack/react-start": "^1.168.28",
|
|
146
|
+
"@tanstack/react-virtual": "^3.14.6",
|
|
147
|
+
"class-variance-authority": "^0.7.1",
|
|
148
|
+
clsx: "^2.1.1",
|
|
149
|
+
conf: "^15.1.0",
|
|
150
|
+
diff: "^9.0.0",
|
|
151
|
+
jszip: "^3.10.1",
|
|
152
|
+
"lucide-react": "^0.563.0",
|
|
153
|
+
"radix-ui": "^1.6.2",
|
|
154
|
+
react: "^19.2.7",
|
|
155
|
+
"react-dom": "^19.2.7",
|
|
156
|
+
"react-markdown": "^10.1.0",
|
|
157
|
+
swr: "^2.4.2",
|
|
158
|
+
"tailwind-merge": "^3.6.0",
|
|
159
|
+
three: "0.185.1",
|
|
160
|
+
"tw-animate-css": "^1.4.0",
|
|
161
|
+
zod: "^4.4.3"
|
|
162
|
+
},
|
|
163
|
+
optionalDependencies: {
|
|
164
|
+
"@tonyclaw/agent-inspector-backend-darwin-arm64": "3.1.0",
|
|
165
|
+
"@tonyclaw/agent-inspector-backend-linux-x64-gnu": "3.1.0",
|
|
166
|
+
"@tonyclaw/agent-inspector-backend-win32-x64-msvc": "3.1.0",
|
|
167
|
+
"better-sqlite3": "^12.11.1",
|
|
168
|
+
rcedit: "4.0.1"
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
"@biomejs/biome": "^2.5.3",
|
|
172
|
+
"@eslint/js": "^10.0.1",
|
|
173
|
+
"@ladle/react": "^5.1.1",
|
|
174
|
+
"@radix-ui/colors": "^3.0.0",
|
|
175
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
176
|
+
"@tanstack/router-plugin": "^1.168.20",
|
|
177
|
+
"@types/bun": "^1.3.14",
|
|
178
|
+
"@types/react": "^19.2.17",
|
|
179
|
+
"@types/react-dom": "^19.2.3",
|
|
180
|
+
"@types/three": "0.185.1",
|
|
181
|
+
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
182
|
+
"@typescript-eslint/parser": "^8.64.0",
|
|
183
|
+
"@vitejs/plugin-react": "^5.2.0",
|
|
184
|
+
ajv: "8.20.0",
|
|
185
|
+
"ajv-formats": "3.0.1",
|
|
186
|
+
"colorjs.io": "^0.7.0",
|
|
187
|
+
esbuild: "^0.28.1",
|
|
188
|
+
eslint: "^9.39.5",
|
|
189
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
190
|
+
"eslint-plugin-functional": "^9.0.5",
|
|
191
|
+
"eslint-plugin-unicorn": "^63.0.0",
|
|
192
|
+
husky: "^9.1.7",
|
|
193
|
+
knip: "^5.88.1",
|
|
194
|
+
nitro: "^3.0.260610-beta",
|
|
195
|
+
playwright: "^1.61.1",
|
|
196
|
+
tailwindcss: "^4.3.2",
|
|
197
|
+
typescript: "^5.9.3",
|
|
198
|
+
vite: "^7.3.6",
|
|
199
|
+
"vite-tsconfig-paths": "^6.1.1"
|
|
200
|
+
},
|
|
201
|
+
overrides: {
|
|
202
|
+
undici: ">=7.24.0 <8.0.0",
|
|
203
|
+
rollup: ">=4.59.0 <5.0.0",
|
|
204
|
+
postcss: ">=8.5.10 <9.0.0",
|
|
205
|
+
"js-yaml": ">=4.1.2 <5.0.0",
|
|
206
|
+
flatted: ">=3.4.0 <4.0.0",
|
|
207
|
+
"smol-toml": ">=1.6.1 <2.0.0",
|
|
208
|
+
koa: ">=2.16.4 <3.0.0",
|
|
209
|
+
"@babel/core": ">=7.29.1 <8.0.0",
|
|
210
|
+
"@typescript-eslint/typescript-estree": "^8.63.0",
|
|
211
|
+
vite: "^7.3.6"
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
17
217
|
// node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
18
218
|
// @__NO_SIDE_EFFECTS__
|
|
19
219
|
function $constructor(name, initializer3, params) {
|
|
@@ -15374,555 +15574,868 @@ var init_networkHints = __esm({
|
|
|
15374
15574
|
}
|
|
15375
15575
|
});
|
|
15376
15576
|
|
|
15377
|
-
// src/
|
|
15378
|
-
|
|
15379
|
-
|
|
15380
|
-
|
|
15381
|
-
}
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
case "lock_timeout":
|
|
15387
|
-
case "port_exhausted":
|
|
15388
|
-
case "registry_io":
|
|
15389
|
-
case "restart_failed_degraded":
|
|
15390
|
-
case "restart_failed_rolled_back":
|
|
15391
|
-
case "shutdown_failed":
|
|
15392
|
-
return true;
|
|
15393
|
-
case "argument_invalid":
|
|
15394
|
-
case "config_mismatch":
|
|
15395
|
-
case "data_dir_conflict":
|
|
15396
|
-
case "data_dir_invalid":
|
|
15397
|
-
case "identity_mismatch":
|
|
15398
|
-
case "instance_not_found":
|
|
15399
|
-
case "lock_not_owned":
|
|
15400
|
-
case "name_invalid":
|
|
15401
|
-
case "port_conflict":
|
|
15402
|
-
case "port_invalid":
|
|
15403
|
-
case "record_invalid":
|
|
15404
|
-
case "self_lifecycle_unsupported":
|
|
15405
|
-
return false;
|
|
15406
|
-
}
|
|
15577
|
+
// src/cli/rustBackendPackage.ts
|
|
15578
|
+
import { spawnSync } from "node:child_process";
|
|
15579
|
+
import { createHash } from "node:crypto";
|
|
15580
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
15581
|
+
import { createRequire } from "node:module";
|
|
15582
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
15583
|
+
import { fileURLToPath } from "node:url";
|
|
15584
|
+
function packageFailure(code, message) {
|
|
15585
|
+
return { kind: "error", error: { code, message } };
|
|
15407
15586
|
}
|
|
15408
|
-
function
|
|
15409
|
-
return {
|
|
15587
|
+
function targetFailure(message) {
|
|
15588
|
+
return { kind: "error", error: { code: "unsupported_platform", message } };
|
|
15410
15589
|
}
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
INSTANCE_API_SCHEMA_VERSION = 1;
|
|
15419
|
-
InstanceStateSchema = external_exports.enum(["running", "stopped", "conflict"]);
|
|
15420
|
-
InstanceStateReasonSchema = external_exports.enum(["identity_mismatch", "port_conflict"]);
|
|
15421
|
-
InstanceUrlsSchema = external_exports.object({
|
|
15422
|
-
ui: external_exports.url(),
|
|
15423
|
-
api: external_exports.url(),
|
|
15424
|
-
health: external_exports.url(),
|
|
15425
|
-
proxy: external_exports.url(),
|
|
15426
|
-
openai: external_exports.url(),
|
|
15427
|
-
mcp: external_exports.url()
|
|
15428
|
-
}).strict();
|
|
15429
|
-
InstanceStatusOutputSchema = external_exports.object({
|
|
15430
|
-
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15431
|
-
name: external_exports.string().min(1),
|
|
15432
|
-
instanceId: external_exports.string().min(1),
|
|
15433
|
-
state: InstanceStateSchema,
|
|
15434
|
-
stateReason: InstanceStateReasonSchema.nullable(),
|
|
15435
|
-
checkedAt: external_exports.iso.datetime(),
|
|
15436
|
-
createdAt: external_exports.iso.datetime(),
|
|
15437
|
-
updatedAt: external_exports.iso.datetime(),
|
|
15438
|
-
launch: external_exports.object({
|
|
15439
|
-
launchId: external_exports.string().min(1),
|
|
15440
|
-
startedAt: external_exports.iso.datetime()
|
|
15441
|
-
}).strict(),
|
|
15442
|
-
host: external_exports.string().min(1),
|
|
15443
|
-
publicPort: external_exports.number().int().min(1).max(65535),
|
|
15444
|
-
upstreamPort: external_exports.number().int().min(1).max(65535),
|
|
15445
|
-
basePath: external_exports.string().refine(isCanonicalPublicBasePath, "Invalid public Base Path"),
|
|
15446
|
-
captureMode: external_exports.enum(["simple", "full"]),
|
|
15447
|
-
backend: BackendImplementationSchema.default("typescript"),
|
|
15448
|
-
uiEnabled: external_exports.boolean(),
|
|
15449
|
-
dataDir: external_exports.string().min(1),
|
|
15450
|
-
supervisorPid: external_exports.number().int().positive().nullable(),
|
|
15451
|
-
urls: InstanceUrlsSchema
|
|
15452
|
-
}).strict();
|
|
15453
|
-
InstanceConnectionSurfacesSchema = external_exports.object({
|
|
15454
|
-
ui: external_exports.object({
|
|
15455
|
-
enabled: external_exports.boolean(),
|
|
15456
|
-
baseUrl: external_exports.url().nullable()
|
|
15457
|
-
}).strict(),
|
|
15458
|
-
control: external_exports.object({ baseUrl: external_exports.url() }).strict(),
|
|
15459
|
-
agent: external_exports.object({ baseUrl: external_exports.url() }).strict()
|
|
15460
|
-
}).strict();
|
|
15461
|
-
InstanceConnectionOutputSchema = external_exports.object({
|
|
15462
|
-
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15463
|
-
uiEnabled: external_exports.boolean(),
|
|
15464
|
-
instance: InstanceStatusOutputSchema,
|
|
15465
|
-
surfaces: InstanceConnectionSurfacesSchema,
|
|
15466
|
-
/** @deprecated Use surfaces.control.baseUrl or surfaces.agent.baseUrl. */
|
|
15467
|
-
baseUrl: external_exports.url(),
|
|
15468
|
-
endpoints: InstanceUrlsSchema,
|
|
15469
|
-
environment: external_exports.object({
|
|
15470
|
-
ANTHROPIC_BASE_URL: external_exports.url(),
|
|
15471
|
-
LLM_BASE_URL: external_exports.url(),
|
|
15472
|
-
OPENAI_BASE_URL: external_exports.url(),
|
|
15473
|
-
AGENT_INSPECTOR_MCP_URL: external_exports.url()
|
|
15474
|
-
}).strict(),
|
|
15475
|
-
mcp: external_exports.object({
|
|
15476
|
-
serverName: external_exports.string().min(1),
|
|
15477
|
-
transport: external_exports.literal("streamable-http"),
|
|
15478
|
-
url: external_exports.url()
|
|
15479
|
-
}).strict(),
|
|
15480
|
-
codex: external_exports.object({
|
|
15481
|
-
providerName: external_exports.string().min(1),
|
|
15482
|
-
baseUrl: external_exports.url(),
|
|
15483
|
-
wireApi: external_exports.literal("responses")
|
|
15484
|
-
}).strict()
|
|
15485
|
-
}).strict();
|
|
15486
|
-
InstanceStartRequestSchema = external_exports.object({
|
|
15487
|
-
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
15488
|
-
basePath: external_exports.string().min(1).max(2048).refine(
|
|
15489
|
-
(value) => normalizePublicBasePath(value) !== null,
|
|
15490
|
-
"Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters"
|
|
15491
|
-
).optional(),
|
|
15492
|
-
host: external_exports.string().min(1).max(253).refine(
|
|
15493
|
-
(value) => value.trim() === value && !value.includes("://") && !value.includes("/") && !value.includes("\\") && !/\s/u.test(value),
|
|
15494
|
-
"Host must be a hostname or IP address without protocol, path, or whitespace"
|
|
15495
|
-
).optional(),
|
|
15496
|
-
captureMode: external_exports.enum(["simple", "full"]).optional(),
|
|
15497
|
-
backend: BackendImplementationSchema.optional(),
|
|
15498
|
-
uiEnabled: external_exports.boolean().optional(),
|
|
15499
|
-
dataDir: external_exports.string().trim().min(1).max(32767).optional()
|
|
15500
|
-
}).strict();
|
|
15501
|
-
InstanceEmptyRequestSchema = external_exports.object({}).strict();
|
|
15502
|
-
InstanceOperationSchema = external_exports.enum(["start", "stop", "restart"]);
|
|
15503
|
-
InstanceOperationOutcomeSchema = external_exports.enum([
|
|
15504
|
-
"created",
|
|
15505
|
-
"started",
|
|
15506
|
-
"already_running",
|
|
15507
|
-
"stopped",
|
|
15508
|
-
"already_stopped",
|
|
15509
|
-
"restarted"
|
|
15510
|
-
]);
|
|
15511
|
-
InstanceOperationOutputSchema = external_exports.object({
|
|
15512
|
-
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15513
|
-
operation: InstanceOperationSchema,
|
|
15514
|
-
outcome: InstanceOperationOutcomeSchema,
|
|
15515
|
-
instance: InstanceStatusOutputSchema
|
|
15516
|
-
}).strict();
|
|
15517
|
-
InstanceListOutputSchema = external_exports.object({
|
|
15518
|
-
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15519
|
-
instances: external_exports.array(InstanceStatusOutputSchema)
|
|
15520
|
-
}).strict();
|
|
15521
|
-
InstancePublicErrorCodeSchema = external_exports.enum([
|
|
15522
|
-
"argument_invalid",
|
|
15523
|
-
"config_mismatch",
|
|
15524
|
-
"data_dir_conflict",
|
|
15525
|
-
"data_dir_invalid",
|
|
15526
|
-
"identity_mismatch",
|
|
15527
|
-
"instance_not_found",
|
|
15528
|
-
"launch_failed",
|
|
15529
|
-
"launch_unavailable",
|
|
15530
|
-
"lock_not_owned",
|
|
15531
|
-
"lock_timeout",
|
|
15532
|
-
"name_invalid",
|
|
15533
|
-
"port_conflict",
|
|
15534
|
-
"port_exhausted",
|
|
15535
|
-
"port_invalid",
|
|
15536
|
-
"record_invalid",
|
|
15537
|
-
"registry_io",
|
|
15538
|
-
"restart_failed_degraded",
|
|
15539
|
-
"restart_failed_rolled_back",
|
|
15540
|
-
"self_lifecycle_unsupported",
|
|
15541
|
-
"shutdown_failed"
|
|
15542
|
-
]);
|
|
15543
|
-
InstancePublicErrorSchema = external_exports.object({
|
|
15544
|
-
code: InstancePublicErrorCodeSchema,
|
|
15545
|
-
message: external_exports.string().min(1),
|
|
15546
|
-
retryable: external_exports.boolean()
|
|
15547
|
-
}).strict();
|
|
15548
|
-
InstanceErrorOutputSchema = external_exports.object({
|
|
15549
|
-
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15550
|
-
error: InstancePublicErrorSchema
|
|
15551
|
-
}).strict();
|
|
15590
|
+
function detectLinuxLibc() {
|
|
15591
|
+
if (process.platform !== "linux") return "not-applicable";
|
|
15592
|
+
const report = process.report.getReport();
|
|
15593
|
+
const header = objectProperty(report, "header");
|
|
15594
|
+
const glibcVersion = objectProperty(header, "glibcVersionRuntime");
|
|
15595
|
+
if (typeof glibcVersion === "string" && glibcVersion.length > 0) {
|
|
15596
|
+
return "glibc";
|
|
15552
15597
|
}
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
// src/proxy/privateDataPath.ts
|
|
15556
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
15557
|
-
import {
|
|
15558
|
-
closeSync,
|
|
15559
|
-
constants,
|
|
15560
|
-
fchmodSync,
|
|
15561
|
-
fstatSync,
|
|
15562
|
-
lstatSync,
|
|
15563
|
-
mkdirSync,
|
|
15564
|
-
openSync,
|
|
15565
|
-
readdirSync,
|
|
15566
|
-
realpathSync
|
|
15567
|
-
} from "node:fs";
|
|
15568
|
-
import { join, parse as parse3, resolve as resolve2, sep } from "node:path";
|
|
15569
|
-
function windowsSystemPath(executable) {
|
|
15570
|
-
const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
|
|
15571
|
-
return `${systemRoot}\\System32\\${executable}`;
|
|
15572
|
-
}
|
|
15573
|
-
function runWindowsCommand(executable, args) {
|
|
15574
|
-
const resolved = process.platform === "win32" && /^(whoami|icacls)\.exe$/iu.test(executable) ? windowsSystemPath(executable) : executable;
|
|
15575
|
-
const result = spawnSync2(resolved, [...args], {
|
|
15576
|
-
encoding: "utf8",
|
|
15577
|
-
shell: false,
|
|
15578
|
-
windowsHide: true
|
|
15579
|
-
});
|
|
15580
|
-
return { status: result.status, stdout: result.stdout };
|
|
15581
|
-
}
|
|
15582
|
-
function windowsUserSid(output) {
|
|
15583
|
-
const match = /S-\d+(?:-\d+)+/.exec(output);
|
|
15584
|
-
return match?.[0] ?? null;
|
|
15585
|
-
}
|
|
15586
|
-
function resolveWindowsUserSid(run) {
|
|
15587
|
-
if (run === runWindowsCommand && cachedWindowsSid !== null) return cachedWindowsSid;
|
|
15588
|
-
const identity = run("whoami.exe", ["/user", "/fo", "csv", "/nh"]);
|
|
15589
|
-
if (identity.status !== 0) return null;
|
|
15590
|
-
const sid = windowsUserSid(identity.stdout);
|
|
15591
|
-
if (run === runWindowsCommand) cachedWindowsSid = sid;
|
|
15592
|
-
return sid;
|
|
15598
|
+
const sharedObjects = objectProperty(report, "sharedObjects");
|
|
15599
|
+
return Array.isArray(sharedObjects) && sharedObjects.some((path2) => typeof path2 === "string" && path2.toLowerCase().includes("musl")) ? "musl" : "unknown";
|
|
15593
15600
|
}
|
|
15594
|
-
function
|
|
15595
|
-
|
|
15601
|
+
function objectProperty(value, name) {
|
|
15602
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
15603
|
+
return Object.getOwnPropertyDescriptor(value, name)?.value;
|
|
15596
15604
|
}
|
|
15597
|
-
function
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15605
|
+
function resolveRustBackendTarget(platform = process.platform, arch = process.arch, libc = platform === "linux" ? detectLinuxLibc() : "not-applicable") {
|
|
15606
|
+
switch (platform) {
|
|
15607
|
+
case "win32":
|
|
15608
|
+
return arch === "x64" ? {
|
|
15609
|
+
kind: "success",
|
|
15610
|
+
value: {
|
|
15611
|
+
packageName: "@tonyclaw/agent-inspector-backend-win32-x64-msvc",
|
|
15612
|
+
target: "x86_64-pc-windows-msvc",
|
|
15613
|
+
binary: "bin/agent-inspector-backend.exe",
|
|
15614
|
+
os: "win32",
|
|
15615
|
+
cpu: "x64",
|
|
15616
|
+
libc: null
|
|
15617
|
+
}
|
|
15618
|
+
} : targetFailure(`Rust backend packages do not support Windows ${arch}.`);
|
|
15619
|
+
case "linux":
|
|
15620
|
+
if (arch !== "x64")
|
|
15621
|
+
return targetFailure(`Rust backend packages do not support Linux ${arch}.`);
|
|
15622
|
+
if (libc !== "glibc") {
|
|
15623
|
+
return targetFailure(
|
|
15624
|
+
`The Linux Rust backend package requires glibc; detected ${libc}. No binary was started.`
|
|
15625
|
+
);
|
|
15626
|
+
}
|
|
15627
|
+
return {
|
|
15628
|
+
kind: "success",
|
|
15629
|
+
value: {
|
|
15630
|
+
packageName: "@tonyclaw/agent-inspector-backend-linux-x64-gnu",
|
|
15631
|
+
target: "x86_64-unknown-linux-gnu",
|
|
15632
|
+
binary: "bin/agent-inspector-backend",
|
|
15633
|
+
os: "linux",
|
|
15634
|
+
cpu: "x64",
|
|
15635
|
+
libc: "glibc"
|
|
15636
|
+
}
|
|
15637
|
+
};
|
|
15638
|
+
case "darwin":
|
|
15639
|
+
return arch === "arm64" ? {
|
|
15640
|
+
kind: "success",
|
|
15641
|
+
value: {
|
|
15642
|
+
packageName: "@tonyclaw/agent-inspector-backend-darwin-arm64",
|
|
15643
|
+
target: "aarch64-apple-darwin",
|
|
15644
|
+
binary: "bin/agent-inspector-backend",
|
|
15645
|
+
os: "darwin",
|
|
15646
|
+
cpu: "arm64",
|
|
15647
|
+
libc: null
|
|
15648
|
+
}
|
|
15649
|
+
} : targetFailure(`Rust backend packages do not support macOS ${arch}.`);
|
|
15650
|
+
default:
|
|
15651
|
+
return targetFailure(`Rust backend packages do not support ${platform} ${arch}.`);
|
|
15621
15652
|
}
|
|
15622
|
-
const absolute = resolve2(normalized);
|
|
15623
|
-
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
15624
|
-
}
|
|
15625
|
-
function pathIdentity(stats) {
|
|
15626
|
-
return `${String(stats.dev)}:${String(stats.ino)}:${String(stats.birthtimeMs)}`;
|
|
15627
15653
|
}
|
|
15628
|
-
function
|
|
15629
|
-
let stats;
|
|
15630
|
-
try {
|
|
15631
|
-
const observed = lstatSync(path2);
|
|
15632
|
-
if (observed === void 0) return { status: "missing" };
|
|
15633
|
-
stats = observed;
|
|
15634
|
-
} catch {
|
|
15635
|
-
return { status: "missing" };
|
|
15636
|
-
}
|
|
15637
|
-
if (stats.isSymbolicLink()) return { status: "unsafe" };
|
|
15638
|
-
const kind = stats.isDirectory() ? "directory" : stats.isFile() ? "file" : null;
|
|
15639
|
-
if (kind === null) return { status: "unsafe" };
|
|
15640
|
-
let canonical;
|
|
15654
|
+
function readJson(path2) {
|
|
15641
15655
|
try {
|
|
15642
|
-
|
|
15656
|
+
const value = JSON.parse(readFileSync(path2, "utf8"));
|
|
15657
|
+
return { kind: "success", value };
|
|
15643
15658
|
} catch {
|
|
15644
|
-
return {
|
|
15659
|
+
return { kind: "error" };
|
|
15645
15660
|
}
|
|
15646
|
-
if (comparablePath(canonical) !== comparablePath(path2)) return { status: "unsafe" };
|
|
15647
|
-
return {
|
|
15648
|
-
status: "ok",
|
|
15649
|
-
entry: {
|
|
15650
|
-
path: path2,
|
|
15651
|
-
kind,
|
|
15652
|
-
identity: pathIdentity(stats),
|
|
15653
|
-
mode: stats.mode & 511
|
|
15654
|
-
}
|
|
15655
|
-
};
|
|
15656
15661
|
}
|
|
15657
|
-
function
|
|
15658
|
-
const
|
|
15659
|
-
|
|
15660
|
-
const suffix = absolutePath.slice(root.length);
|
|
15661
|
-
const parts = suffix === "" ? [] : suffix.split(sep).filter((part) => part !== "");
|
|
15662
|
-
const entries = [];
|
|
15663
|
-
let current = root;
|
|
15664
|
-
const rootRead = readPathEntry(root);
|
|
15665
|
-
if (rootRead.status !== "ok" || rootRead.entry.kind !== "directory") return null;
|
|
15666
|
-
entries.push(rootRead.entry);
|
|
15667
|
-
for (let index = 0; index < parts.length; index += 1) {
|
|
15668
|
-
const part = parts[index];
|
|
15669
|
-
if (part === void 0) return null;
|
|
15670
|
-
current = join(current, part);
|
|
15671
|
-
const read = readPathEntry(current);
|
|
15672
|
-
if (read.status === "unsafe") return null;
|
|
15673
|
-
if (read.status === "missing") {
|
|
15674
|
-
return {
|
|
15675
|
-
absolutePath,
|
|
15676
|
-
complete: false,
|
|
15677
|
-
entries,
|
|
15678
|
-
missingParts: parts.slice(index)
|
|
15679
|
-
};
|
|
15680
|
-
}
|
|
15681
|
-
if (index < parts.length - 1 && read.entry.kind !== "directory") return null;
|
|
15682
|
-
entries.push(read.entry);
|
|
15683
|
-
}
|
|
15684
|
-
return { absolutePath, complete: true, entries, missingParts: [] };
|
|
15662
|
+
function isConfinedPath(root, candidate) {
|
|
15663
|
+
const child = relative(root, candidate);
|
|
15664
|
+
return child.length > 0 && !child.startsWith("..") && !isAbsolute(child);
|
|
15685
15665
|
}
|
|
15686
|
-
function
|
|
15687
|
-
return
|
|
15666
|
+
function identitiesMatch(left, right) {
|
|
15667
|
+
return left.schemaVersion === right.schemaVersion && left.name === right.name && left.packageVersion === right.packageVersion && left.target === right.target && left.binary === right.binary && left.cliProtocol === right.cliProtocol && left.productionReady === right.productionReady && left.capabilities.nodeProtectedIngress === right.capabilities.nodeProtectedIngress && left.capabilities.basePath === right.capabilities.basePath && left.capabilities.namedInstances === right.capabilities.namedInstances && left.capabilities.gracefulShutdown === right.capabilities.gracefulShutdown;
|
|
15688
15668
|
}
|
|
15689
|
-
function
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15669
|
+
function probeBinaryIdentity(binaryPath, identityFlag) {
|
|
15670
|
+
const result = spawnSync(binaryPath, [identityFlag], {
|
|
15671
|
+
encoding: "utf8",
|
|
15672
|
+
windowsHide: true,
|
|
15673
|
+
timeout: RUST_BACKEND_IDENTITY_TIMEOUT_MS,
|
|
15674
|
+
maxBuffer: 64 * 1024
|
|
15694
15675
|
});
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
if (
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
}
|
|
15702
|
-
|
|
15703
|
-
|
|
15676
|
+
if (result.error !== void 0) {
|
|
15677
|
+
return { kind: "error", message: result.error.message };
|
|
15678
|
+
}
|
|
15679
|
+
if (result.status !== 0) {
|
|
15680
|
+
return {
|
|
15681
|
+
kind: "error",
|
|
15682
|
+
message: `identity command exited with status ${String(result.status)}`
|
|
15683
|
+
};
|
|
15684
|
+
}
|
|
15685
|
+
let raw;
|
|
15704
15686
|
try {
|
|
15705
|
-
|
|
15706
|
-
descriptor = openSync(path2, constants.O_RDONLY | constants.O_NOFOLLOW | directoryFlag);
|
|
15707
|
-
const before = fstatSync(descriptor);
|
|
15708
|
-
if (pathIdentity(before) !== expectedIdentity2) return false;
|
|
15709
|
-
fchmodSync(descriptor, kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15710
|
-
const after = fstatSync(descriptor);
|
|
15711
|
-
return pathIdentity(after) === expectedIdentity2 && (after.mode & 511) === (kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15687
|
+
raw = JSON.parse(result.stdout);
|
|
15712
15688
|
} catch {
|
|
15713
|
-
return
|
|
15714
|
-
} finally {
|
|
15715
|
-
if (descriptor !== null) {
|
|
15716
|
-
try {
|
|
15717
|
-
closeSync(descriptor);
|
|
15718
|
-
} catch {
|
|
15719
|
-
}
|
|
15720
|
-
}
|
|
15721
|
-
}
|
|
15722
|
-
}
|
|
15723
|
-
function permissionsArePrivate(entry) {
|
|
15724
|
-
if (process.platform === "win32") return true;
|
|
15725
|
-
return entry.mode === (entry.kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15726
|
-
}
|
|
15727
|
-
function secureExistingPath(path2, kind, forcePermissions = false) {
|
|
15728
|
-
const before = completeSnapshotForKind(path2, kind);
|
|
15729
|
-
if (before === null) return false;
|
|
15730
|
-
const leaf = before.entries.at(-1);
|
|
15731
|
-
if (leaf === void 0) return false;
|
|
15732
|
-
const cacheKey = `${kind}:${before.absolutePath}`;
|
|
15733
|
-
const beforeFingerprint = chainFingerprint(before);
|
|
15734
|
-
if (!forcePermissions && securedPathFingerprints.get(cacheKey) === beforeFingerprint && permissionsArePrivate(leaf)) {
|
|
15735
|
-
const cachedCheck = completeSnapshotForKind(path2, kind);
|
|
15736
|
-
return cachedCheck !== null && chainFingerprint(cachedCheck) === beforeFingerprint;
|
|
15689
|
+
return { kind: "error", message: "identity command did not return JSON" };
|
|
15737
15690
|
}
|
|
15738
|
-
const
|
|
15739
|
-
|
|
15740
|
-
const after = completeSnapshotForKind(path2, kind);
|
|
15741
|
-
if (after === null || !snapshotsRetainPrefix(before, after)) return false;
|
|
15742
|
-
const afterLeaf = after.entries.at(-1);
|
|
15743
|
-
if (afterLeaf === void 0 || afterLeaf.identity !== leaf.identity) return false;
|
|
15744
|
-
if (!permissionsArePrivate(afterLeaf)) return false;
|
|
15745
|
-
securedPathFingerprints.set(cacheKey, chainFingerprint(after));
|
|
15746
|
-
return true;
|
|
15691
|
+
const parsed = RustBackendIdentitySchema.safeParse(raw);
|
|
15692
|
+
return parsed.success ? { kind: "success", value: parsed.data } : { kind: "error", message: "identity command returned an unsupported contract" };
|
|
15747
15693
|
}
|
|
15748
|
-
function
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
const
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
|
|
15694
|
+
function defaultResolvePackageJson(specifier) {
|
|
15695
|
+
try {
|
|
15696
|
+
return packageRequire.resolve(specifier);
|
|
15697
|
+
} catch {
|
|
15698
|
+
const suffix = "/package.json";
|
|
15699
|
+
const packageName = specifier.endsWith(suffix) ? specifier.slice(0, -suffix.length) : "";
|
|
15700
|
+
const directoryName = packageName.split("/").at(-1);
|
|
15701
|
+
if (directoryName !== void 0 && directoryName.length > 0) {
|
|
15702
|
+
const localPackageJson = resolve(
|
|
15703
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
15704
|
+
"../packages",
|
|
15705
|
+
directoryName,
|
|
15706
|
+
"package.json"
|
|
15707
|
+
);
|
|
15708
|
+
if (existsSync(localPackageJson)) return localPackageJson;
|
|
15760
15709
|
}
|
|
15761
|
-
|
|
15762
|
-
current = next;
|
|
15710
|
+
return packageRequire.resolve(specifier);
|
|
15763
15711
|
}
|
|
15764
|
-
const complete = completeSnapshotForKind(path2, "directory");
|
|
15765
|
-
if (complete === null || !snapshotsRetainPrefix(before, complete)) return false;
|
|
15766
|
-
return secureExistingPath(path2, "directory");
|
|
15767
15712
|
}
|
|
15768
|
-
function
|
|
15769
|
-
|
|
15770
|
-
}
|
|
15771
|
-
|
|
15772
|
-
|
|
15773
|
-
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
try {
|
|
15783
|
-
entries = readdirSync(path2);
|
|
15784
|
-
} catch {
|
|
15785
|
-
return false;
|
|
15786
|
-
}
|
|
15787
|
-
const afterRead = completeSnapshotForKind(path2, "directory");
|
|
15788
|
-
if (afterRead === null || chainFingerprint(afterRead) !== fingerprint) return false;
|
|
15789
|
-
for (const entry of entries) {
|
|
15790
|
-
if (!visit(join(path2, entry))) return false;
|
|
15791
|
-
}
|
|
15792
|
-
return true;
|
|
15793
|
-
};
|
|
15794
|
-
return visit(root) ? collected : null;
|
|
15795
|
-
}
|
|
15796
|
-
function treeFingerprintsMatch(expected, observed) {
|
|
15797
|
-
if (observed === null || observed.length !== expected.length) return false;
|
|
15798
|
-
return expected.every((entry, index) => {
|
|
15799
|
-
const next = observed[index];
|
|
15800
|
-
return next !== void 0 && next.path === entry.path && next.kind === entry.kind && next.fingerprint === entry.fingerprint;
|
|
15801
|
-
});
|
|
15802
|
-
}
|
|
15803
|
-
function isPathTreeFreeOfIndirectionSync(root) {
|
|
15804
|
-
return collectSafeTree(root) !== null;
|
|
15805
|
-
}
|
|
15806
|
-
function applyWindowsUserOnlyTreeAcl(root, run = runWindowsCommand) {
|
|
15807
|
-
const entries = collectSafeTree(root);
|
|
15808
|
-
if (entries === null) return false;
|
|
15809
|
-
const sid = resolveWindowsUserSid(run);
|
|
15810
|
-
if (sid === null) return false;
|
|
15811
|
-
for (const entry of entries) {
|
|
15812
|
-
const before = completeSnapshotForKind(entry.path, entry.kind);
|
|
15813
|
-
if (before === null || chainFingerprint(before) !== entry.fingerprint) return false;
|
|
15814
|
-
if (!applyWindowsAclWithSid(entry.path, entry.kind, sid, run)) return false;
|
|
15815
|
-
const after = completeSnapshotForKind(entry.path, entry.kind);
|
|
15816
|
-
if (after === null || chainFingerprint(after) !== entry.fingerprint) return false;
|
|
15817
|
-
securedPathFingerprints.set(`${entry.kind}:${entry.path}`, entry.fingerprint);
|
|
15713
|
+
function resolveRustBackendPackage(options) {
|
|
15714
|
+
const target = resolveRustBackendTarget(options.platform, options.arch, options.libc);
|
|
15715
|
+
if (target.kind === "error") return { kind: "error", error: target.error };
|
|
15716
|
+
const packageJsonSpecifier = `${target.value.packageName}/package.json`;
|
|
15717
|
+
let packageJsonPath;
|
|
15718
|
+
try {
|
|
15719
|
+
packageJsonPath = (options.resolvePackageJson ?? defaultResolvePackageJson)(
|
|
15720
|
+
packageJsonSpecifier
|
|
15721
|
+
);
|
|
15722
|
+
} catch {
|
|
15723
|
+
return packageFailure(
|
|
15724
|
+
"package_missing",
|
|
15725
|
+
`Rust backend ${target.value.target} was requested, but optional package ${target.value.packageName}@${options.rootVersion} is not installed. Reinstall without omitting optional dependencies; no TypeScript fallback was used.`
|
|
15726
|
+
);
|
|
15818
15727
|
}
|
|
15819
|
-
|
|
15820
|
-
|
|
15821
|
-
|
|
15822
|
-
const entries = collectSafeTree(root);
|
|
15823
|
-
if (entries === null) return false;
|
|
15824
|
-
if (process.platform === "win32") return applyWindowsUserOnlyTreeAcl(root);
|
|
15825
|
-
for (const entry of entries) {
|
|
15826
|
-
if (!secureExistingPath(entry.path, entry.kind, true)) return false;
|
|
15728
|
+
const rawPackage = readJson(packageJsonPath);
|
|
15729
|
+
if (rawPackage.kind === "error") {
|
|
15730
|
+
return packageFailure("package_invalid", `Cannot read ${packageJsonSpecifier}.`);
|
|
15827
15731
|
}
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
|
|
15834
|
-
PRIVATE_DIRECTORY_MODE = 448;
|
|
15835
|
-
PRIVATE_FILE_MODE = 384;
|
|
15836
|
-
securedPathFingerprints = /* @__PURE__ */ new Map();
|
|
15837
|
-
cachedWindowsSid = null;
|
|
15732
|
+
const packageJson = RustBackendPackageJsonSchema.safeParse(rawPackage.value);
|
|
15733
|
+
if (!packageJson.success) {
|
|
15734
|
+
return packageFailure(
|
|
15735
|
+
"package_invalid",
|
|
15736
|
+
`Package ${target.value.packageName} does not contain the Rust backend package contract.`
|
|
15737
|
+
);
|
|
15838
15738
|
}
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
15843
|
-
|
|
15844
|
-
function getDataDir() {
|
|
15845
|
-
migrateLegacyDataDirs();
|
|
15846
|
-
return resolveDataDir(existsSync2);
|
|
15847
|
-
}
|
|
15848
|
-
function hasDataDirState(pathExists, dir) {
|
|
15849
|
-
return pathExists(join2(dir, "providers.json")) || pathExists(join2(dir, "config.json")) || pathExists(join2(dir, "logs")) || pathExists(join2(dir, "chunks"));
|
|
15850
|
-
}
|
|
15851
|
-
function defaultBaseDir(env = process.env, platform = process.platform) {
|
|
15852
|
-
return platform === "win32" ? env["USERPROFILE"] ?? env["APPDATA"] ?? "C:\\" : env["HOME"] ?? "/tmp";
|
|
15853
|
-
}
|
|
15854
|
-
function resolveDataDir(_pathExists, env = process.env, platform = process.platform) {
|
|
15855
|
-
const base = defaultBaseDir(env, platform);
|
|
15856
|
-
const dirEnv = env["AGENT_INSPECTOR_DATA_DIR"];
|
|
15857
|
-
if (dirEnv !== void 0 && dirEnv !== "") {
|
|
15858
|
-
return isAbsolute2(dirEnv) ? dirEnv : join2(base, dirEnv);
|
|
15739
|
+
if (packageJson.data.name !== target.value.packageName || packageJson.data.version !== options.rootVersion) {
|
|
15740
|
+
return packageFailure(
|
|
15741
|
+
"version_mismatch",
|
|
15742
|
+
`Rust backend package must exactly match @tonyclaw/agent-inspector@${options.rootVersion}; found ${packageJson.data.name}@${packageJson.data.version}.`
|
|
15743
|
+
);
|
|
15859
15744
|
}
|
|
15860
|
-
const
|
|
15861
|
-
|
|
15862
|
-
|
|
15745
|
+
const metadata = packageJson.data.agentInspectorRustBackend;
|
|
15746
|
+
const expectedLibc = target.value.libc === null ? void 0 : [target.value.libc];
|
|
15747
|
+
if (metadata.target !== target.value.target || metadata.binary !== target.value.binary || metadata.manifest !== "binary-manifest.json" || packageJson.data.os.length !== 1 || packageJson.data.os[0] !== target.value.os || packageJson.data.cpu.length !== 1 || packageJson.data.cpu[0] !== target.value.cpu || JSON.stringify(packageJson.data.libc) !== JSON.stringify(expectedLibc)) {
|
|
15748
|
+
return packageFailure(
|
|
15749
|
+
"target_mismatch",
|
|
15750
|
+
`Package ${target.value.packageName} does not match target ${target.value.target}.`
|
|
15751
|
+
);
|
|
15863
15752
|
}
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
const base = defaultBaseDir();
|
|
15872
|
-
const currentDir = join2(base, CURRENT_DATA_DIR_NAME);
|
|
15873
|
-
if (hasDataDirState(existsSync2, currentDir)) return;
|
|
15874
|
-
for (const legacyName of LEGACY_DATA_DIR_NAMES) {
|
|
15875
|
-
const legacyDir = join2(base, legacyName);
|
|
15876
|
-
if (!existsSync2(legacyDir)) continue;
|
|
15877
|
-
const stagingDir = join2(
|
|
15878
|
-
base,
|
|
15879
|
-
`${CURRENT_DATA_DIR_NAME}.migration-${String(process.pid)}-${String(Date.now())}`
|
|
15753
|
+
const packageRoot = dirname(packageJsonPath);
|
|
15754
|
+
const manifestPath = resolve(packageRoot, metadata.manifest);
|
|
15755
|
+
const binaryPath = resolve(packageRoot, metadata.binary);
|
|
15756
|
+
if (!isConfinedPath(packageRoot, manifestPath) || !isConfinedPath(packageRoot, binaryPath)) {
|
|
15757
|
+
return packageFailure(
|
|
15758
|
+
"package_invalid",
|
|
15759
|
+
`Package ${target.value.packageName} contains an unsafe artifact path.`
|
|
15880
15760
|
);
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
15886
|
-
}
|
|
15887
|
-
|
|
15888
|
-
|
|
15889
|
-
|
|
15890
|
-
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
|
|
15894
|
-
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15761
|
+
}
|
|
15762
|
+
const rawManifest = readJson(manifestPath);
|
|
15763
|
+
if (rawManifest.kind === "error") {
|
|
15764
|
+
return packageFailure(
|
|
15765
|
+
"manifest_invalid",
|
|
15766
|
+
`Rust backend package ${target.value.packageName} is incomplete: binary-manifest.json is missing or invalid.`
|
|
15767
|
+
);
|
|
15768
|
+
}
|
|
15769
|
+
const manifest = RustBackendBinaryManifestSchema.safeParse(rawManifest.value);
|
|
15770
|
+
if (!manifest.success) {
|
|
15771
|
+
return packageFailure(
|
|
15772
|
+
"manifest_invalid",
|
|
15773
|
+
`Rust backend package ${target.value.packageName} has an invalid target manifest.`
|
|
15774
|
+
);
|
|
15775
|
+
}
|
|
15776
|
+
if (manifest.data.packageName !== target.value.packageName || manifest.data.packageVersion !== options.rootVersion || manifest.data.identity.packageVersion !== options.rootVersion) {
|
|
15777
|
+
return packageFailure(
|
|
15778
|
+
"version_mismatch",
|
|
15779
|
+
`Rust backend manifest must exactly match @tonyclaw/agent-inspector@${options.rootVersion}.`
|
|
15780
|
+
);
|
|
15781
|
+
}
|
|
15782
|
+
if (manifest.data.target !== target.value.target || manifest.data.binary !== target.value.binary || manifest.data.identity.target !== target.value.target || manifest.data.identity.binary !== target.value.binary) {
|
|
15783
|
+
return packageFailure(
|
|
15784
|
+
"target_mismatch",
|
|
15785
|
+
`Rust backend manifest identity does not match target ${target.value.target}.`
|
|
15786
|
+
);
|
|
15787
|
+
}
|
|
15788
|
+
let binaryBytes;
|
|
15789
|
+
try {
|
|
15790
|
+
const stats = statSync(binaryPath);
|
|
15791
|
+
if (!stats.isFile()) {
|
|
15792
|
+
return packageFailure("binary_invalid", `Rust backend artifact is not a regular file.`);
|
|
15793
|
+
}
|
|
15794
|
+
if (target.value.os !== "win32" && (stats.mode & 73) === 0) {
|
|
15795
|
+
return packageFailure("binary_invalid", `Rust backend artifact is not executable.`);
|
|
15909
15796
|
}
|
|
15797
|
+
binaryBytes = readFileSync(binaryPath);
|
|
15798
|
+
} catch {
|
|
15799
|
+
return packageFailure(
|
|
15800
|
+
"binary_missing",
|
|
15801
|
+
`Rust backend binary ${target.value.binary} is missing from ${target.value.packageName}.`
|
|
15802
|
+
);
|
|
15803
|
+
}
|
|
15804
|
+
const sha256 = createHash("sha256").update(binaryBytes).digest("hex");
|
|
15805
|
+
if (sha256 !== manifest.data.sha256) {
|
|
15806
|
+
return packageFailure(
|
|
15807
|
+
"hash_mismatch",
|
|
15808
|
+
`Rust backend binary integrity check failed for ${target.value.packageName}; no binary was started.`
|
|
15809
|
+
);
|
|
15810
|
+
}
|
|
15811
|
+
const probed = (options.identityProbe ?? probeBinaryIdentity)(binaryPath, metadata.identityFlag);
|
|
15812
|
+
if (probed.kind === "error") {
|
|
15813
|
+
return packageFailure(
|
|
15814
|
+
"identity_failed",
|
|
15815
|
+
`Rust backend binary did not pass the production identity gate: ${probed.message}. No TypeScript fallback was used.`
|
|
15816
|
+
);
|
|
15817
|
+
}
|
|
15818
|
+
if (!identitiesMatch(probed.value, manifest.data.identity)) {
|
|
15819
|
+
return packageFailure(
|
|
15820
|
+
"identity_mismatch",
|
|
15821
|
+
`Rust backend binary identity differs from its signed package manifest; no binary was started.`
|
|
15822
|
+
);
|
|
15910
15823
|
}
|
|
15824
|
+
return {
|
|
15825
|
+
kind: "success",
|
|
15826
|
+
value: {
|
|
15827
|
+
command: binaryPath,
|
|
15828
|
+
args: [],
|
|
15829
|
+
packageName: target.value.packageName,
|
|
15830
|
+
packageVersion: options.rootVersion,
|
|
15831
|
+
target: target.value.target,
|
|
15832
|
+
sha256
|
|
15833
|
+
}
|
|
15834
|
+
};
|
|
15911
15835
|
}
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15836
|
+
function resolvePreferredBackendImplementation(options) {
|
|
15837
|
+
return resolveRustBackendPackage(options).kind === "success" ? "rust" : "typescript";
|
|
15838
|
+
}
|
|
15839
|
+
var RUST_BACKEND_IDENTITY_FLAG, RUST_BACKEND_CLI_PROTOCOL, RUST_BACKEND_IDENTITY_TIMEOUT_MS, RustBackendIdentitySchema, RustBackendPackageJsonSchema, RustBackendBinaryManifestSchema, packageRequire;
|
|
15840
|
+
var init_rustBackendPackage = __esm({
|
|
15841
|
+
"src/cli/rustBackendPackage.ts"() {
|
|
15915
15842
|
"use strict";
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15843
|
+
init_zod();
|
|
15844
|
+
RUST_BACKEND_IDENTITY_FLAG = "--agent-inspector-package-identity";
|
|
15845
|
+
RUST_BACKEND_CLI_PROTOCOL = "agent-inspector-cli-backend-v1";
|
|
15846
|
+
RUST_BACKEND_IDENTITY_TIMEOUT_MS = 2e3;
|
|
15847
|
+
RustBackendIdentitySchema = external_exports.object({
|
|
15848
|
+
schemaVersion: external_exports.literal(1),
|
|
15849
|
+
name: external_exports.literal("agent-inspector-backend"),
|
|
15850
|
+
packageVersion: external_exports.string().min(1),
|
|
15851
|
+
target: external_exports.string().min(1),
|
|
15852
|
+
binary: external_exports.string().min(1),
|
|
15853
|
+
cliProtocol: external_exports.literal(RUST_BACKEND_CLI_PROTOCOL),
|
|
15854
|
+
productionReady: external_exports.literal(true),
|
|
15855
|
+
capabilities: external_exports.object({
|
|
15856
|
+
nodeProtectedIngress: external_exports.literal(true),
|
|
15857
|
+
basePath: external_exports.literal(true),
|
|
15858
|
+
namedInstances: external_exports.literal(true),
|
|
15859
|
+
gracefulShutdown: external_exports.literal(true)
|
|
15860
|
+
}).strict()
|
|
15861
|
+
}).strict();
|
|
15862
|
+
RustBackendPackageJsonSchema = external_exports.object({
|
|
15863
|
+
name: external_exports.string().min(1),
|
|
15864
|
+
version: external_exports.string().min(1),
|
|
15865
|
+
os: external_exports.array(external_exports.string()).min(1),
|
|
15866
|
+
cpu: external_exports.array(external_exports.string()).min(1),
|
|
15867
|
+
libc: external_exports.array(external_exports.string()).optional(),
|
|
15868
|
+
agentInspectorRustBackend: external_exports.object({
|
|
15869
|
+
schemaVersion: external_exports.literal(1),
|
|
15870
|
+
target: external_exports.string().min(1),
|
|
15871
|
+
binary: external_exports.string().min(1),
|
|
15872
|
+
manifest: external_exports.string().min(1),
|
|
15873
|
+
identityFlag: external_exports.literal(RUST_BACKEND_IDENTITY_FLAG),
|
|
15874
|
+
cliProtocol: external_exports.literal(RUST_BACKEND_CLI_PROTOCOL)
|
|
15875
|
+
}).strict()
|
|
15876
|
+
}).passthrough();
|
|
15877
|
+
RustBackendBinaryManifestSchema = external_exports.object({
|
|
15878
|
+
schemaVersion: external_exports.literal(1),
|
|
15879
|
+
packageName: external_exports.string().min(1),
|
|
15880
|
+
packageVersion: external_exports.string().min(1),
|
|
15881
|
+
target: external_exports.string().min(1),
|
|
15882
|
+
binary: external_exports.string().min(1),
|
|
15883
|
+
sha256: external_exports.string().regex(/^[a-f0-9]{64}$/u),
|
|
15884
|
+
identity: RustBackendIdentitySchema
|
|
15885
|
+
}).strict();
|
|
15886
|
+
packageRequire = createRequire(import.meta.url);
|
|
15919
15887
|
}
|
|
15920
15888
|
});
|
|
15921
15889
|
|
|
15922
|
-
// src/
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15890
|
+
// src/lib/instanceContract.ts
|
|
15891
|
+
function isCanonicalPublicBasePath(value) {
|
|
15892
|
+
const normalized = normalizePublicBasePath(value);
|
|
15893
|
+
return normalized !== null && (normalized.length === 0 ? "/" : normalized) === value;
|
|
15894
|
+
}
|
|
15895
|
+
function isRetryableInstanceError(code) {
|
|
15896
|
+
switch (code) {
|
|
15897
|
+
case "launch_failed":
|
|
15898
|
+
case "launch_unavailable":
|
|
15899
|
+
case "lock_timeout":
|
|
15900
|
+
case "port_exhausted":
|
|
15901
|
+
case "registry_io":
|
|
15902
|
+
case "restart_failed_degraded":
|
|
15903
|
+
case "restart_failed_rolled_back":
|
|
15904
|
+
case "shutdown_failed":
|
|
15905
|
+
return true;
|
|
15906
|
+
case "argument_invalid":
|
|
15907
|
+
case "config_mismatch":
|
|
15908
|
+
case "data_dir_conflict":
|
|
15909
|
+
case "data_dir_invalid":
|
|
15910
|
+
case "identity_mismatch":
|
|
15911
|
+
case "instance_not_found":
|
|
15912
|
+
case "lock_not_owned":
|
|
15913
|
+
case "name_invalid":
|
|
15914
|
+
case "port_conflict":
|
|
15915
|
+
case "port_invalid":
|
|
15916
|
+
case "record_invalid":
|
|
15917
|
+
case "self_lifecycle_unsupported":
|
|
15918
|
+
return false;
|
|
15919
|
+
}
|
|
15920
|
+
}
|
|
15921
|
+
function instanceErrorOutput(error51) {
|
|
15922
|
+
return { schemaVersion: INSTANCE_API_SCHEMA_VERSION, error: error51 };
|
|
15923
|
+
}
|
|
15924
|
+
var INSTANCE_API_SCHEMA_VERSION, InstanceStateSchema, InstanceStateReasonSchema, InstanceUrlsSchema, InstanceStatusOutputSchema, InstanceConnectionSurfacesSchema, InstanceConnectionOutputSchema, InstanceStartRequestSchema, InstanceEmptyRequestSchema, InstanceOperationSchema, InstanceOperationOutcomeSchema, InstanceOperationOutputSchema, InstanceListOutputSchema, InstancePublicErrorCodeSchema, InstancePublicErrorSchema, InstanceErrorOutputSchema;
|
|
15925
|
+
var init_instanceContract = __esm({
|
|
15926
|
+
"src/lib/instanceContract.ts"() {
|
|
15927
|
+
"use strict";
|
|
15928
|
+
init_zod();
|
|
15929
|
+
init_backendImplementation();
|
|
15930
|
+
init_basePath();
|
|
15931
|
+
INSTANCE_API_SCHEMA_VERSION = 1;
|
|
15932
|
+
InstanceStateSchema = external_exports.enum(["running", "stopped", "conflict"]);
|
|
15933
|
+
InstanceStateReasonSchema = external_exports.enum(["identity_mismatch", "port_conflict"]);
|
|
15934
|
+
InstanceUrlsSchema = external_exports.object({
|
|
15935
|
+
ui: external_exports.url(),
|
|
15936
|
+
api: external_exports.url(),
|
|
15937
|
+
health: external_exports.url(),
|
|
15938
|
+
proxy: external_exports.url(),
|
|
15939
|
+
openai: external_exports.url(),
|
|
15940
|
+
mcp: external_exports.url()
|
|
15941
|
+
}).strict();
|
|
15942
|
+
InstanceStatusOutputSchema = external_exports.object({
|
|
15943
|
+
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15944
|
+
name: external_exports.string().min(1),
|
|
15945
|
+
instanceId: external_exports.string().min(1),
|
|
15946
|
+
state: InstanceStateSchema,
|
|
15947
|
+
stateReason: InstanceStateReasonSchema.nullable(),
|
|
15948
|
+
checkedAt: external_exports.iso.datetime(),
|
|
15949
|
+
createdAt: external_exports.iso.datetime(),
|
|
15950
|
+
updatedAt: external_exports.iso.datetime(),
|
|
15951
|
+
launch: external_exports.object({
|
|
15952
|
+
launchId: external_exports.string().min(1),
|
|
15953
|
+
startedAt: external_exports.iso.datetime()
|
|
15954
|
+
}).strict(),
|
|
15955
|
+
host: external_exports.string().min(1),
|
|
15956
|
+
publicPort: external_exports.number().int().min(1).max(65535),
|
|
15957
|
+
upstreamPort: external_exports.number().int().min(1).max(65535),
|
|
15958
|
+
basePath: external_exports.string().refine(isCanonicalPublicBasePath, "Invalid public Base Path"),
|
|
15959
|
+
captureMode: external_exports.enum(["simple", "full"]),
|
|
15960
|
+
backend: BackendImplementationSchema.default("typescript"),
|
|
15961
|
+
uiEnabled: external_exports.boolean(),
|
|
15962
|
+
dataDir: external_exports.string().min(1),
|
|
15963
|
+
supervisorPid: external_exports.number().int().positive().nullable(),
|
|
15964
|
+
urls: InstanceUrlsSchema
|
|
15965
|
+
}).strict();
|
|
15966
|
+
InstanceConnectionSurfacesSchema = external_exports.object({
|
|
15967
|
+
ui: external_exports.object({
|
|
15968
|
+
enabled: external_exports.boolean(),
|
|
15969
|
+
baseUrl: external_exports.url().nullable()
|
|
15970
|
+
}).strict(),
|
|
15971
|
+
control: external_exports.object({ baseUrl: external_exports.url() }).strict(),
|
|
15972
|
+
agent: external_exports.object({ baseUrl: external_exports.url() }).strict()
|
|
15973
|
+
}).strict();
|
|
15974
|
+
InstanceConnectionOutputSchema = external_exports.object({
|
|
15975
|
+
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
15976
|
+
uiEnabled: external_exports.boolean(),
|
|
15977
|
+
instance: InstanceStatusOutputSchema,
|
|
15978
|
+
surfaces: InstanceConnectionSurfacesSchema,
|
|
15979
|
+
/** @deprecated Use surfaces.control.baseUrl or surfaces.agent.baseUrl. */
|
|
15980
|
+
baseUrl: external_exports.url(),
|
|
15981
|
+
endpoints: InstanceUrlsSchema,
|
|
15982
|
+
environment: external_exports.object({
|
|
15983
|
+
ANTHROPIC_BASE_URL: external_exports.url(),
|
|
15984
|
+
LLM_BASE_URL: external_exports.url(),
|
|
15985
|
+
OPENAI_BASE_URL: external_exports.url(),
|
|
15986
|
+
AGENT_INSPECTOR_MCP_URL: external_exports.url()
|
|
15987
|
+
}).strict(),
|
|
15988
|
+
mcp: external_exports.object({
|
|
15989
|
+
serverName: external_exports.string().min(1),
|
|
15990
|
+
transport: external_exports.literal("streamable-http"),
|
|
15991
|
+
url: external_exports.url()
|
|
15992
|
+
}).strict(),
|
|
15993
|
+
codex: external_exports.object({
|
|
15994
|
+
providerName: external_exports.string().min(1),
|
|
15995
|
+
baseUrl: external_exports.url(),
|
|
15996
|
+
wireApi: external_exports.literal("responses")
|
|
15997
|
+
}).strict()
|
|
15998
|
+
}).strict();
|
|
15999
|
+
InstanceStartRequestSchema = external_exports.object({
|
|
16000
|
+
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
16001
|
+
basePath: external_exports.string().min(1).max(2048).refine(
|
|
16002
|
+
(value) => normalizePublicBasePath(value) !== null,
|
|
16003
|
+
"Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters"
|
|
16004
|
+
).optional(),
|
|
16005
|
+
host: external_exports.string().min(1).max(253).refine(
|
|
16006
|
+
(value) => value.trim() === value && !value.includes("://") && !value.includes("/") && !value.includes("\\") && !/\s/u.test(value),
|
|
16007
|
+
"Host must be a hostname or IP address without protocol, path, or whitespace"
|
|
16008
|
+
).optional(),
|
|
16009
|
+
captureMode: external_exports.enum(["simple", "full"]).optional(),
|
|
16010
|
+
backend: BackendImplementationSchema.optional(),
|
|
16011
|
+
uiEnabled: external_exports.boolean().optional(),
|
|
16012
|
+
dataDir: external_exports.string().trim().min(1).max(32767).optional()
|
|
16013
|
+
}).strict();
|
|
16014
|
+
InstanceEmptyRequestSchema = external_exports.object({}).strict();
|
|
16015
|
+
InstanceOperationSchema = external_exports.enum(["start", "stop", "restart"]);
|
|
16016
|
+
InstanceOperationOutcomeSchema = external_exports.enum([
|
|
16017
|
+
"created",
|
|
16018
|
+
"started",
|
|
16019
|
+
"already_running",
|
|
16020
|
+
"stopped",
|
|
16021
|
+
"already_stopped",
|
|
16022
|
+
"restarted"
|
|
16023
|
+
]);
|
|
16024
|
+
InstanceOperationOutputSchema = external_exports.object({
|
|
16025
|
+
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
16026
|
+
operation: InstanceOperationSchema,
|
|
16027
|
+
outcome: InstanceOperationOutcomeSchema,
|
|
16028
|
+
instance: InstanceStatusOutputSchema
|
|
16029
|
+
}).strict();
|
|
16030
|
+
InstanceListOutputSchema = external_exports.object({
|
|
16031
|
+
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
16032
|
+
instances: external_exports.array(InstanceStatusOutputSchema)
|
|
16033
|
+
}).strict();
|
|
16034
|
+
InstancePublicErrorCodeSchema = external_exports.enum([
|
|
16035
|
+
"argument_invalid",
|
|
16036
|
+
"config_mismatch",
|
|
16037
|
+
"data_dir_conflict",
|
|
16038
|
+
"data_dir_invalid",
|
|
16039
|
+
"identity_mismatch",
|
|
16040
|
+
"instance_not_found",
|
|
16041
|
+
"launch_failed",
|
|
16042
|
+
"launch_unavailable",
|
|
16043
|
+
"lock_not_owned",
|
|
16044
|
+
"lock_timeout",
|
|
16045
|
+
"name_invalid",
|
|
16046
|
+
"port_conflict",
|
|
16047
|
+
"port_exhausted",
|
|
16048
|
+
"port_invalid",
|
|
16049
|
+
"record_invalid",
|
|
16050
|
+
"registry_io",
|
|
16051
|
+
"restart_failed_degraded",
|
|
16052
|
+
"restart_failed_rolled_back",
|
|
16053
|
+
"self_lifecycle_unsupported",
|
|
16054
|
+
"shutdown_failed"
|
|
16055
|
+
]);
|
|
16056
|
+
InstancePublicErrorSchema = external_exports.object({
|
|
16057
|
+
code: InstancePublicErrorCodeSchema,
|
|
16058
|
+
message: external_exports.string().min(1),
|
|
16059
|
+
retryable: external_exports.boolean()
|
|
16060
|
+
}).strict();
|
|
16061
|
+
InstanceErrorOutputSchema = external_exports.object({
|
|
16062
|
+
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
16063
|
+
error: InstancePublicErrorSchema
|
|
16064
|
+
}).strict();
|
|
16065
|
+
}
|
|
16066
|
+
});
|
|
16067
|
+
|
|
16068
|
+
// src/proxy/privateDataPath.ts
|
|
16069
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16070
|
+
import {
|
|
16071
|
+
closeSync,
|
|
16072
|
+
constants,
|
|
16073
|
+
fchmodSync,
|
|
16074
|
+
fstatSync,
|
|
16075
|
+
lstatSync,
|
|
16076
|
+
mkdirSync,
|
|
16077
|
+
openSync,
|
|
16078
|
+
readdirSync,
|
|
16079
|
+
realpathSync
|
|
16080
|
+
} from "node:fs";
|
|
16081
|
+
import { join, parse as parse3, resolve as resolve2, sep } from "node:path";
|
|
16082
|
+
function windowsSystemPath(executable) {
|
|
16083
|
+
const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
|
|
16084
|
+
return `${systemRoot}\\System32\\${executable}`;
|
|
16085
|
+
}
|
|
16086
|
+
function runWindowsCommand(executable, args) {
|
|
16087
|
+
const resolved = process.platform === "win32" && /^(whoami|icacls)\.exe$/iu.test(executable) ? windowsSystemPath(executable) : executable;
|
|
16088
|
+
const result = spawnSync2(resolved, [...args], {
|
|
16089
|
+
encoding: "utf8",
|
|
16090
|
+
shell: false,
|
|
16091
|
+
windowsHide: true
|
|
16092
|
+
});
|
|
16093
|
+
return { status: result.status, stdout: result.stdout };
|
|
16094
|
+
}
|
|
16095
|
+
function windowsUserSid(output) {
|
|
16096
|
+
const match = /S-\d+(?:-\d+)+/.exec(output);
|
|
16097
|
+
return match?.[0] ?? null;
|
|
16098
|
+
}
|
|
16099
|
+
function resolveWindowsUserSid(run) {
|
|
16100
|
+
if (run === runWindowsCommand && cachedWindowsSid !== null) return cachedWindowsSid;
|
|
16101
|
+
const identity = run("whoami.exe", ["/user", "/fo", "csv", "/nh"]);
|
|
16102
|
+
if (identity.status !== 0) return null;
|
|
16103
|
+
const sid = windowsUserSid(identity.stdout);
|
|
16104
|
+
if (run === runWindowsCommand) cachedWindowsSid = sid;
|
|
16105
|
+
return sid;
|
|
16106
|
+
}
|
|
16107
|
+
function windowsBroadGroupRemovalArgs() {
|
|
16108
|
+
return ["/remove:g", "*S-1-1-0", "*S-1-5-11", "*S-1-5-32-545"];
|
|
16109
|
+
}
|
|
16110
|
+
function applyWindowsAclWithSid(path2, kind, sid, run) {
|
|
16111
|
+
const access = kind === "directory" ? `(OI)(CI)F` : "F";
|
|
16112
|
+
const result = run("icacls.exe", [
|
|
16113
|
+
path2,
|
|
16114
|
+
"/inheritance:r",
|
|
16115
|
+
"/grant:r",
|
|
16116
|
+
`*${sid}:${access}`,
|
|
16117
|
+
...windowsBroadGroupRemovalArgs(),
|
|
16118
|
+
"/q"
|
|
16119
|
+
]);
|
|
16120
|
+
return result.status === 0;
|
|
16121
|
+
}
|
|
16122
|
+
function applyWindowsUserOnlyAcl(path2, kind, run = runWindowsCommand) {
|
|
16123
|
+
const sid = resolveWindowsUserSid(run);
|
|
16124
|
+
return sid !== null && applyWindowsAclWithSid(path2, kind, sid, run);
|
|
16125
|
+
}
|
|
16126
|
+
function comparablePath(path2) {
|
|
16127
|
+
let normalized = path2;
|
|
16128
|
+
if (process.platform === "win32") {
|
|
16129
|
+
if (normalized.startsWith("\\\\?\\UNC\\")) {
|
|
16130
|
+
normalized = `\\\\${normalized.slice(8)}`;
|
|
16131
|
+
} else if (normalized.startsWith("\\\\?\\")) {
|
|
16132
|
+
normalized = normalized.slice(4);
|
|
16133
|
+
}
|
|
16134
|
+
}
|
|
16135
|
+
const absolute = resolve2(normalized);
|
|
16136
|
+
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
16137
|
+
}
|
|
16138
|
+
function pathIdentity(stats) {
|
|
16139
|
+
return `${String(stats.dev)}:${String(stats.ino)}:${String(stats.birthtimeMs)}`;
|
|
16140
|
+
}
|
|
16141
|
+
function readPathEntry(path2) {
|
|
16142
|
+
let stats;
|
|
16143
|
+
try {
|
|
16144
|
+
const observed = lstatSync(path2);
|
|
16145
|
+
if (observed === void 0) return { status: "missing" };
|
|
16146
|
+
stats = observed;
|
|
16147
|
+
} catch {
|
|
16148
|
+
return { status: "missing" };
|
|
16149
|
+
}
|
|
16150
|
+
if (stats.isSymbolicLink()) return { status: "unsafe" };
|
|
16151
|
+
const kind = stats.isDirectory() ? "directory" : stats.isFile() ? "file" : null;
|
|
16152
|
+
if (kind === null) return { status: "unsafe" };
|
|
16153
|
+
let canonical;
|
|
16154
|
+
try {
|
|
16155
|
+
canonical = realpathSync.native(path2);
|
|
16156
|
+
} catch {
|
|
16157
|
+
return { status: "unsafe" };
|
|
16158
|
+
}
|
|
16159
|
+
if (comparablePath(canonical) !== comparablePath(path2)) return { status: "unsafe" };
|
|
16160
|
+
return {
|
|
16161
|
+
status: "ok",
|
|
16162
|
+
entry: {
|
|
16163
|
+
path: path2,
|
|
16164
|
+
kind,
|
|
16165
|
+
identity: pathIdentity(stats),
|
|
16166
|
+
mode: stats.mode & 511
|
|
16167
|
+
}
|
|
16168
|
+
};
|
|
16169
|
+
}
|
|
16170
|
+
function snapshotPathChain(path2) {
|
|
16171
|
+
const absolutePath = resolve2(path2);
|
|
16172
|
+
const root = parse3(absolutePath).root;
|
|
16173
|
+
const suffix = absolutePath.slice(root.length);
|
|
16174
|
+
const parts = suffix === "" ? [] : suffix.split(sep).filter((part) => part !== "");
|
|
16175
|
+
const entries = [];
|
|
16176
|
+
let current = root;
|
|
16177
|
+
const rootRead = readPathEntry(root);
|
|
16178
|
+
if (rootRead.status !== "ok" || rootRead.entry.kind !== "directory") return null;
|
|
16179
|
+
entries.push(rootRead.entry);
|
|
16180
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
16181
|
+
const part = parts[index];
|
|
16182
|
+
if (part === void 0) return null;
|
|
16183
|
+
current = join(current, part);
|
|
16184
|
+
const read = readPathEntry(current);
|
|
16185
|
+
if (read.status === "unsafe") return null;
|
|
16186
|
+
if (read.status === "missing") {
|
|
16187
|
+
return {
|
|
16188
|
+
absolutePath,
|
|
16189
|
+
complete: false,
|
|
16190
|
+
entries,
|
|
16191
|
+
missingParts: parts.slice(index)
|
|
16192
|
+
};
|
|
16193
|
+
}
|
|
16194
|
+
if (index < parts.length - 1 && read.entry.kind !== "directory") return null;
|
|
16195
|
+
entries.push(read.entry);
|
|
16196
|
+
}
|
|
16197
|
+
return { absolutePath, complete: true, entries, missingParts: [] };
|
|
16198
|
+
}
|
|
16199
|
+
function chainFingerprint(snapshot) {
|
|
16200
|
+
return snapshot.entries.map((entry) => `${entry.path}:${entry.identity}`).join("|");
|
|
16201
|
+
}
|
|
16202
|
+
function snapshotsRetainPrefix(before, after) {
|
|
16203
|
+
if (after.entries.length < before.entries.length) return false;
|
|
16204
|
+
return before.entries.every((entry, index) => {
|
|
16205
|
+
const next = after.entries[index];
|
|
16206
|
+
return next !== void 0 && next.path === entry.path && next.identity === entry.identity;
|
|
16207
|
+
});
|
|
16208
|
+
}
|
|
16209
|
+
function completeSnapshotForKind(path2, kind) {
|
|
16210
|
+
const snapshot = snapshotPathChain(path2);
|
|
16211
|
+
if (snapshot === null || !snapshot.complete) return null;
|
|
16212
|
+
const leaf = snapshot.entries.at(-1);
|
|
16213
|
+
return leaf !== void 0 && leaf.kind === kind ? snapshot : null;
|
|
16214
|
+
}
|
|
16215
|
+
function applyPosixPermissions(path2, kind, expectedIdentity2) {
|
|
16216
|
+
let descriptor = null;
|
|
16217
|
+
try {
|
|
16218
|
+
const directoryFlag = kind === "directory" ? constants.O_DIRECTORY : 0;
|
|
16219
|
+
descriptor = openSync(path2, constants.O_RDONLY | constants.O_NOFOLLOW | directoryFlag);
|
|
16220
|
+
const before = fstatSync(descriptor);
|
|
16221
|
+
if (pathIdentity(before) !== expectedIdentity2) return false;
|
|
16222
|
+
fchmodSync(descriptor, kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
16223
|
+
const after = fstatSync(descriptor);
|
|
16224
|
+
return pathIdentity(after) === expectedIdentity2 && (after.mode & 511) === (kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
16225
|
+
} catch {
|
|
16226
|
+
return false;
|
|
16227
|
+
} finally {
|
|
16228
|
+
if (descriptor !== null) {
|
|
16229
|
+
try {
|
|
16230
|
+
closeSync(descriptor);
|
|
16231
|
+
} catch {
|
|
16232
|
+
}
|
|
16233
|
+
}
|
|
16234
|
+
}
|
|
16235
|
+
}
|
|
16236
|
+
function permissionsArePrivate(entry) {
|
|
16237
|
+
if (process.platform === "win32") return true;
|
|
16238
|
+
return entry.mode === (entry.kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
16239
|
+
}
|
|
16240
|
+
function secureExistingPath(path2, kind, forcePermissions = false) {
|
|
16241
|
+
const before = completeSnapshotForKind(path2, kind);
|
|
16242
|
+
if (before === null) return false;
|
|
16243
|
+
const leaf = before.entries.at(-1);
|
|
16244
|
+
if (leaf === void 0) return false;
|
|
16245
|
+
const cacheKey = `${kind}:${before.absolutePath}`;
|
|
16246
|
+
const beforeFingerprint = chainFingerprint(before);
|
|
16247
|
+
if (!forcePermissions && securedPathFingerprints.get(cacheKey) === beforeFingerprint && permissionsArePrivate(leaf)) {
|
|
16248
|
+
const cachedCheck = completeSnapshotForKind(path2, kind);
|
|
16249
|
+
return cachedCheck !== null && chainFingerprint(cachedCheck) === beforeFingerprint;
|
|
16250
|
+
}
|
|
16251
|
+
const applied = process.platform === "win32" ? applyWindowsUserOnlyAcl(path2, kind) : applyPosixPermissions(path2, kind, leaf.identity);
|
|
16252
|
+
if (!applied) return false;
|
|
16253
|
+
const after = completeSnapshotForKind(path2, kind);
|
|
16254
|
+
if (after === null || !snapshotsRetainPrefix(before, after)) return false;
|
|
16255
|
+
const afterLeaf = after.entries.at(-1);
|
|
16256
|
+
if (afterLeaf === void 0 || afterLeaf.identity !== leaf.identity) return false;
|
|
16257
|
+
if (!permissionsArePrivate(afterLeaf)) return false;
|
|
16258
|
+
securedPathFingerprints.set(cacheKey, chainFingerprint(after));
|
|
16259
|
+
return true;
|
|
16260
|
+
}
|
|
16261
|
+
function ensurePrivateDirectorySync(path2) {
|
|
16262
|
+
const before = snapshotPathChain(path2);
|
|
16263
|
+
if (before === null) return false;
|
|
16264
|
+
let current = before.entries.at(-1)?.path;
|
|
16265
|
+
if (current === void 0) return false;
|
|
16266
|
+
for (const part of before.missingParts) {
|
|
16267
|
+
const next = join(current, part);
|
|
16268
|
+
try {
|
|
16269
|
+
mkdirSync(next, { mode: PRIVATE_DIRECTORY_MODE });
|
|
16270
|
+
} catch {
|
|
16271
|
+
const raced = readPathEntry(next);
|
|
16272
|
+
if (raced.status !== "ok" || raced.entry.kind !== "directory") return false;
|
|
16273
|
+
}
|
|
16274
|
+
if (!secureExistingPath(next, "directory", true)) return false;
|
|
16275
|
+
current = next;
|
|
16276
|
+
}
|
|
16277
|
+
const complete = completeSnapshotForKind(path2, "directory");
|
|
16278
|
+
if (complete === null || !snapshotsRetainPrefix(before, complete)) return false;
|
|
16279
|
+
return secureExistingPath(path2, "directory");
|
|
16280
|
+
}
|
|
16281
|
+
function securePrivateFileSync(path2, forceAcl = false) {
|
|
16282
|
+
return secureExistingPath(path2, "file", forceAcl);
|
|
16283
|
+
}
|
|
16284
|
+
function collectSafeTree(root) {
|
|
16285
|
+
const collected = [];
|
|
16286
|
+
const visit = (path2) => {
|
|
16287
|
+
const snapshot = snapshotPathChain(path2);
|
|
16288
|
+
if (snapshot === null || !snapshot.complete) return false;
|
|
16289
|
+
const leaf = snapshot.entries.at(-1);
|
|
16290
|
+
if (leaf === void 0) return false;
|
|
16291
|
+
const fingerprint = chainFingerprint(snapshot);
|
|
16292
|
+
collected.push({ path: snapshot.absolutePath, kind: leaf.kind, fingerprint });
|
|
16293
|
+
if (leaf.kind === "file") return true;
|
|
16294
|
+
let entries;
|
|
16295
|
+
try {
|
|
16296
|
+
entries = readdirSync(path2);
|
|
16297
|
+
} catch {
|
|
16298
|
+
return false;
|
|
16299
|
+
}
|
|
16300
|
+
const afterRead = completeSnapshotForKind(path2, "directory");
|
|
16301
|
+
if (afterRead === null || chainFingerprint(afterRead) !== fingerprint) return false;
|
|
16302
|
+
for (const entry of entries) {
|
|
16303
|
+
if (!visit(join(path2, entry))) return false;
|
|
16304
|
+
}
|
|
16305
|
+
return true;
|
|
16306
|
+
};
|
|
16307
|
+
return visit(root) ? collected : null;
|
|
16308
|
+
}
|
|
16309
|
+
function treeFingerprintsMatch(expected, observed) {
|
|
16310
|
+
if (observed === null || observed.length !== expected.length) return false;
|
|
16311
|
+
return expected.every((entry, index) => {
|
|
16312
|
+
const next = observed[index];
|
|
16313
|
+
return next !== void 0 && next.path === entry.path && next.kind === entry.kind && next.fingerprint === entry.fingerprint;
|
|
16314
|
+
});
|
|
16315
|
+
}
|
|
16316
|
+
function isPathTreeFreeOfIndirectionSync(root) {
|
|
16317
|
+
return collectSafeTree(root) !== null;
|
|
16318
|
+
}
|
|
16319
|
+
function applyWindowsUserOnlyTreeAcl(root, run = runWindowsCommand) {
|
|
16320
|
+
const entries = collectSafeTree(root);
|
|
16321
|
+
if (entries === null) return false;
|
|
16322
|
+
const sid = resolveWindowsUserSid(run);
|
|
16323
|
+
if (sid === null) return false;
|
|
16324
|
+
for (const entry of entries) {
|
|
16325
|
+
const before = completeSnapshotForKind(entry.path, entry.kind);
|
|
16326
|
+
if (before === null || chainFingerprint(before) !== entry.fingerprint) return false;
|
|
16327
|
+
if (!applyWindowsAclWithSid(entry.path, entry.kind, sid, run)) return false;
|
|
16328
|
+
const after = completeSnapshotForKind(entry.path, entry.kind);
|
|
16329
|
+
if (after === null || chainFingerprint(after) !== entry.fingerprint) return false;
|
|
16330
|
+
securedPathFingerprints.set(`${entry.kind}:${entry.path}`, entry.fingerprint);
|
|
16331
|
+
}
|
|
16332
|
+
return treeFingerprintsMatch(entries, collectSafeTree(root));
|
|
16333
|
+
}
|
|
16334
|
+
function securePrivateTreeSync(root) {
|
|
16335
|
+
const entries = collectSafeTree(root);
|
|
16336
|
+
if (entries === null) return false;
|
|
16337
|
+
if (process.platform === "win32") return applyWindowsUserOnlyTreeAcl(root);
|
|
16338
|
+
for (const entry of entries) {
|
|
16339
|
+
if (!secureExistingPath(entry.path, entry.kind, true)) return false;
|
|
16340
|
+
}
|
|
16341
|
+
return treeFingerprintsMatch(entries, collectSafeTree(root));
|
|
16342
|
+
}
|
|
16343
|
+
var PRIVATE_DIRECTORY_MODE, PRIVATE_FILE_MODE, securedPathFingerprints, cachedWindowsSid;
|
|
16344
|
+
var init_privateDataPath = __esm({
|
|
16345
|
+
"src/proxy/privateDataPath.ts"() {
|
|
16346
|
+
"use strict";
|
|
16347
|
+
PRIVATE_DIRECTORY_MODE = 448;
|
|
16348
|
+
PRIVATE_FILE_MODE = 384;
|
|
16349
|
+
securedPathFingerprints = /* @__PURE__ */ new Map();
|
|
16350
|
+
cachedWindowsSid = null;
|
|
16351
|
+
}
|
|
16352
|
+
});
|
|
16353
|
+
|
|
16354
|
+
// src/proxy/dataDir.ts
|
|
16355
|
+
import { cpSync, existsSync as existsSync2, readdirSync as readdirSync2, renameSync, rmSync } from "node:fs";
|
|
16356
|
+
import { isAbsolute as isAbsolute2, join as join2 } from "node:path";
|
|
16357
|
+
function getDataDir() {
|
|
16358
|
+
migrateLegacyDataDirs();
|
|
16359
|
+
return resolveDataDir(existsSync2);
|
|
16360
|
+
}
|
|
16361
|
+
function hasDataDirState(pathExists, dir) {
|
|
16362
|
+
return pathExists(join2(dir, "providers.json")) || pathExists(join2(dir, "config.json")) || pathExists(join2(dir, "logs")) || pathExists(join2(dir, "chunks"));
|
|
16363
|
+
}
|
|
16364
|
+
function defaultBaseDir(env = process.env, platform = process.platform) {
|
|
16365
|
+
return platform === "win32" ? env["USERPROFILE"] ?? env["APPDATA"] ?? "C:\\" : env["HOME"] ?? "/tmp";
|
|
16366
|
+
}
|
|
16367
|
+
function resolveDataDir(_pathExists, env = process.env, platform = process.platform) {
|
|
16368
|
+
const base = defaultBaseDir(env, platform);
|
|
16369
|
+
const dirEnv = env["AGENT_INSPECTOR_DATA_DIR"];
|
|
16370
|
+
if (dirEnv !== void 0 && dirEnv !== "") {
|
|
16371
|
+
return isAbsolute2(dirEnv) ? dirEnv : join2(base, dirEnv);
|
|
16372
|
+
}
|
|
16373
|
+
const legacyDirEnv = env["AGENT_INSPECTOR_CONFIG_DIR"];
|
|
16374
|
+
if (legacyDirEnv !== void 0 && legacyDirEnv !== "") {
|
|
16375
|
+
return isAbsolute2(legacyDirEnv) ? legacyDirEnv : join2(base, legacyDirEnv);
|
|
16376
|
+
}
|
|
16377
|
+
return join2(base, CURRENT_DATA_DIR_NAME);
|
|
16378
|
+
}
|
|
16379
|
+
function hasExplicitDataDir() {
|
|
16380
|
+
return process.env["AGENT_INSPECTOR_DATA_DIR"] !== void 0 && process.env["AGENT_INSPECTOR_DATA_DIR"] !== "" || process.env["AGENT_INSPECTOR_CONFIG_DIR"] !== void 0 && process.env["AGENT_INSPECTOR_CONFIG_DIR"] !== "";
|
|
16381
|
+
}
|
|
16382
|
+
function migrateLegacyDataDirs() {
|
|
16383
|
+
if (hasExplicitDataDir()) return;
|
|
16384
|
+
const base = defaultBaseDir();
|
|
16385
|
+
const currentDir = join2(base, CURRENT_DATA_DIR_NAME);
|
|
16386
|
+
if (hasDataDirState(existsSync2, currentDir)) return;
|
|
16387
|
+
for (const legacyName of LEGACY_DATA_DIR_NAMES) {
|
|
16388
|
+
const legacyDir = join2(base, legacyName);
|
|
16389
|
+
if (!existsSync2(legacyDir)) continue;
|
|
16390
|
+
const stagingDir = join2(
|
|
16391
|
+
base,
|
|
16392
|
+
`${CURRENT_DATA_DIR_NAME}.migration-${String(process.pid)}-${String(Date.now())}`
|
|
16393
|
+
);
|
|
16394
|
+
try {
|
|
16395
|
+
if (!isPathTreeFreeOfIndirectionSync(legacyDir)) return;
|
|
16396
|
+
if (existsSync2(currentDir)) {
|
|
16397
|
+
if (readdirSync2(currentDir).length > 0) return;
|
|
16398
|
+
rmSync(currentDir, { recursive: true, force: false });
|
|
16399
|
+
}
|
|
16400
|
+
if (!ensurePrivateDirectorySync(stagingDir)) return;
|
|
16401
|
+
for (const entry of readdirSync2(legacyDir)) {
|
|
16402
|
+
cpSync(join2(legacyDir, entry), join2(stagingDir, entry), {
|
|
16403
|
+
recursive: true,
|
|
16404
|
+
force: false,
|
|
16405
|
+
errorOnExist: true
|
|
16406
|
+
});
|
|
16407
|
+
}
|
|
16408
|
+
if (!isPathTreeFreeOfIndirectionSync(legacyDir)) {
|
|
16409
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
16410
|
+
return;
|
|
16411
|
+
}
|
|
16412
|
+
if (!securePrivateTreeSync(stagingDir)) {
|
|
16413
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
16414
|
+
return;
|
|
16415
|
+
}
|
|
16416
|
+
renameSync(stagingDir, currentDir);
|
|
16417
|
+
rmSync(legacyDir, { recursive: true, force: true });
|
|
16418
|
+
return;
|
|
16419
|
+
} catch {
|
|
16420
|
+
if (existsSync2(stagingDir)) rmSync(stagingDir, { recursive: true, force: true });
|
|
16421
|
+
return;
|
|
16422
|
+
}
|
|
16423
|
+
}
|
|
16424
|
+
}
|
|
16425
|
+
var CURRENT_DATA_DIR_NAME, LEGACY_DATA_DIR_NAMES;
|
|
16426
|
+
var init_dataDir = __esm({
|
|
16427
|
+
"src/proxy/dataDir.ts"() {
|
|
16428
|
+
"use strict";
|
|
16429
|
+
init_privateDataPath();
|
|
16430
|
+
CURRENT_DATA_DIR_NAME = ".agent-inspector";
|
|
16431
|
+
LEGACY_DATA_DIR_NAMES = [".llm-inspector", ".llm-agent"];
|
|
16432
|
+
}
|
|
16433
|
+
});
|
|
16434
|
+
|
|
16435
|
+
// src/proxy/platformCommands.ts
|
|
16436
|
+
import { statSync as statSync2 } from "node:fs";
|
|
16437
|
+
import { execFileSync } from "node:child_process";
|
|
16438
|
+
import { delimiter, isAbsolute as isAbsolute3, join as join3 } from "node:path";
|
|
15926
16439
|
function pathEntries(envPath) {
|
|
15927
16440
|
if (envPath === void 0 || envPath.length === 0) return [];
|
|
15928
16441
|
return envPath.split(delimiter).filter((entry) => entry.length > 0);
|
|
@@ -16277,7 +16790,7 @@ Usage:
|
|
|
16277
16790
|
agent-inspector instance restart <name> [options]
|
|
16278
16791
|
|
|
16279
16792
|
Start/restart options:
|
|
16280
|
-
--backend <typescript|rust> Persist the backend implementation (default:
|
|
16793
|
+
--backend <typescript|rust> Persist the backend implementation (default: rust when native package is available)
|
|
16281
16794
|
--port, -p <port> Public ingress port (auto-allocated on first start)
|
|
16282
16795
|
--base-path <path> Public Base Path (default: /inspector; use / for root)
|
|
16283
16796
|
--host, -H <host> Bind host (default: 127.0.0.1)
|
|
@@ -17427,6 +17940,9 @@ function validateInstanceDataDirectory(rawPath, records, excludeName) {
|
|
|
17427
17940
|
}
|
|
17428
17941
|
return success2(assigned.value.path);
|
|
17429
17942
|
}
|
|
17943
|
+
function resolveDefaultInstanceBackend(deps) {
|
|
17944
|
+
return deps.resolveDefaultBackend?.() ?? resolvePreferredBackendImplementation({ rootVersion: package_default.version });
|
|
17945
|
+
}
|
|
17430
17946
|
async function createNewRecord(root, action, deps) {
|
|
17431
17947
|
const recordsResult = listInstanceRecords(root);
|
|
17432
17948
|
if (recordsResult.kind === "error") return fromRegistry(recordsResult);
|
|
@@ -17450,7 +17966,7 @@ async function createNewRecord(root, action, deps) {
|
|
|
17450
17966
|
upstreamPort: pairResult.value.upstreamPort,
|
|
17451
17967
|
basePath: action.overrides.basePath ?? getConfiguredBasePath(process.env),
|
|
17452
17968
|
captureMode: action.overrides.captureMode ?? DEFAULT_INSTANCE_CAPTURE_MODE,
|
|
17453
|
-
backend: action.overrides.backend ??
|
|
17969
|
+
backend: action.overrides.backend ?? resolveDefaultInstanceBackend(deps),
|
|
17454
17970
|
uiEnabled: action.overrides.uiEnabled ?? true,
|
|
17455
17971
|
dataDir: dataDirResult.value
|
|
17456
17972
|
});
|
|
@@ -17827,6 +18343,7 @@ var DEFAULT_INSTANCE_HOST, DEFAULT_INSTANCE_CAPTURE_MODE, INSTANCE_READY_TIMEOUT
|
|
|
17827
18343
|
var init_instance = __esm({
|
|
17828
18344
|
"src/cli/instance.ts"() {
|
|
17829
18345
|
"use strict";
|
|
18346
|
+
init_package();
|
|
17830
18347
|
init_basePath();
|
|
17831
18348
|
init_instanceContract();
|
|
17832
18349
|
init_managedInstance();
|
|
@@ -17836,6 +18353,7 @@ var init_instance = __esm({
|
|
|
17836
18353
|
init_instanceControl();
|
|
17837
18354
|
init_instanceRegistry();
|
|
17838
18355
|
init_networkHints();
|
|
18356
|
+
init_rustBackendPackage();
|
|
17839
18357
|
DEFAULT_INSTANCE_HOST = "127.0.0.1";
|
|
17840
18358
|
DEFAULT_INSTANCE_CAPTURE_MODE = "simple";
|
|
17841
18359
|
INSTANCE_READY_TIMEOUT_MS = 15e3;
|
|
@@ -20542,1336 +21060,839 @@ function providerCountFromParsed(value) {
|
|
|
20542
21060
|
}
|
|
20543
21061
|
function providerCountFromJson(raw) {
|
|
20544
21062
|
try {
|
|
20545
|
-
const parsed = JSON.parse(raw);
|
|
20546
|
-
return providerCountFromParsed(parsed);
|
|
20547
|
-
} catch {
|
|
20548
|
-
return "invalid";
|
|
20549
|
-
}
|
|
20550
|
-
}
|
|
20551
|
-
function resolveMaybeRelative(base, value) {
|
|
20552
|
-
return isAbsolute5(value) ? value : resolve5(base, value);
|
|
20553
|
-
}
|
|
20554
|
-
function resolveDefaultDataDir(env, platform, exists) {
|
|
20555
|
-
return resolveDataDir(exists, env, platform);
|
|
20556
|
-
}
|
|
20557
|
-
function providerConfigCandidates(options, deps) {
|
|
20558
|
-
const explicitPath = deps.env["AGENT_INSPECTOR_CONFIG_PATH"];
|
|
20559
|
-
if (explicitPath !== void 0 && explicitPath !== "") {
|
|
20560
|
-
return [resolveMaybeRelative(deps.cwd, explicitPath)];
|
|
20561
|
-
}
|
|
20562
|
-
if (options.configDir !== null) {
|
|
20563
|
-
const dir = resolveMaybeRelative(deps.cwd, options.configDir);
|
|
20564
|
-
return [join8(dir, "providers.json"), join8(dir, "config.json")];
|
|
20565
|
-
}
|
|
20566
|
-
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
20567
|
-
return [join8(dataDir, "providers.json"), join8(dataDir, "config.json")];
|
|
20568
|
-
}
|
|
20569
|
-
function checkProviderConfig(options, deps) {
|
|
20570
|
-
const providersJson = options.providersJson ?? deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] ?? null;
|
|
20571
|
-
if (providersJson !== null) {
|
|
20572
|
-
const count = providerCountFromJson(providersJson);
|
|
20573
|
-
if (count === "invalid") {
|
|
20574
|
-
return check2(
|
|
20575
|
-
"Provider config",
|
|
20576
|
-
"fail",
|
|
20577
|
-
"Provider JSON override is not valid JSON.",
|
|
20578
|
-
"Fix --providers or AGENT_INSPECTOR_PROVIDERS_JSON; doctor never prints the raw value."
|
|
20579
|
-
);
|
|
20580
|
-
}
|
|
20581
|
-
if (count !== null) {
|
|
20582
|
-
return check2("Provider config", "pass", `${count} provider(s) found in JSON override.`);
|
|
20583
|
-
}
|
|
20584
|
-
return check2(
|
|
20585
|
-
"Provider config",
|
|
20586
|
-
"fail",
|
|
20587
|
-
"Provider JSON override does not look like a provider array or { providers } object.",
|
|
20588
|
-
"Use an exported providers JSON payload or configure providers in the UI."
|
|
20589
|
-
);
|
|
20590
|
-
}
|
|
20591
|
-
const candidates = providerConfigCandidates(options, deps);
|
|
20592
|
-
for (const candidate of candidates) {
|
|
20593
|
-
if (!deps.exists(candidate)) continue;
|
|
20594
|
-
const text = deps.readText(candidate);
|
|
20595
|
-
if (text === null) {
|
|
20596
|
-
return check2(
|
|
20597
|
-
"Provider config",
|
|
20598
|
-
"fail",
|
|
20599
|
-
`Provider config exists but could not be read: ${candidate}`
|
|
20600
|
-
);
|
|
20601
|
-
}
|
|
20602
|
-
const count = providerCountFromJson(text);
|
|
20603
|
-
if (count === "invalid") {
|
|
20604
|
-
return check2("Provider config", "fail", `Provider config is invalid JSON: ${candidate}`);
|
|
20605
|
-
}
|
|
20606
|
-
if (count !== null && count > 0) {
|
|
20607
|
-
return check2("Provider config", "pass", `${count} provider(s) configured.`);
|
|
20608
|
-
}
|
|
20609
|
-
return check2(
|
|
20610
|
-
"Provider config",
|
|
20611
|
-
"warn",
|
|
20612
|
-
`Provider config exists but no providers were found: ${candidate}`,
|
|
20613
|
-
"Add a provider through the control API/MCP, or use Settings when a UI is available."
|
|
20614
|
-
);
|
|
20615
|
-
}
|
|
20616
|
-
return check2(
|
|
20617
|
-
"Provider config",
|
|
20618
|
-
"warn",
|
|
20619
|
-
"No provider config was found.",
|
|
20620
|
-
"Add a provider through the control API/MCP, use Settings when a UI is available, or pass --providers for this check."
|
|
20621
|
-
);
|
|
20622
|
-
}
|
|
20623
|
-
function publicOriginForDoctor(port, env) {
|
|
20624
|
-
const configured = env["AGENT_INSPECTOR_PUBLIC_ORIGIN"];
|
|
20625
|
-
if (configured !== void 0 && configured.trim() === configured) {
|
|
20626
|
-
try {
|
|
20627
|
-
const parsed = new URL(configured);
|
|
20628
|
-
if ((parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username.length === 0 && parsed.password.length === 0 && parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
|
|
20629
|
-
return parsed.origin;
|
|
20630
|
-
}
|
|
20631
|
-
} catch {
|
|
20632
|
-
}
|
|
20633
|
-
}
|
|
20634
|
-
return `http://localhost:${String(port)}`;
|
|
20635
|
-
}
|
|
20636
|
-
async function checkProxyHealth(port, deps) {
|
|
20637
|
-
const basePath = getConfiguredBasePath(deps.env);
|
|
20638
|
-
const health = await deps.fetchHealth(port, basePath);
|
|
20639
|
-
const url2 = publicOriginForDoctor(port, deps.env);
|
|
20640
|
-
const proxyUrl = appendBasePathToOrigin(url2, "/proxy", basePath);
|
|
20641
|
-
const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
|
|
20642
|
-
if (health.ok) {
|
|
20643
|
-
return check2(
|
|
20644
|
-
"Backend health",
|
|
20645
|
-
"pass",
|
|
20646
|
-
`Agent Inspector backend is healthy at ${appendBasePathToOrigin(url2, "/", basePath)}.`,
|
|
20647
|
-
`Agent proxy URL: ${proxyUrl}. UI availability is not part of this backend check.`
|
|
20648
|
-
);
|
|
20649
|
-
}
|
|
20650
|
-
if (await deps.isPortOpen(port)) {
|
|
20651
|
-
const suffix = health.status === null ? "" : ` (HTTP ${health.status})`;
|
|
20652
|
-
return check2(
|
|
20653
|
-
"Backend health",
|
|
20654
|
-
"fail",
|
|
20655
|
-
`Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
|
|
20656
|
-
"Stop that process, choose --port <n>, or start agent-inspector with --force-restart."
|
|
20657
|
-
);
|
|
20658
|
-
}
|
|
20659
|
-
return check2(
|
|
20660
|
-
"Backend health",
|
|
20661
|
-
"warn",
|
|
20662
|
-
`No process is listening on port ${port}.`,
|
|
20663
|
-
"Start with `agent-inspector`, `agent-inspector --background`, or `bun run dev`."
|
|
20664
|
-
);
|
|
20665
|
-
}
|
|
20666
|
-
function findProjectRoot(startDir, deps) {
|
|
20667
|
-
let current = resolve5(startDir);
|
|
20668
|
-
for (let i = 0; i < 6; i++) {
|
|
20669
|
-
if (deps.exists(join8(current, "package.json"))) return current;
|
|
20670
|
-
const next = resolve5(current, "..");
|
|
20671
|
-
if (next === current) return startDir;
|
|
20672
|
-
current = next;
|
|
20673
|
-
}
|
|
20674
|
-
return startDir;
|
|
20675
|
-
}
|
|
20676
|
-
function checkPackage(rootDir, deps) {
|
|
20677
|
-
const packagePath = join8(rootDir, "package.json");
|
|
20678
|
-
const text = deps.readText(packagePath);
|
|
20679
|
-
if (text === null) {
|
|
20680
|
-
return check2("Package metadata", "warn", "package.json was not found near the CLI bundle.");
|
|
20681
|
-
}
|
|
20682
|
-
try {
|
|
20683
|
-
const parsed = JSON.parse(text);
|
|
20684
|
-
if (!isObject3(parsed)) {
|
|
20685
|
-
return check2("Package metadata", "warn", "package.json is not an object.");
|
|
20686
|
-
}
|
|
20687
|
-
const name = parsed["name"];
|
|
20688
|
-
const version2 = parsed["version"];
|
|
20689
|
-
if (typeof name === "string" && typeof version2 === "string") {
|
|
20690
|
-
return check2("Package metadata", "pass", `${name}@${version2}`);
|
|
20691
|
-
}
|
|
20692
|
-
return check2("Package metadata", "warn", "package.json is missing name or version.");
|
|
20693
|
-
} catch {
|
|
20694
|
-
return check2("Package metadata", "warn", "package.json is not valid JSON.");
|
|
20695
|
-
}
|
|
20696
|
-
}
|
|
20697
|
-
function countFilesInDir(dir, deps) {
|
|
20698
|
-
const files = deps.listDir(dir);
|
|
20699
|
-
if (files === null) return null;
|
|
20700
|
-
let count = 0;
|
|
20701
|
-
for (const file2 of files) {
|
|
20702
|
-
const fileStat = deps.stat(join8(dir, file2));
|
|
20703
|
-
if (fileStat !== null && !fileStat.isDirectory) count += 1;
|
|
20704
|
-
}
|
|
20705
|
-
return count;
|
|
20706
|
-
}
|
|
20707
|
-
function formatBytes(bytes) {
|
|
20708
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
20709
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
20710
|
-
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MiB`;
|
|
20711
|
-
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GiB`;
|
|
20712
|
-
}
|
|
20713
|
-
function dirSize(dir, deps, depth = 0) {
|
|
20714
|
-
const dirStat = deps.stat(dir);
|
|
20715
|
-
if (dirStat === null) return null;
|
|
20716
|
-
if (!dirStat.isDirectory) return dirStat.size;
|
|
20717
|
-
if (depth > 4) return 0;
|
|
20718
|
-
const files = deps.listDir(dir);
|
|
20719
|
-
if (files === null) return null;
|
|
20720
|
-
let total = 0;
|
|
20721
|
-
for (const file2 of files) {
|
|
20722
|
-
const childSize = dirSize(join8(dir, file2), deps, depth + 1);
|
|
20723
|
-
if (childSize !== null) total += childSize;
|
|
20724
|
-
}
|
|
20725
|
-
return total;
|
|
20726
|
-
}
|
|
20727
|
-
function verboseChecks(options, deps) {
|
|
20728
|
-
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
20729
|
-
const logsDir = join8(dataDir, "logs");
|
|
20730
|
-
const chunksDir = join8(dataDir, "chunks");
|
|
20731
|
-
const providerCandidates = providerConfigCandidates(options, deps);
|
|
20732
|
-
const captureMode = deps.env["AGENT_INSPECTOR_MODE"] ?? deps.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? "simple";
|
|
20733
|
-
const logCount = countFilesInDir(logsDir, deps);
|
|
20734
|
-
const logSize = dirSize(logsDir, deps);
|
|
20735
|
-
const chunkSize = dirSize(chunksDir, deps);
|
|
20736
|
-
const homeDir = deps.platform === "win32" ? deps.env["USERPROFILE"] ?? deps.env["APPDATA"] ?? null : deps.env["HOME"] ?? null;
|
|
20737
|
-
const claudeSkill = homeDir === null ? null : join8(homeDir, ".claude", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
20738
|
-
const claudeCommand = homeDir === null ? null : join8(homeDir, ".claude", "commands", "agent-inspector-onboard.md");
|
|
20739
|
-
const codexSkill = homeDir === null ? null : join8(homeDir, ".codex", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
20740
|
-
const hasClaudeOnboarding = claudeSkill !== null && claudeCommand !== null && deps.exists(claudeSkill) && deps.exists(claudeCommand);
|
|
20741
|
-
const hasCodexOnboarding = codexSkill !== null && deps.exists(codexSkill);
|
|
20742
|
-
return [
|
|
20743
|
-
check2("Data directory", "pass", dataDir),
|
|
20744
|
-
check2("Provider config search", "pass", providerCandidates.join(", ")),
|
|
20745
|
-
check2("Capture mode env", "pass", captureMode),
|
|
20746
|
-
check2(
|
|
20747
|
-
"Log storage",
|
|
20748
|
-
logCount === null ? "warn" : "pass",
|
|
20749
|
-
logCount === null ? `Log directory was not found or could not be read: ${logsDir}` : `${logCount} file(s), ${formatBytes(logSize ?? 0)} under ${logsDir}`
|
|
20750
|
-
),
|
|
20751
|
-
check2(
|
|
20752
|
-
"Chunk storage",
|
|
20753
|
-
chunkSize === null ? "warn" : "pass",
|
|
20754
|
-
chunkSize === null ? `Chunk directory was not found or could not be read: ${chunksDir}` : `${formatBytes(chunkSize)} under ${chunksDir}`
|
|
20755
|
-
),
|
|
20756
|
-
check2(
|
|
20757
|
-
"Claude onboarding",
|
|
20758
|
-
hasClaudeOnboarding ? "pass" : "warn",
|
|
20759
|
-
hasClaudeOnboarding ? "Claude Code skill and slash command are installed." : "Claude Code onboarding files were not found.",
|
|
20760
|
-
"Run `agent-inspector onboard --force` after installing or upgrading."
|
|
20761
|
-
),
|
|
20762
|
-
check2(
|
|
20763
|
-
"Codex onboarding",
|
|
20764
|
-
hasCodexOnboarding ? "pass" : "warn",
|
|
20765
|
-
hasCodexOnboarding ? "Codex onboarding skill is installed." : "Codex onboarding skill was not found.",
|
|
20766
|
-
"Run `agent-inspector onboard --force` after installing or upgrading."
|
|
20767
|
-
)
|
|
20768
|
-
];
|
|
20769
|
-
}
|
|
20770
|
-
function checkExtensionSource(rootDir, deps) {
|
|
20771
|
-
const extensionDir = join8(rootDir, "extensions", "chrome");
|
|
20772
|
-
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(join8(extensionDir, file2)));
|
|
20773
|
-
if (missing.length === 0) {
|
|
20774
|
-
return check2(
|
|
20775
|
-
"Chrome extension source",
|
|
20776
|
-
"pass",
|
|
20777
|
-
"Manifest, side panel, service worker, and icons are present."
|
|
20778
|
-
);
|
|
20779
|
-
}
|
|
20780
|
-
return check2(
|
|
20781
|
-
"Chrome extension source",
|
|
20782
|
-
"fail",
|
|
20783
|
-
`Missing ${missing.length} Chrome extension file(s): ${missing.join(", ")}`,
|
|
20784
|
-
"Restore the extension files before packaging or loading unpacked."
|
|
20785
|
-
);
|
|
20786
|
-
}
|
|
20787
|
-
function extensionManifestVersion(rootDir, deps) {
|
|
20788
|
-
const manifestText = deps.readText(join8(rootDir, "extensions", "chrome", "manifest.json"));
|
|
20789
|
-
if (manifestText === null) return null;
|
|
20790
|
-
try {
|
|
20791
|
-
const manifest = JSON.parse(manifestText);
|
|
20792
|
-
if (!isObject3(manifest)) return null;
|
|
20793
|
-
const version2 = manifest["version"];
|
|
20794
|
-
return typeof version2 === "string" && version2 !== "" ? version2 : null;
|
|
21063
|
+
const parsed = JSON.parse(raw);
|
|
21064
|
+
return providerCountFromParsed(parsed);
|
|
20795
21065
|
} catch {
|
|
20796
|
-
return
|
|
21066
|
+
return "invalid";
|
|
20797
21067
|
}
|
|
20798
21068
|
}
|
|
20799
|
-
function
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
20803
|
-
|
|
20804
|
-
|
|
20805
|
-
|
|
20806
|
-
|
|
20807
|
-
|
|
20808
|
-
);
|
|
21069
|
+
function resolveMaybeRelative(base, value) {
|
|
21070
|
+
return isAbsolute5(value) ? value : resolve5(base, value);
|
|
21071
|
+
}
|
|
21072
|
+
function resolveDefaultDataDir(env, platform, exists) {
|
|
21073
|
+
return resolveDataDir(exists, env, platform);
|
|
21074
|
+
}
|
|
21075
|
+
function providerConfigCandidates(options, deps) {
|
|
21076
|
+
const explicitPath = deps.env["AGENT_INSPECTOR_CONFIG_PATH"];
|
|
21077
|
+
if (explicitPath !== void 0 && explicitPath !== "") {
|
|
21078
|
+
return [resolveMaybeRelative(deps.cwd, explicitPath)];
|
|
20809
21079
|
}
|
|
20810
|
-
|
|
20811
|
-
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
21080
|
+
if (options.configDir !== null) {
|
|
21081
|
+
const dir = resolveMaybeRelative(deps.cwd, options.configDir);
|
|
21082
|
+
return [join8(dir, "providers.json"), join8(dir, "config.json")];
|
|
21083
|
+
}
|
|
21084
|
+
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
21085
|
+
return [join8(dataDir, "providers.json"), join8(dataDir, "config.json")];
|
|
21086
|
+
}
|
|
21087
|
+
function checkProviderConfig(options, deps) {
|
|
21088
|
+
const providersJson = options.providersJson ?? deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] ?? null;
|
|
21089
|
+
if (providersJson !== null) {
|
|
21090
|
+
const count = providerCountFromJson(providersJson);
|
|
21091
|
+
if (count === "invalid") {
|
|
20820
21092
|
return check2(
|
|
20821
|
-
"
|
|
20822
|
-
"
|
|
20823
|
-
|
|
20824
|
-
"
|
|
21093
|
+
"Provider config",
|
|
21094
|
+
"fail",
|
|
21095
|
+
"Provider JSON override is not valid JSON.",
|
|
21096
|
+
"Fix --providers or AGENT_INSPECTOR_PROVIDERS_JSON; doctor never prints the raw value."
|
|
20825
21097
|
);
|
|
20826
21098
|
}
|
|
21099
|
+
if (count !== null) {
|
|
21100
|
+
return check2("Provider config", "pass", `${count} provider(s) found in JSON override.`);
|
|
21101
|
+
}
|
|
20827
21102
|
return check2(
|
|
20828
|
-
"
|
|
20829
|
-
"
|
|
20830
|
-
|
|
20831
|
-
"
|
|
21103
|
+
"Provider config",
|
|
21104
|
+
"fail",
|
|
21105
|
+
"Provider JSON override does not look like a provider array or { providers } object.",
|
|
21106
|
+
"Use an exported providers JSON payload or configure providers in the UI."
|
|
20832
21107
|
);
|
|
20833
21108
|
}
|
|
20834
|
-
const
|
|
20835
|
-
const
|
|
20836
|
-
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
21109
|
+
const candidates = providerConfigCandidates(options, deps);
|
|
21110
|
+
for (const candidate of candidates) {
|
|
21111
|
+
if (!deps.exists(candidate)) continue;
|
|
21112
|
+
const text = deps.readText(candidate);
|
|
21113
|
+
if (text === null) {
|
|
21114
|
+
return check2(
|
|
21115
|
+
"Provider config",
|
|
21116
|
+
"fail",
|
|
21117
|
+
`Provider config exists but could not be read: ${candidate}`
|
|
21118
|
+
);
|
|
21119
|
+
}
|
|
21120
|
+
const count = providerCountFromJson(text);
|
|
21121
|
+
if (count === "invalid") {
|
|
21122
|
+
return check2("Provider config", "fail", `Provider config is invalid JSON: ${candidate}`);
|
|
21123
|
+
}
|
|
21124
|
+
if (count !== null && count > 0) {
|
|
21125
|
+
return check2("Provider config", "pass", `${count} provider(s) configured.`);
|
|
21126
|
+
}
|
|
20840
21127
|
return check2(
|
|
20841
|
-
"
|
|
21128
|
+
"Provider config",
|
|
20842
21129
|
"warn",
|
|
20843
|
-
|
|
20844
|
-
"
|
|
21130
|
+
`Provider config exists but no providers were found: ${candidate}`,
|
|
21131
|
+
"Add a provider through the control API/MCP, or use Settings when a UI is available."
|
|
20845
21132
|
);
|
|
20846
21133
|
}
|
|
20847
21134
|
return check2(
|
|
20848
|
-
"
|
|
21135
|
+
"Provider config",
|
|
20849
21136
|
"warn",
|
|
20850
|
-
"No
|
|
20851
|
-
"
|
|
20852
|
-
);
|
|
20853
|
-
}
|
|
20854
|
-
async function buildDoctorReport(options, deps = createDoctorDeps()) {
|
|
20855
|
-
const rootDir = findProjectRoot(deps.cwd, deps);
|
|
20856
|
-
const coreChecks = [
|
|
20857
|
-
await checkProxyHealth(options.port, deps),
|
|
20858
|
-
checkProviderConfig(options, deps),
|
|
20859
|
-
checkPackage(rootDir, deps)
|
|
20860
|
-
];
|
|
20861
|
-
const checksWithExtension = options.chromeExtension ? [...coreChecks, checkExtensionSource(rootDir, deps), checkExtensionPackage(rootDir, deps)] : coreChecks;
|
|
20862
|
-
const checks = options.verbose ? [...checksWithExtension, ...verboseChecks(options, deps)] : checksWithExtension;
|
|
20863
|
-
const passCount = checks.filter((item) => item.severity === "pass").length;
|
|
20864
|
-
const warnCount = checks.filter((item) => item.severity === "warn").length;
|
|
20865
|
-
const failCount = checks.filter((item) => item.severity === "fail").length;
|
|
20866
|
-
return {
|
|
20867
|
-
checks,
|
|
20868
|
-
passCount,
|
|
20869
|
-
warnCount,
|
|
20870
|
-
failCount,
|
|
20871
|
-
exitCode: failCount > 0 ? 1 : 0
|
|
20872
|
-
};
|
|
20873
|
-
}
|
|
20874
|
-
function formatDoctorReport(report) {
|
|
20875
|
-
const lines = ["agent-inspector doctor", ""];
|
|
20876
|
-
for (const item of report.checks) {
|
|
20877
|
-
lines.push(`${item.severity.toUpperCase().padEnd(4)} ${item.name}: ${item.message}`);
|
|
20878
|
-
if (item.hint !== null) {
|
|
20879
|
-
lines.push(` ${item.hint}`);
|
|
20880
|
-
}
|
|
20881
|
-
}
|
|
20882
|
-
lines.push("");
|
|
20883
|
-
lines.push(
|
|
20884
|
-
`Summary: ${report.passCount} pass, ${report.warnCount} warn, ${report.failCount} fail`
|
|
21137
|
+
"No provider config was found.",
|
|
21138
|
+
"Add a provider through the control API/MCP, use Settings when a UI is available, or pass --providers for this check."
|
|
20885
21139
|
);
|
|
20886
|
-
return `${lines.join("\n")}
|
|
20887
|
-
`;
|
|
20888
|
-
}
|
|
20889
|
-
function formatDoctorJson(report) {
|
|
20890
|
-
return `${JSON.stringify(report, null, 2)}
|
|
20891
|
-
`;
|
|
20892
|
-
}
|
|
20893
|
-
function doctorHelp() {
|
|
20894
|
-
return [
|
|
20895
|
-
"agent-inspector doctor",
|
|
20896
|
-
"",
|
|
20897
|
-
"Usage:",
|
|
20898
|
-
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--verbose] [--chrome-extension]",
|
|
20899
|
-
"",
|
|
20900
|
-
"Checks local backend health, provider config presence, and package metadata.",
|
|
20901
|
-
"The backend check probes the local health endpoint and does not require a composed Web UI.",
|
|
20902
|
-
"Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
|
|
20903
|
-
"Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
|
|
20904
|
-
"Pass --verbose to include local paths, capture mode, and log storage checks.",
|
|
20905
|
-
"Pass --chrome-extension to also check Chrome companion source and packaged artifacts.",
|
|
20906
|
-
""
|
|
20907
|
-
].join("\n");
|
|
20908
21140
|
}
|
|
20909
|
-
|
|
20910
|
-
const
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
cache: "no-store",
|
|
20917
|
-
signal: controller.signal
|
|
21141
|
+
function publicOriginForDoctor(port, env) {
|
|
21142
|
+
const configured = env["AGENT_INSPECTOR_PUBLIC_ORIGIN"];
|
|
21143
|
+
if (configured !== void 0 && configured.trim() === configured) {
|
|
21144
|
+
try {
|
|
21145
|
+
const parsed = new URL(configured);
|
|
21146
|
+
if ((parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username.length === 0 && parsed.password.length === 0 && parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
|
|
21147
|
+
return parsed.origin;
|
|
20918
21148
|
}
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
} catch {
|
|
20922
|
-
return { ok: false, status: null };
|
|
20923
|
-
} finally {
|
|
20924
|
-
clearTimeout(timeout);
|
|
20925
|
-
}
|
|
20926
|
-
}
|
|
20927
|
-
function isPortOpen(port) {
|
|
20928
|
-
return new Promise((resolveOpen) => {
|
|
20929
|
-
const socket = createConnection({ host: "127.0.0.1", port });
|
|
20930
|
-
const finish = (value) => {
|
|
20931
|
-
socket.removeAllListeners();
|
|
20932
|
-
socket.destroy();
|
|
20933
|
-
resolveOpen(value);
|
|
20934
|
-
};
|
|
20935
|
-
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
20936
|
-
socket.once("connect", () => finish(true));
|
|
20937
|
-
socket.once("timeout", () => finish(false));
|
|
20938
|
-
socket.once("error", () => finish(false));
|
|
20939
|
-
});
|
|
20940
|
-
}
|
|
20941
|
-
function readText(path2) {
|
|
20942
|
-
try {
|
|
20943
|
-
return readFileSync4(path2, "utf8");
|
|
20944
|
-
} catch {
|
|
20945
|
-
return null;
|
|
20946
|
-
}
|
|
20947
|
-
}
|
|
20948
|
-
function listDir(path2) {
|
|
20949
|
-
try {
|
|
20950
|
-
return readdirSync5(path2);
|
|
20951
|
-
} catch {
|
|
20952
|
-
return null;
|
|
20953
|
-
}
|
|
20954
|
-
}
|
|
20955
|
-
function stat3(path2) {
|
|
20956
|
-
try {
|
|
20957
|
-
const fileStat = statSync4(path2);
|
|
20958
|
-
return { isDirectory: fileStat.isDirectory(), size: fileStat.size };
|
|
20959
|
-
} catch {
|
|
20960
|
-
return null;
|
|
21149
|
+
} catch {
|
|
21150
|
+
}
|
|
20961
21151
|
}
|
|
21152
|
+
return `http://localhost:${String(port)}`;
|
|
20962
21153
|
}
|
|
20963
|
-
function
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
async function runDoctor(argv) {
|
|
20977
|
-
const parsed = parseDoctorArgs(argv);
|
|
20978
|
-
if (parsed.kind === "error") {
|
|
20979
|
-
process.stderr.write(`${parsed.message}
|
|
20980
|
-
`);
|
|
20981
|
-
return 2;
|
|
20982
|
-
}
|
|
20983
|
-
if (parsed.options.help) {
|
|
20984
|
-
process.stdout.write(doctorHelp());
|
|
20985
|
-
return 0;
|
|
21154
|
+
async function checkProxyHealth(port, deps) {
|
|
21155
|
+
const basePath = getConfiguredBasePath(deps.env);
|
|
21156
|
+
const health = await deps.fetchHealth(port, basePath);
|
|
21157
|
+
const url2 = publicOriginForDoctor(port, deps.env);
|
|
21158
|
+
const proxyUrl = appendBasePathToOrigin(url2, "/proxy", basePath);
|
|
21159
|
+
const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
|
|
21160
|
+
if (health.ok) {
|
|
21161
|
+
return check2(
|
|
21162
|
+
"Backend health",
|
|
21163
|
+
"pass",
|
|
21164
|
+
`Agent Inspector backend is healthy at ${appendBasePathToOrigin(url2, "/", basePath)}.`,
|
|
21165
|
+
`Agent proxy URL: ${proxyUrl}. UI availability is not part of this backend check.`
|
|
21166
|
+
);
|
|
20986
21167
|
}
|
|
20987
|
-
if (
|
|
20988
|
-
|
|
21168
|
+
if (await deps.isPortOpen(port)) {
|
|
21169
|
+
const suffix = health.status === null ? "" : ` (HTTP ${health.status})`;
|
|
21170
|
+
return check2(
|
|
21171
|
+
"Backend health",
|
|
21172
|
+
"fail",
|
|
21173
|
+
`Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
|
|
21174
|
+
"Stop that process, choose --port <n>, or start agent-inspector with --force-restart."
|
|
21175
|
+
);
|
|
20989
21176
|
}
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
21177
|
+
return check2(
|
|
21178
|
+
"Backend health",
|
|
21179
|
+
"warn",
|
|
21180
|
+
`No process is listening on port ${port}.`,
|
|
21181
|
+
"Start with `agent-inspector`, `agent-inspector --background`, or `bun run dev`."
|
|
21182
|
+
);
|
|
20993
21183
|
}
|
|
20994
|
-
|
|
20995
|
-
|
|
20996
|
-
|
|
20997
|
-
"
|
|
20998
|
-
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
PROBE_TIMEOUT_MS = 2e3;
|
|
21002
|
-
EXTENSION_REQUIRED_FILES = [
|
|
21003
|
-
"manifest.json",
|
|
21004
|
-
"sidepanel.html",
|
|
21005
|
-
"sidepanel.js",
|
|
21006
|
-
"sidepanel.css",
|
|
21007
|
-
"service-worker.js",
|
|
21008
|
-
"icons/icon.svg",
|
|
21009
|
-
"icons/icon-16.png",
|
|
21010
|
-
"icons/icon-32.png",
|
|
21011
|
-
"icons/icon-48.png",
|
|
21012
|
-
"icons/icon-128.png"
|
|
21013
|
-
];
|
|
21184
|
+
function findProjectRoot(startDir, deps) {
|
|
21185
|
+
let current = resolve5(startDir);
|
|
21186
|
+
for (let i = 0; i < 6; i++) {
|
|
21187
|
+
if (deps.exists(join8(current, "package.json"))) return current;
|
|
21188
|
+
const next = resolve5(current, "..");
|
|
21189
|
+
if (next === current) return startDir;
|
|
21190
|
+
current = next;
|
|
21014
21191
|
}
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
// src/cli/alias.ts
|
|
21018
|
-
var alias_exports = {};
|
|
21019
|
-
__export(alias_exports, {
|
|
21020
|
-
parseAliasArgs: () => parseAliasArgs,
|
|
21021
|
-
runAlias: () => runAlias
|
|
21022
|
-
});
|
|
21023
|
-
function parsePort3(raw) {
|
|
21024
|
-
if (raw === void 0 || raw.trim() === "") return null;
|
|
21025
|
-
const port = Number(raw);
|
|
21026
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
21027
|
-
return port;
|
|
21028
|
-
}
|
|
21029
|
-
function normalizeAliasPath(raw) {
|
|
21030
|
-
return normalizePublicBasePath(raw);
|
|
21031
|
-
}
|
|
21032
|
-
function isValidHost(host) {
|
|
21033
|
-
const trimmed = host.trim();
|
|
21034
|
-
return trimmed.length > 0 && !trimmed.includes("://") && !trimmed.includes("/") && !trimmed.includes("?") && !trimmed.includes("#") && !/\s/u.test(trimmed);
|
|
21035
|
-
}
|
|
21036
|
-
function formatHostForUrl(host) {
|
|
21037
|
-
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
21038
|
-
return host.includes(":") ? `[${host}]` : host;
|
|
21192
|
+
return startDir;
|
|
21039
21193
|
}
|
|
21040
|
-
function
|
|
21041
|
-
|
|
21042
|
-
|
|
21043
|
-
|
|
21044
|
-
|
|
21045
|
-
const positional = [];
|
|
21046
|
-
for (let index = 0; index < argv.length; index++) {
|
|
21047
|
-
const arg = argv[index];
|
|
21048
|
-
switch (arg) {
|
|
21049
|
-
case void 0:
|
|
21050
|
-
continue;
|
|
21051
|
-
case "--help":
|
|
21052
|
-
case "-h":
|
|
21053
|
-
help = true;
|
|
21054
|
-
break;
|
|
21055
|
-
case "--host": {
|
|
21056
|
-
const value = argv[index + 1];
|
|
21057
|
-
if (value === void 0 || !isValidHost(value)) {
|
|
21058
|
-
return {
|
|
21059
|
-
kind: "error",
|
|
21060
|
-
message: "alias: --host requires a hostname or IP address without a protocol or path"
|
|
21061
|
-
};
|
|
21062
|
-
}
|
|
21063
|
-
host = value.trim();
|
|
21064
|
-
index++;
|
|
21065
|
-
break;
|
|
21066
|
-
}
|
|
21067
|
-
case "--port":
|
|
21068
|
-
case "-p": {
|
|
21069
|
-
const value = parsePort3(argv[index + 1]);
|
|
21070
|
-
if (value === null) {
|
|
21071
|
-
return {
|
|
21072
|
-
kind: "error",
|
|
21073
|
-
message: "alias: --port must be an integer from 1 to 65535"
|
|
21074
|
-
};
|
|
21075
|
-
}
|
|
21076
|
-
port = value;
|
|
21077
|
-
index++;
|
|
21078
|
-
break;
|
|
21079
|
-
}
|
|
21080
|
-
case "--json":
|
|
21081
|
-
json2 = true;
|
|
21082
|
-
break;
|
|
21083
|
-
default:
|
|
21084
|
-
if (arg.startsWith("-")) {
|
|
21085
|
-
return { kind: "error", message: `alias: unknown option ${arg}` };
|
|
21086
|
-
}
|
|
21087
|
-
positional.push(arg);
|
|
21088
|
-
}
|
|
21194
|
+
function checkPackage(rootDir, deps) {
|
|
21195
|
+
const packagePath = join8(rootDir, "package.json");
|
|
21196
|
+
const text = deps.readText(packagePath);
|
|
21197
|
+
if (text === null) {
|
|
21198
|
+
return check2("Package metadata", "warn", "package.json was not found near the CLI bundle.");
|
|
21089
21199
|
}
|
|
21090
|
-
|
|
21091
|
-
|
|
21092
|
-
|
|
21093
|
-
|
|
21094
|
-
if (positional.length !== 1) {
|
|
21095
|
-
return { kind: "error", message: "alias: list does not accept a base path" };
|
|
21096
|
-
}
|
|
21097
|
-
return { kind: "ok", options: { action: { kind: "list" }, host, port, json: json2 } };
|
|
21098
|
-
case "add":
|
|
21099
|
-
case "remove": {
|
|
21100
|
-
const rawBasePath = positional[1];
|
|
21101
|
-
if (rawBasePath === void 0 || positional.length !== 2) {
|
|
21102
|
-
return { kind: "error", message: `alias: ${command} requires exactly one base path` };
|
|
21103
|
-
}
|
|
21104
|
-
const basePath = normalizeAliasPath(rawBasePath);
|
|
21105
|
-
if (basePath === null) {
|
|
21106
|
-
return {
|
|
21107
|
-
kind: "error",
|
|
21108
|
-
message: `alias: ${command} base path must be an absolute URL path without a query or hash`
|
|
21109
|
-
};
|
|
21110
|
-
}
|
|
21111
|
-
const action = { kind: command, basePath };
|
|
21112
|
-
return { kind: "ok", options: { action, host, port, json: json2 } };
|
|
21200
|
+
try {
|
|
21201
|
+
const parsed = JSON.parse(text);
|
|
21202
|
+
if (!isObject3(parsed)) {
|
|
21203
|
+
return check2("Package metadata", "warn", "package.json is not an object.");
|
|
21113
21204
|
}
|
|
21114
|
-
|
|
21115
|
-
|
|
21116
|
-
|
|
21117
|
-
return
|
|
21205
|
+
const name = parsed["name"];
|
|
21206
|
+
const version2 = parsed["version"];
|
|
21207
|
+
if (typeof name === "string" && typeof version2 === "string") {
|
|
21208
|
+
return check2("Package metadata", "pass", `${name}@${version2}`);
|
|
21209
|
+
}
|
|
21210
|
+
return check2("Package metadata", "warn", "package.json is missing name or version.");
|
|
21211
|
+
} catch {
|
|
21212
|
+
return check2("Package metadata", "warn", "package.json is not valid JSON.");
|
|
21118
21213
|
}
|
|
21119
21214
|
}
|
|
21120
|
-
function
|
|
21121
|
-
|
|
21122
|
-
|
|
21123
|
-
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21129
|
-
--host <host> Running instance host (default: localhost)
|
|
21130
|
-
--port <port> Running instance port (default: 9527)
|
|
21131
|
-
--json Emit machine-readable output
|
|
21132
|
-
--help, -h Show this help
|
|
21133
|
-
|
|
21134
|
-
Aliases are runtime-scoped. This command never starts another Agent Inspector runtime.
|
|
21135
|
-
`;
|
|
21215
|
+
function countFilesInDir(dir, deps) {
|
|
21216
|
+
const files = deps.listDir(dir);
|
|
21217
|
+
if (files === null) return null;
|
|
21218
|
+
let count = 0;
|
|
21219
|
+
for (const file2 of files) {
|
|
21220
|
+
const fileStat = deps.stat(join8(dir, file2));
|
|
21221
|
+
if (fileStat !== null && !fileStat.isDirectory) count += 1;
|
|
21222
|
+
}
|
|
21223
|
+
return count;
|
|
21136
21224
|
}
|
|
21137
|
-
function
|
|
21138
|
-
|
|
21225
|
+
function formatBytes(bytes) {
|
|
21226
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
21227
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
21228
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MiB`;
|
|
21229
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GiB`;
|
|
21139
21230
|
}
|
|
21140
|
-
function
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21231
|
+
function dirSize(dir, deps, depth = 0) {
|
|
21232
|
+
const dirStat = deps.stat(dir);
|
|
21233
|
+
if (dirStat === null) return null;
|
|
21234
|
+
if (!dirStat.isDirectory) return dirStat.size;
|
|
21235
|
+
if (depth > 4) return 0;
|
|
21236
|
+
const files = deps.listDir(dir);
|
|
21237
|
+
if (files === null) return null;
|
|
21238
|
+
let total = 0;
|
|
21239
|
+
for (const file2 of files) {
|
|
21240
|
+
const childSize = dirSize(join8(dir, file2), deps, depth + 1);
|
|
21241
|
+
if (childSize !== null) total += childSize;
|
|
21147
21242
|
}
|
|
21148
|
-
return
|
|
21149
|
-
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
21243
|
+
return total;
|
|
21244
|
+
}
|
|
21245
|
+
function verboseChecks(options, deps) {
|
|
21246
|
+
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
21247
|
+
const logsDir = join8(dataDir, "logs");
|
|
21248
|
+
const chunksDir = join8(dataDir, "chunks");
|
|
21249
|
+
const providerCandidates = providerConfigCandidates(options, deps);
|
|
21250
|
+
const captureMode = deps.env["AGENT_INSPECTOR_MODE"] ?? deps.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? "simple";
|
|
21251
|
+
const logCount = countFilesInDir(logsDir, deps);
|
|
21252
|
+
const logSize = dirSize(logsDir, deps);
|
|
21253
|
+
const chunkSize = dirSize(chunksDir, deps);
|
|
21254
|
+
const homeDir = deps.platform === "win32" ? deps.env["USERPROFILE"] ?? deps.env["APPDATA"] ?? null : deps.env["HOME"] ?? null;
|
|
21255
|
+
const claudeSkill = homeDir === null ? null : join8(homeDir, ".claude", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
21256
|
+
const claudeCommand = homeDir === null ? null : join8(homeDir, ".claude", "commands", "agent-inspector-onboard.md");
|
|
21257
|
+
const codexSkill = homeDir === null ? null : join8(homeDir, ".codex", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
21258
|
+
const hasClaudeOnboarding = claudeSkill !== null && claudeCommand !== null && deps.exists(claudeSkill) && deps.exists(claudeCommand);
|
|
21259
|
+
const hasCodexOnboarding = codexSkill !== null && deps.exists(codexSkill);
|
|
21260
|
+
return [
|
|
21261
|
+
check2("Data directory", "pass", dataDir),
|
|
21262
|
+
check2("Provider config search", "pass", providerCandidates.join(", ")),
|
|
21263
|
+
check2("Capture mode env", "pass", captureMode),
|
|
21264
|
+
check2(
|
|
21265
|
+
"Log storage",
|
|
21266
|
+
logCount === null ? "warn" : "pass",
|
|
21267
|
+
logCount === null ? `Log directory was not found or could not be read: ${logsDir}` : `${logCount} file(s), ${formatBytes(logSize ?? 0)} under ${logsDir}`
|
|
21268
|
+
),
|
|
21269
|
+
check2(
|
|
21270
|
+
"Chunk storage",
|
|
21271
|
+
chunkSize === null ? "warn" : "pass",
|
|
21272
|
+
chunkSize === null ? `Chunk directory was not found or could not be read: ${chunksDir}` : `${formatBytes(chunkSize)} under ${chunksDir}`
|
|
21273
|
+
),
|
|
21274
|
+
check2(
|
|
21275
|
+
"Claude onboarding",
|
|
21276
|
+
hasClaudeOnboarding ? "pass" : "warn",
|
|
21277
|
+
hasClaudeOnboarding ? "Claude Code skill and slash command are installed." : "Claude Code onboarding files were not found.",
|
|
21278
|
+
"Run `agent-inspector onboard --force` after installing or upgrading."
|
|
21279
|
+
),
|
|
21280
|
+
check2(
|
|
21281
|
+
"Codex onboarding",
|
|
21282
|
+
hasCodexOnboarding ? "pass" : "warn",
|
|
21283
|
+
hasCodexOnboarding ? "Codex onboarding skill is installed." : "Codex onboarding skill was not found.",
|
|
21284
|
+
"Run `agent-inspector onboard --force` after installing or upgrading."
|
|
21285
|
+
)
|
|
21286
|
+
];
|
|
21153
21287
|
}
|
|
21154
|
-
function
|
|
21155
|
-
|
|
21156
|
-
const
|
|
21157
|
-
if (
|
|
21158
|
-
|
|
21159
|
-
|
|
21288
|
+
function checkExtensionSource(rootDir, deps) {
|
|
21289
|
+
const extensionDir = join8(rootDir, "extensions", "chrome");
|
|
21290
|
+
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(join8(extensionDir, file2)));
|
|
21291
|
+
if (missing.length === 0) {
|
|
21292
|
+
return check2(
|
|
21293
|
+
"Chrome extension source",
|
|
21294
|
+
"pass",
|
|
21295
|
+
"Manifest, side panel, service worker, and icons are present."
|
|
21296
|
+
);
|
|
21297
|
+
}
|
|
21298
|
+
return check2(
|
|
21299
|
+
"Chrome extension source",
|
|
21300
|
+
"fail",
|
|
21301
|
+
`Missing ${missing.length} Chrome extension file(s): ${missing.join(", ")}`,
|
|
21302
|
+
"Restore the extension files before packaging or loading unpacked."
|
|
21303
|
+
);
|
|
21160
21304
|
}
|
|
21161
|
-
|
|
21305
|
+
function extensionManifestVersion(rootDir, deps) {
|
|
21306
|
+
const manifestText = deps.readText(join8(rootDir, "extensions", "chrome", "manifest.json"));
|
|
21307
|
+
if (manifestText === null) return null;
|
|
21162
21308
|
try {
|
|
21163
|
-
const
|
|
21164
|
-
return
|
|
21309
|
+
const manifest = JSON.parse(manifestText);
|
|
21310
|
+
if (!isObject3(manifest)) return null;
|
|
21311
|
+
const version2 = manifest["version"];
|
|
21312
|
+
return typeof version2 === "string" && version2 !== "" ? version2 : null;
|
|
21165
21313
|
} catch {
|
|
21166
21314
|
return null;
|
|
21167
21315
|
}
|
|
21168
21316
|
}
|
|
21169
|
-
|
|
21170
|
-
const
|
|
21171
|
-
const
|
|
21172
|
-
|
|
21173
|
-
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21178
|
-
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
}
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
|
|
21187
|
-
|
|
21188
|
-
const value = await readJson2(response);
|
|
21189
|
-
if (!response.ok) {
|
|
21190
|
-
return { kind: "http-error", status: response.status, detail: readErrorDetail(value) };
|
|
21317
|
+
function checkExtensionPackage(rootDir, deps) {
|
|
21318
|
+
const distDir = join8(rootDir, "dist", "chrome-extension");
|
|
21319
|
+
const files = deps.listDir(distDir);
|
|
21320
|
+
if (files === null) {
|
|
21321
|
+
return check2(
|
|
21322
|
+
"Chrome extension package",
|
|
21323
|
+
"warn",
|
|
21324
|
+
"No packaged Chrome extension artifact was found.",
|
|
21325
|
+
"Run `npm run extension:zip` before Chrome Web Store upload."
|
|
21326
|
+
);
|
|
21327
|
+
}
|
|
21328
|
+
const manifestVersion = extensionManifestVersion(rootDir, deps);
|
|
21329
|
+
if (manifestVersion !== null) {
|
|
21330
|
+
const zipName = `agent-inspector-companion-v${manifestVersion}.zip`;
|
|
21331
|
+
const checksumName = `${zipName}.sha256`;
|
|
21332
|
+
const hasZip = files.includes(zipName);
|
|
21333
|
+
const hasChecksum = files.includes(checksumName);
|
|
21334
|
+
if (hasZip && hasChecksum) {
|
|
21335
|
+
return check2("Chrome extension package", "pass", `${zipName} with checksum is present.`);
|
|
21191
21336
|
}
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
|
|
21337
|
+
if (hasZip) {
|
|
21338
|
+
return check2(
|
|
21339
|
+
"Chrome extension package",
|
|
21340
|
+
"warn",
|
|
21341
|
+
`${zipName} exists but no .sha256 checksum was found.`,
|
|
21342
|
+
"Run `npm run extension:zip` to regenerate the zip and checksum."
|
|
21343
|
+
);
|
|
21344
|
+
}
|
|
21345
|
+
return check2(
|
|
21346
|
+
"Chrome extension package",
|
|
21347
|
+
"warn",
|
|
21348
|
+
`Current Chrome extension package was not found: ${zipName}`,
|
|
21349
|
+
"Run `npm run extension:zip` before Chrome Web Store upload."
|
|
21350
|
+
);
|
|
21199
21351
|
}
|
|
21352
|
+
const zip = files.find((file2) => file2.endsWith(".zip"));
|
|
21353
|
+
const checksum = files.find((file2) => file2.endsWith(".zip.sha256"));
|
|
21354
|
+
if (zip !== void 0 && checksum !== void 0) {
|
|
21355
|
+
return check2("Chrome extension package", "pass", `${zip} with checksum is present.`);
|
|
21356
|
+
}
|
|
21357
|
+
if (zip !== void 0) {
|
|
21358
|
+
return check2(
|
|
21359
|
+
"Chrome extension package",
|
|
21360
|
+
"warn",
|
|
21361
|
+
`${zip} exists but no .sha256 checksum was found.`,
|
|
21362
|
+
"Run `npm run extension:zip` to regenerate the zip and checksum."
|
|
21363
|
+
);
|
|
21364
|
+
}
|
|
21365
|
+
return check2(
|
|
21366
|
+
"Chrome extension package",
|
|
21367
|
+
"warn",
|
|
21368
|
+
"No Chrome extension zip artifact was found.",
|
|
21369
|
+
"Run `npm run extension:zip` before Chrome Web Store upload."
|
|
21370
|
+
);
|
|
21200
21371
|
}
|
|
21201
|
-
function
|
|
21202
|
-
const
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21208
|
-
|
|
21372
|
+
async function buildDoctorReport(options, deps = createDoctorDeps()) {
|
|
21373
|
+
const rootDir = findProjectRoot(deps.cwd, deps);
|
|
21374
|
+
const coreChecks = [
|
|
21375
|
+
await checkProxyHealth(options.port, deps),
|
|
21376
|
+
checkProviderConfig(options, deps),
|
|
21377
|
+
checkPackage(rootDir, deps)
|
|
21378
|
+
];
|
|
21379
|
+
const checksWithExtension = options.chromeExtension ? [...coreChecks, checkExtensionSource(rootDir, deps), checkExtensionPackage(rootDir, deps)] : coreChecks;
|
|
21380
|
+
const checks = options.verbose ? [...checksWithExtension, ...verboseChecks(options, deps)] : checksWithExtension;
|
|
21381
|
+
const passCount = checks.filter((item) => item.severity === "pass").length;
|
|
21382
|
+
const warnCount = checks.filter((item) => item.severity === "warn").length;
|
|
21383
|
+
const failCount = checks.filter((item) => item.severity === "fail").length;
|
|
21209
21384
|
return {
|
|
21210
|
-
|
|
21211
|
-
|
|
21212
|
-
|
|
21213
|
-
|
|
21214
|
-
|
|
21215
|
-
runtimeScoped: true
|
|
21385
|
+
checks,
|
|
21386
|
+
passCount,
|
|
21387
|
+
warnCount,
|
|
21388
|
+
failCount,
|
|
21389
|
+
exitCode: failCount > 0 ? 1 : 0
|
|
21216
21390
|
};
|
|
21217
21391
|
}
|
|
21218
|
-
function
|
|
21219
|
-
const
|
|
21220
|
-
const
|
|
21221
|
-
|
|
21222
|
-
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21228
|
-
|
|
21392
|
+
function formatDoctorReport(report) {
|
|
21393
|
+
const lines = ["agent-inspector doctor", ""];
|
|
21394
|
+
for (const item of report.checks) {
|
|
21395
|
+
lines.push(`${item.severity.toUpperCase().padEnd(4)} ${item.name}: ${item.message}`);
|
|
21396
|
+
if (item.hint !== null) {
|
|
21397
|
+
lines.push(` ${item.hint}`);
|
|
21398
|
+
}
|
|
21399
|
+
}
|
|
21400
|
+
lines.push("");
|
|
21401
|
+
lines.push(
|
|
21402
|
+
`Summary: ${report.passCount} pass, ${report.warnCount} warn, ${report.failCount} fail`
|
|
21403
|
+
);
|
|
21404
|
+
return `${lines.join("\n")}
|
|
21229
21405
|
`;
|
|
21230
21406
|
}
|
|
21231
|
-
function
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
`);
|
|
21235
|
-
return;
|
|
21236
|
-
}
|
|
21237
|
-
io.stderr(`agent-inspector alias: ${message}
|
|
21238
|
-
`);
|
|
21407
|
+
function formatDoctorJson(report) {
|
|
21408
|
+
return `${JSON.stringify(report, null, 2)}
|
|
21409
|
+
`;
|
|
21239
21410
|
}
|
|
21240
|
-
function
|
|
21241
|
-
return
|
|
21411
|
+
function doctorHelp() {
|
|
21412
|
+
return [
|
|
21413
|
+
"agent-inspector doctor",
|
|
21414
|
+
"",
|
|
21415
|
+
"Usage:",
|
|
21416
|
+
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--verbose] [--chrome-extension]",
|
|
21417
|
+
"",
|
|
21418
|
+
"Checks local backend health, provider config presence, and package metadata.",
|
|
21419
|
+
"The backend check probes the local health endpoint and does not require a composed Web UI.",
|
|
21420
|
+
"Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
|
|
21421
|
+
"Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
|
|
21422
|
+
"Pass --verbose to include local paths, capture mode, and log storage checks.",
|
|
21423
|
+
"Pass --chrome-extension to also check Chrome companion source and packaged artifacts.",
|
|
21424
|
+
""
|
|
21425
|
+
].join("\n");
|
|
21242
21426
|
}
|
|
21243
|
-
async function
|
|
21244
|
-
|
|
21245
|
-
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
|
|
21249
|
-
|
|
21250
|
-
"
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
|
|
21254
|
-
}
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
probe.timedOut ? "request_timeout" : "runtime_unreachable",
|
|
21260
|
-
`${probe.timedOut ? "timed out contacting" : "cannot reach"} ${origin}. Confirm the existing runtime is running and the --host/--port values are correct; no runtime was started.`
|
|
21261
|
-
);
|
|
21262
|
-
return 1;
|
|
21263
|
-
}
|
|
21264
|
-
if (probe.kind === "http-error" && (probe.status === 404 || probe.status === 405)) {
|
|
21265
|
-
writeError2(io, options.json, "unsupported_runtime", oldRuntimeMessage(origin));
|
|
21266
|
-
return 1;
|
|
21267
|
-
}
|
|
21427
|
+
async function fetchHealth(port, basePath) {
|
|
21428
|
+
const controller = new AbortController();
|
|
21429
|
+
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
21430
|
+
try {
|
|
21431
|
+
const response = await fetch(
|
|
21432
|
+
appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/api/health", basePath),
|
|
21433
|
+
{
|
|
21434
|
+
cache: "no-store",
|
|
21435
|
+
signal: controller.signal
|
|
21436
|
+
}
|
|
21437
|
+
);
|
|
21438
|
+
return { ok: response.ok, status: response.status };
|
|
21439
|
+
} catch {
|
|
21440
|
+
return { ok: false, status: null };
|
|
21441
|
+
} finally {
|
|
21442
|
+
clearTimeout(timeout);
|
|
21268
21443
|
}
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
|
|
21444
|
+
}
|
|
21445
|
+
function isPortOpen(port) {
|
|
21446
|
+
return new Promise((resolveOpen) => {
|
|
21447
|
+
const socket = createConnection({ host: "127.0.0.1", port });
|
|
21448
|
+
const finish = (value) => {
|
|
21449
|
+
socket.removeAllListeners();
|
|
21450
|
+
socket.destroy();
|
|
21451
|
+
resolveOpen(value);
|
|
21452
|
+
};
|
|
21453
|
+
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
21454
|
+
socket.once("connect", () => finish(true));
|
|
21455
|
+
socket.once("timeout", () => finish(false));
|
|
21456
|
+
socket.once("error", () => finish(false));
|
|
21457
|
+
});
|
|
21458
|
+
}
|
|
21459
|
+
function readText(path2) {
|
|
21460
|
+
try {
|
|
21461
|
+
return readFileSync4(path2, "utf8");
|
|
21462
|
+
} catch {
|
|
21463
|
+
return null;
|
|
21272
21464
|
}
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21279
|
-
);
|
|
21280
|
-
return 1;
|
|
21465
|
+
}
|
|
21466
|
+
function listDir(path2) {
|
|
21467
|
+
try {
|
|
21468
|
+
return readdirSync5(path2);
|
|
21469
|
+
} catch {
|
|
21470
|
+
return null;
|
|
21281
21471
|
}
|
|
21282
|
-
const detail = result.detail === null ? "the runtime rejected the request" : result.detail;
|
|
21283
|
-
writeError2(
|
|
21284
|
-
io,
|
|
21285
|
-
options.json,
|
|
21286
|
-
"runtime_rejected_request",
|
|
21287
|
-
`${detail} (HTTP ${result.status}). Check the running instance and retry; no runtime was started.`
|
|
21288
|
-
);
|
|
21289
|
-
return 1;
|
|
21290
21472
|
}
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21473
|
+
function stat3(path2) {
|
|
21474
|
+
try {
|
|
21475
|
+
const fileStat = statSync4(path2);
|
|
21476
|
+
return { isDirectory: fileStat.isDirectory(), size: fileStat.size };
|
|
21477
|
+
} catch {
|
|
21478
|
+
return null;
|
|
21296
21479
|
}
|
|
21480
|
+
}
|
|
21481
|
+
function createDoctorDeps() {
|
|
21482
|
+
return {
|
|
21483
|
+
env: process.env,
|
|
21484
|
+
platform: process.platform,
|
|
21485
|
+
cwd: process.cwd(),
|
|
21486
|
+
exists: existsSync7,
|
|
21487
|
+
readText,
|
|
21488
|
+
listDir,
|
|
21489
|
+
stat: stat3,
|
|
21490
|
+
fetchHealth,
|
|
21491
|
+
isPortOpen
|
|
21492
|
+
};
|
|
21493
|
+
}
|
|
21494
|
+
async function runDoctor(argv) {
|
|
21495
|
+
const parsed = parseDoctorArgs(argv);
|
|
21297
21496
|
if (parsed.kind === "error") {
|
|
21298
|
-
|
|
21299
|
-
|
|
21300
|
-
${aliasHelp()}`);
|
|
21497
|
+
process.stderr.write(`${parsed.message}
|
|
21498
|
+
`);
|
|
21301
21499
|
return 2;
|
|
21302
21500
|
}
|
|
21303
|
-
|
|
21304
|
-
|
|
21305
|
-
|
|
21306
|
-
|
|
21307
|
-
|
|
21308
|
-
|
|
21309
|
-
const output = buildOutput(origin, options.action, result.snapshot);
|
|
21310
|
-
io.stdout(options.json ? `${JSON.stringify(output, null, 2)}
|
|
21311
|
-
` : formatHumanOutput(output));
|
|
21312
|
-
return 0;
|
|
21313
|
-
}
|
|
21314
|
-
case "network-error":
|
|
21315
|
-
writeError2(
|
|
21316
|
-
io,
|
|
21317
|
-
options.json,
|
|
21318
|
-
result.timedOut ? "request_timeout" : "runtime_unreachable",
|
|
21319
|
-
`${result.timedOut ? "timed out contacting" : "cannot reach"} ${origin}. Confirm the existing runtime is running and the --host/--port values are correct; no runtime was started.`
|
|
21320
|
-
);
|
|
21321
|
-
return 1;
|
|
21322
|
-
case "invalid-response":
|
|
21323
|
-
writeError2(
|
|
21324
|
-
io,
|
|
21325
|
-
options.json,
|
|
21326
|
-
"invalid_runtime_response",
|
|
21327
|
-
`the runtime at ${origin} returned an invalid alias response. Upgrade and restart that instance, then retry; no runtime was started.`
|
|
21328
|
-
);
|
|
21329
|
-
return 1;
|
|
21330
|
-
case "http-error":
|
|
21331
|
-
return handleHttpError(options, endpoint2, origin, result, fetchImpl, io);
|
|
21501
|
+
if (parsed.options.help) {
|
|
21502
|
+
process.stdout.write(doctorHelp());
|
|
21503
|
+
return 0;
|
|
21504
|
+
}
|
|
21505
|
+
if (parsed.options.fix) {
|
|
21506
|
+
migrateLegacyDataDirs();
|
|
21332
21507
|
}
|
|
21508
|
+
const report = await buildDoctorReport(parsed.options);
|
|
21509
|
+
process.stdout.write(parsed.options.json ? formatDoctorJson(report) : formatDoctorReport(report));
|
|
21510
|
+
return report.exitCode;
|
|
21333
21511
|
}
|
|
21334
|
-
var
|
|
21335
|
-
var
|
|
21336
|
-
"src/cli/
|
|
21512
|
+
var DEFAULT_PORT3, PROBE_TIMEOUT_MS, EXTENSION_REQUIRED_FILES;
|
|
21513
|
+
var init_doctor = __esm({
|
|
21514
|
+
"src/cli/doctor.ts"() {
|
|
21337
21515
|
"use strict";
|
|
21338
21516
|
init_basePath();
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21517
|
+
init_dataDir();
|
|
21518
|
+
DEFAULT_PORT3 = 9527;
|
|
21519
|
+
PROBE_TIMEOUT_MS = 2e3;
|
|
21520
|
+
EXTENSION_REQUIRED_FILES = [
|
|
21521
|
+
"manifest.json",
|
|
21522
|
+
"sidepanel.html",
|
|
21523
|
+
"sidepanel.js",
|
|
21524
|
+
"sidepanel.css",
|
|
21525
|
+
"service-worker.js",
|
|
21526
|
+
"icons/icon.svg",
|
|
21527
|
+
"icons/icon-16.png",
|
|
21528
|
+
"icons/icon-32.png",
|
|
21529
|
+
"icons/icon-48.png",
|
|
21530
|
+
"icons/icon-128.png"
|
|
21531
|
+
];
|
|
21351
21532
|
}
|
|
21352
21533
|
});
|
|
21353
21534
|
|
|
21354
|
-
// src/cli.ts
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
version: "3.0.47",
|
|
21366
|
-
type: "module",
|
|
21367
|
-
description: "Agent observability and knowledge capture layer for AI coding tools.",
|
|
21368
|
-
license: "MIT",
|
|
21369
|
-
engines: {
|
|
21370
|
-
node: ">=22.0.0"
|
|
21371
|
-
},
|
|
21372
|
-
author: "TonyClaw",
|
|
21373
|
-
repository: {
|
|
21374
|
-
type: "git",
|
|
21375
|
-
url: "git+https://gitcode.com/TonyClaw/agent-inspector.git"
|
|
21376
|
-
},
|
|
21377
|
-
homepage: "https://gitcode.com/TonyClaw/agent-inspector#readme",
|
|
21378
|
-
workspaces: [
|
|
21379
|
-
"packages/*"
|
|
21380
|
-
],
|
|
21381
|
-
bugs: {
|
|
21382
|
-
url: "https://gitcode.com/TonyClaw/agent-inspector/issues"
|
|
21383
|
-
},
|
|
21384
|
-
publishConfig: {
|
|
21385
|
-
access: "public",
|
|
21386
|
-
registry: "https://registry.npmjs.org/"
|
|
21387
|
-
},
|
|
21388
|
-
keywords: [
|
|
21389
|
-
"claude",
|
|
21390
|
-
"anthropic",
|
|
21391
|
-
"openai",
|
|
21392
|
-
"proxy",
|
|
21393
|
-
"api-inspector",
|
|
21394
|
-
"agent-observability",
|
|
21395
|
-
"agent-memory",
|
|
21396
|
-
"debugging",
|
|
21397
|
-
"llm",
|
|
21398
|
-
"ai-coding-tools"
|
|
21399
|
-
],
|
|
21400
|
-
bin: {
|
|
21401
|
-
"agent-inspector": "bin/agent-inspector.js"
|
|
21402
|
-
},
|
|
21403
|
-
files: [
|
|
21404
|
-
"bin",
|
|
21405
|
-
"src",
|
|
21406
|
-
"!src/**/*.test.ts",
|
|
21407
|
-
"!src/**/*.stories.tsx",
|
|
21408
|
-
"!src/**/__fixtures__",
|
|
21409
|
-
"!src/ui/routeTree.gen.ts",
|
|
21410
|
-
"!src/backend/routeTree.gen.ts",
|
|
21411
|
-
"scripts/setup-windows-runtime.mjs",
|
|
21412
|
-
"scripts/setup-agent-skills.mjs",
|
|
21413
|
-
"scripts/generate-theme-scales.mjs",
|
|
21414
|
-
"docs/instance-control.openapi.yaml",
|
|
21415
|
-
"styles",
|
|
21416
|
-
".output",
|
|
21417
|
-
"!.output/*.exe"
|
|
21418
|
-
],
|
|
21419
|
-
scripts: {
|
|
21420
|
-
dev: "node scripts/dev-decoupled.mjs",
|
|
21421
|
-
"dev:ui": "vite dev --config vite.ui.config.ts",
|
|
21422
|
-
"dev:backend": "node scripts/dev-backend.mjs",
|
|
21423
|
-
"test:smoke": "bun scripts/run-test-suite.mjs smoke",
|
|
21424
|
-
"test:fast": "bun scripts/run-test-suite.mjs smoke",
|
|
21425
|
-
"test:unit": "bun scripts/run-test-suite.mjs unit",
|
|
21426
|
-
"test:integration": "bun scripts/run-test-suite.mjs integration",
|
|
21427
|
-
"test:changed": "bun scripts/run-test-suite.mjs changed",
|
|
21428
|
-
"test:all": "bun scripts/run-test-suite.mjs all",
|
|
21429
|
-
"test:compatibility-tooling": "node --test scripts/compatibility/*.test.mjs",
|
|
21430
|
-
start: "node .output/cli.js",
|
|
21431
|
-
"start:backend": "node .output/cli.js --backend-only --no-open",
|
|
21432
|
-
"preview:ui": "vite preview --config vite.ui.config.ts",
|
|
21433
|
-
"clean:output": "node scripts/clean-output.mjs",
|
|
21434
|
-
build: "bun clean:output && bun build:backend && bun build:ui && bun smoke:ui-preview && bun build:workers && bun build:cli && bun build:mcp && bun check:bundle && bun check:boundaries",
|
|
21435
|
-
"build:backend": "vite build --config vite.backend.config.ts",
|
|
21436
|
-
"build:ui": "vite build --config vite.ui.config.ts",
|
|
21437
|
-
"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",
|
|
21438
|
-
"smoke:workers": "node scripts/smoke-production-workers.mjs",
|
|
21439
|
-
"smoke:packed-workers": "node scripts/smoke-packed-workers.mjs",
|
|
21440
|
-
"smoke:instances": "node scripts/smoke-windows-instances.mjs",
|
|
21441
|
-
"smoke:coding-agent-instances": "node scripts/smoke-coding-agent-instances.mjs",
|
|
21442
|
-
"smoke:ui-preview": "node scripts/smoke-decoupled-ui-preview.mjs",
|
|
21443
|
-
"check:bundle": "node scripts/check-client-bundle.mjs",
|
|
21444
|
-
"check:boundaries": "node scripts/check-runtime-boundaries.mjs",
|
|
21445
|
-
"compatibility:bdd": "node scripts/lint-bdd-catalog.mjs",
|
|
21446
|
-
"compatibility:compare": "node scripts/compatibility/cli.mjs compare",
|
|
21447
|
-
"compatibility:drift": "node scripts/check-backend-compatibility.mjs",
|
|
21448
|
-
"compatibility:fixtures": "bun scripts/validate-compatibility-fixtures.ts",
|
|
21449
|
-
"compatibility:freeze": "node scripts/freeze-backend-baseline.mjs",
|
|
21450
|
-
"compatibility:run": "node scripts/compatibility/cli.mjs run",
|
|
21451
|
-
"compatibility:performance": "node scripts/compatibility/performance-cli.mjs",
|
|
21452
|
-
"compatibility:performance:verify": "node scripts/compatibility/performance-cli.mjs verify",
|
|
21453
|
-
"build:cli": "npx esbuild src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=.output/cli.js",
|
|
21454
|
-
"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",
|
|
21455
|
-
"rust:build-package": "node scripts/rust/build-platform.mjs",
|
|
21456
|
-
"rust:stage-package": "node scripts/rust/stage-platform.mjs",
|
|
21457
|
-
"rust:verify-package": "node scripts/rust/verify-platform.mjs",
|
|
21458
|
-
"rust:pack-package": "node scripts/rust/pack-platform.mjs",
|
|
21459
|
-
"rust:native-artifact": "node scripts/rust/create-native-artifact.mjs",
|
|
21460
|
-
"rust:native:aggregate": "node scripts/rust/aggregate-native-artifacts.mjs",
|
|
21461
|
-
"test:rust-packaging": "bun test scripts/rust/npmPackaging.test.ts scripts/rust/nativeReleaseContract.test.ts scripts/rust/nativeStartupSmoke.test.ts scripts/releaseCore.test.ts",
|
|
21462
|
-
"rust:packages:check": "node scripts/rust/sync-package-versions.mjs",
|
|
21463
|
-
"rust:packages:sync": "node scripts/rust/sync-package-versions.mjs --write",
|
|
21464
|
-
"themes:generate": "node scripts/generate-theme-scales.mjs",
|
|
21465
|
-
postinstall: "node scripts/setup-windows-runtime.mjs && node scripts/setup-agent-skills.mjs",
|
|
21466
|
-
prepublishOnly: "node scripts/rust/require-release-orchestrator.mjs",
|
|
21467
|
-
typecheck: "tsc --noEmit --incremental --tsBuildInfoFile .cache/tsconfig.tsbuildinfo",
|
|
21468
|
-
lint: "eslint . --cache --cache-location .cache/eslintcache",
|
|
21469
|
-
format: "biome format --write .",
|
|
21470
|
-
"format:check": "biome format .",
|
|
21471
|
-
"verify:fast": "bun scripts/run-static-checks.mjs --format && bun test:changed",
|
|
21472
|
-
"extension:zip": "node scripts/pack-chrome-extension.mjs",
|
|
21473
|
-
"extension:release": "node scripts/release-chrome-extension.mjs",
|
|
21474
|
-
release: "node scripts/release.mjs",
|
|
21475
|
-
knip: "knip",
|
|
21476
|
-
check: "bun scripts/run-static-checks.mjs --format && bun check:boundaries && bun compatibility:bdd && bun rust:packages:check",
|
|
21477
|
-
prepare: "husky",
|
|
21478
|
-
ladle: "ladle serve",
|
|
21479
|
-
"ladle:build": "ladle build"
|
|
21480
|
-
},
|
|
21481
|
-
dependencies: {
|
|
21482
|
-
"@mariozechner/pi-agent-core": "^0.73.1",
|
|
21483
|
-
"@mariozechner/pi-ai": "^0.73.1",
|
|
21484
|
-
"@modelcontextprotocol/server": "^2.0.0-beta.4",
|
|
21485
|
-
"@tailwindcss/typography": "^0.5.20",
|
|
21486
|
-
"@tanstack/react-router": "^1.170.18",
|
|
21487
|
-
"@tanstack/react-start": "^1.168.28",
|
|
21488
|
-
"@tanstack/react-virtual": "^3.14.6",
|
|
21489
|
-
"class-variance-authority": "^0.7.1",
|
|
21490
|
-
clsx: "^2.1.1",
|
|
21491
|
-
conf: "^15.1.0",
|
|
21492
|
-
diff: "^9.0.0",
|
|
21493
|
-
jszip: "^3.10.1",
|
|
21494
|
-
"lucide-react": "^0.563.0",
|
|
21495
|
-
"radix-ui": "^1.6.2",
|
|
21496
|
-
react: "^19.2.7",
|
|
21497
|
-
"react-dom": "^19.2.7",
|
|
21498
|
-
"react-markdown": "^10.1.0",
|
|
21499
|
-
swr: "^2.4.2",
|
|
21500
|
-
"tailwind-merge": "^3.6.0",
|
|
21501
|
-
three: "0.185.1",
|
|
21502
|
-
"tw-animate-css": "^1.4.0",
|
|
21503
|
-
zod: "^4.4.3"
|
|
21504
|
-
},
|
|
21505
|
-
optionalDependencies: {
|
|
21506
|
-
"@tonyclaw/agent-inspector-backend-darwin-arm64": "3.0.47",
|
|
21507
|
-
"@tonyclaw/agent-inspector-backend-linux-x64-gnu": "3.0.47",
|
|
21508
|
-
"@tonyclaw/agent-inspector-backend-win32-x64-msvc": "3.0.47",
|
|
21509
|
-
"better-sqlite3": "^12.11.1",
|
|
21510
|
-
rcedit: "4.0.1"
|
|
21511
|
-
},
|
|
21512
|
-
devDependencies: {
|
|
21513
|
-
"@biomejs/biome": "^2.5.3",
|
|
21514
|
-
"@eslint/js": "^10.0.1",
|
|
21515
|
-
"@ladle/react": "^5.1.1",
|
|
21516
|
-
"@radix-ui/colors": "^3.0.0",
|
|
21517
|
-
"@tailwindcss/vite": "^4.3.2",
|
|
21518
|
-
"@tanstack/router-plugin": "^1.168.20",
|
|
21519
|
-
"@types/bun": "^1.3.14",
|
|
21520
|
-
"@types/react": "^19.2.17",
|
|
21521
|
-
"@types/react-dom": "^19.2.3",
|
|
21522
|
-
"@types/three": "0.185.1",
|
|
21523
|
-
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
21524
|
-
"@typescript-eslint/parser": "^8.64.0",
|
|
21525
|
-
"@vitejs/plugin-react": "^5.2.0",
|
|
21526
|
-
ajv: "8.20.0",
|
|
21527
|
-
"ajv-formats": "3.0.1",
|
|
21528
|
-
"colorjs.io": "^0.7.0",
|
|
21529
|
-
esbuild: "^0.28.1",
|
|
21530
|
-
eslint: "^9.39.5",
|
|
21531
|
-
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
21532
|
-
"eslint-plugin-functional": "^9.0.5",
|
|
21533
|
-
"eslint-plugin-unicorn": "^63.0.0",
|
|
21534
|
-
husky: "^9.1.7",
|
|
21535
|
-
knip: "^5.88.1",
|
|
21536
|
-
nitro: "^3.0.260610-beta",
|
|
21537
|
-
playwright: "^1.61.1",
|
|
21538
|
-
tailwindcss: "^4.3.2",
|
|
21539
|
-
typescript: "^5.9.3",
|
|
21540
|
-
vite: "^7.3.6",
|
|
21541
|
-
"vite-tsconfig-paths": "^6.1.1"
|
|
21542
|
-
},
|
|
21543
|
-
overrides: {
|
|
21544
|
-
undici: ">=7.24.0 <8.0.0",
|
|
21545
|
-
rollup: ">=4.59.0 <5.0.0",
|
|
21546
|
-
postcss: ">=8.5.10 <9.0.0",
|
|
21547
|
-
"js-yaml": ">=4.1.2 <5.0.0",
|
|
21548
|
-
flatted: ">=3.4.0 <4.0.0",
|
|
21549
|
-
"smol-toml": ">=1.6.1 <2.0.0",
|
|
21550
|
-
koa: ">=2.16.4 <3.0.0",
|
|
21551
|
-
"@babel/core": ">=7.29.1 <8.0.0",
|
|
21552
|
-
"@typescript-eslint/typescript-estree": "^8.63.0",
|
|
21553
|
-
vite: "^7.3.6"
|
|
21554
|
-
}
|
|
21555
|
-
};
|
|
21556
|
-
|
|
21557
|
-
// src/cli.ts
|
|
21558
|
-
init_backendImplementation();
|
|
21559
|
-
init_basePath();
|
|
21560
|
-
init_managedInstance();
|
|
21561
|
-
init_networkHints();
|
|
21562
|
-
|
|
21563
|
-
// src/cli/startupOutput.ts
|
|
21564
|
-
function shouldSuppressServerOutputLine(line, context) {
|
|
21565
|
-
if (!context.enableIdentityProxy) return false;
|
|
21566
|
-
if (context.publicPort === context.upstreamPort) return false;
|
|
21567
|
-
const lowerLine = line.toLowerCase();
|
|
21568
|
-
return lowerLine.includes("listening on") && lowerLine.includes(`:${context.upstreamPort}`);
|
|
21569
|
-
}
|
|
21570
|
-
|
|
21571
|
-
// src/cli/rustBackendPackage.ts
|
|
21572
|
-
import { spawnSync } from "node:child_process";
|
|
21573
|
-
init_zod();
|
|
21574
|
-
import { createHash } from "node:crypto";
|
|
21575
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
21576
|
-
import { createRequire } from "node:module";
|
|
21577
|
-
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
21578
|
-
import { fileURLToPath } from "node:url";
|
|
21579
|
-
var RUST_BACKEND_IDENTITY_FLAG = "--agent-inspector-package-identity";
|
|
21580
|
-
var RUST_BACKEND_CLI_PROTOCOL = "agent-inspector-cli-backend-v1";
|
|
21581
|
-
var RUST_BACKEND_IDENTITY_TIMEOUT_MS = 2e3;
|
|
21582
|
-
var RustBackendIdentitySchema = external_exports.object({
|
|
21583
|
-
schemaVersion: external_exports.literal(1),
|
|
21584
|
-
name: external_exports.literal("agent-inspector-backend"),
|
|
21585
|
-
packageVersion: external_exports.string().min(1),
|
|
21586
|
-
target: external_exports.string().min(1),
|
|
21587
|
-
binary: external_exports.string().min(1),
|
|
21588
|
-
cliProtocol: external_exports.literal(RUST_BACKEND_CLI_PROTOCOL),
|
|
21589
|
-
productionReady: external_exports.literal(true),
|
|
21590
|
-
capabilities: external_exports.object({
|
|
21591
|
-
nodeProtectedIngress: external_exports.literal(true),
|
|
21592
|
-
basePath: external_exports.literal(true),
|
|
21593
|
-
namedInstances: external_exports.literal(true),
|
|
21594
|
-
gracefulShutdown: external_exports.literal(true)
|
|
21595
|
-
}).strict()
|
|
21596
|
-
}).strict();
|
|
21597
|
-
var RustBackendPackageJsonSchema = external_exports.object({
|
|
21598
|
-
name: external_exports.string().min(1),
|
|
21599
|
-
version: external_exports.string().min(1),
|
|
21600
|
-
os: external_exports.array(external_exports.string()).min(1),
|
|
21601
|
-
cpu: external_exports.array(external_exports.string()).min(1),
|
|
21602
|
-
libc: external_exports.array(external_exports.string()).optional(),
|
|
21603
|
-
agentInspectorRustBackend: external_exports.object({
|
|
21604
|
-
schemaVersion: external_exports.literal(1),
|
|
21605
|
-
target: external_exports.string().min(1),
|
|
21606
|
-
binary: external_exports.string().min(1),
|
|
21607
|
-
manifest: external_exports.string().min(1),
|
|
21608
|
-
identityFlag: external_exports.literal(RUST_BACKEND_IDENTITY_FLAG),
|
|
21609
|
-
cliProtocol: external_exports.literal(RUST_BACKEND_CLI_PROTOCOL)
|
|
21610
|
-
}).strict()
|
|
21611
|
-
}).passthrough();
|
|
21612
|
-
var RustBackendBinaryManifestSchema = external_exports.object({
|
|
21613
|
-
schemaVersion: external_exports.literal(1),
|
|
21614
|
-
packageName: external_exports.string().min(1),
|
|
21615
|
-
packageVersion: external_exports.string().min(1),
|
|
21616
|
-
target: external_exports.string().min(1),
|
|
21617
|
-
binary: external_exports.string().min(1),
|
|
21618
|
-
sha256: external_exports.string().regex(/^[a-f0-9]{64}$/u),
|
|
21619
|
-
identity: RustBackendIdentitySchema
|
|
21620
|
-
}).strict();
|
|
21621
|
-
function packageFailure(code, message) {
|
|
21622
|
-
return { kind: "error", error: { code, message } };
|
|
21535
|
+
// src/cli/alias.ts
|
|
21536
|
+
var alias_exports = {};
|
|
21537
|
+
__export(alias_exports, {
|
|
21538
|
+
parseAliasArgs: () => parseAliasArgs,
|
|
21539
|
+
runAlias: () => runAlias
|
|
21540
|
+
});
|
|
21541
|
+
function parsePort3(raw) {
|
|
21542
|
+
if (raw === void 0 || raw.trim() === "") return null;
|
|
21543
|
+
const port = Number(raw);
|
|
21544
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
21545
|
+
return port;
|
|
21623
21546
|
}
|
|
21624
|
-
function
|
|
21625
|
-
return
|
|
21547
|
+
function normalizeAliasPath(raw) {
|
|
21548
|
+
return normalizePublicBasePath(raw);
|
|
21626
21549
|
}
|
|
21627
|
-
function
|
|
21628
|
-
|
|
21629
|
-
|
|
21630
|
-
const header = objectProperty(report, "header");
|
|
21631
|
-
const glibcVersion = objectProperty(header, "glibcVersionRuntime");
|
|
21632
|
-
if (typeof glibcVersion === "string" && glibcVersion.length > 0) {
|
|
21633
|
-
return "glibc";
|
|
21634
|
-
}
|
|
21635
|
-
const sharedObjects = objectProperty(report, "sharedObjects");
|
|
21636
|
-
return Array.isArray(sharedObjects) && sharedObjects.some((path2) => typeof path2 === "string" && path2.toLowerCase().includes("musl")) ? "musl" : "unknown";
|
|
21550
|
+
function isValidHost(host) {
|
|
21551
|
+
const trimmed = host.trim();
|
|
21552
|
+
return trimmed.length > 0 && !trimmed.includes("://") && !trimmed.includes("/") && !trimmed.includes("?") && !trimmed.includes("#") && !/\s/u.test(trimmed);
|
|
21637
21553
|
}
|
|
21638
|
-
function
|
|
21639
|
-
if (
|
|
21640
|
-
return
|
|
21554
|
+
function formatHostForUrl(host) {
|
|
21555
|
+
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
21556
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
21641
21557
|
}
|
|
21642
|
-
function
|
|
21643
|
-
|
|
21644
|
-
|
|
21645
|
-
|
|
21646
|
-
|
|
21647
|
-
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
|
|
21558
|
+
function parseAliasArgs(argv) {
|
|
21559
|
+
let host = DEFAULT_ALIAS_HOST;
|
|
21560
|
+
let port = DEFAULT_ALIAS_PORT;
|
|
21561
|
+
let json2 = false;
|
|
21562
|
+
let help = false;
|
|
21563
|
+
const positional = [];
|
|
21564
|
+
for (let index = 0; index < argv.length; index++) {
|
|
21565
|
+
const arg = argv[index];
|
|
21566
|
+
switch (arg) {
|
|
21567
|
+
case void 0:
|
|
21568
|
+
continue;
|
|
21569
|
+
case "--help":
|
|
21570
|
+
case "-h":
|
|
21571
|
+
help = true;
|
|
21572
|
+
break;
|
|
21573
|
+
case "--host": {
|
|
21574
|
+
const value = argv[index + 1];
|
|
21575
|
+
if (value === void 0 || !isValidHost(value)) {
|
|
21576
|
+
return {
|
|
21577
|
+
kind: "error",
|
|
21578
|
+
message: "alias: --host requires a hostname or IP address without a protocol or path"
|
|
21579
|
+
};
|
|
21580
|
+
}
|
|
21581
|
+
host = value.trim();
|
|
21582
|
+
index++;
|
|
21583
|
+
break;
|
|
21663
21584
|
}
|
|
21664
|
-
|
|
21665
|
-
|
|
21666
|
-
value
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
|
|
21671
|
-
|
|
21672
|
-
libc: "glibc"
|
|
21585
|
+
case "--port":
|
|
21586
|
+
case "-p": {
|
|
21587
|
+
const value = parsePort3(argv[index + 1]);
|
|
21588
|
+
if (value === null) {
|
|
21589
|
+
return {
|
|
21590
|
+
kind: "error",
|
|
21591
|
+
message: "alias: --port must be an integer from 1 to 65535"
|
|
21592
|
+
};
|
|
21673
21593
|
}
|
|
21674
|
-
|
|
21675
|
-
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
|
|
21679
|
-
|
|
21680
|
-
|
|
21681
|
-
|
|
21682
|
-
|
|
21683
|
-
|
|
21684
|
-
libc: null
|
|
21594
|
+
port = value;
|
|
21595
|
+
index++;
|
|
21596
|
+
break;
|
|
21597
|
+
}
|
|
21598
|
+
case "--json":
|
|
21599
|
+
json2 = true;
|
|
21600
|
+
break;
|
|
21601
|
+
default:
|
|
21602
|
+
if (arg.startsWith("-")) {
|
|
21603
|
+
return { kind: "error", message: `alias: unknown option ${arg}` };
|
|
21685
21604
|
}
|
|
21686
|
-
|
|
21687
|
-
|
|
21688
|
-
return targetFailure(`Rust backend packages do not support ${platform} ${arch}.`);
|
|
21605
|
+
positional.push(arg);
|
|
21606
|
+
}
|
|
21689
21607
|
}
|
|
21690
|
-
}
|
|
21691
|
-
|
|
21692
|
-
|
|
21693
|
-
|
|
21694
|
-
|
|
21695
|
-
|
|
21696
|
-
|
|
21608
|
+
if (help) return { kind: "help" };
|
|
21609
|
+
const command = positional[0];
|
|
21610
|
+
switch (command) {
|
|
21611
|
+
case "list":
|
|
21612
|
+
if (positional.length !== 1) {
|
|
21613
|
+
return { kind: "error", message: "alias: list does not accept a base path" };
|
|
21614
|
+
}
|
|
21615
|
+
return { kind: "ok", options: { action: { kind: "list" }, host, port, json: json2 } };
|
|
21616
|
+
case "add":
|
|
21617
|
+
case "remove": {
|
|
21618
|
+
const rawBasePath = positional[1];
|
|
21619
|
+
if (rawBasePath === void 0 || positional.length !== 2) {
|
|
21620
|
+
return { kind: "error", message: `alias: ${command} requires exactly one base path` };
|
|
21621
|
+
}
|
|
21622
|
+
const basePath = normalizeAliasPath(rawBasePath);
|
|
21623
|
+
if (basePath === null) {
|
|
21624
|
+
return {
|
|
21625
|
+
kind: "error",
|
|
21626
|
+
message: `alias: ${command} base path must be an absolute URL path without a query or hash`
|
|
21627
|
+
};
|
|
21628
|
+
}
|
|
21629
|
+
const action = { kind: command, basePath };
|
|
21630
|
+
return { kind: "ok", options: { action, host, port, json: json2 } };
|
|
21631
|
+
}
|
|
21632
|
+
case void 0:
|
|
21633
|
+
return { kind: "error", message: "alias: expected add <path>, list, or remove <path>" };
|
|
21634
|
+
default:
|
|
21635
|
+
return { kind: "error", message: `alias: unknown command ${command}` };
|
|
21697
21636
|
}
|
|
21698
21637
|
}
|
|
21699
|
-
function
|
|
21700
|
-
|
|
21701
|
-
|
|
21638
|
+
function aliasHelp() {
|
|
21639
|
+
return `agent-inspector alias - mount additional base paths on a running runtime
|
|
21640
|
+
|
|
21641
|
+
Usage:
|
|
21642
|
+
agent-inspector alias add <path> [--host <host>] [--port <port>] [--json]
|
|
21643
|
+
agent-inspector alias list [--host <host>] [--port <port>] [--json]
|
|
21644
|
+
agent-inspector alias remove <path> [--host <host>] [--port <port>] [--json]
|
|
21645
|
+
|
|
21646
|
+
Options:
|
|
21647
|
+
--host <host> Running instance host (default: localhost)
|
|
21648
|
+
--port <port> Running instance port (default: 9527)
|
|
21649
|
+
--json Emit machine-readable output
|
|
21650
|
+
--help, -h Show this help
|
|
21651
|
+
|
|
21652
|
+
Aliases are runtime-scoped. This command never starts another Agent Inspector runtime.
|
|
21653
|
+
`;
|
|
21702
21654
|
}
|
|
21703
|
-
function
|
|
21704
|
-
return
|
|
21655
|
+
function isObject4(value) {
|
|
21656
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21705
21657
|
}
|
|
21706
|
-
function
|
|
21707
|
-
|
|
21708
|
-
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
21712
|
-
|
|
21713
|
-
if (result.error !== void 0) {
|
|
21714
|
-
return { kind: "error", message: result.error.message };
|
|
21715
|
-
}
|
|
21716
|
-
if (result.status !== 0) {
|
|
21717
|
-
return {
|
|
21718
|
-
kind: "error",
|
|
21719
|
-
message: `identity command exited with status ${String(result.status)}`
|
|
21720
|
-
};
|
|
21721
|
-
}
|
|
21722
|
-
let raw;
|
|
21723
|
-
try {
|
|
21724
|
-
raw = JSON.parse(result.stdout);
|
|
21725
|
-
} catch {
|
|
21726
|
-
return { kind: "error", message: "identity command did not return JSON" };
|
|
21658
|
+
function parseAliasSnapshot(value) {
|
|
21659
|
+
if (!isObject4(value)) return null;
|
|
21660
|
+
const canonicalBasePath = value["canonicalBasePath"];
|
|
21661
|
+
const aliases = value["aliases"];
|
|
21662
|
+
const runtimeScoped = value["runtimeScoped"];
|
|
21663
|
+
if (typeof canonicalBasePath !== "string" || !Array.isArray(aliases) || !aliases.every((alias) => typeof alias === "string") || runtimeScoped !== true) {
|
|
21664
|
+
return null;
|
|
21727
21665
|
}
|
|
21728
|
-
|
|
21729
|
-
|
|
21666
|
+
return {
|
|
21667
|
+
canonicalBasePath: normalizeBasePath(canonicalBasePath),
|
|
21668
|
+
aliases: aliases.map((alias) => normalizeBasePath(alias)),
|
|
21669
|
+
runtimeScoped: true
|
|
21670
|
+
};
|
|
21730
21671
|
}
|
|
21731
|
-
|
|
21732
|
-
|
|
21672
|
+
function readErrorDetail(value) {
|
|
21673
|
+
if (!isObject4(value)) return null;
|
|
21674
|
+
const error51 = value["error"];
|
|
21675
|
+
if (typeof error51 === "string" && error51.trim() !== "") return error51;
|
|
21676
|
+
const message = value["message"];
|
|
21677
|
+
return typeof message === "string" && message.trim() !== "" ? message : null;
|
|
21678
|
+
}
|
|
21679
|
+
async function readJson2(response) {
|
|
21733
21680
|
try {
|
|
21734
|
-
|
|
21681
|
+
const value = await response.json();
|
|
21682
|
+
return value;
|
|
21735
21683
|
} catch {
|
|
21736
|
-
|
|
21737
|
-
const packageName = specifier.endsWith(suffix) ? specifier.slice(0, -suffix.length) : "";
|
|
21738
|
-
const directoryName = packageName.split("/").at(-1);
|
|
21739
|
-
if (directoryName !== void 0 && directoryName.length > 0) {
|
|
21740
|
-
const localPackageJson = resolve(
|
|
21741
|
-
dirname(fileURLToPath(import.meta.url)),
|
|
21742
|
-
"../packages",
|
|
21743
|
-
directoryName,
|
|
21744
|
-
"package.json"
|
|
21745
|
-
);
|
|
21746
|
-
if (existsSync(localPackageJson)) return localPackageJson;
|
|
21747
|
-
}
|
|
21748
|
-
return packageRequire.resolve(specifier);
|
|
21684
|
+
return null;
|
|
21749
21685
|
}
|
|
21750
21686
|
}
|
|
21751
|
-
function
|
|
21752
|
-
const
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21687
|
+
async function requestAliases(endpoint2, action, fetchImpl) {
|
|
21688
|
+
const controller = new AbortController();
|
|
21689
|
+
const timeout = setTimeout(() => {
|
|
21690
|
+
controller.abort();
|
|
21691
|
+
}, REQUEST_TIMEOUT_MS);
|
|
21692
|
+
const method = action.kind === "list" ? "GET" : action.kind === "add" ? "POST" : "DELETE";
|
|
21693
|
+
const basePath = action.kind === "list" ? null : action.basePath;
|
|
21694
|
+
const init = {
|
|
21695
|
+
method,
|
|
21696
|
+
headers: action.kind === "list" ? { accept: "application/json" } : {
|
|
21697
|
+
accept: "application/json",
|
|
21698
|
+
"content-type": "application/json",
|
|
21699
|
+
"x-agent-inspector-csrf": "1"
|
|
21700
|
+
},
|
|
21701
|
+
signal: controller.signal
|
|
21702
|
+
};
|
|
21703
|
+
if (basePath !== null) init.body = JSON.stringify({ basePath });
|
|
21756
21704
|
try {
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
)
|
|
21760
|
-
|
|
21761
|
-
|
|
21762
|
-
|
|
21763
|
-
|
|
21764
|
-
|
|
21765
|
-
|
|
21766
|
-
|
|
21767
|
-
|
|
21768
|
-
|
|
21769
|
-
}
|
|
21770
|
-
const packageJson = RustBackendPackageJsonSchema.safeParse(rawPackage.value);
|
|
21771
|
-
if (!packageJson.success) {
|
|
21772
|
-
return packageFailure(
|
|
21773
|
-
"package_invalid",
|
|
21774
|
-
`Package ${target.value.packageName} does not contain the Rust backend package contract.`
|
|
21775
|
-
);
|
|
21776
|
-
}
|
|
21777
|
-
if (packageJson.data.name !== target.value.packageName || packageJson.data.version !== options.rootVersion) {
|
|
21778
|
-
return packageFailure(
|
|
21779
|
-
"version_mismatch",
|
|
21780
|
-
`Rust backend package must exactly match @tonyclaw/agent-inspector@${options.rootVersion}; found ${packageJson.data.name}@${packageJson.data.version}.`
|
|
21781
|
-
);
|
|
21782
|
-
}
|
|
21783
|
-
const metadata = packageJson.data.agentInspectorRustBackend;
|
|
21784
|
-
const expectedLibc = target.value.libc === null ? void 0 : [target.value.libc];
|
|
21785
|
-
if (metadata.target !== target.value.target || metadata.binary !== target.value.binary || metadata.manifest !== "binary-manifest.json" || packageJson.data.os.length !== 1 || packageJson.data.os[0] !== target.value.os || packageJson.data.cpu.length !== 1 || packageJson.data.cpu[0] !== target.value.cpu || JSON.stringify(packageJson.data.libc) !== JSON.stringify(expectedLibc)) {
|
|
21786
|
-
return packageFailure(
|
|
21787
|
-
"target_mismatch",
|
|
21788
|
-
`Package ${target.value.packageName} does not match target ${target.value.target}.`
|
|
21789
|
-
);
|
|
21790
|
-
}
|
|
21791
|
-
const packageRoot = dirname(packageJsonPath);
|
|
21792
|
-
const manifestPath = resolve(packageRoot, metadata.manifest);
|
|
21793
|
-
const binaryPath = resolve(packageRoot, metadata.binary);
|
|
21794
|
-
if (!isConfinedPath(packageRoot, manifestPath) || !isConfinedPath(packageRoot, binaryPath)) {
|
|
21795
|
-
return packageFailure(
|
|
21796
|
-
"package_invalid",
|
|
21797
|
-
`Package ${target.value.packageName} contains an unsafe artifact path.`
|
|
21798
|
-
);
|
|
21799
|
-
}
|
|
21800
|
-
const rawManifest = readJson(manifestPath);
|
|
21801
|
-
if (rawManifest.kind === "error") {
|
|
21802
|
-
return packageFailure(
|
|
21803
|
-
"manifest_invalid",
|
|
21804
|
-
`Rust backend package ${target.value.packageName} is incomplete: binary-manifest.json is missing or invalid.`
|
|
21805
|
-
);
|
|
21806
|
-
}
|
|
21807
|
-
const manifest = RustBackendBinaryManifestSchema.safeParse(rawManifest.value);
|
|
21808
|
-
if (!manifest.success) {
|
|
21809
|
-
return packageFailure(
|
|
21810
|
-
"manifest_invalid",
|
|
21811
|
-
`Rust backend package ${target.value.packageName} has an invalid target manifest.`
|
|
21812
|
-
);
|
|
21813
|
-
}
|
|
21814
|
-
if (manifest.data.packageName !== target.value.packageName || manifest.data.packageVersion !== options.rootVersion || manifest.data.identity.packageVersion !== options.rootVersion) {
|
|
21815
|
-
return packageFailure(
|
|
21816
|
-
"version_mismatch",
|
|
21817
|
-
`Rust backend manifest must exactly match @tonyclaw/agent-inspector@${options.rootVersion}.`
|
|
21818
|
-
);
|
|
21705
|
+
const response = await fetchImpl(endpoint2, init);
|
|
21706
|
+
const value = await readJson2(response);
|
|
21707
|
+
if (!response.ok) {
|
|
21708
|
+
return { kind: "http-error", status: response.status, detail: readErrorDetail(value) };
|
|
21709
|
+
}
|
|
21710
|
+
const snapshot = parseAliasSnapshot(value);
|
|
21711
|
+
return snapshot === null ? { kind: "invalid-response" } : { kind: "success", snapshot };
|
|
21712
|
+
} catch (error51) {
|
|
21713
|
+
const timedOut = controller.signal.aborted || error51 instanceof Error && error51.name === "AbortError";
|
|
21714
|
+
return { kind: "network-error", timedOut };
|
|
21715
|
+
} finally {
|
|
21716
|
+
clearTimeout(timeout);
|
|
21819
21717
|
}
|
|
21820
|
-
|
|
21821
|
-
|
|
21822
|
-
|
|
21823
|
-
|
|
21824
|
-
|
|
21718
|
+
}
|
|
21719
|
+
function displayBasePath2(basePath) {
|
|
21720
|
+
const normalized = normalizeBasePath(basePath);
|
|
21721
|
+
return normalized === "" ? "/" : normalized;
|
|
21722
|
+
}
|
|
21723
|
+
function fullUrl(origin, basePath) {
|
|
21724
|
+
return `${origin}${normalizeBasePath(basePath)}`;
|
|
21725
|
+
}
|
|
21726
|
+
function buildOutput(origin, action, snapshot) {
|
|
21727
|
+
return {
|
|
21728
|
+
action: action.kind,
|
|
21729
|
+
canonicalBasePath: displayBasePath2(snapshot.canonicalBasePath),
|
|
21730
|
+
canonicalUrl: fullUrl(origin, snapshot.canonicalBasePath),
|
|
21731
|
+
aliases: snapshot.aliases.map(displayBasePath2),
|
|
21732
|
+
aliasUrls: snapshot.aliases.map((alias) => fullUrl(origin, alias)),
|
|
21733
|
+
runtimeScoped: true
|
|
21734
|
+
};
|
|
21735
|
+
}
|
|
21736
|
+
function formatHumanOutput(output) {
|
|
21737
|
+
const heading = output.action === "add" ? "Base-path alias added." : output.action === "remove" ? "Base-path alias removed." : "Base-path aliases for the running runtime:";
|
|
21738
|
+
const aliasPaths = output.aliases.length === 0 ? "(none)" : output.aliases.join(", ");
|
|
21739
|
+
const aliasUrls = output.aliasUrls.length === 0 ? " (none)" : output.aliasUrls.map((url2) => ` - ${url2}`).join("\n");
|
|
21740
|
+
return `${heading}
|
|
21741
|
+
Canonical base path: ${output.canonicalBasePath}
|
|
21742
|
+
Canonical URL: ${output.canonicalUrl}
|
|
21743
|
+
Alias base paths: ${aliasPaths}
|
|
21744
|
+
Alias URLs:
|
|
21745
|
+
${aliasUrls}
|
|
21746
|
+
Scope: current runtime only; no second runtime was started and aliases are not persisted.
|
|
21747
|
+
`;
|
|
21748
|
+
}
|
|
21749
|
+
function writeError2(io, json2, code, message) {
|
|
21750
|
+
if (json2) {
|
|
21751
|
+
io.stderr(`${JSON.stringify({ error: code, message, runtimeStarted: false })}
|
|
21752
|
+
`);
|
|
21753
|
+
return;
|
|
21825
21754
|
}
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
|
|
21829
|
-
|
|
21830
|
-
|
|
21755
|
+
io.stderr(`agent-inspector alias: ${message}
|
|
21756
|
+
`);
|
|
21757
|
+
}
|
|
21758
|
+
function oldRuntimeMessage(origin) {
|
|
21759
|
+
return `the runtime at ${origin} does not support base-path aliases. Upgrade Agent Inspector and restart that instance, then retry; this command did not start a runtime.`;
|
|
21760
|
+
}
|
|
21761
|
+
async function handleHttpError(options, endpoint2, origin, result, fetchImpl, io) {
|
|
21762
|
+
if (result.status === 404 && options.action.kind === "remove") {
|
|
21763
|
+
const probe = await requestAliases(endpoint2, { kind: "list" }, fetchImpl);
|
|
21764
|
+
if (probe.kind === "success") {
|
|
21765
|
+
writeError2(
|
|
21766
|
+
io,
|
|
21767
|
+
options.json,
|
|
21768
|
+
"alias_not_found",
|
|
21769
|
+
`base path ${displayBasePath2(options.action.basePath)} is not mounted. Run "agent-inspector alias list --host ${options.host} --port ${options.port}" to inspect the current runtime.`
|
|
21770
|
+
);
|
|
21771
|
+
return 1;
|
|
21831
21772
|
}
|
|
21832
|
-
if (
|
|
21833
|
-
|
|
21773
|
+
if (probe.kind === "network-error") {
|
|
21774
|
+
writeError2(
|
|
21775
|
+
io,
|
|
21776
|
+
options.json,
|
|
21777
|
+
probe.timedOut ? "request_timeout" : "runtime_unreachable",
|
|
21778
|
+
`${probe.timedOut ? "timed out contacting" : "cannot reach"} ${origin}. Confirm the existing runtime is running and the --host/--port values are correct; no runtime was started.`
|
|
21779
|
+
);
|
|
21780
|
+
return 1;
|
|
21781
|
+
}
|
|
21782
|
+
if (probe.kind === "http-error" && (probe.status === 404 || probe.status === 405)) {
|
|
21783
|
+
writeError2(io, options.json, "unsupported_runtime", oldRuntimeMessage(origin));
|
|
21784
|
+
return 1;
|
|
21834
21785
|
}
|
|
21835
|
-
binaryBytes = readFileSync(binaryPath);
|
|
21836
|
-
} catch {
|
|
21837
|
-
return packageFailure(
|
|
21838
|
-
"binary_missing",
|
|
21839
|
-
`Rust backend binary ${target.value.binary} is missing from ${target.value.packageName}.`
|
|
21840
|
-
);
|
|
21841
21786
|
}
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
return
|
|
21845
|
-
"hash_mismatch",
|
|
21846
|
-
`Rust backend binary integrity check failed for ${target.value.packageName}; no binary was started.`
|
|
21847
|
-
);
|
|
21787
|
+
if (result.status === 404 || result.status === 405) {
|
|
21788
|
+
writeError2(io, options.json, "unsupported_runtime", oldRuntimeMessage(origin));
|
|
21789
|
+
return 1;
|
|
21848
21790
|
}
|
|
21849
|
-
|
|
21850
|
-
|
|
21851
|
-
|
|
21852
|
-
|
|
21853
|
-
|
|
21791
|
+
if (result.status === 409) {
|
|
21792
|
+
writeError2(
|
|
21793
|
+
io,
|
|
21794
|
+
options.json,
|
|
21795
|
+
"alias_conflict",
|
|
21796
|
+
`${result.detail ?? "the base path is already mounted or conflicts with another mount"}. Run "agent-inspector alias list --host ${options.host} --port ${options.port}" to inspect the current runtime.`
|
|
21854
21797
|
);
|
|
21798
|
+
return 1;
|
|
21855
21799
|
}
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
|
|
21860
|
-
|
|
21800
|
+
const detail = result.detail === null ? "the runtime rejected the request" : result.detail;
|
|
21801
|
+
writeError2(
|
|
21802
|
+
io,
|
|
21803
|
+
options.json,
|
|
21804
|
+
"runtime_rejected_request",
|
|
21805
|
+
`${detail} (HTTP ${result.status}). Check the running instance and retry; no runtime was started.`
|
|
21806
|
+
);
|
|
21807
|
+
return 1;
|
|
21808
|
+
}
|
|
21809
|
+
async function runAlias(argv, fetchImpl = fetch, io = processIo2) {
|
|
21810
|
+
const parsed = parseAliasArgs(argv);
|
|
21811
|
+
if (parsed.kind === "help") {
|
|
21812
|
+
io.stdout(aliasHelp());
|
|
21813
|
+
return 0;
|
|
21861
21814
|
}
|
|
21862
|
-
|
|
21863
|
-
|
|
21864
|
-
|
|
21865
|
-
|
|
21866
|
-
|
|
21867
|
-
|
|
21868
|
-
|
|
21869
|
-
|
|
21870
|
-
|
|
21815
|
+
if (parsed.kind === "error") {
|
|
21816
|
+
io.stderr(`${parsed.message}
|
|
21817
|
+
|
|
21818
|
+
${aliasHelp()}`);
|
|
21819
|
+
return 2;
|
|
21820
|
+
}
|
|
21821
|
+
const { options } = parsed;
|
|
21822
|
+
const origin = `http://${formatHostForUrl(options.host)}:${options.port}`;
|
|
21823
|
+
const endpoint2 = `${origin}${ALIAS_CONTROL_PATH}`;
|
|
21824
|
+
const result = await requestAliases(endpoint2, options.action, fetchImpl);
|
|
21825
|
+
switch (result.kind) {
|
|
21826
|
+
case "success": {
|
|
21827
|
+
const output = buildOutput(origin, options.action, result.snapshot);
|
|
21828
|
+
io.stdout(options.json ? `${JSON.stringify(output, null, 2)}
|
|
21829
|
+
` : formatHumanOutput(output));
|
|
21830
|
+
return 0;
|
|
21871
21831
|
}
|
|
21872
|
-
|
|
21832
|
+
case "network-error":
|
|
21833
|
+
writeError2(
|
|
21834
|
+
io,
|
|
21835
|
+
options.json,
|
|
21836
|
+
result.timedOut ? "request_timeout" : "runtime_unreachable",
|
|
21837
|
+
`${result.timedOut ? "timed out contacting" : "cannot reach"} ${origin}. Confirm the existing runtime is running and the --host/--port values are correct; no runtime was started.`
|
|
21838
|
+
);
|
|
21839
|
+
return 1;
|
|
21840
|
+
case "invalid-response":
|
|
21841
|
+
writeError2(
|
|
21842
|
+
io,
|
|
21843
|
+
options.json,
|
|
21844
|
+
"invalid_runtime_response",
|
|
21845
|
+
`the runtime at ${origin} returned an invalid alias response. Upgrade and restart that instance, then retry; no runtime was started.`
|
|
21846
|
+
);
|
|
21847
|
+
return 1;
|
|
21848
|
+
case "http-error":
|
|
21849
|
+
return handleHttpError(options, endpoint2, origin, result, fetchImpl, io);
|
|
21850
|
+
}
|
|
21851
|
+
}
|
|
21852
|
+
var DEFAULT_ALIAS_HOST, DEFAULT_ALIAS_PORT, ALIAS_CONTROL_PATH, REQUEST_TIMEOUT_MS, processIo2;
|
|
21853
|
+
var init_alias = __esm({
|
|
21854
|
+
"src/cli/alias.ts"() {
|
|
21855
|
+
"use strict";
|
|
21856
|
+
init_basePath();
|
|
21857
|
+
DEFAULT_ALIAS_HOST = "localhost";
|
|
21858
|
+
DEFAULT_ALIAS_PORT = 9527;
|
|
21859
|
+
ALIAS_CONTROL_PATH = "/.agent-inspector/base-path-aliases";
|
|
21860
|
+
REQUEST_TIMEOUT_MS = 5e3;
|
|
21861
|
+
processIo2 = {
|
|
21862
|
+
stdout: (text) => {
|
|
21863
|
+
process.stdout.write(text);
|
|
21864
|
+
},
|
|
21865
|
+
stderr: (text) => {
|
|
21866
|
+
process.stderr.write(text);
|
|
21867
|
+
}
|
|
21868
|
+
};
|
|
21869
|
+
}
|
|
21870
|
+
});
|
|
21871
|
+
|
|
21872
|
+
// src/cli.ts
|
|
21873
|
+
init_package();
|
|
21874
|
+
init_backendImplementation();
|
|
21875
|
+
init_basePath();
|
|
21876
|
+
init_managedInstance();
|
|
21877
|
+
init_networkHints();
|
|
21878
|
+
import { Buffer as Buffer5 } from "node:buffer";
|
|
21879
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
21880
|
+
import { createConnection as createConnection2 } from "node:net";
|
|
21881
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
21882
|
+
import { dirname as dirname3, join as join9, resolve as resolvePath2 } from "node:path";
|
|
21883
|
+
import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "node:fs";
|
|
21884
|
+
|
|
21885
|
+
// src/cli/startupOutput.ts
|
|
21886
|
+
function shouldSuppressServerOutputLine(line, context) {
|
|
21887
|
+
if (!context.enableIdentityProxy) return false;
|
|
21888
|
+
if (context.publicPort === context.upstreamPort) return false;
|
|
21889
|
+
const lowerLine = line.toLowerCase();
|
|
21890
|
+
return lowerLine.includes("listening on") && lowerLine.includes(`:${context.upstreamPort}`);
|
|
21873
21891
|
}
|
|
21874
21892
|
|
|
21893
|
+
// src/cli.ts
|
|
21894
|
+
init_rustBackendPackage();
|
|
21895
|
+
|
|
21875
21896
|
// src/cli/instanceApi.ts
|
|
21876
21897
|
init_instance();
|
|
21877
21898
|
init_instanceContract();
|
|
@@ -24753,6 +24774,7 @@ function toBuffer(chunk) {
|
|
|
24753
24774
|
|
|
24754
24775
|
// src/cli.ts
|
|
24755
24776
|
init_platformCommands();
|
|
24777
|
+
init_dataDir();
|
|
24756
24778
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
24757
24779
|
var __dirname2 = dirname3(__filename2);
|
|
24758
24780
|
var DEFAULT_PORT4 = 9527;
|
|
@@ -24764,6 +24786,7 @@ var BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
|
|
|
24764
24786
|
var DEFAULT_CAPTURE_MODE2 = "simple";
|
|
24765
24787
|
var DEFAULT_SHUTDOWN_DEADLINE_MS = 15e3;
|
|
24766
24788
|
var SUPERVISOR_SHUTDOWN_GRACE_MS = 2e3;
|
|
24789
|
+
var AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV = "AGENT_INSPECTOR_BACKGROUND_SUPERVISOR";
|
|
24767
24790
|
process.title = "Agent Inspector";
|
|
24768
24791
|
var subcommand = process.argv[2];
|
|
24769
24792
|
function printGlobalHelp() {
|
|
@@ -24781,7 +24804,7 @@ Global options:
|
|
|
24781
24804
|
-v, --version Print the package version
|
|
24782
24805
|
|
|
24783
24806
|
Start options:
|
|
24784
|
-
--backend <typescript|rust> Select the backend implementation (default:
|
|
24807
|
+
--backend <typescript|rust> Select the backend implementation (default: rust when native package is available)
|
|
24785
24808
|
--backend-only, --headless Start the API, proxy, and MCP runtime without serving the UI
|
|
24786
24809
|
--with-ui Serve the independently built UI through the protected ingress
|
|
24787
24810
|
--port, -p <port> Public ingress port (default: 9527)
|
|
@@ -24952,6 +24975,25 @@ function pipeServerOutput(child, context) {
|
|
|
24952
24975
|
pipeServerOutputStream(child.stdout, process.stdout, context);
|
|
24953
24976
|
pipeServerOutputStream(child.stderr, process.stderr, context);
|
|
24954
24977
|
}
|
|
24978
|
+
function createBackgroundSupervisorLogs(env) {
|
|
24979
|
+
try {
|
|
24980
|
+
const logsDir = join9(resolveDataDir(existsSync8, env), "logs");
|
|
24981
|
+
mkdirSync4(logsDir, { recursive: true });
|
|
24982
|
+
const stdoutPath = join9(logsDir, "backend.stdout.log");
|
|
24983
|
+
const stderrPath = join9(logsDir, "backend.stderr.log");
|
|
24984
|
+
const header = `
|
|
24985
|
+
[${(/* @__PURE__ */ new Date()).toISOString()}] Agent Inspector backend process started
|
|
24986
|
+
`;
|
|
24987
|
+
appendFileSync(stdoutPath, header, { encoding: "utf8" });
|
|
24988
|
+
appendFileSync(stderrPath, header, { encoding: "utf8" });
|
|
24989
|
+
return { stdoutPath, stderrPath };
|
|
24990
|
+
} catch (error51) {
|
|
24991
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
24992
|
+
process.stderr.write(`Failed to open Agent Inspector background logs: ${message}
|
|
24993
|
+
`);
|
|
24994
|
+
return null;
|
|
24995
|
+
}
|
|
24996
|
+
}
|
|
24955
24997
|
function pipeServerOutputStream(stream, target, context) {
|
|
24956
24998
|
if (stream === null) return;
|
|
24957
24999
|
let buffered = "";
|
|
@@ -24974,6 +25016,34 @@ function pipeServerOutputStream(stream, target, context) {
|
|
|
24974
25016
|
buffered = "";
|
|
24975
25017
|
});
|
|
24976
25018
|
}
|
|
25019
|
+
function appendBackgroundLog(path2, text) {
|
|
25020
|
+
try {
|
|
25021
|
+
appendFileSync(path2, text, { encoding: "utf8" });
|
|
25022
|
+
} catch {
|
|
25023
|
+
}
|
|
25024
|
+
}
|
|
25025
|
+
function pipeServerOutputStreamToFile(stream, targetPath, context) {
|
|
25026
|
+
if (stream === null) return;
|
|
25027
|
+
let buffered = "";
|
|
25028
|
+
stream.on("data", (chunk) => {
|
|
25029
|
+
const text = Buffer5.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
25030
|
+
const lines = `${buffered}${text}`.split(/\r?\n/);
|
|
25031
|
+
buffered = lines.pop() ?? "";
|
|
25032
|
+
for (const line of lines) {
|
|
25033
|
+
if (!shouldSuppressServerOutputLine(line, context)) {
|
|
25034
|
+
appendBackgroundLog(targetPath, `${line}
|
|
25035
|
+
`);
|
|
25036
|
+
}
|
|
25037
|
+
}
|
|
25038
|
+
});
|
|
25039
|
+
stream.on("end", () => {
|
|
25040
|
+
if (buffered.length === 0) return;
|
|
25041
|
+
if (!shouldSuppressServerOutputLine(buffered, context)) {
|
|
25042
|
+
appendBackgroundLog(targetPath, buffered);
|
|
25043
|
+
}
|
|
25044
|
+
buffered = "";
|
|
25045
|
+
});
|
|
25046
|
+
}
|
|
24977
25047
|
function resolveServerCommand(outputDir, serverPath, backend) {
|
|
24978
25048
|
if (backend === "rust") {
|
|
24979
25049
|
const resolved = resolveRustBackendPackage({ rootVersion: package_default.version });
|
|
@@ -25144,7 +25214,8 @@ async function runStart(args) {
|
|
|
25144
25214
|
const portDefault = envPort !== void 0 ? Number(envPort) : DEFAULT_PORT4;
|
|
25145
25215
|
const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
|
|
25146
25216
|
const envMode = process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
|
|
25147
|
-
const
|
|
25217
|
+
const rawEnvBackend = process.env[AGENT_INSPECTOR_BACKEND_ENV];
|
|
25218
|
+
const envBackend = rawEnvBackend === void 0 ? void 0 : parseBackendImplementation(rawEnvBackend);
|
|
25148
25219
|
if (envBackend === null) {
|
|
25149
25220
|
console.error(
|
|
25150
25221
|
`Invalid ${AGENT_INSPECTOR_BACKEND_ENV}. Use typescript or rust; no backend was started.`
|
|
@@ -25165,7 +25236,8 @@ async function runStart(args) {
|
|
|
25165
25236
|
let enableIdentityProxy = true;
|
|
25166
25237
|
let legacyAliasEnabled = false;
|
|
25167
25238
|
let serveUi = process.env["AGENT_INSPECTOR_BACKEND_ONLY"] !== "1";
|
|
25168
|
-
let backend = envBackend;
|
|
25239
|
+
let backend = envBackend ?? "typescript";
|
|
25240
|
+
let backendWasSpecified = envBackend !== void 0;
|
|
25169
25241
|
if (envMode !== void 0 && envMode !== "") {
|
|
25170
25242
|
const parsedMode = parseCaptureMode(envMode);
|
|
25171
25243
|
if (parsedMode === null) {
|
|
@@ -25219,6 +25291,7 @@ async function runStart(args) {
|
|
|
25219
25291
|
return;
|
|
25220
25292
|
}
|
|
25221
25293
|
backend = parsedBackend.data;
|
|
25294
|
+
backendWasSpecified = true;
|
|
25222
25295
|
continue;
|
|
25223
25296
|
}
|
|
25224
25297
|
switch (arg) {
|
|
@@ -25249,6 +25322,7 @@ async function runStart(args) {
|
|
|
25249
25322
|
return;
|
|
25250
25323
|
}
|
|
25251
25324
|
backend = parsedBackend.data;
|
|
25325
|
+
backendWasSpecified = true;
|
|
25252
25326
|
i++;
|
|
25253
25327
|
break;
|
|
25254
25328
|
}
|
|
@@ -25338,6 +25412,9 @@ async function runStart(args) {
|
|
|
25338
25412
|
process.exitCode = 1;
|
|
25339
25413
|
return;
|
|
25340
25414
|
}
|
|
25415
|
+
if (!backendWasSpecified) {
|
|
25416
|
+
backend = managedLaunch?.backend ?? resolvePreferredBackendImplementation({ rootVersion: package_default.version });
|
|
25417
|
+
}
|
|
25341
25418
|
if (managedLaunch !== null && managedLaunch.backend !== backend) {
|
|
25342
25419
|
console.error(
|
|
25343
25420
|
`Managed instance backend mismatch: launch identity requires ${managedLaunch.backend}, but ${backend} was selected.`
|
|
@@ -25494,7 +25571,11 @@ async function runStart(args) {
|
|
|
25494
25571
|
const supervisorProcess = spawn2(process.execPath, [cliEntry, ...supervisorArgs], {
|
|
25495
25572
|
stdio: "ignore",
|
|
25496
25573
|
detached: true,
|
|
25497
|
-
env: {
|
|
25574
|
+
env: {
|
|
25575
|
+
...process.env,
|
|
25576
|
+
[AGENT_INSPECTOR_CLI_ENTRY_ENV]: cliEntry,
|
|
25577
|
+
[AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV]: "1"
|
|
25578
|
+
},
|
|
25498
25579
|
windowsHide: true
|
|
25499
25580
|
});
|
|
25500
25581
|
supervisorProcess.once("error", (err) => {
|
|
@@ -25558,22 +25639,38 @@ async function runStart(args) {
|
|
|
25558
25639
|
if (existsSync8(workerDir)) {
|
|
25559
25640
|
serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
|
|
25560
25641
|
}
|
|
25642
|
+
const runningInBackgroundSupervisor = process.env[AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV] === "1";
|
|
25643
|
+
const backgroundSupervisorLogs = runningInBackgroundSupervisor ? createBackgroundSupervisorLogs(serverEnv) : null;
|
|
25561
25644
|
const serverProcess = spawn2(serverCommand.command, serverCommand.args, {
|
|
25562
25645
|
stdio: managedLaunch === null ? background ? ["ignore", "ignore", "ignore"] : ["ignore", "pipe", "pipe"] : backend === "typescript" ? background ? ["ignore", "ignore", "ignore", "ipc"] : ["ignore", "pipe", "pipe", "ipc"] : background ? ["ignore", "ignore", "ignore"] : ["ignore", "pipe", "pipe"],
|
|
25563
25646
|
detached: background,
|
|
25564
25647
|
env: serverEnv,
|
|
25565
|
-
windowsHide: background
|
|
25648
|
+
windowsHide: background || runningInBackgroundSupervisor
|
|
25566
25649
|
});
|
|
25567
25650
|
serverProcess.once("error", (err) => {
|
|
25568
25651
|
console.error(`Failed to start Agent Inspector runtime: ${err.message}`);
|
|
25569
25652
|
});
|
|
25570
25653
|
const serverExit = waitForProcessExit(serverProcess);
|
|
25571
25654
|
if (!background) {
|
|
25572
|
-
|
|
25655
|
+
const outputContext = {
|
|
25573
25656
|
enableIdentityProxy,
|
|
25574
25657
|
publicPort: port,
|
|
25575
25658
|
upstreamPort
|
|
25576
|
-
}
|
|
25659
|
+
};
|
|
25660
|
+
if (backgroundSupervisorLogs === null) {
|
|
25661
|
+
pipeServerOutput(serverProcess, outputContext);
|
|
25662
|
+
} else {
|
|
25663
|
+
pipeServerOutputStreamToFile(
|
|
25664
|
+
serverProcess.stdout,
|
|
25665
|
+
backgroundSupervisorLogs.stdoutPath,
|
|
25666
|
+
outputContext
|
|
25667
|
+
);
|
|
25668
|
+
pipeServerOutputStreamToFile(
|
|
25669
|
+
serverProcess.stderr,
|
|
25670
|
+
backgroundSupervisorLogs.stderrPath,
|
|
25671
|
+
outputContext
|
|
25672
|
+
);
|
|
25673
|
+
}
|
|
25577
25674
|
}
|
|
25578
25675
|
const identityProxies = [];
|
|
25579
25676
|
let supervisorShutdownPromise = null;
|