@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.d.cts CHANGED
@@ -256,7 +256,7 @@ interface HttpIngestConfig {
256
256
  tokenEnv: string;
257
257
  maxBytes: number;
258
258
  }
259
- declare function resolveHttpIngestConfig(options: StudioServerOptions, registryEnabled?: boolean): HttpIngestConfig;
259
+ declare function resolveHttpIngestConfig(options: StudioServerOptions, registryHttp?: StudioRegistryHttpIngest): HttpIngestConfig;
260
260
  declare function isHttpIngestRoute(pathname: string, config: HttpIngestConfig): boolean;
261
261
  declare function handleHttpIngestRequest(req: IncomingMessage, res: ServerResponse, ctx: StudioContext, options: StudioServerOptions, pathname: string): Promise<boolean>;
262
262
 
package/dist/index.d.ts CHANGED
@@ -256,7 +256,7 @@ interface HttpIngestConfig {
256
256
  tokenEnv: string;
257
257
  maxBytes: number;
258
258
  }
259
- declare function resolveHttpIngestConfig(options: StudioServerOptions, registryEnabled?: boolean): HttpIngestConfig;
259
+ declare function resolveHttpIngestConfig(options: StudioServerOptions, registryHttp?: StudioRegistryHttpIngest): HttpIngestConfig;
260
260
  declare function isHttpIngestRoute(pathname: string, config: HttpIngestConfig): boolean;
261
261
  declare function handleHttpIngestRequest(req: IncomingMessage, res: ServerResponse, ctx: StudioContext, options: StudioServerOptions, pathname: string): Promise<boolean>;
262
262
 
package/dist/index.mjs CHANGED
@@ -855,9 +855,9 @@ var DEFAULT_HTTP_INGEST_BASE_PATH = "/api/ingest";
855
855
  var HTTP_INGEST_BUNDLE_PATH = "/api/ingest/bundle";
856
856
  var HTTP_INGEST_ARTIFACT_PATH = "/api/ingest/artifact";
857
857
  var DEFAULT_MAX_INGEST_BYTES = 52428800;
