@agent-inspect/studio 6.7.2 → 6.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +35 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +35 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -862,9 +862,9 @@ var DEFAULT_HTTP_INGEST_BASE_PATH = "/api/ingest";
|
|
|
862
862
|
var HTTP_INGEST_BUNDLE_PATH = "/api/ingest/bundle";
|
|
863
863
|
var HTTP_INGEST_ARTIFACT_PATH = "/api/ingest/artifact";
|
|
864
864
|
var DEFAULT_MAX_INGEST_BYTES = 52428800;
|
|
865
|
-
function resolveHttpIngestConfig(options,
|
|
866
|
-
const http = options.context?.registry.ingest?.http;
|
|
867
|
-
const enabled = options.ingestHttp === true ||
|
|
865
|
+
function resolveHttpIngestConfig(options, registryHttp) {
|
|
866
|
+
const http = registryHttp ?? options.context?.registry.ingest?.http;
|
|
867
|
+
const enabled = options.ingestHttp === true || http?.enabled === true;
|
|
868
868
|
const basePath = (http?.path ?? DEFAULT_HTTP_INGEST_BASE_PATH).trim() || DEFAULT_HTTP_INGEST_BASE_PATH;
|
|
869
869
|
const tokenEnv = resolveIngestTokenEnv({
|
|
870
870
|
...options.ingestTokenEnv !== void 0 ? { tokenEnv: options.ingestTokenEnv } : {},
|
|
@@ -897,7 +897,7 @@ function isHttpIngestRoute(pathname, config) {
|
|
|
897
897
|
return pathname === HTTP_INGEST_BUNDLE_PATH || pathname === HTTP_INGEST_ARTIFACT_PATH || pathname === `${config.basePath}/bundle` || pathname === `${config.basePath}/artifact`;
|
|
898
898
|
}
|
|
899
899
|
async function handleHttpIngestRequest(req, res, ctx, options, pathname) {
|
|
900
|
-
const config = resolveHttpIngestConfig(options, ctx.registry.ingest?.http
|
|
900
|
+
const config = resolveHttpIngestConfig(options, ctx.registry.ingest?.http);
|
|
901
901
|
if (!isHttpIngestRoute(pathname, config)) return false;
|
|
902
902
|
if (!config.enabled) {
|
|
903
903
|
sendJson(res, 404, { error: "HTTP ingest is disabled" });
|
|
@@ -1344,8 +1344,6 @@ async function loadBundleExportView(ctx, params) {
|
|
|
1344
1344
|
note: "Studio does not mutate traces or upload bundles. Run the CLI locally to assemble a share-safe bundle."
|
|
1345
1345
|
};
|
|
1346
1346
|
}
|
|
1347
|
-
|
|
1348
|
-
// packages/studio/src/auth.ts
|
|
1349
1347
|
function resolveStudioAuthMode(options) {
|
|
1350
1348
|
return options.auth === "basic" ? "basic" : "none";
|
|
1351
1349
|
}
|
|
@@ -1365,13 +1363,19 @@ function parseBasicAuth(header) {
|
|
|
1365
1363
|
return void 0;
|
|
1366
1364
|
}
|
|
1367
1365
|
}
|
|
1366
|
+
function timingSafePasswordEqual(provided, expected) {
|
|
1367
|
+
const providedDigest = crypto.createHash("sha256").update(provided, "utf8").digest();
|
|
1368
|
+
const expectedDigest = crypto.createHash("sha256").update(expected, "utf8").digest();
|
|
1369
|
+
return crypto.timingSafeEqual(providedDigest, expectedDigest);
|
|
1370
|
+
}
|
|
1368
1371
|
function isStudioRequestAuthorized(req, options) {
|
|
1369
1372
|
const mode = resolveStudioAuthMode(options);
|
|
1370
1373
|
if (mode === "none") return true;
|
|
1371
1374
|
const expected = resolveStudioPassword(options);
|
|
1372
1375
|
if (!expected) return false;
|
|
1373
1376
|
const provided = parseBasicAuth(req.headers.authorization);
|
|
1374
|
-
|
|
1377
|
+
if (provided === void 0) return false;
|
|
1378
|
+
return timingSafePasswordEqual(provided, expected);
|
|
1375
1379
|
}
|
|
1376
1380
|
function studioAuthRequiredResponse() {
|
|
1377
1381
|
return {
|
|
@@ -1616,7 +1620,7 @@ function createStudioServer(options = {}) {
|
|
|
1616
1620
|
contextPromise = createStudioContext(options);
|
|
1617
1621
|
}
|
|
1618
1622
|
const ctx = await contextPromise;
|
|
1619
|
-
const httpConfig = resolveHttpIngestConfig(options, ctx.registry.ingest?.http
|
|
1623
|
+
const httpConfig = resolveHttpIngestConfig(options, ctx.registry.ingest?.http);
|
|
1620
1624
|
if (isHttpIngestRoute(pathname, httpConfig)) {
|
|
1621
1625
|
const handled2 = await handleHttpIngestRequest(req, res, ctx, options, pathname);
|
|
1622
1626
|
if (handled2) return;
|
|
@@ -1895,6 +1899,28 @@ async function validateBundleDirectory(bundleDir) {
|
|
|
1895
1899
|
}
|
|
1896
1900
|
return errors;
|
|
1897
1901
|
}
|
|
1902
|
+
async function hashBundleContents(bundleDir) {
|
|
1903
|
+
const hash = crypto.createHash("sha256");
|
|
1904
|
+
const walk = async (dir, relPrefix) => {
|
|
1905
|
+
const entries = (await promises.readdir(dir, { withFileTypes: true })).sort(
|
|
1906
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
1907
|
+
);
|
|
1908
|
+
for (const entry of entries) {
|
|
1909
|
+
const abs = path8__default.default.join(dir, entry.name);
|
|
1910
|
+
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
1911
|
+
if (entry.isDirectory()) {
|
|
1912
|
+
await walk(abs, rel);
|
|
1913
|
+
} else if (entry.isFile()) {
|
|
1914
|
+
hash.update(rel);
|
|
1915
|
+
hash.update("\0");
|
|
1916
|
+
hash.update(await promises.readFile(abs));
|
|
1917
|
+
hash.update("\0");
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
await walk(bundleDir, "");
|
|
1922
|
+
return hash.digest("hex");
|
|
1923
|
+
}
|
|
1898
1924
|
async function copyDirectoryRecursive(sourceDir, destDir) {
|
|
1899
1925
|
await promises.mkdir(destDir, { recursive: true });
|
|
1900
1926
|
const entries = await promises.readdir(sourceDir, { withFileTypes: true });
|
|
@@ -1949,8 +1975,7 @@ async function importBundleUpload(options) {
|
|
|
1949
1975
|
registryImportWarnings
|
|
1950
1976
|
};
|
|
1951
1977
|
}
|
|
1952
|
-
const
|
|
1953
|
-
const contentHash = crypto.createHash("sha256").update(metadataRaw).digest("hex");
|
|
1978
|
+
const contentHash = await hashBundleContents(bundlePath);
|
|
1954
1979
|
const sourceKey = `bundle:${bundlePath}`;
|
|
1955
1980
|
const existing = findIngestFileBySourceKey(options.db, sourceKey);
|
|
1956
1981
|
if (existing && existing.contentHash === contentHash) {
|