ahp-inspector 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { existsSync as existsSync2, readFileSync, statSync as statSync3 } from "fs";
5
- import { dirname as dirname3, join as join6, resolve as resolvePath } from "path";
5
+ import { dirname as dirname3, join as join7, resolve as resolvePath } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
 
8
8
  // ../host-node/src/discovery.ts
@@ -19,10 +19,10 @@ var MAX_DEPTH_BELOW_LAUNCH = 3;
19
19
  function defaultRoots() {
20
20
  const home = homedir();
21
21
  const platform = process.platform;
22
- const ossDev = {
23
- origin: "vscode-oss-dev",
24
- dir: join(home, ".vscode-oss-agents-dev", "logs")
25
- };
22
+ const ossDevRoots = [
23
+ { origin: "vscode-oss-dev", dir: join(home, ".vscode-oss-dev", "logs") },
24
+ { origin: "vscode-oss-dev", dir: join(home, ".vscode-oss-agents-dev", "logs") }
25
+ ];
26
26
  if (platform === "darwin") {
27
27
  return [
28
28
  { origin: "vscode", dir: join(home, "Library", "Application Support", "Code", "logs") },
@@ -30,7 +30,7 @@ function defaultRoots() {
30
30
  origin: "vscode-insiders",
31
31
  dir: join(home, "Library", "Application Support", "Code - Insiders", "logs")
32
32
  },
33
- ossDev
33
+ ...ossDevRoots
34
34
  ];
35
35
  }
36
36
  if (platform === "win32") {
@@ -38,13 +38,13 @@ function defaultRoots() {
38
38
  return [
39
39
  { origin: "vscode", dir: join(appData, "Code", "logs") },
40
40
  { origin: "vscode-insiders", dir: join(appData, "Code - Insiders", "logs") },
41
- ossDev
41
+ ...ossDevRoots
42
42
  ];
43
43
  }
44
44
  return [
45
45
  { origin: "vscode", dir: join(home, ".config", "Code", "logs") },
46
46
  { origin: "vscode-insiders", dir: join(home, ".config", "Code - Insiders", "logs") },
47
- ossDev
47
+ ...ossDevRoots
48
48
  ];
49
49
  }
50
50
  var FILENAME_RE_AHP_JSONL = /^(agenthost|agent-host|ahp).*\.jsonl$/i;
@@ -474,6 +474,20 @@ function normalize(raw2, meta) {
474
474
  };
475
475
  }
476
476
 
477
+ // ../parser/src/wire-meta.ts
478
+ function extractWireMeta(raw2) {
479
+ if (raw2 === null || typeof raw2 !== "object") return null;
480
+ const log = raw2._ahpLog;
481
+ if (log === null || typeof log !== "object") return null;
482
+ const tsField = log.ts;
483
+ if (typeof tsField !== "string" || tsField.length === 0) return null;
484
+ const parsedTs = Date.parse(tsField);
485
+ if (!Number.isFinite(parsedTs)) return null;
486
+ const dirField = log.dir;
487
+ const dir = dirField === "c2s" || dirField === "s2c" ? dirField : null;
488
+ return { ts: parsedTs, tsRaw: tsField, dir };
489
+ }
490
+
477
491
  // ../host-node/src/find-latest-ahp-log.ts
478
492
  var DEFAULT_TIME_BUDGET_MS2 = 1500;
479
493
  var DEFAULT_MAX_STATS2 = 5e3;
@@ -4754,10 +4768,13 @@ async function createAppState(opts) {
4754
4768
  }
4755
4769
  for (const line of lines) {
4756
4770
  const byteLength = Buffer2.byteLength(line, "utf8");
4757
- const ts = Date.now();
4758
4771
  const parsed = parseLine(line, byteOffset, byteLength);
4759
- const dir = parsed.error ? "c2s" : inferDir(parsed.raw);
4760
- const m = { seq, ts, tsRaw: String(ts), dir, byteOffset, byteLength };
4772
+ const wire = parsed.error ? null : extractWireMeta(parsed.raw);
4773
+ const ingestNow = Date.now();
4774
+ const ts = wire?.ts ?? ingestNow;
4775
+ const tsRaw = wire?.tsRaw ?? String(ingestNow);
4776
+ const dir = parsed.error ? "c2s" : wire?.dir ?? inferDir(parsed.raw);
4777
+ const m = { seq, ts, tsRaw, dir, byteOffset, byteLength };
4761
4778
  const ev = parsed.error ? makeParseErrorEvent(m, parsed.error.reason, parsed.text) : normalize(parsed.raw, m);
4762
4779
  store.append(ev);
4763
4780
  seq += 1;
@@ -4866,6 +4883,30 @@ async function createAppState(opts) {
4866
4883
  };
4867
4884
  }
