@agent-inspect/studio 6.7.1 → 6.7.3
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/README.md +39 -9
- 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/README.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# @agent-inspect/studio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Customer-owned, read-only Studio analyzer for registered AgentInspect workspaces. **No maintainer-hosted cloud.**
|
|
4
|
+
|
|
5
|
+
**Support level:** Beta — see [SUPPORT-LEVELS.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/SUPPORT-LEVELS.md).
|
|
4
6
|
|
|
5
7
|
## When to use
|
|
6
8
|
|
|
7
|
-
- Team-internal browser UI over
|
|
8
|
-
- Localhost
|
|
9
|
+
- Team-internal browser UI over one or more local workspaces
|
|
10
|
+
- Localhost review of projects, runs, search, and imports
|
|
11
|
+
- Explicit, opt-in ingest of files / GitHub artifacts / HTTP (all **disabled by default**)
|
|
9
12
|
|
|
10
13
|
## When not to use
|
|
11
14
|
|
|
12
15
|
- Maintainer-hosted dashboards or SaaS
|
|
13
|
-
-
|
|
16
|
+
- Production HA / Postgres / enterprise SSO (not supported)
|
|
17
|
+
- Trace mutation (Studio is read-only over registered workspaces)
|
|
14
18
|
|
|
15
19
|
## Install
|
|
16
20
|
|
|
@@ -18,17 +22,30 @@ Self-hosted, read-only Studio analyzer for multi-project AgentInspect workspaces
|
|
|
18
22
|
npm install agent-inspect @agent-inspect/studio
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
Optional index: `@agent-inspect/index-sqlite` (Beta).
|
|
26
|
+
|
|
27
|
+
## Two-minute start
|
|
22
28
|
|
|
23
29
|
```bash
|
|
24
30
|
npx agent-inspect studio
|
|
31
|
+
# default: http://127.0.0.1:7340
|
|
25
32
|
npx agent-inspect studio --port 7340 --host 127.0.0.1
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
Register workspaces via the Studio registry / CLI docs. SQLite is the current supported store.
|
|
36
|
+
|
|
37
|
+
## Privacy and network
|
|
38
|
+
|
|
39
|
+
- Binds to **localhost** by default
|
|
40
|
+
- **No default upload** or hidden telemetry
|
|
41
|
+
- File-drop ingest: local only
|
|
42
|
+
- GitHub artifact import: **explicit**; uses your credentials → GitHub
|
|
43
|
+
- HTTP ingest: **disabled by default**; requires token + binding when enabled
|
|
44
|
+
- See [NETWORK-BEHAVIOR.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/NETWORK-BEHAVIOR.md) · [SELF-HOSTING.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/SELF-HOSTING.md)
|
|
45
|
+
|
|
46
|
+
## Current pages (honest)
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
- No default upload or telemetry
|
|
48
|
+
Useful HTML for projects, runs, and search. Sessions / suites / safety views may still be thinner than the underlying APIs (JSON-oriented). Treat Studio as Beta.
|
|
32
49
|
|
|
33
50
|
## API
|
|
34
51
|
|
|
@@ -38,9 +55,22 @@ npx agent-inspect studio --port 7340 --host 127.0.0.1
|
|
|
38
55
|
|
|
39
56
|
`npx agent-inspect studio` (requires this package installed)
|
|
40
57
|
|
|
58
|
+
## Limitations
|
|
59
|
+
|
|
60
|
+
- No Postgres mode
|
|
61
|
+
- Not a production APM
|
|
62
|
+
- External design-partner evidence still pending for broad adoption claims
|
|
63
|
+
- Docker Compose examples are customer-owned templates — verify before production use
|
|
64
|
+
|
|
41
65
|
## Docs
|
|
42
66
|
|
|
43
|
-
- [SELF-
|
|
67
|
+
- [SELF-HOSTING.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/SELF-HOSTING.md)
|
|
68
|
+
- [WORKSPACE.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/WORKSPACE.md)
|
|
69
|
+
- [PRE-V7-PILOT-KIT.md](https://github.com/rajudandigam/agent-inspect/blob/main/docs/PRE-V7-PILOT-KIT.md)
|
|
70
|
+
|
|
71
|
+
## Version
|
|
72
|
+
|
|
73
|
+
Part of the fixed AgentInspect release line. See the npm badge / package manifest for the current version.
|
|
44
74
|
|
|
45
75
|
## License
|
|
46
76
|
|
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) {
|