veryfront 0.1.1005 → 0.1.1007

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1005",
3
+ "version": "0.1.1007",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -1 +1 @@
1
- {"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAAmB,KAAK,IAAI,EAAgB,MAAM,kBAAkB,CAAC;AAU5E,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAsCxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;IAC5C,mBAAmB,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,cAAc,CAAC,EAAE,+BAA+B,CAAC,gBAAgB,CAAC,CAAC;IACnE,8BAA8B,CAAC,EAAE,CAC/B,KAAK,EAAE,+BAA+B,KACnC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAoCD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,sBAAsB,EACtC,2BAA2B,CAAC,EAAE,MAAM,EAAE,EACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;;cAsFrD;AAyOD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAoVnB"}
1
+ {"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAEL,KAAK,IAAI,EAGV,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAsCxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;IAC5C,mBAAmB,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,cAAc,CAAC,EAAE,+BAA+B,CAAC,gBAAgB,CAAC,CAAC;IACnE,8BAA8B,CAAC,EAAE,CAC/B,KAAK,EAAE,+BAA+B,KACnC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAoCD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,sBAAsB,EACtC,2BAA2B,CAAC,EAAE,MAAM,EAAE,EACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;;cAsFrD;AAwSD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAyWnB"}
@@ -2,6 +2,7 @@ import * as dntShim from "../../_dnt.shims.js";
2
2
  import { AgentRuntime, } from "../agent/index.js";
3
3
  import { normalizeAgUiRuntimeMessages } from "../agent/ag-ui/runtime-support.js";
4
4
  import { compactForStep, estimateOverhead } from "../chat/message-prep.js";
5
+ import { getRuntimeRemoteToolSources, } from "../agent/runtime/mcp-server-tool-sources.js";
5
6
  import { convertAgentRuntimeMessagesToProviderMessages, convertProviderMessagesToAgentRuntimeMessages, } from "../agent/runtime/message-adapter.js";
6
7
  import { createAgentServiceSandboxTools } from "../sandbox/index.js";
7
8
  import { tryResolve } from "../extensions/contracts.js";
@@ -9,7 +10,7 @@ import { importFirstPartyExtensionModule } from "../extensions/first-party-impor
9
10
  import { SandboxShellToolsProviderName, } from "../extensions/sandbox/index.js";
10
11
  import { resolveHostedRuntimeAllowedToolNames } from "../agent/hosted/runtime-essential-tools.js";
11
12
  import { SKILL_TOOL_IDS } from "../skill/types.js";
12
- import { isToolVisibleTo, toolRegistry } from "../tool/index.js";
13
+ import { isToolVisibleTo, toolRegistry, } from "../tool/index.js";
13
14
  import { defineSchema, lazySchema } from "../schemas/index.js";
14
15
  import { createStreamTransformState, finalizeRunEvents, formatAgUiEvent, mapRuntimeEventToAgUi, parseSseJsonEvents, } from "./ag-ui-sse.js";
15
16
  import { AgentRunCancelledError } from "./session-manager.js";
@@ -238,11 +239,22 @@ function getRuntimeToolAllowlist(forwardedProps) {
238
239
  * Intersects the merged run tool set with the restrictive tool allowlist.
239
240
  * Skill runtime/delegation tools are preserved for skill-enabled agents,
240
241
  * mirroring the hosted chat runtime's allowlist semantics.
242
+ *
243
+ * The allowlist bounds this run's direct tool surface only: preserved skill
244
+ * delegation tools (`invoke_agent`) can spawn child runs whose tool assembly
245
+ * derives from the child agent's own config and is not capped by this run's
246
+ * allowlist. Strict schedules must exclude delegation via agent config.
241
247
  */
242
248
  function applyRuntimeToolAllowlist(mergedTools, toolAllowlist, agent) {
243
- if (!toolAllowlist || !mergedTools || mergedTools === true) {
249
+ if (!toolAllowlist || !mergedTools) {
244
250
  return mergedTools;
245
251
  }
252
+ if (mergedTools === true) {
253
+ // buildMergedTools always expands `tools: true` into a concrete record,
254
+ // so this is unreachable; if that invariant ever breaks, fail closed
255
+ // rather than silently skipping enforcement.
256
+ return {};
257
+ }
246
258
  const availableSkillIds = agent.config.skills === true
247
259
  ? ["*"]
248
260
  : Array.isArray(agent.config.skills)
@@ -287,6 +299,48 @@ function getForwardedIntegrationToolDefinitions(forwardedProps) {
287
299
  parameters: def.inputSchema ?? def.parameters ?? { type: "object", properties: {} },
288
300
  }));
289
301
  }