4868
4885
 
4886
+ // ../server/src/cors.ts
4887
+ var corsMiddleware = async (c, next) => {
4888
+ const origin = c.req.header("origin");
4889
+ if (c.req.method === "OPTIONS") {
4890
+ const reqHeaders = c.req.header("access-control-request-headers") ?? "*";
4891
+ const reqMethod = c.req.header("access-control-request-method") ?? "GET";
4892
+ return new Response(null, {
4893
+ status: 204,
4894
+ headers: {
4895
+ "access-control-allow-origin": origin ?? "*",
4896
+ "access-control-allow-methods": `${reqMethod}, GET, POST, OPTIONS`,
4897
+ "access-control-allow-headers": reqHeaders,
4898
+ "access-control-max-age": "600",
4899
+ vary: "Origin"
4900
+ }
4901
+ });
4902
+ }
4903
+ await next();
4904
+ if (origin) {
4905
+ c.res.headers.set("access-control-allow-origin", origin);
4906
+ c.res.headers.append("vary", "Origin");
4907
+ }
4908
+ };
4909
+
4869
4910
  // ../server/src/csp.ts
4870
4911
  var CSP_VALUE = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'";
4871
4912
  var cspMiddleware = async (c, next) => {
@@ -8266,12 +8307,132 @@ function registerStaticUi(app, distDir) {
8266
8307
  app.get("/", serveStatic({ path: `${distDir}/index.html` }));
8267
8308
  }
8268
8309
 
8310
+ // ../server/src/upload-routes.ts
8311
+ import { mkdir, rm, writeFile } from "fs/promises";
8312
+ import { tmpdir } from "os";
8313
+ import { extname as extname2, join as join6, basename as pathBasename } from "path";
8314
+ var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
8315
+ var UPLOAD_DIR_PREFIX = "ahp-inspector-upload-";
8316
+ function sanitizeFilename(raw2) {
8317
+ let decoded;
8318
+ try {
8319
+ decoded = decodeURIComponent(raw2);
8320
+ } catch {
8321
+ return null;
8322
+ }
8323
+ const stripped = pathBasename(decoded);
8324
+ let base = "";
8325
+ for (let i = 0; i < stripped.length; i++) {
8326
+ const code = stripped.charCodeAt(i);
8327
+ if (code >= 32 && code !== 127) base += stripped[i];
8328
+ }
8329
+ if (base.length === 0 || base.length > 255) return null;
8330
+ if (extname2(base).toLowerCase() !== ".jsonl") return null;
8331
+ return base;
8332
+ }
8333
+ function createUploadStore() {
8334
+ const dirs = /* @__PURE__ */ new Map();
8335
+ async function write(filename, bytes) {
8336
+ const dir = join6(
8337
+ tmpdir(),
8338
+ `${UPLOAD_DIR_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`
8339
+ );
8340
+ await mkdir(dir, { recursive: true });
8341
+ const filePath = join6(dir, filename);
8342
+ await writeFile(filePath, bytes);
8343
+ dirs.set(filePath, dir);
8344
+ return filePath;
8345
+ }
8346
+ async function removeDir(dir) {
8347
+ try {
8348
+ await rm(dir, { recursive: true, force: true });
8349
+ } catch {
8350
+ }
8351
+ }
8352
+ async function cleanupAllExcept(keepPath) {
8353
+ const tasks = [];
8354
+ for (const [path, dir] of dirs) {
8355
+ if (path === keepPath) continue;
8356
+ dirs.delete(path);
8357
+ tasks.push(removeDir(dir));
8358
+ }
8359
+ await Promise.all(tasks);
8360
+ }
8361
+ async function disposeAll() {
8362
+ await cleanupAllExcept(null);
8363
+ }
8364
+ return { write, cleanupAllExcept, disposeAll };
8365
+ }
8366
+ function registerUploadRoutes(app, sessions) {
8367
+ const store = createUploadStore();
8368
+ const unsubscribe = sessions.onChange((active) => {
8369
+ if (active === null) {
8370
+ void store.disposeAll();
8371
+ }
8372
+ });
8373
+ const exitCleanup = () => {
8374
+ void store.disposeAll();
8375
+ };
8376
+ process.once("exit", exitCleanup);
8377
+ app.post("/api/sessions/upload", async (c) => {
8378
+ const filenameHeader = c.req.header("x-filename");
8379
+ if (typeof filenameHeader !== "string" || filenameHeader.length === 0) {
8380
+ return c.json({ code: "bad-request", message: "missing X-Filename" }, 400);
8381
+ }
8382
+ const safeName = sanitizeFilename(filenameHeader);
8383
+ if (safeName === null) {
8384
+ return c.json({ code: "not-jsonl", message: "not-jsonl" }, 400);
8385
+ }
8386
+ const lengthHeader = c.req.header("content-length");
8387
+ const declaredLength = lengthHeader ? Number.parseInt(lengthHeader, 10) : Number.NaN;
8388
+ if (Number.isFinite(declaredLength) && declaredLength > MAX_UPLOAD_BYTES) {
8389
+ return c.json({ code: "too-large", message: "too-large" }, 413);
8390
+ }
8391
+ let buf;
8392
+ try {
8393
+ buf = await c.req.arrayBuffer();
8394
+ } catch {
8395
+ return c.json({ code: "bad-request", message: "could not read body" }, 400);
8396
+ }
8397
+ if (buf.byteLength === 0) {
8398
+ return c.json({ code: "bad-request", message: "empty body" }, 400);
8399
+ }
8400
+ if (buf.byteLength > MAX_UPLOAD_BYTES) {
8401
+ return c.json({ code: "too-large", message: "too-large" }, 413);
8402
+ }
8403
+ let tempPath;
8404
+ try {
8405
+ tempPath = await store.write(safeName, new Uint8Array(buf));
8406
+ } catch {
8407
+ return c.json({ code: "io-error", message: "io-error" }, 500);
8408
+ }
8409
+ try {
8410
+ const active = await sessions.open({ path: tempPath });
8411
+ void store.cleanupAllExcept(tempPath);
8412
+ return c.json({ active: { logKey: active.logKey, meta: active.appState.meta } });
8413
+ } catch (err) {
8414
+ void store.cleanupAllExcept(null);
8415
+ const e = err;
8416
+ const code = typeof e.code === "string" ? e.code : "not-found";
8417
+ return c.json({ code, message: code }, 400);
8418
+ }
8419
+ });
8420
+ return {
8421
+ async dispose() {
8422
+ unsubscribe();
8423
+ process.removeListener("exit", exitCleanup);
8424
+ await store.disposeAll();
8425
+ }
8426
+ };
8427
+ }
8428
+
8269
8429
  // ../server/src/log-server.ts
8270
8430
  var HOSTNAME = "127.0.0.1";
8271
8431
  function startLogServer(opts) {
8272
8432
  const { sessions } = opts;
8273
8433
  const app = new Hono2();
8274
8434
  app.use("*", hostGuardMiddleware);
8435
+ app.use("*", corsMiddleware);
8275
8436
  app.use("*", cspMiddleware);
8276
8437
  app.get("/health", (c) => c.json({ status: "ok", version: opts.version }));
8277
8438
  registerLogRoutes(app, sessions);
@@ -8279,6 +8440,7 @@ function startLogServer(opts) {
8279
8440
  registerSearchRoutes(app, sessions);
8280
8441
  registerStateRoutes(app, sessions);
8281
8442
  registerSessionRoutes(app, sessions);
8443
+ registerUploadRoutes(app, sessions);
8282
8444
  if (opts.uiDistDir) registerStaticUi(app, opts.uiDistDir);
8283
8445
  return new Promise((resolve3, reject) => {
8284
8446
  const server = serve(
@@ -8422,7 +8584,7 @@ function classifyDirection(raw2) {
8422
8584
  var __filename2 = fileURLToPath(import.meta.url);
8423
8585
  var __dirname2 = dirname3(__filename2);
8424
8586
  function loadVersion() {
8425
- const candidates = [join6(__dirname2, "..", "package.json"), join6(__dirname2, "package.json")];
8587
+ const candidates = [join7(__dirname2, "..", "package.json"), join7(__dirname2, "package.json")];
8426
8588
  for (const p of candidates) {
8427
8589
  try {
8428
8590
  return JSON.parse(readFileSync(p, "utf8")).version ?? "0.0.0";
@@ -8450,7 +8612,7 @@ function locateUiDist() {
8450
8612
  );
8451
8613
  for (const c of candidates) {
8452
8614
  try {
8453
- if (existsSync2(join6(c, "index.html"))) return c;
8615
+ if (existsSync2(join7(c, "index.html"))) return c;
8454
8616
  } catch {
8455
8617
  }
8456
8618
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ahp-inspector",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Local-first viewer for VS Code Agent Host Protocol (AHP) JSONL logs.",
6
6
  "keywords": [