858
- function resolveHttpIngestConfig(options, registryEnabled) {
859
- const http = options.context?.registry.ingest?.http;
860
- const enabled = options.ingestHttp === true || registryEnabled === true || http?.enabled === true;
858
+ function resolveHttpIngestConfig(options, registryHttp) {
859
+ const http = registryHttp ?? options.context?.registry.ingest?.http;
860
+ const enabled = options.ingestHttp === true || http?.enabled === true;
861
861
  const basePath = (http?.path ?? DEFAULT_HTTP_INGEST_BASE_PATH).trim() || DEFAULT_HTTP_INGEST_BASE_PATH;
862
862
  const tokenEnv = resolveIngestTokenEnv({
863
863
  ...options.ingestTokenEnv !== void 0 ? { tokenEnv: options.ingestTokenEnv } : {},
@@ -890,7 +890,7 @@ function isHttpIngestRoute(pathname, config) {
890
890
  return pathname === HTTP_INGEST_BUNDLE_PATH || pathname === HTTP_INGEST_ARTIFACT_PATH || pathname === `${config.basePath}/bundle` || pathname === `${config.basePath}/artifact`;
891
891
  }
892
892
  async function handleHttpIngestRequest(req, res, ctx, options, pathname) {
893
- const config = resolveHttpIngestConfig(options, ctx.registry.ingest?.http?.enabled);
893
+ const config = resolveHttpIngestConfig(options, ctx.registry.ingest?.http);
894
894
  if (!isHttpIngestRoute(pathname, config)) return false;
895
895
  if (!config.enabled) {
896
896
  sendJson(res, 404, { error: "HTTP ingest is disabled" });
@@ -1337,8 +1337,6 @@ async function loadBundleExportView(ctx, params) {
1337
1337
  note: "Studio does not mutate traces or upload bundles. Run the CLI locally to assemble a share-safe bundle."
1338
1338
  };
1339
1339
  }
1340
-
1341
- // packages/studio/src/auth.ts
1342
1340
  function resolveStudioAuthMode(options) {
1343
1341
  return options.auth === "basic" ? "basic" : "none";
1344
1342
  }
@@ -1358,13 +1356,19 @@ function parseBasicAuth(header) {
1358
1356
  return void 0;
1359
1357
  }
1360
1358
  }
1359
+ function timingSafePasswordEqual(provided, expected) {
1360
+ const providedDigest = createHash("sha256").update(provided, "utf8").digest();
1361
+ const expectedDigest = createHash("sha256").update(expected, "utf8").digest();
1362
+ return timingSafeEqual(providedDigest, expectedDigest);
1363
+ }
1361
1364
  function isStudioRequestAuthorized(req, options) {
1362
1365
  const mode = resolveStudioAuthMode(options);
1363
1366
  if (mode === "none") return true;
1364
1367
  const expected = resolveStudioPassword(options);
1365
1368
  if (!expected) return false;
1366
1369
  const provided = parseBasicAuth(req.headers.authorization);
1367
- return provided === expected;
1370
+ if (provided === void 0) return false;
1371
+ return timingSafePasswordEqual(provided, expected);
1368
1372
  }
1369
1373
  function studioAuthRequiredResponse() {
1370
1374
  return {
@@ -1609,7 +1613,7 @@ function createStudioServer(options = {}) {
1609
1613
  contextPromise = createStudioContext(options);
1610
1614
  }
1611
1615
  const ctx = await contextPromise;
1612
- const httpConfig = resolveHttpIngestConfig(options, ctx.registry.ingest?.http?.enabled);
1616
+ const httpConfig = resolveHttpIngestConfig(options, ctx.registry.ingest?.http);
1613
1617
  if (isHttpIngestRoute(pathname, httpConfig)) {
1614
1618
  const handled2 = await handleHttpIngestRequest(req, res, ctx, options, pathname);
1615
1619
  if (handled2) return;
@@ -1888,6 +1892,28 @@ async function validateBundleDirectory(bundleDir) {
1888
1892
  }
1889
1893
  return errors;
1890
1894
  }
1895
+ async function hashBundleContents(bundleDir) {
1896
+ const hash = createHash("sha256");
1897
+ const walk = async (dir, relPrefix) => {
1898
+ const entries = (await readdir(dir, { withFileTypes: true })).sort(
1899
+ (a, b) => a.name.localeCompare(b.name)
1900
+ );
1901
+ for (const entry of entries) {
1902
+ const abs = path8.join(dir, entry.name);
1903
+ const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
1904
+ if (entry.isDirectory()) {
1905
+ await walk(abs, rel);
1906
+ } else if (entry.isFile()) {
1907
+ hash.update(rel);
1908
+ hash.update("\0");
1909
+ hash.update(await readFile(abs));
1910
+ hash.update("\0");
1911
+ }
1912
+ }
1913
+ };
1914
+ await walk(bundleDir, "");
1915
+ return hash.digest("hex");
1916
+ }
1891
1917
  async function copyDirectoryRecursive(sourceDir, destDir) {
1892
1918
  await mkdir(destDir, { recursive: true });
1893
1919
  const entries = await readdir(sourceDir, { withFileTypes: true });
@@ -1942,8 +1968,7 @@ async function importBundleUpload(options) {
1942
1968
  registryImportWarnings
1943
1969
  };
1944
1970
  }
1945
- const metadataRaw = await readFile(path8.join(bundlePath, "metadata.json"), "utf8");
1946
- const contentHash = createHash("sha256").update(metadataRaw).digest("hex");
1971
+ const contentHash = await hashBundleContents(bundlePath);
1947
1972
  const sourceKey = `bundle:${bundlePath}`;
1948
1973
  const existing = findIngestFileBySourceKey(options.db, sourceKey);
1949
1974
  if (existing && existing.contentHash === contentHash) {