302
+ function getRemoteToolDiscoveryContext(input) {
303
+ return {
304
+ agentId: input.agent.id,
305
+ runId: input.runInput.runId,
306
+ ...(input.deps.projectAgentSandbox?.authToken
307
+ ? { authToken: input.deps.projectAgentSandbox.authToken }
308
+ : {}),
309
+ ...(input.deps.projectAgentSandbox?.projectId
310
+ ? { projectId: input.deps.projectAgentSandbox.projectId }
311
+ : {}),
312
+ ...(input.runInput.parentRunId ? { parentRunId: input.runInput.parentRunId } : {}),
313
+ ...(input.runInput.state !== undefined ? { state: input.runInput.state } : {}),
314
+ context: input.runInput.context,
315
+ forwardedProps: input.runInput.forwardedProps,
316
+ };
317
+ }
318
+ async function getDeclaredRemoteSourceToolNames(input) {
319
+ const sources = getRuntimeRemoteToolSources(input.agent.config);
320
+ if (!sources?.length) {
321
+ return [];
322
+ }
323
+ const context = getRemoteToolDiscoveryContext(input);
324
+ const toolNames = new Set();
325
+ for (const source of sources) {
326
+ try {
327
+ for (const definition of await source.listTools(context)) {
328
+ if (typeof definition.name === "string" && definition.name.length > 0) {
329
+ toolNames.add(definition.name);
330
+ }
331
+ }
332
+ }
333
+ catch (error) {
334
+ logger.warn("Failed to inspect remote tool source for restrictive allowlist fallback", {
335
+ runId: input.runInput.runId,
336
+ agentId: input.agent.id,
337
+ sourceId: source.id,
338
+ error: error instanceof Error ? error.message : String(error),
339
+ });
340
+ }
341
+ }
342
+ return [...toolNames];
343
+ }
290
344
  function compactRuntimeMessagesForStream(messages, mergedTools) {
291
345
  const toolCount = mergedTools && mergedTools !== true ? Object.keys(mergedTools).length : 0;
292
346
  return convertProviderMessagesToAgentRuntimeMessages(compactForStep(convertAgentRuntimeMessagesToProviderMessages(messages), estimateOverhead("", toolCount)));
@@ -309,31 +363,46 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
309
363
  const grantedRemoteToolNames = forwardedAllowedRemoteToolNames === undefined
310
364
  ? undefined
311
365
  : mergeRemoteToolNames(sourceAllowedRemoteToolNames, forwardedAllowedRemoteToolNames);
366
+ const runtimeToolAllowlist = getRuntimeToolAllowlist(input.forwardedProps);
367
+ if (runtimeToolAllowlist === null && forwardedAllowedRemoteToolNames !== undefined) {
368
+ logger.debug("runtimeOverrides.allowedTools is additive on internal runs; use runtimeOverrides.toolAllowlist to restrict the run tool surface", { runId: input.runId, agentId: agent.id });
369
+ }
370
+ const forwardedIntegrationToolDefs = getForwardedIntegrationToolDefinitions(input.forwardedProps);
371
+ const availableForwardedToolNames = forwardedIntegrationToolDefs?.map((tool) => tool.name);
312
372
  // A restrictive toolAllowlist caps remote exposure too: it intersects the
313
373
  // tightest existing remote filter (forwarded grants, else the agent source's
314
- // own __vfAllowedRemoteTools), and with neither present it becomes the
315
- // remote filter directly. It can only narrow what the agent's sources
316
- // already expose, never add.
317
- const runtimeToolAllowlist = getRuntimeToolAllowlist(input.forwardedProps);
374
+ // own __vfAllowedRemoteTools). With neither present, discover the agent's
375
+ // declared remote-source surface and intersect with that. A bare allowlist
376
+ // entry can narrow actual remote sources, but never becomes an implicit
377
+ // project-scoped integration grant (#2768).
318
378
  const sourceRemoteFilterBase = Object.hasOwn(agent.config, "__vfAllowedRemoteTools")
319
379
  ? sourceAllowedRemoteToolNames
320
380
  : undefined;
381
+ const remoteFallbackBase = runtimeToolAllowlist !== null &&
382
+ grantedRemoteToolNames === undefined &&
383
+ sourceRemoteFilterBase === undefined
384
+ ? await getDeclaredRemoteSourceToolNames({ agent, runInput: input, deps })
385
+ : undefined;
321
386
  const allowedRemoteToolNames = runtimeToolAllowlist === null
322
387
  ? grantedRemoteToolNames
323
- : (grantedRemoteToolNames ?? sourceRemoteFilterBase ?? [...runtimeToolAllowlist]).filter((toolName) => runtimeToolAllowlist.has(toolName));
324
- const forwardedIntegrationToolDefs = getForwardedIntegrationToolDefinitions(input.forwardedProps);
325
- const availableForwardedToolNames = forwardedIntegrationToolDefs?.map((tool) => tool.name);
388
+ : (grantedRemoteToolNames ?? sourceRemoteFilterBase ?? remoteFallbackBase ?? []).filter((toolName) => runtimeToolAllowlist.has(toolName));
326
389
  const sandboxTools = await buildProjectAgentSandboxTools({ agent, deps });
327
390
  const availableLocalTools = {
328
391
  ...(deps.localTools ?? {}),
329
392
  ...(sandboxTools.tools ?? {}),
330
393
  };
331
394
  const mergedTools = applyRuntimeToolAllowlist(buildMergedTools(agent, input, deps.sessionManager, availableForwardedToolNames, Object.keys(availableLocalTools).length > 0 ? availableLocalTools : undefined), runtimeToolAllowlist, agent);
395
+ // Provider-native tools (e.g. web_search) are provider-side and bypass the
396
+ // merged tool record, so cap them against the allowlist explicitly.
397
+ const cappedProviderTools = runtimeToolAllowlist !== null && Array.isArray(agent.config.providerTools)
398
+ ? agent.config.providerTools.filter((toolName) => typeof toolName === "string" && runtimeToolAllowlist.has(toolName))
399
+ : undefined;
332
400
  const runtimeAgent = {
333
401
  ...agent,
334
402
  config: {
335
403
  ...agent.config,
336
404
  tools: mergedTools,
405
+ ...(cappedProviderTools !== undefined ? { providerTools: cappedProviderTools } : {}),
337
406
  ...(allowedRemoteToolNames !== undefined
338
407
  ? { __vfAllowedRemoteTools: allowedRemoteToolNames }
339
408
  : {}),
@@ -19,5 +19,6 @@ export interface WebSocketUpgradeResult {
19
19
  export interface WebSocketUpgradeOptions {
20
20
  protocol?: string;
21
21
  headers?: Headers | Record<string, string>;
22
+ idleTimeout?: number;
22
23
  }
23
24
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
@@ -1,4 +1,6 @@
1
+ import * as dntShim from "../../../../_dnt.shims.js";
1
2
  import type { WebSocketUpgradeOptions, WebSocketUpgradeResult } from "./types.js";
2
3
  export declare function upgradeWebSocket(request: Request, options?: WebSocketUpgradeOptions): WebSocketUpgradeResult;
3
4
  export declare function isWebSocketUpgrade(request: Request): boolean;
5
+ export declare function resolveDenoUpgradeWebSocketOptions(options?: WebSocketUpgradeOptions): dntShim.Deno.UpgradeWebSocketOptions | undefined;
4
6
  //# sourceMappingURL=websocket.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/websocket.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIlF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,uBAAuB,GAChC,sBAAsB,CASxB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D"}
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAErD,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIlF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,uBAAuB,GAChC,sBAAsB,CASxB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D;AAsBD,wBAAgB,kCAAkC,CAChD,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAOlD"}
@@ -22,9 +22,14 @@ function upgradeWebSocketDeno(request, options) {
22
22
  detail: "Deno.upgradeWebSocket() is not available in this runtime.",
23
23
  });
24
24
  }
25
- const denoOptions = options?.protocol
26
- ? { protocol: options.protocol }
27
- : undefined;
28
- const { socket, response } = nativeDeno.upgradeWebSocket(request, denoOptions);
25
+ const { socket, response } = nativeDeno.upgradeWebSocket(request, resolveDenoUpgradeWebSocketOptions(options));
29
26
  return { socket, response };
30
27
  }
28
+ export function resolveDenoUpgradeWebSocketOptions(options) {
29
+ if (!options?.protocol && options?.idleTimeout === undefined)
30
+ return undefined;
31
+ return {
32
+ ...(options.protocol ? { protocol: options.protocol } : {}),
33
+ ...(options.idleTimeout !== undefined ? { idleTimeout: options.idleTimeout } : {}),
34
+ };
35
+ }
@@ -23,7 +23,7 @@ import * as dntShim from "../../_dnt.shims.js";
23
23
  import { createProxyHandler, INTERNAL_PROXY_HEADERS } from "./handler.js";
24
24
  import { createCacheFromEnv } from "./cache/index.js";
25
25
  import { isRetryableConnectionError } from "./retry.js";
26
- import { closeBridgePeer, getServerWebSocketErrorLogLevel } from "./websocket-bridge.js";
26
+ import { closeBridgePeer, createProxyClientWebSocketUpgradeOptions, getServerWebSocketErrorLogLevel, } from "./websocket-bridge.js";
27
27
  import { register } from "../extensions/contracts.js";
28
28
  import { importFirstPartyExtensionModule } from "../extensions/first-party-import.js";
29
29
  import { endSpan, extractContext, initializeOTLPWithApis, injectContext, ProxySpanNames, shutdownOTLP, startServerSpan, withContext, withSpan, } from "./tracing.js";
@@ -138,7 +138,7 @@ function handleWebSocketUpgrade(req, url) {
138
138
  parsedEnvironment: parsed.environment,
139
139
  targetUrl: targetUrl.toString(),
140
140
  });
