veryfront 0.1.67 → 0.1.70
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/factory.d.ts +6 -1
- package/esm/src/agent/factory.d.ts.map +1 -1
- package/esm/src/agent/factory.js +31 -1
- package/esm/src/agent/types.d.ts +2 -0
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/build/binary-plugin-includes.d.ts +5 -0
- package/esm/src/build/binary-plugin-includes.d.ts.map +1 -0
- package/esm/src/build/binary-plugin-includes.js +14 -0
- package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
- package/esm/src/html/styles-builder/plugin-loader.js +2 -1
- package/esm/src/internal-agents/control-plane-auth.d.ts +1 -0
- package/esm/src/internal-agents/control-plane-auth.d.ts.map +1 -1
- package/esm/src/internal-agents/control-plane-auth.js +9 -1
- package/esm/src/security/http/base-handler.d.ts.map +1 -1
- package/esm/src/security/http/base-handler.js +5 -4
- package/esm/src/server/handlers/request/channel-invoke.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/channel-invoke.handler.js +2 -1
- package/package.json +1 -1
- package/scripts/postinstall-lib.js +79 -0
- package/scripts/postinstall.js +8 -1
- package/src/deno.js +1 -1
- package/src/src/agent/factory.ts +35 -1
- package/src/src/agent/types.ts +2 -0
- package/src/src/build/binary-plugin-includes.ts +17 -0
- package/src/src/html/styles-builder/plugin-loader.ts +2 -1
- package/src/src/internal-agents/control-plane-auth.ts +10 -1
- package/src/src/security/http/base-handler.ts +5 -4
- package/src/src/server/handlers/request/channel-invoke.handler.ts +2 -1
package/esm/deno.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import type { Agent, AgentConfig } from "./types.js";
|
|
1
|
+
import type { Agent, AgentConfig, AgentMiddleware } from "./types.js";
|
|
2
2
|
export declare function agent(config: AgentConfig): Agent;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the middleware array for an agent, prepending security middleware
|
|
5
|
+
* unless explicitly opted out with `security: false`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveSecurityMiddleware(config: Pick<AgentConfig, "security" | "middleware">): AgentMiddleware[];
|
|
3
8
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/agent/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/agent/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EACX,eAAe,EAKhB,MAAM,YAAY,CAAC;AA+CpB,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CA4MhD;AAcD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,YAAY,CAAC,GACnD,eAAe,EAAE,CAcnB"}
|
package/esm/src/agent/factory.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createExecuteSkillScriptTool, createLoadSkillReferenceTool, createLoadS
|
|
|
9
9
|
import { agentRegistry } from "./composition/index.js";
|
|
10
10
|
import { agentLogger } from "../utils/logger/logger.js";
|
|
11
11
|
import { createError, toError } from "../errors/veryfront-error.js";
|
|
12
|
+
import { COMMON_BLOCKED_PATTERNS, securityMiddleware } from "./middleware/security/validator.js";
|
|
12
13
|
import { withSpan } from "../observability/tracing/otlp-setup.js";
|
|
13
14
|
import { resolveConfiguredAgentModel } from "./runtime/model-resolution.js";
|
|
14
15
|
const STREAMING_HEADERS = {
|
|
@@ -90,6 +91,7 @@ export function agent(config) {
|
|
|
90
91
|
return `${basePrompt}\n\n${buildSkillManifestPrompt(currentSkills)}`;
|
|
91
92
|
}
|
|
92
93
|
: originalSystem;
|
|
94
|
+
const resolvedMiddleware = resolveSecurityMiddleware(config);
|
|
93
95
|
const platform = detectPlatform();
|
|
94
96
|
const compatibility = validatePlatformCompatibility({
|
|
95
97
|
maxSteps: config.maxSteps,
|
|
@@ -110,6 +112,7 @@ export function agent(config) {
|
|
|
110
112
|
...publicConfig,
|
|
111
113
|
tools: mergedToolsConfig,
|
|
112
114
|
system: augmentedSystem,
|
|
115
|
+
middleware: resolvedMiddleware,
|
|
113
116
|
});
|
|
114
117
|
const agentInstance = {
|
|
115
118
|
id,
|
|
@@ -171,7 +174,34 @@ export function agent(config) {
|
|
|
171
174
|
// Register on globalThis so compiled-binary runtime shim can delegate to the
|
|
172
175
|
// real factory. External temp-file modules can't import from the embedded
|
|
173
176
|
// binary FS, so they use globalThis bridges instead.
|
|
174
|
-
dntShim.dntGlobalThis
|
|
177
|
+
if (!("__vfAgentFactory" in dntShim.dntGlobalThis)) {
|
|
178
|
+
Object.defineProperty(dntShim.dntGlobalThis, "__vfAgentFactory", {
|
|
179
|
+
value: agent,
|
|
180
|
+
writable: false,
|
|
181
|
+
enumerable: false,
|
|
182
|
+
configurable: false,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolve the middleware array for an agent, prepending security middleware
|
|
187
|
+
* unless explicitly opted out with `security: false`.
|
|
188
|
+
*/
|
|
189
|
+
export function resolveSecurityMiddleware(config) {
|
|
190
|
+
if (config.security === false)
|
|
191
|
+
return config.middleware ?? [];
|
|
192
|
+
return [
|
|
193
|
+
securityMiddleware({
|
|
194
|
+
input: {
|
|
195
|
+
maxLength: 50_000,
|
|
196
|
+
blockedPatterns: COMMON_BLOCKED_PATTERNS.promptInjection,
|
|
197
|
+
},
|
|
198
|
+
output: {
|
|
199
|
+
filterPII: true,
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
...(config.middleware ?? []),
|
|
203
|
+
];
|
|
204
|
+
}
|
|
175
205
|
let agentIdCounter = 0;
|
|
176
206
|
function generateAgentId() {
|
|
177
207
|
return `agent_${Date.now()}_${agentIdCounter++}`;
|
package/esm/src/agent/types.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface AgentConfig {
|
|
|
45
45
|
* and registers the skill tools.
|
|
46
46
|
*/
|
|
47
47
|
skills?: true | string[];
|
|
48
|
+
/** Set to false to disable the default security middleware */
|
|
49
|
+
security?: false;
|
|
48
50
|
}
|
|
49
51
|
export type ResolvedAgentConfig = AgentConfig & {
|
|
50
52
|
model: ModelString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/agent/types.ts"],"names":[],"mappings":"AAAA;;4BAE4B;AAC5B,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAG/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAG3D,YAAY,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,YAAY,EACZ,OAAO,EACP,WAAW,EACX,aAAa,EACb,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAE5B;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAGjC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEnE,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,0EAA0E;IAC1E,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/agent/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const BINARY_TAILWIND_PLUGIN_PACKAGES: readonly ["tailwindcss-animate@1.0.7", "@tailwindcss/typography@0.5.19", "@tailwindcss/forms@0.5.11", "tailwind-scrollbar-hide@2.0.0", "daisyui@5.5.14"];
|
|
2
|
+
export declare function getTailwindPluginBundleUrl(packageName: string): string;
|
|
3
|
+
export declare function getBinaryPluginBundleIncludes(): string[];
|
|
4
|
+
export { BINARY_TAILWIND_PLUGIN_PACKAGES };
|
|
5
|
+
//# sourceMappingURL=binary-plugin-includes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-plugin-includes.d.ts","sourceRoot":"","sources":["../../../src/src/build/binary-plugin-includes.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,+BAA+B,0JAM3B,CAAC;AAEX,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,6BAA6B,IAAI,MAAM,EAAE,CAExD;AAED,OAAO,EAAE,+BAA+B,EAAE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const BINARY_TAILWIND_PLUGIN_PACKAGES = [
|
|
2
|
+
"tailwindcss-animate@1.0.7",
|
|
3
|
+
"@tailwindcss/typography@0.5.19",
|
|
4
|
+
"@tailwindcss/forms@0.5.11",
|
|
5
|
+
"tailwind-scrollbar-hide@2.0.0",
|
|
6
|
+
"daisyui@5.5.14",
|
|
7
|
+
];
|
|
8
|
+
export function getTailwindPluginBundleUrl(packageName) {
|
|
9
|
+
return `https://esm.sh/${packageName}?bundle&external=tailwindcss&target=denonext`;
|
|
10
|
+
}
|
|
11
|
+
export function getBinaryPluginBundleIncludes() {
|
|
12
|
+
return BINARY_TAILWIND_PLUGIN_PACKAGES.map(getTailwindPluginBundleUrl);
|
|
13
|
+
}
|
|
14
|
+
export { BINARY_TAILWIND_PLUGIN_PACKAGES };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-loader.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/plugin-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin-loader.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/plugin-loader.ts"],"names":[],"mappings":"AAwGA;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0D/E;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CAmClB"}
|
|
@@ -12,6 +12,7 @@ import defaultTheme from "tailwindcss/defaultTheme";
|
|
|
12
12
|
import colors from "tailwindcss/colors";
|
|
13
13
|
import { serverLogger } from "../../utils/index.js";
|
|
14
14
|
import { getErrorBySlug, IMPORT_RESOLUTION_ERROR, NETWORK_ERROR, VeryfrontError, } from "../../errors/index.js";
|
|
15
|
+
import { getTailwindPluginBundleUrl } from "../../build/binary-plugin-includes.js";
|
|
15
16
|
const logger = serverLogger.component("tailwind");
|
|
16
17
|
// Provide localStorage shim for plugins that use util-deprecate (which checks localStorage)
|
|
17
18
|
// This prevents "LocalStorage is not supported in this context" errors in Deno.
|
|
@@ -88,7 +89,7 @@ async function importBundledModule(code) {
|
|
|
88
89
|
* dynamic imports from URLs. Fetches bundled code, rewrites imports, loads via temp file.
|
|
89
90
|
*/
|
|
90
91
|
export async function loadModuleFromEsmSh(packageName) {
|
|
91
|
-
const stubUrl =
|
|
92
|
+
const stubUrl = getTailwindPluginBundleUrl(packageName);
|
|
92
93
|
logger.debug("Fetching esm.sh stub", { url: stubUrl });
|
|
93
94
|
const stubResponse = await dntShim.fetch(stubUrl);
|
|
94
95
|
if (!stubResponse.ok) {
|
|
@@ -4,6 +4,7 @@ export declare class ControlPlaneRequestError extends Error {
|
|
|
4
4
|
readonly status: number;
|
|
5
5
|
constructor(status: number, message: string);
|
|
6
6
|
}
|
|
7
|
+
export declare function getControlPlaneVerificationPublicKey(ctx: HandlerContext): string | undefined;
|
|
7
8
|
export declare function verifyControlPlaneRequest(req: dntShim.Request, ctx: HandlerContext, rawBody: string, options?: {
|
|
8
9
|
expectedSubject?: string;
|
|
9
10
|
expectedSurface?: "studio" | "channels" | "a2a" | "mcp";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-plane-auth.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/control-plane-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"control-plane-auth.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/control-plane-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAG/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQxD,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK5C;AAED,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAM5F;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,KAAK,GAAG,KAAK,CAAC;CACpD;;;;;;;;;GAsDP"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { verifyControlPlaneJws } from "../channels/control-plane.js";
|
|
2
|
+
import { getHostEnv } from "../platform/compat/process.js";
|
|
2
3
|
import { serverLogger } from "../utils/index.js";
|
|
3
4
|
import { HTTP_INTERNAL_SERVER_ERROR } from "../utils/constants/index.js";
|
|
4
5
|
const CONTROL_PLANE_JWS_HEADER = "x-veryfront-control-plane-jws";
|
|
@@ -12,8 +13,15 @@ export class ControlPlaneRequestError extends Error {
|
|
|
12
13
|
this.name = "ControlPlaneRequestError";
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
export function getControlPlaneVerificationPublicKey(ctx) {
|
|
17
|
+
// Project env overlays intentionally hide host secrets from request-scoped reads.
|
|
18
|
+
// Control-plane verification is framework-owned config, so it must fall back to
|
|
19
|
+
// the host environment when the runtime adapter env is overlay-aware.
|
|
20
|
+
return ctx.adapter.env.get("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY") ??
|
|
21
|
+
getHostEnv("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY");
|
|
22
|
+
}
|
|
15
23
|
export async function verifyControlPlaneRequest(req, ctx, rawBody, options = {}) {
|
|
16
|
-
const publicKeyPem = ctx
|
|
24
|
+
const publicKeyPem = getControlPlaneVerificationPublicKey(ctx);
|
|
17
25
|
if (!publicKeyPem) {
|
|
18
26
|
throw new ControlPlaneRequestError(HTTP_INTERNAL_SERVER_ERROR, "Control-plane verification is not configured");
|
|
19
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/security/http/base-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,qBAAqB,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,eAAe,CAAC;IAChF,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC;IAC3F,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3F,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IAC5C,QAAQ,EAAE,MAAM,aAAa,CAAC;CAC/B;AAED,8BAAsB,WAAY,YAAW,OAAO;IAClD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IAEnC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAMxC;IAEF,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAElF,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO;IAY1E,OAAO,CAAC,cAAc;IAmBtB,SAAS,CAAC,qBAAqB,CAC7B,GAAG,EAAE,cAAc,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,eAAe;IAWlB,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,cAAc,GAAG,IAAI;IAKhG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI;IAIhG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI;IAIhG,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAKjD,SAAS,CAAC,QAAQ,IAAI,aAAa;IAInC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa;IAIhG,SAAS,CAAC,gBAAgB,CAAC,CAAC,EAC1B,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/security/http/base-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,qBAAqB,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,eAAe,CAAC;IAChF,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC;IAC3F,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3F,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IAC5C,QAAQ,EAAE,MAAM,aAAa,CAAC;CAC/B;AAED,8BAAsB,WAAY,YAAW,OAAO;IAClD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IAEnC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAMxC;IAEF,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAElF,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO;IAY1E,OAAO,CAAC,cAAc;IAmBtB,SAAS,CAAC,qBAAqB,CAC7B,GAAG,EAAE,cAAc,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,eAAe;IAWlB,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,cAAc,GAAG,IAAI;IAKhG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI;IAIhG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI;IAIhG,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAKjD,SAAS,CAAC,QAAQ,IAAI,aAAa;IAInC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa;IAIhG,SAAS,CAAC,gBAAgB,CAAC,CAAC,EAC1B,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC,CAAC,CAAC;CAwDd"}
|
|
@@ -66,6 +66,7 @@ export class BaseHandler {
|
|
|
66
66
|
return { response, continue: false, metadata };
|
|
67
67
|
}
|
|
68
68
|
withProxyContext(ctx, fn, options = {}) {
|
|
69
|
+
const effectiveToken = ctx.proxyToken || ctx.adapter.env.get("VERYFRONT_API_TOKEN") || "";
|
|
69
70
|
const fsWrapper = ctx.adapter.fs;
|
|
70
71
|
if (typeof fsWrapper.setRequestBranch === "function") {
|
|
71
72
|
try {
|
|
@@ -76,7 +77,7 @@ export class BaseHandler {
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
const requireToken = options.requireToken ?? false;
|
|
79
|
-
if (!ctx.projectSlug || (requireToken && !
|
|
80
|
+
if (!ctx.projectSlug || (requireToken && !effectiveToken))
|
|
80
81
|
return fn();
|
|
81
82
|
if (fsWrapper.isMultiProjectMode?.()) {
|
|
82
83
|
const isProduction = (ctx.resolvedEnvironment ?? ctx.requestContext?.mode) === "production";
|
|
@@ -87,10 +88,10 @@ export class BaseHandler {
|
|
|
87
88
|
releaseId: ctx.releaseId,
|
|
88
89
|
branch,
|
|
89
90
|
}, ctx);
|
|
90
|
-
return fsWrapper.runWithContext(ctx.projectSlug,
|
|
91
|
+
return fsWrapper.runWithContext(ctx.projectSlug, effectiveToken, fn, ctx.projectId, { productionMode: isProduction, releaseId: ctx.releaseId, branch });
|
|
91
92
|
}
|
|
92
|
-
if (typeof fsWrapper.setRequestToken === "function" &&
|
|
93
|
-
fsWrapper.setRequestToken(
|
|
93
|
+
if (typeof fsWrapper.setRequestToken === "function" && effectiveToken) {
|
|
94
|
+
fsWrapper.setRequestToken(effectiveToken);
|
|
94
95
|
}
|
|
95
96
|
return runWithCacheBatching(fn);
|
|
96
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-invoke.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/channel-invoke.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EACL,KAAK,iBAAiB,EAKvB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"channel-invoke.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/channel-invoke.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EACL,KAAK,iBAAiB,EAKvB,MAAM,6BAA6B,CAAC;AAUrC,qBAAa,oBAAqB,SAAQ,WAAW;IAOvC,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,QAAQ,EAAE,eAAe,CAIvB;gBAE2B,IAAI,GAAE,iBAA4C;IAIzE,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAiEhF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseHandler } from "../response/base.js";
|
|
2
2
|
import { ChannelInvokeRequestSchema, defaultChannelInvokeDeps, executeChannelInvoke, verifyDispatchJws, } from "../../../channels/invoke.js";
|
|
3
|
+
import { getControlPlaneVerificationPublicKey } from "../../../internal-agents/control-plane-auth.js";
|
|
3
4
|
import { HTTP_INTERNAL_SERVER_ERROR, PRIORITY_MEDIUM_API, } from "../../../utils/constants/index.js";
|
|
4
5
|
const DISPATCH_JWS_HEADER = "x-veryfront-dispatch-jws";
|
|
5
6
|
const MAX_DISPATCH_SIGNATURE_AGE_SECONDS = 60;
|
|
@@ -22,7 +23,7 @@ export class ChannelInvokeHandler extends BaseHandler {
|
|
|
22
23
|
const builder = this.createResponseBuilder(ctx)
|
|
23
24
|
.withCORS(req, ctx.securityConfig?.cors)
|
|
24
25
|
.withSecurity(ctx.securityConfig ?? undefined, req);
|
|
25
|
-
const publicKeyPem = ctx
|
|
26
|
+
const publicKeyPem = getControlPlaneVerificationPublicKey(ctx);
|
|
26
27
|
if (!publicKeyPem) {
|
|
27
28
|
this.logWarn("Missing CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY for channel invoke endpoint");
|
|
28
29
|
return this.respond(builder.json({ error: "Channel dispatch verification is not configured" }, HTTP_INTERNAL_SERVER_ERROR));
|
package/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream, existsSync, unlinkSync } from "node:fs";
|
|
3
|
+
import https from "node:https";
|
|
4
|
+
|
|
5
|
+
export function computeFileHash(filePath) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const hash = createHash("sha256");
|
|
8
|
+
const stream = createReadStream(filePath);
|
|
9
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
10
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
11
|
+
stream.on("error", reject);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function downloadText(url, maxRedirects = 5) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
function follow(currentUrl, redirectCount) {
|
|
18
|
+
if (redirectCount > maxRedirects) {
|
|
19
|
+
return reject(new Error("Too many redirects"));
|
|
20
|
+
}
|
|
21
|
+
let settled = false;
|
|
22
|
+
https.get(currentUrl, (response) => {
|
|
23
|
+
const { statusCode = 0, headers } = response;
|
|
24
|
+
if (statusCode >= 301 && statusCode <= 308 && statusCode !== 304) {
|
|
25
|
+
response.resume();
|
|
26
|
+
const location = headers.location;
|
|
27
|
+
if (!location) return reject(new Error("Redirect missing Location"));
|
|
28
|
+
try { return follow(new URL(location, currentUrl).toString(), redirectCount + 1); }
|
|
29
|
+
catch { return reject(new Error(`Invalid redirect URL: ${location}`)); }
|
|
30
|
+
}
|
|
31
|
+
if (statusCode !== 200) {
|
|
32
|
+
response.resume();
|
|
33
|
+
return reject(new Error(`HTTP ${statusCode}`));
|
|
34
|
+
}
|
|
35
|
+
const MAX_CHECKSUM_SIZE = 1024;
|
|
36
|
+
let data = "";
|
|
37
|
+
response.on("data", (chunk) => {
|
|
38
|
+
data += chunk;
|
|
39
|
+
if (!settled && data.length > MAX_CHECKSUM_SIZE) {
|
|
40
|
+
settled = true;
|
|
41
|
+
response.destroy();
|
|
42
|
+
reject(new Error("Checksum file too large"));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
response.on("end", () => { if (!settled) resolve(data); });
|
|
46
|
+
}).on("error", (err) => { if (!settled) reject(err); });
|
|
47
|
+
}
|
|
48
|
+
follow(url, 0);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function verifyChecksum(filePath, checksumUrl, downloadFn = downloadText) {
|
|
53
|
+
let checksumText;
|
|
54
|
+
try {
|
|
55
|
+
checksumText = await downloadFn(checksumUrl);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err.message === "HTTP 404") {
|
|
58
|
+
console.warn("⚠️ No checksum file available — skipping verification");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Failed to fetch checksum: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Checksum file format: "<hash> <filename>" or just "<hash>"
|
|
65
|
+
const expectedHash = checksumText.trim().split(/\s+/)[0].toLowerCase();
|
|
66
|
+
if (!/^[0-9a-f]{64}$/.test(expectedHash)) {
|
|
67
|
+
throw new Error(`Invalid checksum format: ${expectedHash}`);
|
|
68
|
+
}
|
|
69
|
+
const actualHash = await computeFileHash(filePath);
|
|
70
|
+
|
|
71
|
+
if (actualHash !== expectedHash) {
|
|
72
|
+
try { if (existsSync(filePath)) unlinkSync(filePath); } catch {}
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Checksum mismatch!\n Expected: ${expectedHash}\n Actual: ${actualHash}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log("✅ Checksum verified");
|
|
79
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from "node:url";
|
|
|
11
11
|
import https from "node:https";
|
|
12
12
|
import os from "node:os";
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
|
+
import { verifyChecksum } from "./postinstall-lib.js";
|
|
14
15
|
|
|
15
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const platform = os.platform();
|
|
@@ -85,7 +86,7 @@ function downloadBinary(url, dest, maxRedirects = 5) {
|
|
|
85
86
|
|
|
86
87
|
const file = createWriteStream(dest);
|
|
87
88
|
response.pipe(file);
|
|
88
|
-
file.on('finish', () => { file.close()
|
|
89
|
+
file.on('finish', () => { file.close(() => resolve()); });
|
|
89
90
|
file.on("error", (err) => {
|
|
90
91
|
try { if (existsSync(dest)) unlinkSync(dest); }
|
|
91
92
|
catch (e) { console.warn(" Warning: Failed to clean up partial download:", e.message); }
|
|
@@ -103,6 +104,10 @@ async function install() {
|
|
|
103
104
|
console.log(`⬇️ Downloading binary from ${url}...`);
|
|
104
105
|
await downloadBinary(url, binPath);
|
|
105
106
|
|
|
107
|
+
// Verify binary integrity
|
|
108
|
+
const checksumUrl = `${baseUrl}/${binaryName}.sha256`;
|
|
109
|
+
await verifyChecksum(binPath, checksumUrl);
|
|
110
|
+
|
|
106
111
|
// Make binary executable (Unix systems)
|
|
107
112
|
if (platform !== "win32") {
|
|
108
113
|
chmodSync(binPath, 0o755);
|
|
@@ -114,6 +119,8 @@ async function install() {
|
|
|
114
119
|
console.log(' npx veryfront create my-app');
|
|
115
120
|
|
|
116
121
|
} catch (error) {
|
|
122
|
+
// Clean up unverified binary so the JS fallback is used instead
|
|
123
|
+
try { if (existsSync(binPath)) unlinkSync(binPath); } catch {}
|
|
117
124
|
// Graceful fallback - bundled JS CLI will be used instead
|
|
118
125
|
console.warn("⚠️ Binary download failed:", error.message);
|
|
119
126
|
console.warn(" Falling back to bundled JavaScript CLI (slower startup)");
|
package/src/deno.js
CHANGED
package/src/src/agent/factory.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
2
2
|
import type {
|
|
3
3
|
Agent,
|
|
4
4
|
AgentConfig,
|
|
5
|
+
AgentMiddleware,
|
|
5
6
|
AgentResponse,
|
|
6
7
|
AgentStreamResult,
|
|
7
8
|
Message,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
import { agentRegistry } from "./composition/index.js";
|
|
25
26
|
import { agentLogger } from "../utils/logger/logger.js";
|
|
26
27
|
import { createError, toError } from "../errors/veryfront-error.js";
|
|
28
|
+
import { COMMON_BLOCKED_PATTERNS, securityMiddleware } from "./middleware/security/validator.js";
|
|
27
29
|
import { withSpan } from "../observability/tracing/otlp-setup.js";
|
|
28
30
|
import { resolveConfiguredAgentModel } from "./runtime/model-resolution.js";
|
|
29
31
|
|
|
@@ -118,6 +120,8 @@ export function agent(config: AgentConfig): Agent {
|
|
|
118
120
|
}
|
|
119
121
|
: originalSystem;
|
|
120
122
|
|
|
123
|
+
const resolvedMiddleware = resolveSecurityMiddleware(config);
|
|
124
|
+
|
|
121
125
|
const platform = detectPlatform();
|
|
122
126
|
const compatibility = validatePlatformCompatibility(
|
|
123
127
|
{
|
|
@@ -148,6 +152,7 @@ export function agent(config: AgentConfig): Agent {
|
|
|
148
152
|
...publicConfig,
|
|
149
153
|
tools: mergedToolsConfig,
|
|
150
154
|
system: augmentedSystem,
|
|
155
|
+
middleware: resolvedMiddleware,
|
|
151
156
|
});
|
|
152
157
|
|
|
153
158
|
const agentInstance: Agent = {
|
|
@@ -258,7 +263,36 @@ export function agent(config: AgentConfig): Agent {
|
|
|
258
263
|
// Register on globalThis so compiled-binary runtime shim can delegate to the
|
|
259
264
|
// real factory. External temp-file modules can't import from the embedded
|
|
260
265
|
// binary FS, so they use globalThis bridges instead.
|
|
261
|
-
(dntShim.dntGlobalThis
|
|
266
|
+
if (!("__vfAgentFactory" in dntShim.dntGlobalThis)) {
|
|
267
|
+
Object.defineProperty(dntShim.dntGlobalThis, "__vfAgentFactory", {
|
|
268
|
+
value: agent,
|
|
269
|
+
writable: false,
|
|
270
|
+
enumerable: false,
|
|
271
|
+
configurable: false,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Resolve the middleware array for an agent, prepending security middleware
|
|
277
|
+
* unless explicitly opted out with `security: false`.
|
|
278
|
+
*/
|
|
279
|
+
export function resolveSecurityMiddleware(
|
|
280
|
+
config: Pick<AgentConfig, "security" | "middleware">,
|
|
281
|
+
): AgentMiddleware[] {
|
|
282
|
+
if (config.security === false) return config.middleware ?? [];
|
|
283
|
+
return [
|
|
284
|
+
securityMiddleware({
|
|
285
|
+
input: {
|
|
286
|
+
maxLength: 50_000,
|
|
287
|
+
blockedPatterns: COMMON_BLOCKED_PATTERNS.promptInjection,
|
|
288
|
+
},
|
|
289
|
+
output: {
|
|
290
|
+
filterPII: true,
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
...(config.middleware ?? []),
|
|
294
|
+
];
|
|
295
|
+
}
|
|
262
296
|
|
|
263
297
|
let agentIdCounter = 0;
|
|
264
298
|
|
package/src/src/agent/types.ts
CHANGED
|
@@ -78,6 +78,8 @@ export interface AgentConfig {
|
|
|
78
78
|
* and registers the skill tools.
|
|
79
79
|
*/
|
|
80
80
|
skills?: true | string[];
|
|
81
|
+
/** Set to false to disable the default security middleware */
|
|
82
|
+
security?: false;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
export type ResolvedAgentConfig = AgentConfig & { model: ModelString };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const BINARY_TAILWIND_PLUGIN_PACKAGES = [
|
|
2
|
+
"tailwindcss-animate@1.0.7",
|
|
3
|
+
"@tailwindcss/typography@0.5.19",
|
|
4
|
+
"@tailwindcss/forms@0.5.11",
|
|
5
|
+
"tailwind-scrollbar-hide@2.0.0",
|
|
6
|
+
"daisyui@5.5.14",
|
|
7
|
+
] as const;
|
|
8
|
+
|
|
9
|
+
export function getTailwindPluginBundleUrl(packageName: string): string {
|
|
10
|
+
return `https://esm.sh/${packageName}?bundle&external=tailwindcss&target=denonext`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getBinaryPluginBundleIncludes(): string[] {
|
|
14
|
+
return BINARY_TAILWIND_PLUGIN_PACKAGES.map(getTailwindPluginBundleUrl);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { BINARY_TAILWIND_PLUGIN_PACKAGES };
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
NETWORK_ERROR,
|
|
21
21
|
VeryfrontError,
|
|
22
22
|
} from "../../errors/index.js";
|
|
23
|
+
import { getTailwindPluginBundleUrl } from "../../build/binary-plugin-includes.js";
|
|
23
24
|
|
|
24
25
|
const logger = serverLogger.component("tailwind");
|
|
25
26
|
|
|
@@ -108,7 +109,7 @@ async function importBundledModule(code: string): Promise<unknown> {
|
|
|
108
109
|
* dynamic imports from URLs. Fetches bundled code, rewrites imports, loads via temp file.
|
|
109
110
|
*/
|
|
110
111
|
export async function loadModuleFromEsmSh(packageName: string): Promise<unknown> {
|
|
111
|
-
const stubUrl =
|
|
112
|
+
const stubUrl = getTailwindPluginBundleUrl(packageName);
|
|
112
113
|
logger.debug("Fetching esm.sh stub", { url: stubUrl });
|
|
113
114
|
|
|
114
115
|
const stubResponse = await dntShim.fetch(stubUrl);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
2
|
import { verifyControlPlaneJws } from "../channels/control-plane.js";
|
|
3
|
+
import { getHostEnv } from "../platform/compat/process.js";
|
|
3
4
|
import type { HandlerContext } from "../types/index.js";
|
|
4
5
|
import { serverLogger } from "../utils/index.js";
|
|
5
6
|
import { HTTP_INTERNAL_SERVER_ERROR } from "../utils/constants/index.js";
|
|
@@ -18,6 +19,14 @@ export class ControlPlaneRequestError extends Error {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
export function getControlPlaneVerificationPublicKey(ctx: HandlerContext): string | undefined {
|
|
23
|
+
// Project env overlays intentionally hide host secrets from request-scoped reads.
|
|
24
|
+
// Control-plane verification is framework-owned config, so it must fall back to
|
|
25
|
+
// the host environment when the runtime adapter env is overlay-aware.
|
|
26
|
+
return ctx.adapter.env.get("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY") ??
|
|
27
|
+
getHostEnv("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY");
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
export async function verifyControlPlaneRequest(
|
|
22
31
|
req: dntShim.Request,
|
|
23
32
|
ctx: HandlerContext,
|
|
@@ -27,7 +36,7 @@ export async function verifyControlPlaneRequest(
|
|
|
27
36
|
expectedSurface?: "studio" | "channels" | "a2a" | "mcp";
|
|
28
37
|
} = {},
|
|
29
38
|
) {
|
|
30
|
-
const publicKeyPem = ctx
|
|
39
|
+
const publicKeyPem = getControlPlaneVerificationPublicKey(ctx);
|
|
31
40
|
if (!publicKeyPem) {
|
|
32
41
|
throw new ControlPlaneRequestError(
|
|
33
42
|
HTTP_INTERNAL_SERVER_ERROR,
|
|
@@ -108,6 +108,7 @@ export abstract class BaseHandler implements Handler {
|
|
|
108
108
|
fn: () => Promise<T>,
|
|
109
109
|
options: { requireToken?: boolean } = {},
|
|
110
110
|
): Promise<T> {
|
|
111
|
+
const effectiveToken = ctx.proxyToken || ctx.adapter.env.get("VERYFRONT_API_TOKEN") || "";
|
|
111
112
|
const fsWrapper = ctx.adapter.fs as {
|
|
112
113
|
setRequestToken?: (t: string) => void;
|
|
113
114
|
setRequestBranch?: (b: string | null) => void;
|
|
@@ -130,7 +131,7 @@ export abstract class BaseHandler implements Handler {
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
const requireToken = options.requireToken ?? false;
|
|
133
|
-
if (!ctx.projectSlug || (requireToken && !
|
|
134
|
+
if (!ctx.projectSlug || (requireToken && !effectiveToken)) return fn();
|
|
134
135
|
|
|
135
136
|
if (fsWrapper.isMultiProjectMode?.()) {
|
|
136
137
|
const isProduction = (ctx.resolvedEnvironment ?? ctx.requestContext?.mode) === "production";
|
|
@@ -149,15 +150,15 @@ export abstract class BaseHandler implements Handler {
|
|
|
149
150
|
|
|
150
151
|
return fsWrapper.runWithContext!(
|
|
151
152
|
ctx.projectSlug,
|
|
152
|
-
|
|
153
|
+
effectiveToken,
|
|
153
154
|
fn,
|
|
154
155
|
ctx.projectId,
|
|
155
156
|
{ productionMode: isProduction, releaseId: ctx.releaseId, branch },
|
|
156
157
|
);
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
if (typeof fsWrapper.setRequestToken === "function" &&
|
|
160
|
-
fsWrapper.setRequestToken(
|
|
160
|
+
if (typeof fsWrapper.setRequestToken === "function" && effectiveToken) {
|
|
161
|
+
fsWrapper.setRequestToken(effectiveToken);
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
return runWithCacheBatching(fn);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
executeChannelInvoke,
|
|
9
9
|
verifyDispatchJws,
|
|
10
10
|
} from "../../../channels/invoke.js";
|
|
11
|
+
import { getControlPlaneVerificationPublicKey } from "../../../internal-agents/control-plane-auth.js";
|
|
11
12
|
import {
|
|
12
13
|
HTTP_INTERNAL_SERVER_ERROR,
|
|
13
14
|
PRIORITY_MEDIUM_API,
|
|
@@ -37,7 +38,7 @@ export class ChannelInvokeHandler extends BaseHandler {
|
|
|
37
38
|
.withCORS(req, ctx.securityConfig?.cors)
|
|
38
39
|
.withSecurity(ctx.securityConfig ?? undefined, req);
|
|
39
40
|
|
|
40
|
-
const publicKeyPem = ctx
|
|
41
|
+
const publicKeyPem = getControlPlaneVerificationPublicKey(ctx);
|
|
41
42
|
if (!publicKeyPem) {
|
|
42
43
|
this.logWarn("Missing CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY for channel invoke endpoint");
|
|
43
44
|
return this.respond(
|