@anvia/studio 1.3.17 → 1.3.19
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 +9 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +101 -12
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/{index-C9fNYyxm.js → index-DDQA6w5d.js} +2 -2
- package/dist/ui/assets/index-DDQA6w5d.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +7 -7
- package/dist/ui/assets/index-C9fNYyxm.js.map +0 -1
package/README.md
CHANGED
|
@@ -37,8 +37,17 @@ await new Studio([agent]).serve({ port: 4021 });
|
|
|
37
37
|
- Knowledge-graph exploration and browser sandbox viewing with human takeover.
|
|
38
38
|
- Optional SQLite session storage and graceful server shutdown.
|
|
39
39
|
|
|
40
|
+
The knowledge inspector returns HTTP 400 `bad_request` with message `Invalid cursor` for invalid
|
|
41
|
+
static-context cursors or when a dynamic context/tool store raises `VectorInspectCursorError`.
|
|
42
|
+
|
|
40
43
|
Session storage is in memory by default; configure SQLite to persist it across restarts. Studio is intended for local development. Add application authentication and authorization when exposing it remotely.
|
|
41
44
|
|
|
45
|
+
Studio rejects HTTP requests with an untrusted Host or Origin hostname. Loopback hosts are
|
|
46
|
+
trusted by default, as is an explicitly configured non-wildcard bind `hostname`. When binding
|
|
47
|
+
to `0.0.0.0` or `::`, set `allowedHosts` to the hostnames clients use, for example
|
|
48
|
+
`serve({ hostname: "0.0.0.0", allowedHosts: ["studio.lan"] })`. This allowlist also applies to
|
|
49
|
+
Origin hosts; ports are ignored. See the [serving guide](../../docs/packages/tool-studio.md#usage).
|
|
50
|
+
|
|
42
51
|
## Learn more
|
|
43
52
|
|
|
44
53
|
- [Usage guide](https://github.com/anvia-hq/anvia/blob/main/docs/packages/tool-studio.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -914,6 +914,13 @@ type StudioOptions = {
|
|
|
914
914
|
type StudioServeOptions = {
|
|
915
915
|
port?: number;
|
|
916
916
|
hostname?: string;
|
|
917
|
+
/**
|
|
918
|
+
* Extra Host header hostnames (and Origin hosts) trusted by the Studio HTTP
|
|
919
|
+
* API, in addition to the loopback set and an explicitly configured
|
|
920
|
+
* non-wildcard `hostname`. Required when binding to 0.0.0.0 (or ::): list
|
|
921
|
+
* every hostname clients use to reach Studio, for example `["studio.lan"]`.
|
|
922
|
+
*/
|
|
923
|
+
allowedHosts?: readonly string[];
|
|
917
924
|
log?: boolean;
|
|
918
925
|
handleSignals?: boolean;
|
|
919
926
|
shutdownTimeoutMs?: number;
|
package/dist/index.js
CHANGED
|
@@ -1007,6 +1007,61 @@ function optionalStringArray(value) {
|
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
1009
|
// src/runtime/http.ts
|
|
1010
|
+
var STUDIO_LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
1011
|
+
function normalizeHostname(value) {
|
|
1012
|
+
return value.trim().toLowerCase();
|
|
1013
|
+
}
|
|
1014
|
+
function resolveStudioAllowedHostnames(options) {
|
|
1015
|
+
const allowed = new Set(STUDIO_LOCAL_HOSTNAMES);
|
|
1016
|
+
for (const host of options?.allowedHosts ?? []) {
|
|
1017
|
+
const normalized = normalizeHostname(host);
|
|
1018
|
+
if (normalized !== "") allowed.add(normalized);
|
|
1019
|
+
}
|
|
1020
|
+
const bound = normalizeHostname(options?.hostname ?? "");
|
|
1021
|
+
if (bound !== "" && bound !== "0.0.0.0" && bound !== "::" && !allowed.has(bound)) {
|
|
1022
|
+
allowed.add(bound);
|
|
1023
|
+
if (bound.includes(":")) allowed.add(`[${bound}]`);
|
|
1024
|
+
}
|
|
1025
|
+
return allowed;
|
|
1026
|
+
}
|
|
1027
|
+
function isAllowedHostname(hostname3, allowed) {
|
|
1028
|
+
return hostname3 !== void 0 && allowed.has(hostname3.trim().toLowerCase());
|
|
1029
|
+
}
|
|
1030
|
+
function createStudioHostGuard(options) {
|
|
1031
|
+
const allowed = resolveStudioAllowedHostnames(options);
|
|
1032
|
+
return async function studioHostGuard(c, next) {
|
|
1033
|
+
const host = c.req.header("host");
|
|
1034
|
+
if (host !== void 0 && !isAllowedHostname(studioRequestHostname(host), allowed)) {
|
|
1035
|
+
return errorResponse(c, 403, "forbidden", "Host not allowed");
|
|
1036
|
+
}
|
|
1037
|
+
const origin = c.req.header("origin");
|
|
1038
|
+
if (origin !== void 0 && !isAllowedOrigin(origin, allowed)) {
|
|
1039
|
+
return errorResponse(c, 403, "forbidden", "Origin not allowed");
|
|
1040
|
+
}
|
|
1041
|
+
return next();
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
function studioRequestHostname(hostHeader) {
|
|
1045
|
+
if (hostHeader === void 0) return void 0;
|
|
1046
|
+
const host = hostHeader.trim().toLowerCase();
|
|
1047
|
+
if (host.startsWith("[")) {
|
|
1048
|
+
const end = host.indexOf("]");
|
|
1049
|
+
if (end === -1) return void 0;
|
|
1050
|
+
return host.slice(0, end + 1);
|
|
1051
|
+
}
|
|
1052
|
+
const colon = host.lastIndexOf(":");
|
|
1053
|
+
if (colon !== -1 && host.indexOf(":") === colon) return host.slice(0, colon);
|
|
1054
|
+
return host;
|
|
1055
|
+
}
|
|
1056
|
+
function isAllowedOrigin(origin, allowed) {
|
|
1057
|
+
let url2;
|
|
1058
|
+
try {
|
|
1059
|
+
url2 = new URL(origin);
|
|
1060
|
+
} catch {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
return isAllowedHostname(url2.hostname, allowed);
|
|
1064
|
+
}
|
|
1010
1065
|
function errorResponse(c, status, code, message, details) {
|
|
1011
1066
|
const body = {
|
|
1012
1067
|
error: {
|
|
@@ -23773,6 +23828,7 @@ function registerTeamRoutes(app, teams, lifecycle) {
|
|
|
23773
23828
|
|
|
23774
23829
|
// src/runtime/knowledge.ts
|
|
23775
23830
|
import { getAgentToolState } from "@anvia/core/internal/agent";
|
|
23831
|
+
import { parseVectorInspectCursor, VectorInspectCursorError } from "@anvia/core/vector-store";
|
|
23776
23832
|
|
|
23777
23833
|
// src/runtime/query.ts
|
|
23778
23834
|
function optionalQueryString(value) {
|
|
@@ -23841,6 +23897,9 @@ function registerKnowledgeRoutes(app, props) {
|
|
|
23841
23897
|
if (page === void 0) {
|
|
23842
23898
|
return errorResponse(c, 404, "not_found", "Knowledge source not found");
|
|
23843
23899
|
}
|
|
23900
|
+
if (page === false) {
|
|
23901
|
+
return errorResponse(c, 400, "bad_request", "Invalid cursor");
|
|
23902
|
+
}
|
|
23844
23903
|
return c.json(page);
|
|
23845
23904
|
});
|
|
23846
23905
|
}
|
|
@@ -23942,11 +24001,19 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
23942
24001
|
if (inspect === void 0) {
|
|
23943
24002
|
return nonInspectablePage(agent.id, sourceId, "dynamic_context");
|
|
23944
24003
|
}
|
|
23945
|
-
|
|
23946
|
-
|
|
23947
|
-
|
|
23948
|
-
|
|
23949
|
-
|
|
24004
|
+
let page;
|
|
24005
|
+
try {
|
|
24006
|
+
page = await inspect({
|
|
24007
|
+
limit: request.limit,
|
|
24008
|
+
cursor: request.cursor,
|
|
24009
|
+
filter: vectorContext.filter
|
|
24010
|
+
});
|
|
24011
|
+
} catch (error62) {
|
|
24012
|
+
if (VectorInspectCursorError.isCursorError(error62)) {
|
|
24013
|
+
return false;
|
|
24014
|
+
}
|
|
24015
|
+
throw error62;
|
|
24016
|
+
}
|
|
23950
24017
|
const result = {
|
|
23951
24018
|
agentId: agent.id,
|
|
23952
24019
|
sourceId,
|
|
@@ -23968,11 +24035,19 @@ async function knowledgeItemsPage(agent, sourceId, request) {
|
|
|
23968
24035
|
if (inspect === void 0) {
|
|
23969
24036
|
return nonInspectablePage(agent.id, sourceId, "dynamic_tools");
|
|
23970
24037
|
}
|
|
23971
|
-
|
|
23972
|
-
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
|
|
24038
|
+
let page;
|
|
24039
|
+
try {
|
|
24040
|
+
page = await inspect({
|
|
24041
|
+
limit: request.limit,
|
|
24042
|
+
cursor: request.cursor,
|
|
24043
|
+
filter: toolIndex.filter
|
|
24044
|
+
});
|
|
24045
|
+
} catch (error62) {
|
|
24046
|
+
if (VectorInspectCursorError.isCursorError(error62)) {
|
|
24047
|
+
return false;
|
|
24048
|
+
}
|
|
24049
|
+
throw error62;
|
|
24050
|
+
}
|
|
23976
24051
|
const result = {
|
|
23977
24052
|
agentId: agent.id,
|
|
23978
24053
|
sourceId,
|
|
@@ -23995,7 +24070,12 @@ function inspectFn(store) {
|
|
|
23995
24070
|
}
|
|
23996
24071
|
function staticKnowledgeItemsPage(agent, request) {
|
|
23997
24072
|
const staticContext = staticContextDocuments(agent.agent);
|
|
23998
|
-
|
|
24073
|
+
let start;
|
|
24074
|
+
try {
|
|
24075
|
+
start = parseVectorInspectCursor(request.cursor);
|
|
24076
|
+
} catch {
|
|
24077
|
+
return false;
|
|
24078
|
+
}
|
|
23999
24079
|
const page = staticContext.slice(start, start + request.limit);
|
|
24000
24080
|
const nextOffset = start + page.length;
|
|
24001
24081
|
const result = {
|
|
@@ -27174,7 +27254,12 @@ var Studio = class {
|
|
|
27174
27254
|
}
|
|
27175
27255
|
start(serveOptions = {}) {
|
|
27176
27256
|
this.close();
|
|
27177
|
-
|
|
27257
|
+
const serveHostOptions = {};
|
|
27258
|
+
if (serveOptions.allowedHosts !== void 0) {
|
|
27259
|
+
serveHostOptions.allowedHosts = serveOptions.allowedHosts;
|
|
27260
|
+
}
|
|
27261
|
+
if (serveOptions.hostname !== void 0) serveHostOptions.hostname = serveOptions.hostname;
|
|
27262
|
+
this.studio = createStudioApp({ ...this.options, ...serveHostOptions });
|
|
27178
27263
|
this.shutdownPromise = void 0;
|
|
27179
27264
|
const port = serveOptions.port ?? Number(process.env.RUNNER_PORT ?? 4021);
|
|
27180
27265
|
const serverOptions = {
|
|
@@ -27443,6 +27528,10 @@ function createStudioApp(options) {
|
|
|
27443
27528
|
const sandboxRegistry = createStudioSandboxRegistry(agents, options.sandboxes ?? []);
|
|
27444
27529
|
const memorySources = createStudioMemorySourceRegistry(agents, stores.sessions);
|
|
27445
27530
|
const app = new HonoApp();
|
|
27531
|
+
const guardOptions = {};
|
|
27532
|
+
if (options.allowedHosts !== void 0) guardOptions.allowedHosts = options.allowedHosts;
|
|
27533
|
+
if (options.hostname !== void 0) guardOptions.hostname = options.hostname;
|
|
27534
|
+
app.use(createStudioHostGuard(guardOptions));
|
|
27446
27535
|
const uiOptions = isStudioUiEnabled(options.ui) ? resolveStudioUiOptions(options.ui) : void 0;
|
|
27447
27536
|
if (uiOptions !== void 0 && !uiOptions.protectShell) {
|
|
27448
27537
|
registerStudioUi(app, uiOptions);
|