141
- const { socket: clientSocket, response } = upgradeWebSocket(req);
141
+ const { socket: clientSocket, response } = upgradeWebSocket(req, createProxyClientWebSocketUpgradeOptions());
142
142
  let serverSocket = null;
143
143
  let connectTimeoutId = null;
144
144
  let timedOut = false;
@@ -1,6 +1,8 @@
1
+ import type { WebSocketUpgradeOptions } from "../platform/compat/http/index.js";
1
2
  type BridgePeer = Pick<WebSocket, "close" | "readyState">;
2
3
  export type ServerWebSocketErrorLogLevel = "warn" | "error";
3
4
  export declare function getServerWebSocketErrorLogLevel(message: string): ServerWebSocketErrorLogLevel;
4
5
  export declare function closeBridgePeer(peer: BridgePeer | null, code: number, reason: string): void;
6
+ export declare function createProxyClientWebSocketUpgradeOptions(): WebSocketUpgradeOptions;
5
7
  export {};
6
8
  //# sourceMappingURL=websocket-bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-bridge.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/websocket-bridge.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAE1D,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,OAAO,CAAC;AAS5D,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,GAAG,4BAA4B,CAI7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3F"}
1
+ {"version":3,"file":"websocket-bridge.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/websocket-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAEhF,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAE1D,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,OAAO,CAAC;AAS5D,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,GAAG,4BAA4B,CAI7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3F;AAED,wBAAgB,wCAAwC,IAAI,uBAAuB,CAIlF"}
@@ -16,3 +16,8 @@ export function closeBridgePeer(peer, code, reason) {
16
16
  return;
17
17
  peer.close(code, reason);
18
18
  }
