veryfront 0.1.1009 → 0.1.1011
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/esm/deno.js +1 -1
- package/esm/src/agent/conversation/run-event-normalization.d.ts.map +1 -1
- package/esm/src/agent/conversation/run-event-normalization.js +64 -18
- package/esm/src/routing/api/module-loader/esbuild-plugin.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/esbuild-plugin.js +15 -6
- package/esm/src/routing/api/module-loader/http-validator.d.ts +1 -0
- package/esm/src/routing/api/module-loader/http-validator.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/http-validator.js +15 -7
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-event-normalization.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/conversation/run-event-normalization.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wCAAwC,QAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"run-event-normalization.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/conversation/run-event-normalization.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wCAAwC,QAAa,CAAC;AASnE,KAAK,0BAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAS7E,sDAAsD;AACtD,wBAAgB,qCAAqC,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM5E;AAED,oDAAoD;AACpD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,0BAA0B,GAChC,0BAA0B,EAAE,CAQ9B;AA0CD,0CAA0C;AAC1C,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,0BAA0B,EAAE,GACnC,0BAA0B,EAAE,CAE9B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const MAX_CONVERSATION_RUN_EVENT_PAYLOAD_BYTES = 240 * 1024;
|
|
2
|
+
const OMITTED_CONVERSATION_RUN_EVENT_TYPE = "CUSTOM";
|
|
2
3
|
const MAX_SUMMARY_DEPTH = 4;
|
|
3
4
|
const MAX_SUMMARY_ARRAY_ITEMS = 8;
|
|
4
5
|
const MAX_SUMMARY_OBJECT_KEYS = 24;
|
|
@@ -55,13 +56,7 @@ function enforceEventSizeLimit(event) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
return
|
|
59
|
-
type: event.type,
|
|
60
|
-
...(typeof event.messageId === "string" ? { messageId: event.messageId } : {}),
|
|
61
|
-
...(typeof event.toolCallId === "string" ? { toolCallId: event.toolCallId } : {}),
|
|
62
|
-
truncated: true,
|
|
63
|
-
note: "Conversation-run event payload exceeded the size limit and was omitted.",
|
|
64
|
-
};
|
|
59
|
+
return buildOmittedEvent(event);
|
|
65
60
|
}
|
|
66
61
|
/** Normalizes conversation run events. */
|
|
67
62
|
export function normalizeConversationRunEvents(events) {
|
|
@@ -139,6 +134,36 @@ function truncateEventStringFieldToLimit(event, field, suffix) {
|
|
|
139
134
|
}
|
|
140
135
|
return buildCandidate(best);
|
|
141
136
|
}
|
|
137
|
+
function addStringFieldWithinLimit(event, field, value) {
|
|
138
|
+
const candidate = { ...event, [field]: value };
|
|
139
|
+
if (getConversationRunEventJsonByteLength(candidate) <= MAX_CONVERSATION_RUN_EVENT_PAYLOAD_BYTES) {
|
|
140
|
+
return candidate;
|
|
141
|
+
}
|
|
142
|
+
return truncateEventStringFieldToLimit(candidate, field, " [truncated]") ?? event;
|
|
143
|
+
}
|
|
144
|
+
function buildOmittedEvent(event) {
|
|
145
|
+
let omitted = {
|
|
146
|
+
type: OMITTED_CONVERSATION_RUN_EVENT_TYPE,
|
|
147
|
+
name: "conversation-run-event-omitted",
|
|
148
|
+
truncated: true,
|
|
149
|
+
note: "Conversation-run event payload exceeded the size limit and was omitted.",
|
|
150
|
+
};
|
|
151
|
+
omitted = addStringFieldWithinLimit(omitted, "originalType", event.type);
|
|
152
|
+
if (typeof event.messageId === "string") {
|
|
153
|
+
omitted = addStringFieldWithinLimit(omitted, "originalMessageId", event.messageId);
|
|
154
|
+
}
|
|
155
|
+
if (typeof event.toolCallId === "string") {
|
|
156
|
+
omitted = addStringFieldWithinLimit(omitted, "originalToolCallId", event.toolCallId);
|
|
157
|
+
}
|
|
158
|
+
if (getConversationRunEventJsonByteLength(omitted) <= MAX_CONVERSATION_RUN_EVENT_PAYLOAD_BYTES) {
|
|
159
|
+
return omitted;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
type: OMITTED_CONVERSATION_RUN_EVENT_TYPE,
|
|
163
|
+
name: "conversation-run-event-omitted",
|
|
164
|
+
truncated: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
142
167
|
function summarizeGenericEvent(event) {
|
|
143
168
|
const { type, ...rest } = event;
|
|
144
169
|
return {
|
|
@@ -149,17 +174,38 @@ function summarizeGenericEvent(event) {
|
|
|
149
174
|
};
|
|
150
175
|
}
|
|
151
176
|
function splitStringFieldEvent(event, field) {
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
const value = event[field];
|
|
178
|
+
const buildPart = (slice) => ({ ...event, [field]: slice });
|
|
179
|
+
const parts = [];
|
|
180
|
+
let startIndex = 0;
|
|
181
|
+
while (startIndex < value.length) {
|
|
182
|
+
// Largest prefix whose WHOLE serialized event fits the byte limit. Measuring the
|
|
183
|
+
// event (not the raw slice) keeps the split correct for escape-heavy content that
|
|
184
|
+
// expands under JSON.stringify — the same unit the API enforces — so every part
|
|
185
|
+
// fits without the size-limit backstop having to truncate (drop) any data.
|
|
186
|
+
let low = startIndex + 1;
|
|
187
|
+
let high = value.length;
|
|
188
|
+
let bestEndIndex = -1;
|
|
189
|
+
while (low <= high) {
|
|
190
|
+
const mid = Math.floor((low + high) / 2);
|
|
191
|
+
if (getConversationRunEventJsonByteLength(buildPart(value.slice(startIndex, mid))) <=
|
|
192
|
+
MAX_CONVERSATION_RUN_EVENT_PAYLOAD_BYTES) {
|
|
193
|
+
bestEndIndex = mid;
|
|
194
|
+
low = mid + 1;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
high = mid - 1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (bestEndIndex <= startIndex) {
|
|
201
|
+
// Even a single character overflows the envelope; hand off to the size-limit
|
|
202
|
+
// backstop rather than loop forever emitting zero-progress parts.
|
|
203
|
+
return [event];
|
|
204
|
+
}
|
|
205
|
+
parts.push(buildPart(value.slice(startIndex, bestEndIndex)));
|
|
206
|
+
startIndex = bestEndIndex;
|
|
207
|
+
}
|
|
208
|
+
return parts.length > 0 ? parts : [event];
|
|
163
209
|
}
|
|
164
210
|
function splitUtf8String(value, maxBytes) {
|
|
165
211
|
if (encoder.encode(value).byteLength <= maxBytes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/esbuild-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/esbuild-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,sCAAsC,CAAC;AAQ5E,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA8GD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,GAAG,MAAM,CAsR9E"}
|
|
@@ -2,6 +2,7 @@ import * as dntShim from "../../../../_dnt.shims.js";
|
|
|
2
2
|
import { computeHash, computeIntegrity, createLockfileManager, HTTP_MODULE_FETCH_TIMEOUT_MS, HTTP_NETWORK_CONNECT_TIMEOUT, serverLogger, } from "../../../utils/index.js";
|
|
3
3
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
4
4
|
import * as pathHelper from "../../../platform/compat/path/index.js";
|
|
5
|
+
import { isAllowedRemoteHost } from "./http-validator.js";
|
|
5
6
|
const logger = serverLogger.component("api");
|
|
6
7
|
const HTTP_MODULE_CACHE_DIR = ".veryfront/cache/api-http-imports";
|
|
7
8
|
const HTTP_MODULE_FETCH_MAX_ATTEMPTS = 3;
|
|
@@ -111,15 +112,25 @@ export function createHTTPPlugin(options) {
|
|
|
111
112
|
const name = error.name || "Error";
|
|
112
113
|
return typeof code === "string" && code ? `${name}(${code})` : name;
|
|
113
114
|
}
|
|
115
|
+
function isReadOnlyFileSystemError(error) {
|
|
116
|
+
if (error == null)
|
|
117
|
+
return false;
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
if (/read-only file ?system|os error 30|erofs/i.test(message))
|
|
120
|
+
return true;
|
|
121
|
+
return error instanceof Error && isReadOnlyFileSystemError(error.cause);
|
|
122
|
+
}
|
|
114
123
|
async function persistLockfileEntry(url, entry) {
|
|
115
124
|
if (!lockfile)
|
|
116
125
|
return;
|
|
126
|
+
await lockfile.set(url, entry);
|
|
117
127
|
try {
|
|
118
|
-
await lockfile.set(url, entry);
|
|
119
128
|
await lockfile.flush();
|
|
120
129
|
logger.debug(`[http] lockfile updated: ${url} -> ${entry.resolved}`);
|
|
121
130
|
}
|
|
122
131
|
catch (error) {
|
|
132
|
+
if (!isReadOnlyFileSystemError(error))
|
|
133
|
+
throw error;
|
|
123
134
|
logger.warn(`[http] could not persist lockfile entry for ${url}: ${describePersistenceError(error)}`);
|
|
124
135
|
}
|
|
125
136
|
}
|
|
@@ -177,14 +188,12 @@ export function createHTTPPlugin(options) {
|
|
|
177
188
|
try {
|
|
178
189
|
const u = new URL(args.path);
|
|
179
190
|
if (allowedHosts?.length) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (!isAllowed) {
|
|
183
|
-
const remediation = `Add "${hostUrl}" to security.remoteHosts in veryfront.config.(ts|js) or replace with an approved CDN (e.g., https://esm.sh).`;
|
|
191
|
+
if (!isAllowedRemoteHost(u, allowedHosts)) {
|
|
192
|
+
const remediation = `Add "${u.origin}" to security.remoteHosts in veryfront.config.(ts|js) or replace with an approved CDN (e.g., https://esm.sh).`;
|
|
184
193
|
return {
|
|
185
194
|
errors: [
|
|
186
195
|
{
|
|
187
|
-
text: `Remote import blocked by allow-list: ${
|
|
196
|
+
text: `Remote import blocked by allow-list: ${u.origin}. ${remediation}`,
|
|
188
197
|
},
|
|
189
198
|
],
|
|
190
199
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-validator.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/http-validator.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"http-validator.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/http-validator.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAQ7E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAiChF"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { createError, toError } from "../../../errors/veryfront-error.js";
|
|
2
|
+
export function isAllowedRemoteHost(url, allowedHosts) {
|
|
3
|
+
return allowedHosts.some((host) => {
|
|
4
|
+
try {
|
|
5
|
+
return new URL(host).origin === url.origin;
|
|
6
|
+
}
|
|
7
|
+
catch (_) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
2
12
|
export function validateHTTPImports(source, allowedHosts) {
|
|
3
13
|
if (!allowedHosts?.length)
|
|
4
14
|
return;
|
|
@@ -9,22 +19,20 @@ export function validateHTTPImports(source, allowedHosts) {
|
|
|
9
19
|
const url = match[0].match(/https?:\/\/[^'"]+/)?.[0];
|
|
10
20
|
if (!url)
|
|
11
21
|
continue;
|
|
12
|
-
let
|
|
22
|
+
let u;
|
|
13
23
|
try {
|
|
14
|
-
|
|
15
|
-
hostUrl = `${u.protocol}//${u.host}`;
|
|
24
|
+
u = new URL(url);
|
|
16
25
|
}
|
|
17
26
|
catch (_) {
|
|
18
27
|
/* expected: URL may be malformed */
|
|
19
28
|
continue;
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
if (isAllowed)
|
|
30
|
+
if (isAllowedRemoteHost(u, allowedHosts))
|
|
23
31
|
continue;
|
|
24
|
-
const remediation = `Add "${
|
|
32
|
+
const remediation = `Add "${u.origin}" to security.remoteHosts in veryfront.config.(ts|js) or replace with an approved CDN (e.g., https://esm.sh).`;
|
|
25
33
|
throw toError(createError({
|
|
26
34
|
type: "api",
|
|
27
|
-
message: `[API] handler build failed: Remote import blocked by allow-list: ${
|
|
35
|
+
message: `[API] handler build failed: Remote import blocked by allow-list: ${u.origin}. ${remediation}`,
|
|
28
36
|
}));
|
|
29
37
|
}
|
|
30
38
|
}
|