@tonyclaw/agent-inspector 3.0.28 → 3.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/cli.js +791 -191
- package/.output/nitro.json +1 -1
- package/.output/public/assets/CompareDrawer-B6cXJohL.js +1 -0
- package/.output/public/assets/InspectorPet-mAzeGmEE.js +4108 -0
- package/.output/public/assets/ProxyViewerContainer-CkFWv_Nm.js +126 -0
- package/.output/public/assets/{ReplayDialog-CZMApaF4.js → ReplayDialog-DodiTL6E.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DNWccR7O.js → RequestAnatomy-B3IauToI.js} +1 -1
- package/.output/public/assets/ResponseView-Hn-5ordK.js +3 -0
- package/.output/public/assets/StreamingChunkSequence-vLDOVQM_.js +1 -0
- package/.output/public/assets/_sessionId-BFH4TOaI.js +1 -0
- package/.output/public/assets/{_sessionId-DRfvuE2k.js → _sessionId-Cj_HIFZx.js} +1 -1
- package/.output/public/assets/{index-D4HkS7Ct.js → index-Bmj2lcWE.js} +1 -1
- package/.output/public/assets/index-CgJj-HBC.css +1 -0
- package/.output/public/assets/index-Ch7uqnCT.js +1 -0
- package/.output/public/assets/index-DkDdKvLl.js +76 -0
- package/.output/public/assets/{index-B3aAuqWq.js → index-ZlhqCrSg.js} +6 -6
- package/.output/public/assets/{json-viewer-wuJSKaed.js → json-viewer-BdrnVQu-.js} +1 -1
- package/.output/public/assets/jszip.min-CS_nt_o1.js +2 -0
- package/.output/public/assets/qwen-mMn3f5ul.webp +0 -0
- package/.output/server/_libs/jszip.mjs +35 -9
- package/.output/server/_libs/lucide-react.mjs +86 -86
- package/.output/server/_libs/radix-ui__react-dialog.mjs +3 -3
- package/.output/server/_libs/tanstack__react-router.mjs +1 -1
- package/.output/server/{_sessionId-BJTNiP_O.mjs → _sessionId-C5S3pGZd.mjs} +15 -15
- package/.output/server/_sessionId-g6D69lKq.mjs +81 -0
- package/.output/server/_ssr/{CompareDrawer-BMRH3N97.mjs → CompareDrawer-CqUmGHal.mjs} +138 -103
- package/.output/server/_ssr/InspectorPet-DI0UJd01.mjs +925 -0
- package/.output/server/_ssr/{ProxyViewerContainer-AFwcHAIR.mjs → ProxyViewerContainer-CvsUmbJ3.mjs} +1216 -1659
- package/.output/server/_ssr/{ReplayDialog-CbWvRHiN.mjs → ReplayDialog-DS48dpFG.mjs} +18 -18
- package/.output/server/_ssr/{RequestAnatomy-C4kcPyqN.mjs → RequestAnatomy-DxYVQU1C.mjs} +58 -58
- package/.output/server/_ssr/{ResponseView-D8VYt-gZ.mjs → ResponseView-BLXW73eq.mjs} +60 -60
- package/.output/server/_ssr/{StreamingChunkSequence-BUUo6vU0.mjs → StreamingChunkSequence-DD0iFuy0.mjs} +16 -16
- package/.output/server/_ssr/{index-DTwMEEXO.mjs → index-B10LIaK0.mjs} +15 -15
- package/.output/server/_ssr/index-RHVJnU2p.mjs +81 -0
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-BqF1pSQD.mjs → json-viewer-LDu2XBaK.mjs} +58 -58
- package/.output/server/_ssr/{router-C8BOns7y.mjs → router-GKvrwifJ.mjs} +9005 -6488
- package/.output/server/_tanstack-start-manifest_v-Df2n4mbT.mjs +4 -0
- package/.output/server/index.mjs +105 -77
- package/.output/workers/logFinalizer.worker.js +16908 -0
- package/.output/workers/sessionWorkerEntry.js +16904 -0
- package/README.md +21 -5
- package/package.json +6 -2
- package/src/assets/logos/qwen.webp +0 -0
- package/src/cli.ts +85 -10
- package/src/components/OnboardingBanner.tsx +41 -21
- package/src/components/ProxyViewer.tsx +198 -269
- package/src/components/ProxyViewerContainer.tsx +128 -17
- package/src/components/clients/ClientLogo.tsx +9 -6
- package/src/components/ecosystem/AgentLabDialog.tsx +166 -19
- package/src/components/errors/SafeErrorBoundary.tsx +201 -0
- package/src/components/pi-agent/PiAgentPanel.tsx +4 -1
- package/src/components/providers/ImportWizardDialog.tsx +18 -42
- package/src/components/providers/ProviderLogo.tsx +1 -1
- package/src/components/providers/ProvidersPanel.tsx +8 -5
- package/src/components/providers/SettingsDialog.tsx +34 -11
- package/src/components/proxy-viewer/ApplicationBar.tsx +63 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +92 -49
- package/src/components/proxy-viewer/ConversationGroupList.tsx +49 -34
- package/src/components/proxy-viewer/ConversationHeader.tsx +22 -18
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +186 -0
- package/src/components/proxy-viewer/LogEntry.tsx +283 -275
- package/src/components/proxy-viewer/RawExportWarning.tsx +77 -0
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +61 -0
- package/src/components/proxy-viewer/ThreadConnector.tsx +16 -1
- package/src/components/proxy-viewer/TurnGroup.tsx +13 -2
- package/src/components/proxy-viewer/accessibility.ts +8 -0
- package/src/components/proxy-viewer/lazy.ts +4 -0
- package/src/components/proxy-viewer/liveConnectionState.ts +158 -0
- package/src/components/proxy-viewer/proxyViewerLogic.ts +10 -0
- package/src/components/proxy-viewer/viewerState.ts +2 -6
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/tabs.tsx +3 -3
- package/src/components/ui/transient-toast.tsx +1 -1
- package/src/lib/apiClient.ts +17 -2
- package/src/lib/ecosystemContract.ts +34 -0
- package/src/lib/export-logs.ts +1 -1
- package/src/lib/providerContract.ts +70 -4
- package/src/lib/providerImportContract.ts +27 -0
- package/src/lib/providerModelMetadata.ts +16 -7
- package/src/lib/resourceLimits.ts +152 -0
- package/src/lib/safeDiagnostic.ts +38 -0
- package/src/lib/useProviders.ts +4 -4
- package/src/lib/utils.ts +5 -1
- package/src/mcp/server.ts +39 -6
- package/src/mcp/toolHandlers.ts +131 -4
- package/src/proxy/chunkStorage.ts +20 -6
- package/src/proxy/config.ts +20 -6
- package/src/proxy/dataDir.ts +3 -0
- package/src/proxy/ecosystemExecutionConfirmation.ts +114 -0
- package/src/proxy/ecosystemExecutionRoute.ts +116 -0
- package/src/proxy/ecosystemTasks.ts +48 -0
- package/src/proxy/evidenceExporter.ts +23 -9
- package/src/proxy/groupEvidenceExporter.ts +26 -5
- package/src/proxy/groupStore.ts +13 -3
- package/src/proxy/handler.ts +103 -20
- package/src/proxy/identityProxy.ts +333 -14
- package/src/proxy/logFinalizer.ts +73 -6
- package/src/proxy/logImportUpload.ts +128 -0
- package/src/proxy/logImporter.ts +321 -70
- package/src/proxy/logIndex.ts +16 -6
- package/src/proxy/logger.ts +294 -41
- package/src/proxy/privateDataPath.ts +183 -0
- package/src/proxy/providerScanStore.ts +87 -0
- package/src/proxy/providerSecretStore.ts +58 -19
- package/src/proxy/providers.ts +175 -58
- package/src/proxy/rawStreamCapture.ts +66 -5
- package/src/proxy/runStore.ts +13 -3
- package/src/proxy/runtimeAdmission.ts +52 -0
- package/src/proxy/runtimeHealth.ts +206 -0
- package/src/proxy/runtimeShutdown.ts +75 -0
- package/src/proxy/sessionArchive.ts +15 -2
- package/src/proxy/sessionProcess.ts +19 -0
- package/src/proxy/sessionRuntime.ts +7 -0
- package/src/proxy/shutdownCoordinator.ts +90 -0
- package/src/proxy/sqliteLogIndex.ts +18 -2
- package/src/proxy/store.ts +18 -2
- package/src/routes/__root.tsx +18 -1
- package/src/routes/api/alerts.summary.ts +12 -8
- package/src/routes/api/alerts.ts +27 -9
- package/src/routes/api/ecosystem.packages.$packageId.help.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.install.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +3 -11
- package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +3 -8
- package/src/routes/api/ecosystem.recipes.$recipeId.run.ts +3 -8
- package/src/routes/api/health.ts +9 -2
- package/src/routes/api/knowledge.project-context.ts +11 -0
- package/src/routes/api/knowledge.search.ts +33 -2
- package/src/routes/api/logs.$id.body.ts +16 -2
- package/src/routes/api/logs.import.ts +7 -18
- package/src/routes/api/logs.stream.ts +147 -69
- package/src/routes/api/logs.ts +55 -14
- package/src/routes/api/providers.$providerId.model-metadata.ts +22 -4
- package/src/routes/api/providers.$providerId.ts +37 -6
- package/src/routes/api/providers.export.ts +34 -16
- package/src/routes/api/providers.import.ts +42 -8
- package/src/routes/api/providers.scan.ts +13 -8
- package/src/routes/api/providers.ts +36 -5
- package/src/routes/api/runs.ts +12 -12
- package/src/routes/api/sessions.ts +15 -8
- package/src/routes/index.tsx +6 -0
- package/src/routes/livez.ts +13 -0
- package/src/routes/readyz.ts +18 -0
- package/src/routes/session/$sessionId.tsx +6 -0
- package/styles/globals.css +19 -3
- package/.output/public/assets/CompareDrawer-TghZr5t1.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-fShBAZjj.js +0 -4234
- package/.output/public/assets/ResponseView-d25MdFhY.js +0 -3
- package/.output/public/assets/StreamingChunkSequence-DIFZEnWe.js +0 -1
- package/.output/public/assets/index-BsxAGqMA.css +0 -1
- package/.output/public/assets/index-DzARL2Fi.js +0 -76
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/server/_tanstack-start-manifest_v-BBf2nRhb.mjs +0 -4
- package/src/assets/logos/mcp.png +0 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/components/ui/mcp-logo.tsx +0 -20
package/.output/cli.js
CHANGED
|
@@ -14,15 +14,156 @@ var __export = (target, all) => {
|
|
|
14
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// src/proxy/privateDataPath.ts
|
|
18
|
+
import { spawnSync } from "node:child_process";
|
|
19
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
function runWindowsCommand(executable, args) {
|
|
22
|
+
const result = spawnSync(executable, [...args], {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
shell: false,
|
|
25
|
+
windowsHide: true
|
|
26
|
+
});
|
|
27
|
+
return { status: result.status, stdout: result.stdout };
|
|
28
|
+
}
|
|
29
|
+
function windowsUserSid(output) {
|
|
30
|
+
const match = /S-\d+(?:-\d+)+/.exec(output);
|
|
31
|
+
return match?.[0] ?? null;
|
|
32
|
+
}
|
|
33
|
+
function resolveWindowsUserSid(run) {
|
|
34
|
+
if (run === runWindowsCommand && cachedWindowsSid !== null) return cachedWindowsSid;
|
|
35
|
+
const identity = run("whoami.exe", ["/user", "/fo", "csv", "/nh"]);
|
|
36
|
+
if (identity.status !== 0) return null;
|
|
37
|
+
const sid = windowsUserSid(identity.stdout);
|
|
38
|
+
if (run === runWindowsCommand) cachedWindowsSid = sid;
|
|
39
|
+
return sid;
|
|
40
|
+
}
|
|
41
|
+
function windowsBroadGroupRemovalArgs() {
|
|
42
|
+
return ["/remove:g", "*S-1-1-0", "*S-1-5-11", "*S-1-5-32-545"];
|
|
43
|
+
}
|
|
44
|
+
function applyWindowsUserOnlyAcl(path2, kind, run = runWindowsCommand) {
|
|
45
|
+
const sid = resolveWindowsUserSid(run);
|
|
46
|
+
if (sid === null) return false;
|
|
47
|
+
const access = kind === "directory" ? `(OI)(CI)F` : "F";
|
|
48
|
+
const result = run("icacls.exe", [
|
|
49
|
+
path2,
|
|
50
|
+
"/inheritance:r",
|
|
51
|
+
"/grant:r",
|
|
52
|
+
`*${sid}:${access}`,
|
|
53
|
+
...windowsBroadGroupRemovalArgs(),
|
|
54
|
+
"/q"
|
|
55
|
+
]);
|
|
56
|
+
return result.status === 0;
|
|
57
|
+
}
|
|
58
|
+
function applyWindowsUserOnlyTreeAcl(root, run = runWindowsCommand) {
|
|
59
|
+
const sid = resolveWindowsUserSid(run);
|
|
60
|
+
if (sid === null) return false;
|
|
61
|
+
const result = run("icacls.exe", [
|
|
62
|
+
root,
|
|
63
|
+
"/inheritance:r",
|
|
64
|
+
"/grant:r",
|
|
65
|
+
`*${sid}:(OI)(CI)F`,
|
|
66
|
+
`*${sid}:F`,
|
|
67
|
+
...windowsBroadGroupRemovalArgs(),
|
|
68
|
+
"/T",
|
|
69
|
+
"/C",
|
|
70
|
+
"/Q"
|
|
71
|
+
]);
|
|
72
|
+
return result.status === 0;
|
|
73
|
+
}
|
|
74
|
+
function applyPrivatePermissionsSync(path2, kind) {
|
|
75
|
+
if (process.platform === "win32") {
|
|
76
|
+
return applyWindowsUserOnlyAcl(path2, kind);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
chmodSync(path2, kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function secureOnce(path2, kind) {
|
|
86
|
+
const cacheKey = `${kind}:${path2}`;
|
|
87
|
+
if (securedPaths.has(cacheKey)) return true;
|
|
88
|
+
if (!applyPrivatePermissionsSync(path2, kind)) return false;
|
|
89
|
+
securedPaths.add(cacheKey);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function ensurePrivateDirectorySync(path2) {
|
|
93
|
+
try {
|
|
94
|
+
mkdirSync(path2, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return secureOnce(path2, "directory");
|
|
99
|
+
}
|
|
100
|
+
function securePrivateFileSync(path2, forceAcl = false) {
|
|
101
|
+
let identity;
|
|
102
|
+
try {
|
|
103
|
+
const stats = statSync(path2);
|
|
104
|
+
identity = `${String(stats.dev)}:${String(stats.ino)}:${String(stats.birthtimeMs)}`;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (securedFileIdentities.get(path2) === identity) return true;
|
|
109
|
+
if (process.platform === "win32" && !forceAcl && securedPaths.has(`directory:${dirname(path2)}`)) {
|
|
110
|
+
securedFileIdentities.set(path2, identity);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (!applyPrivatePermissionsSync(path2, "file")) return false;
|
|
114
|
+
securedFileIdentities.set(path2, identity);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function securePrivateTreeSync(root) {
|
|
118
|
+
if (!existsSync(root)) return false;
|
|
119
|
+
let stats;
|
|
120
|
+
try {
|
|
121
|
+
stats = lstatSync(root);
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (stats.isSymbolicLink()) return true;
|
|
126
|
+
if (!stats.isDirectory()) return securePrivateFileSync(root, true);
|
|
127
|
+
if (process.platform === "win32") {
|
|
128
|
+
if (!applyWindowsUserOnlyTreeAcl(root)) return false;
|
|
129
|
+
securedPaths.add(`directory:${root}`);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
if (!ensurePrivateDirectorySync(root)) return false;
|
|
133
|
+
let entries;
|
|
134
|
+
try {
|
|
135
|
+
entries = readdirSync(root);
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
let secured = true;
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
const childPath = join(root, entry);
|
|
142
|
+
if (!securePrivateTreeSync(childPath)) secured = false;
|
|
143
|
+
}
|
|
144
|
+
return secured;
|
|
145
|
+
}
|
|
146
|
+
var PRIVATE_DIRECTORY_MODE, PRIVATE_FILE_MODE, securedPaths, securedFileIdentities, cachedWindowsSid;
|
|
147
|
+
var init_privateDataPath = __esm({
|
|
148
|
+
"src/proxy/privateDataPath.ts"() {
|
|
149
|
+
"use strict";
|
|
150
|
+
PRIVATE_DIRECTORY_MODE = 448;
|
|
151
|
+
PRIVATE_FILE_MODE = 384;
|
|
152
|
+
securedPaths = /* @__PURE__ */ new Set();
|
|
153
|
+
securedFileIdentities = /* @__PURE__ */ new Map();
|
|
154
|
+
cachedWindowsSid = null;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
17
158
|
// src/proxy/dataDir.ts
|
|
18
|
-
import { cpSync, existsSync, rmSync } from "node:fs";
|
|
19
|
-
import { isAbsolute, join } from "node:path";
|
|
159
|
+
import { cpSync, existsSync as existsSync2, rmSync } from "node:fs";
|
|
160
|
+
import { isAbsolute, join as join2 } from "node:path";
|
|
20
161
|
function getDataDir() {
|
|
21
162
|
migrateLegacyDataDirs();
|
|
22
|
-
return resolveDataDir(
|
|
163
|
+
return resolveDataDir(existsSync2);
|
|
23
164
|
}
|
|
24
165
|
function hasDataDirState(pathExists, dir) {
|
|
25
|
-
return pathExists(
|
|
166
|
+
return pathExists(join2(dir, "providers.json")) || pathExists(join2(dir, "config.json")) || pathExists(join2(dir, "logs")) || pathExists(join2(dir, "chunks"));
|
|
26
167
|
}
|
|
27
168
|
function defaultBaseDir(env = process.env, platform = process.platform) {
|
|
28
169
|
return platform === "win32" ? env["USERPROFILE"] ?? env["APPDATA"] ?? "C:\\" : env["HOME"] ?? "/tmp";
|
|
@@ -31,13 +172,13 @@ function resolveDataDir(_pathExists, env = process.env, platform = process.platf
|
|
|
31
172
|
const base = defaultBaseDir(env, platform);
|
|
32
173
|
const dirEnv = env["AGENT_INSPECTOR_DATA_DIR"];
|
|
33
174
|
if (dirEnv !== void 0 && dirEnv !== "") {
|
|
34
|
-
return isAbsolute(dirEnv) ? dirEnv :
|
|
175
|
+
return isAbsolute(dirEnv) ? dirEnv : join2(base, dirEnv);
|
|
35
176
|
}
|
|
36
177
|
const legacyDirEnv = env["AGENT_INSPECTOR_CONFIG_DIR"];
|
|
37
178
|
if (legacyDirEnv !== void 0 && legacyDirEnv !== "") {
|
|
38
|
-
return isAbsolute(legacyDirEnv) ? legacyDirEnv :
|
|
179
|
+
return isAbsolute(legacyDirEnv) ? legacyDirEnv : join2(base, legacyDirEnv);
|
|
39
180
|
}
|
|
40
|
-
return
|
|
181
|
+
return join2(base, CURRENT_DATA_DIR_NAME);
|
|
41
182
|
}
|
|
42
183
|
function hasExplicitDataDir() {
|
|
43
184
|
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"] !== "";
|
|
@@ -45,13 +186,15 @@ function hasExplicitDataDir() {
|
|
|
45
186
|
function migrateLegacyDataDirs() {
|
|
46
187
|
if (hasExplicitDataDir()) return;
|
|
47
188
|
const base = defaultBaseDir();
|
|
48
|
-
const currentDir =
|
|
49
|
-
if (hasDataDirState(
|
|
189
|
+
const currentDir = join2(base, CURRENT_DATA_DIR_NAME);
|
|
190
|
+
if (hasDataDirState(existsSync2, currentDir)) return;
|
|
50
191
|
for (const legacyName of LEGACY_DATA_DIR_NAMES) {
|
|
51
|
-
const legacyDir =
|
|
52
|
-
if (!
|
|
192
|
+
const legacyDir = join2(base, legacyName);
|
|
193
|
+
if (!existsSync2(legacyDir)) continue;
|
|
53
194
|
try {
|
|
195
|
+
if (!ensurePrivateDirectorySync(currentDir)) return;
|
|
54
196
|
cpSync(legacyDir, currentDir, { recursive: true, force: true });
|
|
197
|
+
if (!securePrivateTreeSync(currentDir)) return;
|
|
55
198
|
rmSync(legacyDir, { recursive: true, force: true });
|
|
56
199
|
} catch {
|
|
57
200
|
}
|
|
@@ -61,15 +204,16 @@ var CURRENT_DATA_DIR_NAME, LEGACY_DATA_DIR_NAMES;
|
|
|
61
204
|
var init_dataDir = __esm({
|
|
62
205
|
"src/proxy/dataDir.ts"() {
|
|
63
206
|
"use strict";
|
|
207
|
+
init_privateDataPath();
|
|
64
208
|
CURRENT_DATA_DIR_NAME = ".agent-inspector";
|
|
65
209
|
LEGACY_DATA_DIR_NAMES = [".llm-inspector", ".llm-agent"];
|
|
66
210
|
}
|
|
67
211
|
});
|
|
68
212
|
|
|
69
213
|
// src/proxy/platformCommands.ts
|
|
70
|
-
import { statSync } from "node:fs";
|
|
214
|
+
import { statSync as statSync2 } from "node:fs";
|
|
71
215
|
import { execFileSync } from "node:child_process";
|
|
72
|
-
import { delimiter, isAbsolute as isAbsolute2, join as
|
|
216
|
+
import { delimiter, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
73
217
|
function pathEntries(envPath) {
|
|
74
218
|
if (envPath === void 0 || envPath.length === 0) return [];
|
|
75
219
|
return envPath.split(delimiter).filter((entry) => entry.length > 0);
|
|
@@ -82,7 +226,7 @@ function windowsExecutableExtensions(envPathExt) {
|
|
|
82
226
|
}
|
|
83
227
|
function isExecutableCandidate(path2, platform) {
|
|
84
228
|
try {
|
|
85
|
-
const stat3 =
|
|
229
|
+
const stat3 = statSync2(path2);
|
|
86
230
|
if (!stat3.isFile()) return false;
|
|
87
231
|
if (platform === "win32" || process.platform === "win32") return true;
|
|
88
232
|
return (stat3.mode & 73) !== 0;
|
|
@@ -93,14 +237,14 @@ function isExecutableCandidate(path2, platform) {
|
|
|
93
237
|
function executableCandidates(bin, platform, env) {
|
|
94
238
|
if (isAbsolute2(bin)) return [bin];
|
|
95
239
|
const entries = pathEntries(env["PATH"] ?? env["Path"] ?? env["path"]);
|
|
96
|
-
if (platform !== "win32") return entries.map((entry) =>
|
|
240
|
+
if (platform !== "win32") return entries.map((entry) => join3(entry, bin));
|
|
97
241
|
const hasExtension = /\.[^\\/]+$/.test(bin);
|
|
98
242
|
const extensions = hasExtension ? [""] : windowsExecutableExtensions(env["PATHEXT"]);
|
|
99
243
|
const candidates = [];
|
|
100
244
|
for (const entry of entries) {
|
|
101
245
|
for (const extension of extensions) {
|
|
102
|
-
candidates.push(
|
|
103
|
-
candidates.push(
|
|
246
|
+
candidates.push(join3(entry, `${bin}${extension.toLowerCase()}`));
|
|
247
|
+
candidates.push(join3(entry, `${bin}${extension.toUpperCase()}`));
|
|
104
248
|
}
|
|
105
249
|
}
|
|
106
250
|
return candidates;
|
|
@@ -220,41 +364,41 @@ var init_platformCommands = __esm({
|
|
|
220
364
|
});
|
|
221
365
|
|
|
222
366
|
// src/cli/detect-tools.ts
|
|
223
|
-
import { existsSync as
|
|
224
|
-
import { join as
|
|
367
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
368
|
+
import { join as join4 } from "node:path";
|
|
225
369
|
import { homedir } from "node:os";
|
|
226
370
|
function which(bin) {
|
|
227
371
|
return resolveExecutableOnPath(bin);
|
|
228
372
|
}
|
|
229
373
|
function tryDir(path2) {
|
|
230
|
-
return
|
|
374
|
+
return existsSync3(path2) ? path2 : null;
|
|
231
375
|
}
|
|
232
376
|
function detectClaudeCode() {
|
|
233
|
-
const configDir = tryDir(
|
|
377
|
+
const configDir = tryDir(join4(homedir(), ".claude"));
|
|
234
378
|
const bin = which("claude");
|
|
235
379
|
if (configDir === null && bin === null) return { found: false };
|
|
236
380
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
237
381
|
}
|
|
238
382
|
function detectOpenCode() {
|
|
239
|
-
const configDir = tryDir(
|
|
383
|
+
const configDir = tryDir(join4(homedir(), ".config", "opencode"));
|
|
240
384
|
const bin = which("opencode");
|
|
241
385
|
if (configDir === null && bin === null) return { found: false };
|
|
242
386
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
243
387
|
}
|
|
244
388
|
function detectMiMo() {
|
|
245
|
-
const configDir = tryDir(
|
|
389
|
+
const configDir = tryDir(join4(homedir(), ".config", "mimocode")) ?? tryDir(join4(homedir(), ".mimocode"));
|
|
246
390
|
const bin = which("mimo");
|
|
247
391
|
if (configDir === null && bin === null) return { found: false };
|
|
248
392
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
249
393
|
}
|
|
250
394
|
function detectCursor() {
|
|
251
|
-
const configDir = tryDir(
|
|
395
|
+
const configDir = tryDir(join4(homedir(), ".cursor"));
|
|
252
396
|
const bin = which("cursor");
|
|
253
397
|
if (configDir === null && bin === null) return { found: false };
|
|
254
398
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
255
399
|
}
|
|
256
400
|
function detectCody() {
|
|
257
|
-
const configDir = tryDir(
|
|
401
|
+
const configDir = tryDir(join4(homedir(), ".config", "cody"));
|
|
258
402
|
const bin = which("cody");
|
|
259
403
|
if (configDir === null && bin === null) return { found: false };
|
|
260
404
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
@@ -1086,15 +1230,15 @@ __export(onboard_exports, {
|
|
|
1086
1230
|
});
|
|
1087
1231
|
import {
|
|
1088
1232
|
mkdirSync as mkdirSync2,
|
|
1089
|
-
writeFileSync
|
|
1090
|
-
existsSync as
|
|
1233
|
+
writeFileSync,
|
|
1234
|
+
existsSync as existsSync4,
|
|
1091
1235
|
readFileSync,
|
|
1092
1236
|
unlinkSync,
|
|
1093
|
-
readdirSync,
|
|
1237
|
+
readdirSync as readdirSync2,
|
|
1094
1238
|
rmdirSync
|
|
1095
1239
|
} from "node:fs";
|
|
1096
1240
|
import { homedir as homedir2 } from "node:os";
|
|
1097
|
-
import { dirname, join as
|
|
1241
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
1098
1242
|
import { fileURLToPath } from "node:url";
|
|
1099
1243
|
function actionForStatus(label, state) {
|
|
1100
1244
|
switch (state) {
|
|
@@ -1350,15 +1494,15 @@ Exit codes:
|
|
|
1350
1494
|
`);
|
|
1351
1495
|
}
|
|
1352
1496
|
function resolveTargets(flags) {
|
|
1353
|
-
const claudeRoot = flags.skillDir ??
|
|
1354
|
-
const claudeSkillDir =
|
|
1355
|
-
const claudeCommandsDir =
|
|
1356
|
-
const codexRoot = process.env["CODEX_HOME"] ??
|
|
1357
|
-
const codexSkillsDir = flags.codexSkillDir ??
|
|
1497
|
+
const claudeRoot = flags.skillDir ?? join5(homedir2(), ".claude");
|
|
1498
|
+
const claudeSkillDir = join5(claudeRoot, "skills", SKILL_DIR_NAME);
|
|
1499
|
+
const claudeCommandsDir = join5(claudeRoot, "commands");
|
|
1500
|
+
const codexRoot = process.env["CODEX_HOME"] ?? join5(homedir2(), ".codex");
|
|
1501
|
+
const codexSkillsDir = flags.codexSkillDir ?? join5(codexRoot, "skills");
|
|
1358
1502
|
return {
|
|
1359
|
-
claudeSkillFile:
|
|
1360
|
-
claudeCommandFile:
|
|
1361
|
-
codexSkillFile:
|
|
1503
|
+
claudeSkillFile: join5(claudeSkillDir, SKILL_FILE_NAME),
|
|
1504
|
+
claudeCommandFile: join5(claudeCommandsDir, COMMAND_FILE_NAME),
|
|
1505
|
+
codexSkillFile: join5(codexSkillsDir, SKILL_DIR_NAME, SKILL_FILE_NAME)
|
|
1362
1506
|
};
|
|
1363
1507
|
}
|
|
1364
1508
|
function isObject2(value) {
|
|
@@ -1377,33 +1521,33 @@ function resolveOpenCodeConfigPath(flags) {
|
|
|
1377
1521
|
if (flags.opencodeConfig !== null) {
|
|
1378
1522
|
return flags.opencodeConfig;
|
|
1379
1523
|
}
|
|
1380
|
-
const configDir =
|
|
1381
|
-
const jsoncPath =
|
|
1382
|
-
if (
|
|
1524
|
+
const configDir = join5(homedir2(), ".config", "opencode");
|
|
1525
|
+
const jsoncPath = join5(configDir, "opencode.jsonc");
|
|
1526
|
+
if (existsSync4(jsoncPath)) {
|
|
1383
1527
|
return jsoncPath;
|
|
1384
1528
|
}
|
|
1385
|
-
return
|
|
1529
|
+
return join5(configDir, "opencode.json");
|
|
1386
1530
|
}
|
|
1387
1531
|
function resolveMiMoConfigPath(flags) {
|
|
1388
1532
|
if (flags.mimoConfig !== null) {
|
|
1389
1533
|
return flags.mimoConfig;
|
|
1390
1534
|
}
|
|
1391
|
-
const configDir =
|
|
1392
|
-
const jsoncPath =
|
|
1393
|
-
if (
|
|
1535
|
+
const configDir = join5(homedir2(), ".config", "mimocode");
|
|
1536
|
+
const jsoncPath = join5(configDir, "mimocode.jsonc");
|
|
1537
|
+
if (existsSync4(jsoncPath)) {
|
|
1394
1538
|
return jsoncPath;
|
|
1395
1539
|
}
|
|
1396
|
-
const jsonPath =
|
|
1397
|
-
if (
|
|
1540
|
+
const jsonPath = join5(configDir, "mimocode.json");
|
|
1541
|
+
if (existsSync4(jsonPath)) {
|
|
1398
1542
|
return jsonPath;
|
|
1399
1543
|
}
|
|
1400
|
-
const legacyDir =
|
|
1401
|
-
const legacyJsoncPath =
|
|
1402
|
-
if (
|
|
1544
|
+
const legacyDir = join5(homedir2(), ".mimocode");
|
|
1545
|
+
const legacyJsoncPath = join5(legacyDir, "config.jsonc");
|
|
1546
|
+
if (existsSync4(legacyJsoncPath)) {
|
|
1403
1547
|
return legacyJsoncPath;
|
|
1404
1548
|
}
|
|
1405
|
-
const legacyJsonPath =
|
|
1406
|
-
if (
|
|
1549
|
+
const legacyJsonPath = join5(legacyDir, "config.json");
|
|
1550
|
+
if (existsSync4(legacyJsonPath)) {
|
|
1407
1551
|
return legacyJsonPath;
|
|
1408
1552
|
}
|
|
1409
1553
|
return jsoncPath;
|
|
@@ -1487,7 +1631,7 @@ function stripJsoncComments(raw) {
|
|
|
1487
1631
|
return result;
|
|
1488
1632
|
}
|
|
1489
1633
|
function readAgentConfig(path2, target) {
|
|
1490
|
-
if (!
|
|
1634
|
+
if (!existsSync4(path2)) {
|
|
1491
1635
|
return { ok: true, exists: false, config: {} };
|
|
1492
1636
|
}
|
|
1493
1637
|
try {
|
|
@@ -1781,12 +1925,12 @@ function buildDetectedSummary() {
|
|
|
1781
1925
|
}
|
|
1782
1926
|
function readPackageVersion() {
|
|
1783
1927
|
const packageJsonPaths = [
|
|
1784
|
-
|
|
1785
|
-
|
|
1928
|
+
join5(__dirname, "..", "package.json"),
|
|
1929
|
+
join5(__dirname, "..", "..", "package.json")
|
|
1786
1930
|
];
|
|
1787
1931
|
try {
|
|
1788
1932
|
for (const packageJsonPath of packageJsonPaths) {
|
|
1789
|
-
if (
|
|
1933
|
+
if (existsSync4(packageJsonPath)) {
|
|
1790
1934
|
const raw = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
1791
1935
|
if (isObject2(raw) && typeof raw["version"] === "string") {
|
|
1792
1936
|
return raw["version"];
|
|
@@ -1881,7 +2025,7 @@ function isMatchingGeneratedFile(path2, currentVersion) {
|
|
|
1881
2025
|
}
|
|
1882
2026
|
}
|
|
1883
2027
|
function readGeneratedFileStatus(file2, currentVersion) {
|
|
1884
|
-
if (!
|
|
2028
|
+
if (!existsSync4(file2.path)) {
|
|
1885
2029
|
return {
|
|
1886
2030
|
label: file2.label,
|
|
1887
2031
|
path: file2.path,
|
|
@@ -1986,7 +2130,7 @@ function shouldWrite(file2, force, currentVersion) {
|
|
|
1986
2130
|
if (!file2.enabled) {
|
|
1987
2131
|
return false;
|
|
1988
2132
|
}
|
|
1989
|
-
if (force || !
|
|
2133
|
+
if (force || !existsSync4(file2.path)) {
|
|
1990
2134
|
return true;
|
|
1991
2135
|
}
|
|
1992
2136
|
const existingVersion = readGeneratedVersion(file2.path);
|
|
@@ -1997,11 +2141,11 @@ function shouldWrite(file2, force, currentVersion) {
|
|
|
1997
2141
|
return comparison !== null && comparison < 0;
|
|
1998
2142
|
}
|
|
1999
2143
|
function shouldUninstall(file2, currentVersion) {
|
|
2000
|
-
return file2.enabled &&
|
|
2144
|
+
return file2.enabled && existsSync4(file2.path) && isMatchingGeneratedFile(file2.path, currentVersion);
|
|
2001
2145
|
}
|
|
2002
2146
|
function removeEmptyDirectory(path2) {
|
|
2003
2147
|
try {
|
|
2004
|
-
if (
|
|
2148
|
+
if (readdirSync2(path2).length === 0) {
|
|
2005
2149
|
rmdirSync(path2);
|
|
2006
2150
|
}
|
|
2007
2151
|
} catch {
|
|
@@ -2049,14 +2193,14 @@ No files were removed.
|
|
|
2049
2193
|
try {
|
|
2050
2194
|
for (const file2 of filesToRemove) {
|
|
2051
2195
|
unlinkSync(file2.path);
|
|
2052
|
-
removeEmptyDirectory(
|
|
2196
|
+
removeEmptyDirectory(dirname2(file2.path));
|
|
2053
2197
|
process.stdout.write(`Removed ${file2.label}: ${file2.path}
|
|
2054
2198
|
`);
|
|
2055
2199
|
}
|
|
2056
2200
|
for (const plan of agentPlans) {
|
|
2057
2201
|
if (plan.shouldRemove && plan.body !== null) {
|
|
2058
|
-
mkdirSync2(
|
|
2059
|
-
|
|
2202
|
+
mkdirSync2(dirname2(plan.status.path), { recursive: true });
|
|
2203
|
+
writeFileSync(plan.status.path, plan.body, "utf8");
|
|
2060
2204
|
process.stdout.write(`Removed ${plan.status.label} entry from: ${plan.status.path}
|
|
2061
2205
|
`);
|
|
2062
2206
|
}
|
|
@@ -2198,7 +2342,7 @@ function runOnboardSync(argv) {
|
|
|
2198
2342
|
|
|
2199
2343
|
`);
|
|
2200
2344
|
for (const file2 of enabledFiles) {
|
|
2201
|
-
const status =
|
|
2345
|
+
const status = existsSync4(file2.path) ? "exists" : "missing";
|
|
2202
2346
|
process.stdout.write(`${file2.label}: ${file2.path} (${status})
|
|
2203
2347
|
`);
|
|
2204
2348
|
}
|
|
@@ -2275,13 +2419,13 @@ No files were written.
|
|
|
2275
2419
|
}
|
|
2276
2420
|
try {
|
|
2277
2421
|
for (const file2 of filesToWrite) {
|
|
2278
|
-
mkdirSync2(
|
|
2279
|
-
|
|
2422
|
+
mkdirSync2(dirname2(file2.path), { recursive: true });
|
|
2423
|
+
writeFileSync(file2.path, file2.body, "utf8");
|
|
2280
2424
|
}
|
|
2281
2425
|
for (const plan of agentPlansToWrite) {
|
|
2282
2426
|
if (plan.body !== null) {
|
|
2283
|
-
mkdirSync2(
|
|
2284
|
-
|
|
2427
|
+
mkdirSync2(dirname2(plan.status.path), { recursive: true });
|
|
2428
|
+
writeFileSync(plan.status.path, plan.body, "utf8");
|
|
2285
2429
|
}
|
|
2286
2430
|
}
|
|
2287
2431
|
} catch (err) {
|
|
@@ -2338,7 +2482,7 @@ var init_onboard = __esm({
|
|
|
2338
2482
|
init_codex_skill_onboard();
|
|
2339
2483
|
init_skill_onboard();
|
|
2340
2484
|
__filename = fileURLToPath(import.meta.url);
|
|
2341
|
-
__dirname =
|
|
2485
|
+
__dirname = dirname2(__filename);
|
|
2342
2486
|
DEFAULT_PORT = 9527;
|
|
2343
2487
|
DEFAULT_MCP_URL = `http://localhost:${DEFAULT_PORT}/api/mcp`;
|
|
2344
2488
|
LEGACY_MCP_URL = "http://localhost:9527/api/mcp";
|
|
@@ -2362,8 +2506,8 @@ __export(doctor_exports, {
|
|
|
2362
2506
|
parseDoctorArgs: () => parseDoctorArgs,
|
|
2363
2507
|
runDoctor: () => runDoctor
|
|
2364
2508
|
});
|
|
2365
|
-
import { existsSync as
|
|
2366
|
-
import { isAbsolute as isAbsolute3, join as
|
|
2509
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
2510
|
+
import { isAbsolute as isAbsolute3, join as join6, resolve } from "node:path";
|
|
2367
2511
|
import { createConnection } from "node:net";
|
|
2368
2512
|
function check2(name, severity, message, hint = "") {
|
|
2369
2513
|
return {
|
|
@@ -2478,10 +2622,10 @@ function providerConfigCandidates(options, deps) {
|
|
|
2478
2622
|
}
|
|
2479
2623
|
if (options.configDir !== null) {
|
|
2480
2624
|
const dir = resolveMaybeRelative(deps.cwd, options.configDir);
|
|
2481
|
-
return [
|
|
2625
|
+
return [join6(dir, "providers.json"), join6(dir, "config.json")];
|
|
2482
2626
|
}
|
|
2483
2627
|
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
2484
|
-
return [
|
|
2628
|
+
return [join6(dataDir, "providers.json"), join6(dataDir, "config.json")];
|
|
2485
2629
|
}
|
|
2486
2630
|
function checkProviderConfig(options, deps) {
|
|
2487
2631
|
const providersJson = options.providersJson ?? deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] ?? null;
|
|
@@ -2567,7 +2711,7 @@ async function checkProxyHealth(port, deps) {
|
|
|
2567
2711
|
function findProjectRoot(startDir, deps) {
|
|
2568
2712
|
let current = resolve(startDir);
|
|
2569
2713
|
for (let i = 0; i < 6; i++) {
|
|
2570
|
-
if (deps.exists(
|
|
2714
|
+
if (deps.exists(join6(current, "package.json"))) return current;
|
|
2571
2715
|
const next = resolve(current, "..");
|
|
2572
2716
|
if (next === current) return startDir;
|
|
2573
2717
|
current = next;
|
|
@@ -2575,7 +2719,7 @@ function findProjectRoot(startDir, deps) {
|
|
|
2575
2719
|
return startDir;
|
|
2576
2720
|
}
|
|
2577
2721
|
function checkPackage(rootDir, deps) {
|
|
2578
|
-
const packagePath =
|
|
2722
|
+
const packagePath = join6(rootDir, "package.json");
|
|
2579
2723
|
const text = deps.readText(packagePath);
|
|
2580
2724
|
if (text === null) {
|
|
2581
2725
|
return check2("Package metadata", "warn", "package.json was not found near the CLI bundle.");
|
|
@@ -2600,7 +2744,7 @@ function countFilesInDir(dir, deps) {
|
|
|
2600
2744
|
if (files === null) return null;
|
|
2601
2745
|
let count = 0;
|
|
2602
2746
|
for (const file2 of files) {
|
|
2603
|
-
const fileStat = deps.stat(
|
|
2747
|
+
const fileStat = deps.stat(join6(dir, file2));
|
|
2604
2748
|
if (fileStat !== null && !fileStat.isDirectory) count += 1;
|
|
2605
2749
|
}
|
|
2606
2750
|
return count;
|
|
@@ -2620,24 +2764,24 @@ function dirSize(dir, deps, depth = 0) {
|
|
|
2620
2764
|
if (files === null) return null;
|
|
2621
2765
|
let total = 0;
|
|
2622
2766
|
for (const file2 of files) {
|
|
2623
|
-
const childSize = dirSize(
|
|
2767
|
+
const childSize = dirSize(join6(dir, file2), deps, depth + 1);
|
|
2624
2768
|
if (childSize !== null) total += childSize;
|
|
2625
2769
|
}
|
|
2626
2770
|
return total;
|
|
2627
2771
|
}
|
|
2628
2772
|
function verboseChecks(options, deps) {
|
|
2629
2773
|
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
2630
|
-
const logsDir =
|
|
2631
|
-
const chunksDir =
|
|
2774
|
+
const logsDir = join6(dataDir, "logs");
|
|
2775
|
+
const chunksDir = join6(dataDir, "chunks");
|
|
2632
2776
|
const providerCandidates = providerConfigCandidates(options, deps);
|
|
2633
2777
|
const captureMode = deps.env["AGENT_INSPECTOR_MODE"] ?? deps.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? "simple";
|
|
2634
2778
|
const logCount = countFilesInDir(logsDir, deps);
|
|
2635
2779
|
const logSize = dirSize(logsDir, deps);
|
|
2636
2780
|
const chunkSize = dirSize(chunksDir, deps);
|
|
2637
2781
|
const homeDir = deps.platform === "win32" ? deps.env["USERPROFILE"] ?? deps.env["APPDATA"] ?? null : deps.env["HOME"] ?? null;
|
|
2638
|
-
const claudeSkill = homeDir === null ? null :
|
|
2639
|
-
const claudeCommand = homeDir === null ? null :
|
|
2640
|
-
const codexSkill = homeDir === null ? null :
|
|
2782
|
+
const claudeSkill = homeDir === null ? null : join6(homeDir, ".claude", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
2783
|
+
const claudeCommand = homeDir === null ? null : join6(homeDir, ".claude", "commands", "agent-inspector-onboard.md");
|
|
2784
|
+
const codexSkill = homeDir === null ? null : join6(homeDir, ".codex", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
2641
2785
|
const hasClaudeOnboarding = claudeSkill !== null && claudeCommand !== null && deps.exists(claudeSkill) && deps.exists(claudeCommand);
|
|
2642
2786
|
const hasCodexOnboarding = codexSkill !== null && deps.exists(codexSkill);
|
|
2643
2787
|
return [
|
|
@@ -2669,8 +2813,8 @@ function verboseChecks(options, deps) {
|
|
|
2669
2813
|
];
|
|
2670
2814
|
}
|
|
2671
2815
|
function checkExtensionSource(rootDir, deps) {
|
|
2672
|
-
const extensionDir =
|
|
2673
|
-
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(
|
|
2816
|
+
const extensionDir = join6(rootDir, "extensions", "chrome");
|
|
2817
|
+
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(join6(extensionDir, file2)));
|
|
2674
2818
|
if (missing.length === 0) {
|
|
2675
2819
|
return check2(
|
|
2676
2820
|
"Chrome extension source",
|
|
@@ -2686,7 +2830,7 @@ function checkExtensionSource(rootDir, deps) {
|
|
|
2686
2830
|
);
|
|
2687
2831
|
}
|
|
2688
2832
|
function extensionManifestVersion(rootDir, deps) {
|
|
2689
|
-
const manifestText = deps.readText(
|
|
2833
|
+
const manifestText = deps.readText(join6(rootDir, "extensions", "chrome", "manifest.json"));
|
|
2690
2834
|
if (manifestText === null) return null;
|
|
2691
2835
|
try {
|
|
2692
2836
|
const manifest = JSON.parse(manifestText);
|
|
@@ -2698,7 +2842,7 @@ function extensionManifestVersion(rootDir, deps) {
|
|
|
2698
2842
|
}
|
|
2699
2843
|
}
|
|
2700
2844
|
function checkExtensionPackage(rootDir, deps) {
|
|
2701
|
-
const distDir =
|
|
2845
|
+
const distDir = join6(rootDir, "dist", "chrome-extension");
|
|
2702
2846
|
const files = deps.listDir(distDir);
|
|
2703
2847
|
if (files === null) {
|
|
2704
2848
|
return check2(
|
|
@@ -2844,14 +2988,14 @@ function readText(path2) {
|
|
|
2844
2988
|
}
|
|
2845
2989
|
function listDir(path2) {
|
|
2846
2990
|
try {
|
|
2847
|
-
return
|
|
2991
|
+
return readdirSync3(path2);
|
|
2848
2992
|
} catch {
|
|
2849
2993
|
return null;
|
|
2850
2994
|
}
|
|
2851
2995
|
}
|
|
2852
2996
|
function stat2(path2) {
|
|
2853
2997
|
try {
|
|
2854
|
-
const fileStat =
|
|
2998
|
+
const fileStat = statSync3(path2);
|
|
2855
2999
|
return { isDirectory: fileStat.isDirectory(), size: fileStat.size };
|
|
2856
3000
|
} catch {
|
|
2857
3001
|
return null;
|
|
@@ -2862,7 +3006,7 @@ function createDoctorDeps() {
|
|
|
2862
3006
|
env: process.env,
|
|
2863
3007
|
platform: process.platform,
|
|
2864
3008
|
cwd: process.cwd(),
|
|
2865
|
-
exists:
|
|
3009
|
+
exists: existsSync5,
|
|
2866
3010
|
readText,
|
|
2867
3011
|
listDir,
|
|
2868
3012
|
stat: stat2,
|
|
@@ -2911,12 +3055,12 @@ var init_doctor = __esm({
|
|
|
2911
3055
|
});
|
|
2912
3056
|
|
|
2913
3057
|
// src/cli.ts
|
|
2914
|
-
import { Buffer as
|
|
3058
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
2915
3059
|
import { spawn } from "node:child_process";
|
|
2916
3060
|
import { createConnection as createConnection2 } from "node:net";
|
|
2917
3061
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2918
|
-
import { dirname as
|
|
2919
|
-
import { existsSync as
|
|
3062
|
+
import { dirname as dirname3, join as join7, resolve as resolvePath } from "node:path";
|
|
3063
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
2920
3064
|
|
|
2921
3065
|
// src/cli/networkHints.ts
|
|
2922
3066
|
import { networkInterfaces } from "node:os";
|
|
@@ -3037,8 +3181,9 @@ function shouldSuppressServerOutputLine(line, context) {
|
|
|
3037
3181
|
|
|
3038
3182
|
// src/proxy/identityProxy.ts
|
|
3039
3183
|
import http from "node:http";
|
|
3040
|
-
import { Buffer as
|
|
3184
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
3041
3185
|
import { timingSafeEqual } from "node:crypto";
|
|
3186
|
+
import { isIP } from "node:net";
|
|
3042
3187
|
|
|
3043
3188
|
// src/proxy/socketTracker.ts
|
|
3044
3189
|
import { execFile } from "node:child_process";
|
|
@@ -17561,10 +17706,15 @@ config(en_default());
|
|
|
17561
17706
|
|
|
17562
17707
|
// src/proxy/logger.ts
|
|
17563
17708
|
init_dataDir();
|
|
17564
|
-
|
|
17565
|
-
import {
|
|
17709
|
+
init_privateDataPath();
|
|
17710
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
17711
|
+
import { appendFileSync } from "node:fs";
|
|
17712
|
+
import { appendFile, readdir, rename, stat, unlink } from "node:fs/promises";
|
|
17566
17713
|
import path from "node:path";
|
|
17567
|
-
var
|
|
17714
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
17715
|
+
var DEFAULT_RETENTION_DAYS = 7;
|
|
17716
|
+
var DEFAULT_APP_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
17717
|
+
var DEFAULT_APP_LOG_MAX_FILES = 5;
|
|
17568
17718
|
function writeStderrFallback(message, error51) {
|
|
17569
17719
|
process.stderr.write(`${message} ${String(error51)}
|
|
17570
17720
|
`);
|
|
@@ -17590,45 +17740,211 @@ function getInspectorLogPath() {
|
|
|
17590
17740
|
}
|
|
17591
17741
|
return path.join(getDataDir(), "logs", "inspector.log");
|
|
17592
17742
|
}
|
|
17743
|
+
function readBoundedInteger(name, fallback, minimum, maximum) {
|
|
17744
|
+
const raw = process.env[name];
|
|
17745
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
17746
|
+
const parsed = Number(raw);
|
|
17747
|
+
if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) return fallback;
|
|
17748
|
+
return parsed;
|
|
17749
|
+
}
|
|
17750
|
+
function getApplicationLogPolicy() {
|
|
17751
|
+
const retentionDays = readBoundedInteger("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS, 1, 3650);
|
|
17752
|
+
return {
|
|
17753
|
+
maxBytes: readBoundedInteger(
|
|
17754
|
+
"AGENT_INSPECTOR_APP_LOG_MAX_BYTES",
|
|
17755
|
+
DEFAULT_APP_LOG_MAX_BYTES,
|
|
17756
|
+
1,
|
|
17757
|
+
1024 * 1024 * 1024
|
|
17758
|
+
),
|
|
17759
|
+
maxFiles: readBoundedInteger(
|
|
17760
|
+
"AGENT_INSPECTOR_APP_LOG_MAX_FILES",
|
|
17761
|
+
DEFAULT_APP_LOG_MAX_FILES,
|
|
17762
|
+
1,
|
|
17763
|
+
100
|
|
17764
|
+
),
|
|
17765
|
+
retentionMs: retentionDays * DAY_MS
|
|
17766
|
+
};
|
|
17767
|
+
}
|
|
17768
|
+
function getRotatedGeneration(entry, baseName) {
|
|
17769
|
+
const prefix = `${baseName}.`;
|
|
17770
|
+
if (!entry.startsWith(prefix)) return null;
|
|
17771
|
+
const suffix = entry.slice(prefix.length);
|
|
17772
|
+
if (!/^\d+$/.test(suffix)) return null;
|
|
17773
|
+
const generation = Number(suffix);
|
|
17774
|
+
if (!Number.isSafeInteger(generation) || generation < 1) return null;
|
|
17775
|
+
return generation;
|
|
17776
|
+
}
|
|
17777
|
+
async function listApplicationLogFiles(logPath) {
|
|
17778
|
+
const directory = path.dirname(logPath);
|
|
17779
|
+
const baseName = path.basename(logPath);
|
|
17780
|
+
const entries = await readdir(directory);
|
|
17781
|
+
const files = [];
|
|
17782
|
+
for (const entry of entries) {
|
|
17783
|
+
const generation = entry === baseName ? 0 : getRotatedGeneration(entry, baseName);
|
|
17784
|
+
if (generation === null) continue;
|
|
17785
|
+
const filePath = path.join(directory, entry);
|
|
17786
|
+
try {
|
|
17787
|
+
const fileStat = await stat(filePath);
|
|
17788
|
+
if (!fileStat.isFile()) continue;
|
|
17789
|
+
files.push({ generation, modifiedAt: fileStat.mtimeMs, path: filePath });
|
|
17790
|
+
} catch {
|
|
17791
|
+
}
|
|
17792
|
+
}
|
|
17793
|
+
return files;
|
|
17794
|
+
}
|
|
17795
|
+
async function removeApplicationLog(filePath) {
|
|
17796
|
+
try {
|
|
17797
|
+
await stat(filePath);
|
|
17798
|
+
} catch {
|
|
17799
|
+
return true;
|
|
17800
|
+
}
|
|
17801
|
+
try {
|
|
17802
|
+
await unlink(filePath);
|
|
17803
|
+
return true;
|
|
17804
|
+
} catch {
|
|
17805
|
+
return false;
|
|
17806
|
+
}
|
|
17807
|
+
}
|
|
17808
|
+
async function maintainApplicationLogs(logPath, policy) {
|
|
17809
|
+
const directory = path.dirname(logPath);
|
|
17810
|
+
if (!ensurePrivateDirectorySync(directory)) return false;
|
|
17811
|
+
let files;
|
|
17812
|
+
try {
|
|
17813
|
+
files = await listApplicationLogFiles(logPath);
|
|
17814
|
+
} catch {
|
|
17815
|
+
return false;
|
|
17816
|
+
}
|
|
17817
|
+
for (const file2 of files) {
|
|
17818
|
+
if (!securePrivateFileSync(file2.path)) return false;
|
|
17819
|
+
}
|
|
17820
|
+
const cutoff = Date.now() - policy.retentionMs;
|
|
17821
|
+
const retained = files.filter((file2) => file2.modifiedAt >= cutoff);
|
|
17822
|
+
let maintained = true;
|
|
17823
|
+
for (const file2 of files) {
|
|
17824
|
+
if (file2.modifiedAt < cutoff && !await removeApplicationLog(file2.path)) maintained = false;
|
|
17825
|
+
}
|
|
17826
|
+
const rotated = retained.filter((file2) => file2.generation > 0).sort((left, right) => right.modifiedAt - left.modifiedAt);
|
|
17827
|
+
const retainedRotatedCount = Math.max(0, policy.maxFiles - 1);
|
|
17828
|
+
for (const file2 of rotated.slice(retainedRotatedCount)) {
|
|
17829
|
+
if (!await removeApplicationLog(file2.path)) maintained = false;
|
|
17830
|
+
}
|
|
17831
|
+
return maintained;
|
|
17832
|
+
}
|
|
17833
|
+
async function moveApplicationLog(source, destination) {
|
|
17834
|
+
try {
|
|
17835
|
+
await stat(source);
|
|
17836
|
+
} catch {
|
|
17837
|
+
return true;
|
|
17838
|
+
}
|
|
17839
|
+
try {
|
|
17840
|
+
await rename(source, destination);
|
|
17841
|
+
if (!securePrivateFileSync(destination)) {
|
|
17842
|
+
writeStderrFallback("[logger] Failed to secure rotated application log:", destination);
|
|
17843
|
+
return false;
|
|
17844
|
+
}
|
|
17845
|
+
return true;
|
|
17846
|
+
} catch (error51) {
|
|
17847
|
+
writeStderrFallback("[logger] Failed to rotate application log:", error51);
|
|
17848
|
+
return false;
|
|
17849
|
+
}
|
|
17850
|
+
}
|
|
17851
|
+
async function rotateApplicationLog(logPath, maxFiles) {
|
|
17852
|
+
if (maxFiles === 1) {
|
|
17853
|
+
return await removeApplicationLog(logPath);
|
|
17854
|
+
}
|
|
17855
|
+
if (!await removeApplicationLog(`${logPath}.${String(maxFiles - 1)}`)) return false;
|
|
17856
|
+
for (let generation = maxFiles - 2; generation >= 1; generation -= 1) {
|
|
17857
|
+
if (!await moveApplicationLog(
|
|
17858
|
+
`${logPath}.${String(generation)}`,
|
|
17859
|
+
`${logPath}.${String(generation + 1)}`
|
|
17860
|
+
)) {
|
|
17861
|
+
return false;
|
|
17862
|
+
}
|
|
17863
|
+
}
|
|
17864
|
+
return await moveApplicationLog(logPath, `${logPath}.1`);
|
|
17865
|
+
}
|
|
17866
|
+
async function rotateApplicationLogIfNeeded(logPath, incomingBytes, policy) {
|
|
17867
|
+
try {
|
|
17868
|
+
const fileStat = await stat(logPath);
|
|
17869
|
+
if (fileStat.size > 0 && fileStat.size + incomingBytes > policy.maxBytes) {
|
|
17870
|
+
return await rotateApplicationLog(logPath, policy.maxFiles);
|
|
17871
|
+
}
|
|
17872
|
+
return true;
|
|
17873
|
+
} catch {
|
|
17874
|
+
return true;
|
|
17875
|
+
}
|
|
17876
|
+
}
|
|
17593
17877
|
async function initLogger() {
|
|
17594
17878
|
const dir = resolveLogDir();
|
|
17595
|
-
const
|
|
17596
|
-
const cutoff = Date.now() - retentionMs;
|
|
17879
|
+
const policy = getApplicationLogPolicy();
|
|
17880
|
+
const cutoff = Date.now() - policy.retentionMs;
|
|
17597
17881
|
try {
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
if (
|
|
17606
|
-
|
|
17882
|
+
if (!ensurePrivateDirectorySync(dir)) {
|
|
17883
|
+
writeStderrFallback("[logger] Failed to secure log directory:", dir);
|
|
17884
|
+
} else if (!securePrivateTreeSync(dir)) {
|
|
17885
|
+
writeStderrFallback("[logger] Failed to secure existing log files:", dir);
|
|
17886
|
+
} else {
|
|
17887
|
+
const entries = await readdir(dir);
|
|
17888
|
+
for (const entry of entries) {
|
|
17889
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
17890
|
+
const fullPath = path.join(dir, entry);
|
|
17891
|
+
try {
|
|
17892
|
+
const s = await stat(fullPath);
|
|
17893
|
+
if (s.mtimeMs < cutoff) {
|
|
17894
|
+
await unlink(fullPath);
|
|
17895
|
+
}
|
|
17896
|
+
} catch {
|
|
17607
17897
|
}
|
|
17608
|
-
} catch {
|
|
17609
17898
|
}
|
|
17610
17899
|
}
|
|
17611
17900
|
} catch (err) {
|
|
17612
17901
|
writeStderrFallback("[logger] Failed to initialize log directory:", err);
|
|
17613
17902
|
}
|
|
17903
|
+
const applicationLogPath = getInspectorLogPath();
|
|
17904
|
+
if (!await maintainApplicationLogs(applicationLogPath, policy)) {
|
|
17905
|
+
writeStderrFallback(
|
|
17906
|
+
"[logger] Failed to initialize application log lifecycle:",
|
|
17907
|
+
applicationLogPath
|
|
17908
|
+
);
|
|
17909
|
+
}
|
|
17614
17910
|
}
|
|
17615
|
-
var
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17911
|
+
var loggerInitialization = null;
|
|
17912
|
+
var applicationLogWriteQueue = Promise.resolve();
|
|
17913
|
+
function ensureLoggerInitialized() {
|
|
17914
|
+
if (loggerInitialization === null) loggerInitialization = initLogger();
|
|
17915
|
+
return loggerInitialization;
|
|
17916
|
+
}
|
|
17917
|
+
async function appendApplicationLogLine(logPath, line, policy) {
|
|
17918
|
+
const logDirPath = path.dirname(logPath);
|
|
17919
|
+
if (!ensurePrivateDirectorySync(logDirPath)) {
|
|
17920
|
+
return { ok: false, reason: "application log directory is not private" };
|
|
17620
17921
|
}
|
|
17621
|
-
const logPath = getInspectorLogPath();
|
|
17622
17922
|
try {
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
await appendFile(logPath,
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17923
|
+
if (!await rotateApplicationLogIfNeeded(logPath, Buffer2.byteLength(line, "utf-8"), policy)) {
|
|
17924
|
+
return { ok: false, reason: "application log rotation failed" };
|
|
17925
|
+
}
|
|
17926
|
+
await appendFile(logPath, line, { encoding: "utf-8", mode: PRIVATE_FILE_MODE });
|
|
17927
|
+
if (!securePrivateFileSync(logPath)) {
|
|
17928
|
+
return { ok: false, reason: "application log permissions could not be secured" };
|
|
17929
|
+
}
|
|
17930
|
+
return { ok: true };
|
|
17931
|
+
} catch (error51) {
|
|
17932
|
+
return { ok: false, reason: error51 };
|
|
17630
17933
|
}
|
|
17631
17934
|
}
|
|
17935
|
+
async function writeAppLog(message) {
|
|
17936
|
+
const logPath = getInspectorLogPath();
|
|
17937
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
17938
|
+
`;
|
|
17939
|
+
const initialization = ensureLoggerInitialized();
|
|
17940
|
+
const write = async () => {
|
|
17941
|
+
await initialization;
|
|
17942
|
+
const result = await appendApplicationLogLine(logPath, line, getApplicationLogPolicy());
|
|
17943
|
+
if (!result.ok) writeStderrFallback(`[logger] Failed to write to ${logPath}:`, result.reason);
|
|
17944
|
+
};
|
|
17945
|
+
applicationLogWriteQueue = applicationLogWriteQueue.then(write, write);
|
|
17946
|
+
await applicationLogWriteQueue;
|
|
17947
|
+
}
|
|
17632
17948
|
var logger = {
|
|
17633
17949
|
debug(message, ...args) {
|
|
17634
17950
|
const msg = args.length > 0 ? `${message} ${args.map(String).join(" ")}` : message;
|
|
@@ -17650,62 +17966,23 @@ var logger = {
|
|
|
17650
17966
|
var writeBuffer = [];
|
|
17651
17967
|
var writeQueue = Promise.resolve();
|
|
17652
17968
|
var logWriteLock = Promise.resolve();
|
|
17653
|
-
async function runWithLogWriteLock(fn) {
|
|
17654
|
-
const previousLock = logWriteLock;
|
|
17655
|
-
let releaseLock = () => {
|
|
17656
|
-
};
|
|
17657
|
-
logWriteLock = new Promise((resolve2) => {
|
|
17658
|
-
releaseLock = resolve2;
|
|
17659
|
-
});
|
|
17660
|
-
await previousLock;
|
|
17661
|
-
try {
|
|
17662
|
-
return await fn();
|
|
17663
|
-
} finally {
|
|
17664
|
-
releaseLock();
|
|
17665
|
-
}
|
|
17666
|
-
}
|
|
17667
17969
|
function drainBuffer() {
|
|
17668
17970
|
const toWrite = writeBuffer.join("");
|
|
17669
17971
|
writeBuffer = [];
|
|
17670
17972
|
return toWrite;
|
|
17671
17973
|
}
|
|
17672
|
-
async function flushWriteBufferUnlocked() {
|
|
17673
|
-
let toWrite;
|
|
17674
|
-
{
|
|
17675
|
-
toWrite = drainBuffer();
|
|
17676
|
-
}
|
|
17677
|
-
if (toWrite.length === 0) return;
|
|
17678
|
-
try {
|
|
17679
|
-
const filePath = getLogFilePath();
|
|
17680
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
17681
|
-
await appendFile(filePath, toWrite, "utf-8");
|
|
17682
|
-
} catch (err) {
|
|
17683
|
-
writeBuffer.unshift(toWrite);
|
|
17684
|
-
writeStderrFallback("[logger] Failed to flush write buffer, re-queued:", err);
|
|
17685
|
-
}
|
|
17686
|
-
}
|
|
17687
|
-
async function flushLogBuffer() {
|
|
17688
|
-
await writeQueue;
|
|
17689
|
-
if (writeBuffer.length > 0) {
|
|
17690
|
-
await runWithLogWriteLock(flushWriteBufferUnlocked);
|
|
17691
|
-
}
|
|
17692
|
-
}
|
|
17693
17974
|
process.on("exit", () => {
|
|
17694
17975
|
if (writeBuffer.length > 0) {
|
|
17695
17976
|
const toWrite = drainBuffer();
|
|
17696
17977
|
try {
|
|
17697
17978
|
const filePath = getLogFilePath();
|
|
17698
|
-
|
|
17699
|
-
|
|
17979
|
+
if (!ensurePrivateDirectorySync(path.dirname(filePath))) return;
|
|
17980
|
+
appendFileSync(filePath, toWrite, { encoding: "utf-8", mode: PRIVATE_FILE_MODE });
|
|
17981
|
+
void securePrivateFileSync(filePath);
|
|
17700
17982
|
} catch {
|
|
17701
17983
|
}
|
|
17702
17984
|
}
|
|
17703
17985
|
});
|
|
17704
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
17705
|
-
process.on(signal, () => {
|
|
17706
|
-
void flushLogBuffer().then(() => process.exit(0));
|
|
17707
|
-
});
|
|
17708
|
-
}
|
|
17709
17986
|
|
|
17710
17987
|
// src/proxy/socketTracker.ts
|
|
17711
17988
|
var execFileAsync = promisify(execFile);
|
|
@@ -17871,6 +18148,12 @@ var IDENTITY_HEADERS = {
|
|
|
17871
18148
|
cwd: "x-agent-inspector-client-cwd",
|
|
17872
18149
|
projectFolder: "x-agent-inspector-client-project-folder"
|
|
17873
18150
|
};
|
|
18151
|
+
var IDENTITY_HEADER_PREFIX = "x-agent-inspector-";
|
|
18152
|
+
var PROXY_TOKEN_HEADER = "x-agent-inspector-proxy-token";
|
|
18153
|
+
var CSRF_HEADER = "x-agent-inspector-csrf";
|
|
18154
|
+
var DEFAULT_MAX_REQUEST_BYTES = 64 * 1024 * 1024;
|
|
18155
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
18156
|
+
var identityProxyRuntimeStates = /* @__PURE__ */ new WeakMap();
|
|
17874
18157
|
var REQUEST_HOP_BY_HOP = /* @__PURE__ */ new Set([
|
|
17875
18158
|
"connection",
|
|
17876
18159
|
"keep-alive",
|
|
@@ -17892,6 +18175,24 @@ var RESPONSE_HOP_BY_HOP = /* @__PURE__ */ new Set([
|
|
|
17892
18175
|
"transfer-encoding",
|
|
17893
18176
|
"upgrade"
|
|
17894
18177
|
]);
|
|
18178
|
+
var BASE_SECURITY_HEADERS = {
|
|
18179
|
+
"permissions-policy": "camera=(), microphone=(), geolocation=()",
|
|
18180
|
+
"referrer-policy": "no-referrer",
|
|
18181
|
+
"x-content-type-options": "nosniff",
|
|
18182
|
+
"x-frame-options": "DENY"
|
|
18183
|
+
};
|
|
18184
|
+
var DOCUMENT_CONTENT_SECURITY_POLICY = [
|
|
18185
|
+
"default-src 'self'",
|
|
18186
|
+
"base-uri 'self'",
|
|
18187
|
+
"connect-src 'self'",
|
|
18188
|
+
"font-src 'self' data:",
|
|
18189
|
+
"frame-ancestors 'none'",
|
|
18190
|
+
"img-src 'self' data: blob:",
|
|
18191
|
+
"object-src 'none'",
|
|
18192
|
+
"script-src 'self' 'unsafe-inline'",
|
|
18193
|
+
"style-src 'self' 'unsafe-inline'",
|
|
18194
|
+
"worker-src 'self' blob:"
|
|
18195
|
+
].join("; ");
|
|
17895
18196
|
function isLoopbackPeer(address) {
|
|
17896
18197
|
if (address === void 0) return false;
|
|
17897
18198
|
const normalized = address.toLowerCase();
|
|
@@ -17904,22 +18205,92 @@ function bearerToken(authorization) {
|
|
|
17904
18205
|
}
|
|
17905
18206
|
function tokensMatch(received, expected) {
|
|
17906
18207
|
if (received === null || expected === void 0 || expected === "") return false;
|
|
17907
|
-
const receivedBuffer =
|
|
17908
|
-
const expectedBuffer =
|
|
18208
|
+
const receivedBuffer = Buffer3.from(received);
|
|
18209
|
+
const expectedBuffer = Buffer3.from(expected);
|
|
17909
18210
|
return receivedBuffer.byteLength === expectedBuffer.byteLength && timingSafeEqual(receivedBuffer, expectedBuffer);
|
|
17910
18211
|
}
|
|
18212
|
+
function firstHeader(value) {
|
|
18213
|
+
return typeof value === "string" ? value : value?.[0];
|
|
18214
|
+
}
|
|
18215
|
+
function normalizedHostname(value) {
|
|
18216
|
+
const candidate = value.trim();
|
|
18217
|
+
if (candidate.length === 0) return null;
|
|
18218
|
+
try {
|
|
18219
|
+
return new URL(`http://${candidate}`).hostname.toLowerCase().replace(/^\[|]$/g, "");
|
|
18220
|
+
} catch {
|
|
18221
|
+
return null;
|
|
18222
|
+
}
|
|
18223
|
+
}
|
|
18224
|
+
function isLoopbackHostname(hostname3) {
|
|
18225
|
+
return hostname3 === "localhost" || hostname3 === "::1" || hostname3.startsWith("127.");
|
|
18226
|
+
}
|
|
18227
|
+
function isTrustedIdentityProxyHost(host, options) {
|
|
18228
|
+
if (host === void 0) return false;
|
|
18229
|
+
const hostname3 = normalizedHostname(host);
|
|
18230
|
+
if (hostname3 === null) return false;
|
|
18231
|
+
if (isLoopbackHostname(hostname3) || isIP(hostname3) !== 0) return true;
|
|
18232
|
+
const listenHostname = normalizedHostname(options.listenHost);
|
|
18233
|
+
if (listenHostname !== null && hostname3 === listenHostname) return true;
|
|
18234
|
+
return (options.trustedHosts ?? []).some(
|
|
18235
|
+
(trustedHost) => normalizedHostname(trustedHost) === hostname3
|
|
18236
|
+
);
|
|
18237
|
+
}
|
|
18238
|
+
function isSafeMethod(method) {
|
|
18239
|
+
const normalized = (method ?? "GET").toUpperCase();
|
|
18240
|
+
return normalized === "GET" || normalized === "HEAD" || normalized === "OPTIONS";
|
|
18241
|
+
}
|
|
18242
|
+
function isPublicHealthPath(pathname) {
|
|
18243
|
+
return pathname === "/livez" || pathname === "/readyz" || pathname === "/api/health";
|
|
18244
|
+
}
|
|
18245
|
+
function configuredPositiveInteger(value, fallback) {
|
|
18246
|
+
return value !== void 0 && Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
18247
|
+
}
|
|
18248
|
+
function declaredContentLength(value) {
|
|
18249
|
+
if (value === void 0) return null;
|
|
18250
|
+
if (!/^\d+$/.test(value)) return Number.NaN;
|
|
18251
|
+
const parsed = Number(value);
|
|
18252
|
+
return Number.isSafeInteger(parsed) ? parsed : Number.NaN;
|
|
18253
|
+
}
|
|
18254
|
+
function isBrowserMutationAllowed(input) {
|
|
18255
|
+
if (isSafeMethod(input.method)) return true;
|
|
18256
|
+
const pathname = new URL(input.path, "http://localhost").pathname;
|
|
18257
|
+
if (pathname === "/proxy" || pathname.startsWith("/proxy/")) return true;
|
|
18258
|
+
const fetchSite = input.secFetchSite?.toLowerCase();
|
|
18259
|
+
const browserRequest = input.origin !== void 0 || fetchSite !== void 0;
|
|
18260
|
+
if (!browserRequest) return true;
|
|
18261
|
+
if (fetchSite !== void 0 && fetchSite !== "same-origin") return false;
|
|
18262
|
+
if (input.origin !== void 0) {
|
|
18263
|
+
try {
|
|
18264
|
+
const originHost = new URL(input.origin).host.toLowerCase();
|
|
18265
|
+
if (originHost !== input.host?.toLowerCase()) return false;
|
|
18266
|
+
} catch {
|
|
18267
|
+
return false;
|
|
18268
|
+
}
|
|
18269
|
+
}
|
|
18270
|
+
return input.csrfToken === "1";
|
|
18271
|
+
}
|
|
17911
18272
|
function isIdentityProxyRequestAllowed(input) {
|
|
17912
18273
|
if (input.options.restrictRemoteControl !== true) return true;
|
|
17913
18274
|
if (input.options.allowRemoteControl === true) return true;
|
|
17914
18275
|
if (isLoopbackPeer(input.remoteAddress)) return true;
|
|
17915
18276
|
const pathname = new URL(input.path, "http://localhost").pathname;
|
|
17916
|
-
if (pathname === "/proxy" || pathname.startsWith("/proxy/"))
|
|
17917
|
-
|
|
18277
|
+
if (pathname === "/proxy" || pathname.startsWith("/proxy/")) {
|
|
18278
|
+
if (input.options.allowUnauthenticatedRemoteProxy === true) return true;
|
|
18279
|
+
const proxyCredential = bearerToken(input.proxyAuthorization) ?? input.proxyTokenHeader ?? null;
|
|
18280
|
+
return tokensMatch(proxyCredential, input.options.proxyToken);
|
|
18281
|
+
}
|
|
18282
|
+
if (isPublicHealthPath(pathname)) return true;
|
|
17918
18283
|
return tokensMatch(bearerToken(input.authorization), input.options.controlToken);
|
|
17919
18284
|
}
|
|
17920
18285
|
function startIdentityProxy(options) {
|
|
17921
18286
|
return new Promise((resolve2, reject) => {
|
|
17922
18287
|
const server = http.createServer();
|
|
18288
|
+
const runtimeState = {
|
|
18289
|
+
acceptingRequests: true,
|
|
18290
|
+
activeRequests: 0,
|
|
18291
|
+
drainWaiters: /* @__PURE__ */ new Set()
|
|
18292
|
+
};
|
|
18293
|
+
identityProxyRuntimeStates.set(server, runtimeState);
|
|
17923
18294
|
server.on("connection", (socket) => {
|
|
17924
18295
|
const remotePort = socket.remotePort;
|
|
17925
18296
|
if (remotePort !== void 0 && remotePort > 0) {
|
|
@@ -17927,6 +18298,29 @@ function startIdentityProxy(options) {
|
|
|
17927
18298
|
}
|
|
17928
18299
|
});
|
|
17929
18300
|
server.on("request", (req, res) => {
|
|
18301
|
+
if (!runtimeState.acceptingRequests) {
|
|
18302
|
+
res.writeHead(503, {
|
|
18303
|
+
"content-type": "application/json; charset=utf-8",
|
|
18304
|
+
"cache-control": "no-store",
|
|
18305
|
+
connection: "close",
|
|
18306
|
+
"retry-after": "5"
|
|
18307
|
+
});
|
|
18308
|
+
res.end(JSON.stringify({ error: "Agent Inspector is shutting down" }));
|
|
18309
|
+
req.resume();
|
|
18310
|
+
return;
|
|
18311
|
+
}
|
|
18312
|
+
runtimeState.activeRequests += 1;
|
|
18313
|
+
let released = false;
|
|
18314
|
+
const release = () => {
|
|
18315
|
+
if (released) return;
|
|
18316
|
+
released = true;
|
|
18317
|
+
runtimeState.activeRequests = Math.max(0, runtimeState.activeRequests - 1);
|
|
18318
|
+
if (runtimeState.activeRequests !== 0) return;
|
|
18319
|
+
for (const resolveDrain of runtimeState.drainWaiters) resolveDrain();
|
|
18320
|
+
runtimeState.drainWaiters.clear();
|
|
18321
|
+
};
|
|
18322
|
+
res.once("finish", release);
|
|
18323
|
+
res.once("close", release);
|
|
17930
18324
|
void handleRequest(req, res, options).catch((err) => {
|
|
17931
18325
|
const message = err instanceof Error ? err.message : String(err);
|
|
17932
18326
|
if (!res.headersSent) {
|
|
@@ -17942,11 +18336,65 @@ function startIdentityProxy(options) {
|
|
|
17942
18336
|
});
|
|
17943
18337
|
});
|
|
17944
18338
|
}
|
|
18339
|
+
function stopIdentityProxyAdmission(server) {
|
|
18340
|
+
const state = identityProxyRuntimeStates.get(server);
|
|
18341
|
+
if (state !== void 0) state.acceptingRequests = false;
|
|
18342
|
+
}
|
|
18343
|
+
async function waitForIdentityProxyDrain(server) {
|
|
18344
|
+
const state = identityProxyRuntimeStates.get(server);
|
|
18345
|
+
if (state === void 0 || state.activeRequests === 0) return;
|
|
18346
|
+
await new Promise((resolve2) => {
|
|
18347
|
+
state.drainWaiters.add(resolve2);
|
|
18348
|
+
});
|
|
18349
|
+
}
|
|
18350
|
+
async function closeIdentityProxyGracefully(server) {
|
|
18351
|
+
stopIdentityProxyAdmission(server);
|
|
18352
|
+
const closed = new Promise((resolve2) => {
|
|
18353
|
+
if (!server.listening) {
|
|
18354
|
+
resolve2();
|
|
18355
|
+
return;
|
|
18356
|
+
}
|
|
18357
|
+
server.close(() => resolve2());
|
|
18358
|
+
});
|
|
18359
|
+
await Promise.all([closed, waitForIdentityProxyDrain(server)]);
|
|
18360
|
+
}
|
|
18361
|
+
function forceCloseIdentityProxy(server) {
|
|
18362
|
+
stopIdentityProxyAdmission(server);
|
|
18363
|
+
server.closeAllConnections();
|
|
18364
|
+
server.close();
|
|
18365
|
+
}
|
|
17945
18366
|
async function handleRequest(req, res, options) {
|
|
18367
|
+
if (!isTrustedIdentityProxyHost(req.headers.host, options)) {
|
|
18368
|
+
res.writeHead(400, {
|
|
18369
|
+
"content-type": "application/json; charset=utf-8",
|
|
18370
|
+
"cache-control": "no-store"
|
|
18371
|
+
});
|
|
18372
|
+
res.end(JSON.stringify({ error: "Untrusted Host header" }));
|
|
18373
|
+
req.resume();
|
|
18374
|
+
return;
|
|
18375
|
+
}
|
|
18376
|
+
if (!isBrowserMutationAllowed({
|
|
18377
|
+
path: req.url ?? "/",
|
|
18378
|
+
method: req.method,
|
|
18379
|
+
host: req.headers.host,
|
|
18380
|
+
origin: req.headers.origin,
|
|
18381
|
+
secFetchSite: firstHeader(req.headers["sec-fetch-site"]),
|
|
18382
|
+
csrfToken: firstHeader(req.headers[CSRF_HEADER])
|
|
18383
|
+
})) {
|
|
18384
|
+
res.writeHead(403, {
|
|
18385
|
+
"content-type": "application/json; charset=utf-8",
|
|
18386
|
+
"cache-control": "no-store"
|
|
18387
|
+
});
|
|
18388
|
+
res.end(JSON.stringify({ error: "Browser mutation requires same-origin CSRF proof" }));
|
|
18389
|
+
req.resume();
|
|
18390
|
+
return;
|
|
18391
|
+
}
|
|
17946
18392
|
if (!isIdentityProxyRequestAllowed({
|
|
17947
18393
|
path: req.url ?? "/",
|
|
17948
18394
|
remoteAddress: req.socket.remoteAddress,
|
|
17949
18395
|
authorization: req.headers.authorization,
|
|
18396
|
+
proxyAuthorization: req.headers["proxy-authorization"],
|
|
18397
|
+
proxyTokenHeader: firstHeader(req.headers[PROXY_TOKEN_HEADER]),
|
|
17950
18398
|
options
|
|
17951
18399
|
})) {
|
|
17952
18400
|
res.writeHead(403, {
|
|
@@ -17955,9 +18403,10 @@ async function handleRequest(req, res, options) {
|
|
|
17955
18403
|
});
|
|
17956
18404
|
res.end(
|
|
17957
18405
|
JSON.stringify({
|
|
17958
|
-
error: "Remote
|
|
18406
|
+
error: "Remote access requires a credential scoped to this endpoint."
|
|
17959
18407
|
})
|
|
17960
18408
|
);
|
|
18409
|
+
req.resume();
|
|
17961
18410
|
return;
|
|
17962
18411
|
}
|
|
17963
18412
|
const remotePort = req.socket.remotePort;
|
|
@@ -17969,14 +18418,34 @@ async function handleRequest(req, res, options) {
|
|
|
17969
18418
|
identity = null;
|
|
17970
18419
|
}
|
|
17971
18420
|
}
|
|
17972
|
-
const
|
|
17973
|
-
|
|
17974
|
-
|
|
18421
|
+
const maxRequestBytes = configuredPositiveInteger(
|
|
18422
|
+
options.maxRequestBytes,
|
|
18423
|
+
DEFAULT_MAX_REQUEST_BYTES
|
|
18424
|
+
);
|
|
18425
|
+
const contentLength = declaredContentLength(req.headers["content-length"]);
|
|
18426
|
+
if (contentLength !== null && (!Number.isFinite(contentLength) || contentLength < 0)) {
|
|
18427
|
+
res.writeHead(400, {
|
|
18428
|
+
"content-type": "application/json; charset=utf-8",
|
|
18429
|
+
"cache-control": "no-store"
|
|
18430
|
+
});
|
|
18431
|
+
res.end(JSON.stringify({ error: "Invalid Content-Length header" }));
|
|
18432
|
+
req.resume();
|
|
18433
|
+
return;
|
|
18434
|
+
}
|
|
18435
|
+
if (contentLength !== null && contentLength > maxRequestBytes) {
|
|
18436
|
+
res.writeHead(413, {
|
|
18437
|
+
"content-type": "application/json; charset=utf-8",
|
|
18438
|
+
"cache-control": "no-store"
|
|
18439
|
+
});
|
|
18440
|
+
res.end(JSON.stringify({ error: `Request body exceeds ${String(maxRequestBytes)} bytes` }));
|
|
18441
|
+
req.resume();
|
|
18442
|
+
return;
|
|
17975
18443
|
}
|
|
17976
|
-
const body = Buffer2.concat(chunks);
|
|
17977
18444
|
const upstreamHeaders = {};
|
|
17978
18445
|
for (const [name, value] of Object.entries(req.headers)) {
|
|
17979
|
-
|
|
18446
|
+
const lowerName = name.toLowerCase();
|
|
18447
|
+
if (REQUEST_HOP_BY_HOP.has(lowerName)) continue;
|
|
18448
|
+
if (lowerName.startsWith(IDENTITY_HEADER_PREFIX)) continue;
|
|
17980
18449
|
if (value === void 0) continue;
|
|
17981
18450
|
upstreamHeaders[name] = value;
|
|
17982
18451
|
}
|
|
@@ -17992,6 +18461,11 @@ async function handleRequest(req, res, options) {
|
|
|
17992
18461
|
upstreamHeaders[IDENTITY_HEADERS.projectFolder] = identity.projectFolder;
|
|
17993
18462
|
}
|
|
17994
18463
|
}
|
|
18464
|
+
let terminal = false;
|
|
18465
|
+
const requestTimeoutMs = configuredPositiveInteger(
|
|
18466
|
+
options.requestTimeoutMs,
|
|
18467
|
+
DEFAULT_REQUEST_TIMEOUT_MS
|
|
18468
|
+
);
|
|
17995
18469
|
const proxyReq = http.request(
|
|
17996
18470
|
{
|
|
17997
18471
|
hostname: options.upstreamHost,
|
|
@@ -18001,38 +18475,100 @@ async function handleRequest(req, res, options) {
|
|
|
18001
18475
|
headers: upstreamHeaders
|
|
18002
18476
|
},
|
|
18003
18477
|
(proxyRes) => {
|
|
18478
|
+
if (terminal) {
|
|
18479
|
+
proxyRes.resume();
|
|
18480
|
+
return;
|
|
18481
|
+
}
|
|
18004
18482
|
const outHeaders = {};
|
|
18005
18483
|
for (const [name, value] of Object.entries(proxyRes.headers)) {
|
|
18006
18484
|
if (RESPONSE_HOP_BY_HOP.has(name.toLowerCase())) continue;
|
|
18007
18485
|
if (value === void 0) continue;
|
|
18008
18486
|
outHeaders[name] = value;
|
|
18009
18487
|
}
|
|
18488
|
+
for (const [name, value] of Object.entries(BASE_SECURITY_HEADERS)) {
|
|
18489
|
+
outHeaders[name] = value;
|
|
18490
|
+
}
|
|
18491
|
+
const contentType = firstHeader(proxyRes.headers["content-type"]);
|
|
18492
|
+
if (contentType?.toLowerCase().includes("text/html") === true) {
|
|
18493
|
+
outHeaders["content-security-policy"] = DOCUMENT_CONTENT_SECURITY_POLICY;
|
|
18494
|
+
}
|
|
18010
18495
|
res.writeHead(proxyRes.statusCode ?? 502, outHeaders);
|
|
18011
18496
|
proxyRes.pipe(res);
|
|
18012
18497
|
}
|
|
18013
18498
|
);
|
|
18499
|
+
const deadline = setTimeout(() => {
|
|
18500
|
+
failRequest(408, "Request body deadline exceeded");
|
|
18501
|
+
}, requestTimeoutMs);
|
|
18502
|
+
deadline.unref();
|
|
18503
|
+
function clearDeadline() {
|
|
18504
|
+
clearTimeout(deadline);
|
|
18505
|
+
}
|
|
18506
|
+
function failRequest(status, error51) {
|
|
18507
|
+
if (terminal) return;
|
|
18508
|
+
terminal = true;
|
|
18509
|
+
clearDeadline();
|
|
18510
|
+
proxyReq.destroy();
|
|
18511
|
+
if (res.headersSent) {
|
|
18512
|
+
res.destroy();
|
|
18513
|
+
} else {
|
|
18514
|
+
res.writeHead(status, {
|
|
18515
|
+
"content-type": "application/json; charset=utf-8",
|
|
18516
|
+
"cache-control": "no-store"
|
|
18517
|
+
});
|
|
18518
|
+
res.end(JSON.stringify({ error: error51 }));
|
|
18519
|
+
}
|
|
18520
|
+
req.resume();
|
|
18521
|
+
}
|
|
18014
18522
|
proxyReq.on("error", (err) => {
|
|
18523
|
+
if (terminal) return;
|
|
18524
|
+
terminal = true;
|
|
18525
|
+
clearDeadline();
|
|
18015
18526
|
if (!res.headersSent) {
|
|
18016
18527
|
res.writeHead(502, { "content-type": "text/plain" });
|
|
18017
18528
|
}
|
|
18018
18529
|
res.end(`Upstream error: ${err.message}`);
|
|
18019
18530
|
});
|
|
18020
|
-
|
|
18021
|
-
|
|
18531
|
+
proxyReq.on("drain", () => req.resume());
|
|
18532
|
+
res.once("close", () => {
|
|
18533
|
+
clearDeadline();
|
|
18534
|
+
if (!res.writableEnded) proxyReq.destroy();
|
|
18535
|
+
});
|
|
18536
|
+
let receivedBytes = 0;
|
|
18537
|
+
req.on("data", (chunk) => {
|
|
18538
|
+
if (terminal) return;
|
|
18539
|
+
const buffer = toBuffer(chunk);
|
|
18540
|
+
receivedBytes += buffer.byteLength;
|
|
18541
|
+
if (receivedBytes > maxRequestBytes) {
|
|
18542
|
+
failRequest(413, `Request body exceeds ${String(maxRequestBytes)} bytes`);
|
|
18543
|
+
return;
|
|
18544
|
+
}
|
|
18545
|
+
if (buffer.byteLength > 0 && !proxyReq.write(buffer)) req.pause();
|
|
18546
|
+
});
|
|
18547
|
+
req.on("end", () => {
|
|
18548
|
+
if (terminal) return;
|
|
18549
|
+
clearDeadline();
|
|
18550
|
+
proxyReq.end();
|
|
18551
|
+
});
|
|
18552
|
+
req.on("aborted", () => {
|
|
18553
|
+
failRequest(400, "Client aborted request body");
|
|
18554
|
+
});
|
|
18555
|
+
req.on("error", () => {
|
|
18556
|
+
failRequest(400, "Request body stream failed");
|
|
18557
|
+
});
|
|
18022
18558
|
}
|
|
18023
18559
|
function toBuffer(chunk) {
|
|
18024
|
-
if (
|
|
18025
|
-
if (typeof chunk === "string") return
|
|
18026
|
-
if (chunk instanceof ArrayBuffer) return
|
|
18560
|
+
if (Buffer3.isBuffer(chunk)) return chunk;
|
|
18561
|
+
if (typeof chunk === "string") return Buffer3.from(chunk);
|
|
18562
|
+
if (chunk instanceof ArrayBuffer) return Buffer3.from(chunk);
|
|
18027
18563
|
if (chunk instanceof Uint8Array)
|
|
18028
|
-
return
|
|
18029
|
-
return
|
|
18564
|
+
return Buffer3.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
18565
|
+
return Buffer3.alloc(0);
|
|
18030
18566
|
}
|
|
18031
18567
|
|
|
18032
18568
|
// src/cli.ts
|
|
18033
18569
|
init_platformCommands();
|
|
18034
18570
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
18035
|
-
var __dirname2 =
|
|
18571
|
+
var __dirname2 = dirname3(__filename2);
|
|
18036
18572
|
var DEFAULT_PORT3 = 9527;
|
|
18037
18573
|
var LEGACY_ALIAS_PORT = 9527;
|
|
18038
18574
|
var DEFAULT_UPSTREAM_PORT = 9529;
|
|
@@ -18040,6 +18576,8 @@ var DEFAULT_BIND_HOST = "127.0.0.1";
|
|
|
18040
18576
|
var LOCAL_PROBE_TIMEOUT_MS = 2e3;
|
|
18041
18577
|
var BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
|
|
18042
18578
|
var DEFAULT_CAPTURE_MODE = "simple";
|
|
18579
|
+
var DEFAULT_SHUTDOWN_DEADLINE_MS = 15e3;
|
|
18580
|
+
var SUPERVISOR_SHUTDOWN_GRACE_MS = 2e3;
|
|
18043
18581
|
process.title = "Agent Inspector";
|
|
18044
18582
|
var subcommand = process.argv[2];
|
|
18045
18583
|
if (subcommand === "onboard") {
|
|
@@ -18142,7 +18680,7 @@ function pipeServerOutputStream(stream, target, context) {
|
|
|
18142
18680
|
if (stream === null) return;
|
|
18143
18681
|
let buffered = "";
|
|
18144
18682
|
stream.on("data", (chunk) => {
|
|
18145
|
-
const text =
|
|
18683
|
+
const text = Buffer4.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
18146
18684
|
const lines = `${buffered}${text}`.split(/\r?\n/);
|
|
18147
18685
|
buffered = lines.pop() ?? "";
|
|
18148
18686
|
for (const line of lines) {
|
|
@@ -18161,8 +18699,8 @@ function pipeServerOutputStream(stream, target, context) {
|
|
|
18161
18699
|
});
|
|
18162
18700
|
}
|
|
18163
18701
|
function resolveServerCommand(outputDir, serverPath) {
|
|
18164
|
-
const brandedRuntime =
|
|
18165
|
-
return process.platform === "win32" &&
|
|
18702
|
+
const brandedRuntime = join7(outputDir, BRANDED_WINDOWS_RUNTIME_EXE);
|
|
18703
|
+
return process.platform === "win32" && existsSync6(brandedRuntime) ? { command: brandedRuntime, args: [serverPath] } : { command: process.execPath, args: [serverPath] };
|
|
18166
18704
|
}
|
|
18167
18705
|
function parseCaptureMode(value) {
|
|
18168
18706
|
switch (value) {
|
|
@@ -18188,16 +18726,33 @@ function printAccessHints(port, host) {
|
|
|
18188
18726
|
console.log(line);
|
|
18189
18727
|
}
|
|
18190
18728
|
if (!isLoopbackHost(host)) {
|
|
18729
|
+
const proxyTokenConfigured = (process.env["AGENT_INSPECTOR_PROXY_TOKEN"] ?? "") !== "";
|
|
18730
|
+
const legacyProxyEnabled = process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1";
|
|
18731
|
+
if (proxyTokenConfigured) {
|
|
18732
|
+
console.log(" Remote proxy: dedicated token authentication enabled");
|
|
18733
|
+
} else if (legacyProxyEnabled) {
|
|
18734
|
+
console.warn(" Remote proxy: UNAUTHENTICATED compatibility mode enabled");
|
|
18735
|
+
} else {
|
|
18736
|
+
console.warn(" Remote proxy: blocked until AGENT_INSPECTOR_PROXY_TOKEN is configured");
|
|
18737
|
+
}
|
|
18191
18738
|
if (process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1") {
|
|
18192
18739
|
console.warn(" Control plane: REMOTE ACCESS ENABLED by administrator override");
|
|
18193
18740
|
} else {
|
|
18194
|
-
console.log(" Control plane: local only
|
|
18741
|
+
console.log(" Control plane: local only by default");
|
|
18195
18742
|
if ((process.env["AGENT_INSPECTOR_CONTROL_TOKEN"] ?? "") !== "") {
|
|
18196
18743
|
console.log(" Remote API: Bearer token authentication enabled");
|
|
18197
18744
|
}
|
|
18198
18745
|
}
|
|
18199
18746
|
}
|
|
18200
18747
|
}
|
|
18748
|
+
function readPositiveIntegerEnv(name) {
|
|
18749
|
+
const raw = process.env[name];
|
|
18750
|
+
if (raw === void 0 || raw.trim().length === 0) return void 0;
|
|
18751
|
+
const parsed = Number(raw);
|
|
18752
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
|
|
18753
|
+
console.warn(`Ignoring invalid ${name}=${raw}; expected a positive integer.`);
|
|
18754
|
+
return void 0;
|
|
18755
|
+
}
|
|
18201
18756
|
function buildBackgroundSupervisorArgs(options) {
|
|
18202
18757
|
const args = ["--no-open", "--port", String(options.port), "--mode", options.captureMode];
|
|
18203
18758
|
if (options.host !== void 0) {
|
|
@@ -18236,7 +18791,12 @@ async function tryStartIdentityProxy(identityPort, upstreamPort, listenHost, ups
|
|
|
18236
18791
|
upstreamPort,
|
|
18237
18792
|
restrictRemoteControl: !isLoopbackHost(listenHost),
|
|
18238
18793
|
allowRemoteControl: process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1",
|
|
18239
|
-
controlToken: process.env["AGENT_INSPECTOR_CONTROL_TOKEN"]
|
|
18794
|
+
controlToken: process.env["AGENT_INSPECTOR_CONTROL_TOKEN"],
|
|
18795
|
+
proxyToken: process.env["AGENT_INSPECTOR_PROXY_TOKEN"],
|
|
18796
|
+
allowUnauthenticatedRemoteProxy: process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1",
|
|
18797
|
+
trustedHosts: (process.env["AGENT_INSPECTOR_TRUSTED_HOSTS"] ?? "").split(",").map((host) => host.trim()).filter((host) => host.length > 0),
|
|
18798
|
+
maxRequestBytes: readPositiveIntegerEnv("AGENT_INSPECTOR_MAX_REQUEST_BYTES"),
|
|
18799
|
+
requestTimeoutMs: readPositiveIntegerEnv("AGENT_INSPECTOR_REQUEST_TIMEOUT_MS")
|
|
18240
18800
|
});
|
|
18241
18801
|
return server;
|
|
18242
18802
|
} catch (err) {
|
|
@@ -18388,9 +18948,9 @@ async function runStart(args) {
|
|
|
18388
18948
|
process.exitCode = 1;
|
|
18389
18949
|
return;
|
|
18390
18950
|
}
|
|
18391
|
-
if (!enableIdentityProxy &&
|
|
18951
|
+
if (!enableIdentityProxy && process.env["AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS"] !== "1") {
|
|
18392
18952
|
console.error(
|
|
18393
|
-
"Refusing
|
|
18953
|
+
"Refusing --no-identity-proxy because it also disables Host, CSRF, credential-scope, and first-hop budget enforcement. Keep the protected ingress enabled or set AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS=1 explicitly for temporary diagnostics."
|
|
18394
18954
|
);
|
|
18395
18955
|
process.exitCode = 1;
|
|
18396
18956
|
return;
|
|
@@ -18518,11 +19078,11 @@ async function runStart(args) {
|
|
|
18518
19078
|
openBrowser(url2);
|
|
18519
19079
|
}
|
|
18520
19080
|
const outputDir = __dirname2;
|
|
18521
|
-
const serverPath =
|
|
19081
|
+
const serverPath = join7(outputDir, "../.output/server/index.mjs");
|
|
18522
19082
|
const serverCommand = resolveServerCommand(outputDir, serverPath);
|
|
18523
19083
|
const serverEnv = { ...process.env };
|
|
18524
19084
|
if (configDir !== void 0) {
|
|
18525
|
-
let resolvedPath =
|
|
19085
|
+
let resolvedPath = join7(configDir);
|
|
18526
19086
|
const msysMatch = /^\\([a-z])\\(.*)$/i.exec(resolvedPath);
|
|
18527
19087
|
if (msysMatch !== null) {
|
|
18528
19088
|
const drive = (msysMatch[1] ?? "").toUpperCase();
|
|
@@ -18541,6 +19101,10 @@ async function runStart(args) {
|
|
|
18541
19101
|
serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
|
|
18542
19102
|
serverEnv["PROXY_PORT"] = String(port);
|
|
18543
19103
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
19104
|
+
const workerDir = join7(__dirname2, "workers");
|
|
19105
|
+
if (existsSync6(workerDir)) {
|
|
19106
|
+
serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
|
|
19107
|
+
}
|
|
18544
19108
|
const serverProcess = spawn(serverCommand.command, serverCommand.args, {
|
|
18545
19109
|
stdio: background ? ["ignore", "ignore", "ignore"] : ["ignore", "pipe", "pipe"],
|
|
18546
19110
|
detached: background,
|
|
@@ -18559,6 +19123,37 @@ async function runStart(args) {
|
|
|
18559
19123
|
});
|
|
18560
19124
|
}
|
|
18561
19125
|
const identityProxies = [];
|
|
19126
|
+
let supervisorShutdownPromise = null;
|
|
19127
|
+
const requestSupervisorShutdown = (signal) => {
|
|
19128
|
+
if (supervisorShutdownPromise !== null) return;
|
|
19129
|
+
for (const proxy of identityProxies) stopIdentityProxyAdmission(proxy.server);
|
|
19130
|
+
serverProcess.kill(signal);
|
|
19131
|
+
const childDeadline = readPositiveIntegerEnv("AGENT_INSPECTOR_SHUTDOWN_TIMEOUT_MS") ?? DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
19132
|
+
let deadline = null;
|
|
19133
|
+
let forced = false;
|
|
19134
|
+
const graceful = Promise.all([
|
|
19135
|
+
serverExit.then(() => void 0),
|
|
19136
|
+
...identityProxies.map(async (proxy) => await closeIdentityProxyGracefully(proxy.server))
|
|
19137
|
+
]).then(() => void 0);
|
|
19138
|
+
const timedOut = new Promise((resolve2) => {
|
|
19139
|
+
deadline = setTimeout(() => {
|
|
19140
|
+
forced = true;
|
|
19141
|
+
for (const proxy of identityProxies) forceCloseIdentityProxy(proxy.server);
|
|
19142
|
+
if (serverProcess.exitCode === null) serverProcess.kill("SIGKILL");
|
|
19143
|
+
resolve2();
|
|
19144
|
+
}, childDeadline + SUPERVISOR_SHUTDOWN_GRACE_MS);
|
|
19145
|
+
});
|
|
19146
|
+
supervisorShutdownPromise = Promise.race([graceful, timedOut]).finally(() => {
|
|
19147
|
+
if (deadline !== null) clearTimeout(deadline);
|
|
19148
|
+
}).then(() => {
|
|
19149
|
+
if (forced) process.exitCode = 1;
|
|
19150
|
+
});
|
|
19151
|
+
};
|
|
19152
|
+
const handleSigint = () => requestSupervisorShutdown("SIGINT");
|
|
19153
|
+
const handleSigterm = () => requestSupervisorShutdown("SIGTERM");
|
|
19154
|
+
const currentSupervisorShutdown = () => supervisorShutdownPromise;
|
|
19155
|
+
process.on("SIGINT", handleSigint);
|
|
19156
|
+
process.on("SIGTERM", handleSigterm);
|
|
18562
19157
|
if (enableIdentityProxy) {
|
|
18563
19158
|
const primaryProxy = await tryStartIdentityProxy(port, upstreamPort, host, upstreamHost);
|
|
18564
19159
|
if (primaryProxy !== null) {
|
|
@@ -18618,7 +19213,12 @@ async function runStart(args) {
|
|
|
18618
19213
|
}
|
|
18619
19214
|
}
|
|
18620
19215
|
process.exitCode = await serverExit;
|
|
18621
|
-
|
|
18622
|
-
|
|
19216
|
+
const activeSupervisorShutdown = currentSupervisorShutdown();
|
|
19217
|
+
if (activeSupervisorShutdown !== null) {
|
|
19218
|
+
await activeSupervisorShutdown;
|
|
19219
|
+
} else {
|
|
19220
|
+
await Promise.all(identityProxies.map((proxy) => closeIdentityProxyGracefully(proxy.server)));
|
|
18623
19221
|
}
|
|
19222
|
+
process.off("SIGINT", handleSigint);
|
|
19223
|
+
process.off("SIGTERM", handleSigterm);
|
|
18624
19224
|
}
|