19
+ export function createProxyClientWebSocketUpgradeOptions() {
20
+ // Proxied project sockets use app-level heartbeats; Deno's transport idle timeout
21
+ // can close otherwise healthy bridges before the browser sends a data frame.
22
+ return { idleTimeout: 0 };
23
+ }
@@ -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,EAKL,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,sCAAsC,CAAC;AAI5E,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;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,GAAG,MAAM,CA2K9E"}
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;AAO5E,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,CAkP9E"}
@@ -1,11 +1,84 @@
1
1
  import * as dntShim from "../../../../_dnt.shims.js";
2
- import { computeIntegrity, createLockfileManager, HTTP_MODULE_FETCH_TIMEOUT_MS, HTTP_NETWORK_CONNECT_TIMEOUT, serverLogger, } from "../../../utils/index.js";
2
+ import { computeHash, computeIntegrity, createLockfileManager, HTTP_MODULE_FETCH_TIMEOUT_MS, HTTP_NETWORK_CONNECT_TIMEOUT, serverLogger, } from "../../../utils/index.js";
3
+ import { createFileSystem } from "../../../platform/compat/fs.js";
4
+ import * as pathHelper from "../../../platform/compat/path/index.js";
3
5
  const logger = serverLogger.component("api");
6
+ const HTTP_MODULE_CACHE_DIR = ".veryfront/cache/api-http-imports";
7
+ const HTTP_MODULE_FETCH_MAX_ATTEMPTS = 3;
8
+ const HTTP_MODULE_FETCH_RETRY_DELAY_MS = 100;
9
+ function createHTTPModuleCache(projectDir) {
10
+ if (!projectDir)
11
+ return null;
12
+ const fs = createFileSystem();
13
+ const cacheDir = pathHelper.join(projectDir, HTTP_MODULE_CACHE_DIR);
14
+ async function cachePaths(url, integrity) {
15
+ const key = await computeHash(`${url}\n${integrity}`);
16
+ return {
17
+ sourcePath: pathHelper.join(cacheDir, `${key}.mjs`),
18
+ metadataPath: pathHelper.join(cacheDir, `${key}.json`),
19
+ };
20
+ }
21
+ async function readMetadata(metadataPath, fs) {
22
+ if (!await fs.exists(metadataPath))
23
+ return null;
24
+ try {
25
+ return JSON.parse(await fs.readTextFile(metadataPath));
26
+ }
27
+ catch (error) {
28
+ logger.debug(`[http] ignoring unreadable module cache metadata: ${error}`);
29
+ return null;
30
+ }
31
+ }
32
+ return {
33
+ async read(url, expectedIntegrity) {
34
+ if (!expectedIntegrity)
35
+ return null;
36
+ try {
37
+ const { sourcePath, metadataPath } = await cachePaths(url, expectedIntegrity);
38
+ if (!await fs.exists(sourcePath))
39
+ return null;
40
+ const contents = await fs.readTextFile(sourcePath);
41
+ const integrity = await computeIntegrity(contents);
42
+ if (expectedIntegrity && integrity !== expectedIntegrity) {
43
+ logger.warn(`[http] cached module integrity mismatch: ${url}`);
44
+ return null;
45
+ }
46
+ const metadata = await readMetadata(metadataPath, fs);
47
+ if (metadata?.integrity && metadata.integrity !== integrity) {
48
+ logger.warn(`[http] cached module metadata integrity mismatch: ${url}`);
49
+ return null;
50
+ }
51
+ return contents;
52
+ }
53
+ catch (error) {
54
+ logger.debug(`[http] module cache read miss for ${url}: ${error}`);
55
+ return null;
56
+ }
57
+ },
58
+ async write(url, contents, resolvedUrl, integrity) {
59
+ try {
60
+ await fs.mkdir(cacheDir, { recursive: true });
61
+ const { sourcePath, metadataPath } = await cachePaths(url, integrity);
62
+ await fs.writeTextFile(sourcePath, contents);
63
+ await fs.writeTextFile(metadataPath, `${JSON.stringify({
64
+ url,
65
+ resolvedUrl,
66
+ integrity,
67
+ fetchedAt: new Date().toISOString(),
68
+ }, null, 2)}\n`);
69
+ }
70
+ catch (error) {
71
+ logger.debug(`[http] could not update module cache for ${url}: ${error}`);
72
+ }
73
+ },
74
+ };
75
+ }
4
76
  export function createHTTPPlugin(options) {
5
77
  const opts = Array.isArray(options) ? { allowedHosts: options } : options;
6
78
  const { allowedHosts, strict = false } = opts;
7
79
  const lockfile = opts.lockfile ??
8
80
  (opts.projectDir ? createLockfileManager(opts.projectDir) : null);
81
+ const moduleCache = createHTTPModuleCache(opts.projectDir);
9
82
  return {
10
83
  name: "vf-api-http-fetch",
11
84
  setup(build) {
@@ -25,6 +98,27 @@ export function createHTTPPlugin(options) {
25
98
  clearTimeout(timeout);
26
99
  }
27
100
  }
101
+ function shouldRetryFetch(status) {
102
+ return status === 429 || status >= 500 || status === HTTP_NETWORK_CONNECT_TIMEOUT;
103
+ }
104
+ function delay(ms) {
105
+ return new Promise((resolve) => dntShim.setTimeout(resolve, ms));
106
+ }
107
+ async function fetchRemoteModule(url) {
108
+ for (let attempt = 1; attempt <= HTTP_MODULE_FETCH_MAX_ATTEMPTS; attempt += 1) {
109
+ const response = await fetchWithTimeout(url).catch((error) => new Response(String(error?.message ?? error), {
110
+ status: HTTP_NETWORK_CONNECT_TIMEOUT,
111
+ }));
112
+ if (!shouldRetryFetch(response.status) || attempt === HTTP_MODULE_FETCH_MAX_ATTEMPTS) {
113
+ return response;
114
+ }
115
+ logger.warn(`[http] fetch attempt ${attempt} failed ${url} ${response.status}; retrying`);
116
+ await delay(HTTP_MODULE_FETCH_RETRY_DELAY_MS * attempt);
117
+ }
118
+ return new Response("Remote module fetch failed", {
119
+ status: HTTP_NETWORK_CONNECT_TIMEOUT,
120
+ });
121
+ }
28
122
  build.onResolve({ filter: /^(http|https):\/\// }, (args) => ({
29
123
  path: args.path,
30
124
  namespace: "http-url",
@@ -90,39 +184,62 @@ export function createHTTPPlugin(options) {
90
184
  catch (e) {
91
185
  logger.warn("API URL parse failed", e);
92
186
  }
93
- if (lockfile) {
94
- const cached = await lockfile.get(args.path);
95
- if (cached) {
96
- logger.debug(`[http] lockfile hit: ${args.path}`);
97
- try {
98
- const res = await fetchWithTimeout(cached.resolved);
99
- if (res.ok) {
100
- const text = await res.text();
101
- const integrity = await computeIntegrity(text);
102
- if (integrity === cached.integrity) {
103
- return { contents: text, loader: "js" };
104
- }
105
- if (strict) {
106
- return {
107
- errors: [
108
- {
109
- text: `Integrity mismatch for ${args.path}: expected ${cached.integrity}, got ${integrity}`,
110
- },
111
- ],
112
- };
113
- }
114
- logger.warn(`[http] integrity mismatch, refetching: ${args.path}`);
187
+ const readCachedModule = async (url, expectedIntegrity) => {
188
+ if (!url || !moduleCache)
189
+ return null;
190
+ return await moduleCache.read(url, expectedIntegrity);
191
+ };
192
+ const lockfileEntry = lockfile ? await lockfile.get(args.path) : null;
193
+ if (lockfileEntry) {
194
+ let canUseLockfileCacheFallback = true;
195
+ logger.debug(`[http] lockfile hit: ${args.path}`);
196
+ try {
197
+ const res = await fetchRemoteModule(lockfileEntry.resolved);
198
+ if (res.ok) {
199
+ const text = await res.text();
200
+ const integrity = await computeIntegrity(text);
201
+ if (integrity === lockfileEntry.integrity) {
202
+ await moduleCache?.write(args.path, text, lockfileEntry.resolved, integrity);
203
+ await moduleCache?.write(lockfileEntry.resolved, text, lockfileEntry.resolved, integrity);
204
+ return { contents: text, loader: "js" };
115
205
  }
206
+ if (strict) {
207
+ return {
208
+ errors: [
209
+ {
210
+ text: `Integrity mismatch for ${args.path}: expected ${lockfileEntry.integrity}, got ${integrity}`,
211
+ },
212
+ ],
213
+ };
214
+ }
215
+ canUseLockfileCacheFallback = false;
216
+ logger.warn(`[http] integrity mismatch, refetching: ${args.path}`);
217
+ }
218
+ else {
219
+ logger.warn(`[http] cached URL returned ${res.status}, trying module cache: ${args.path}`);
116
220
  }
117
- catch (_error) {
118
- logger.warn(`[http] cached URL failed, refetching: ${args.path}`);
221
+ }
222
+ catch (_error) {
223
+ logger.warn(`[http] cached URL failed, trying module cache: ${args.path}`);
224
+ }
225
+ if (canUseLockfileCacheFallback) {
226
+ const cachedText = await readCachedModule(lockfileEntry.resolved, lockfileEntry.integrity) ??
227
+ await readCachedModule(args.path, lockfileEntry.integrity);
228
+ if (cachedText) {
229
+ logger.warn(`[http] serving cached remote import for ${args.path}`);
230
+ return { contents: cachedText, loader: "js" };
119
231
  }
120
232
  }
121
233
  }
122
- const res = await fetchWithTimeout(requestUrl).catch((e) => {
123
- return new Response(String(e?.message ?? e), { status: HTTP_NETWORK_CONNECT_TIMEOUT });
124
- });
234
+ const res = await fetchRemoteModule(requestUrl);
125
235
  if (!res.ok) {
236
+ const cachedText = await readCachedModule(lockfileEntry?.resolved, lockfileEntry?.integrity) ??
237
+ await readCachedModule(requestUrl, lockfileEntry?.integrity) ??
238
+ await readCachedModule(args.path, lockfileEntry?.integrity);
239
+ if (cachedText) {
240
+ logger.warn(`[http] serving cached remote import for ${args.path}`);
241
+ return { contents: cachedText, loader: "js" };
242
+ }
126
243
  logger.error(`[http] fetch failed ${requestUrl} ${res.status}`);
127
244
  return {
128
245
  errors: [
@@ -134,8 +251,8 @@ export function createHTTPPlugin(options) {
134
251
  }
135
252
  const text = await res.text();
136
253
  const resolvedUrl = res.url || requestUrl;
254
+ const integrity = await computeIntegrity(text);
137
255
  if (lockfile) {
138
- const integrity = await computeIntegrity(text);
139
256
  await lockfile.set(args.path, {
140
257
  resolved: resolvedUrl,
141
258
  integrity,
@@ -144,6 +261,9 @@ export function createHTTPPlugin(options) {
144
261
  await lockfile.flush();
145
262
  logger.debug(`[http] lockfile updated: ${args.path} -> ${resolvedUrl}`);
146
263
  }
264
+ await moduleCache?.write(args.path, text, resolvedUrl, integrity);
265
+ await moduleCache?.write(requestUrl, text, resolvedUrl, integrity);
266
+ await moduleCache?.write(resolvedUrl, text, resolvedUrl, integrity);
147
267
  return { contents: text, loader: "js" };
148
268
  });
149
269
  logger.debug(`[API][http] resolvedUrls: ${resolvedUrls.length}, nodeMapped: ${nodeMapped.length}`);
@@ -253,7 +253,7 @@ function loadAndTranspileModule(modulePath, projectDir, adapter, fs, config) {
253
253
  plugins: [
254
254
  createImportMapPlugin(projectDir, adapter, config),
255
255
  createAdapterResolvePlugin(adapter, projectDir),
256
- createHTTPPlugin(allowedHosts),
256
+ createHTTPPlugin({ allowedHosts, projectDir }),
257
257
  ],
258
258
  });
259
259
  if (result.errors?.length) {
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1005";
2
+ export declare const VERSION = "0.1.1007";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.1005";
4
+ export const VERSION = "0.1.1007";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1005",
3
+ "version": "0.1.1007",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",