veryfront 0.1.511 → 0.1.513
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 +46 -5
- package/esm/deno.d.ts +0 -4
- package/esm/deno.js +1 -5
- package/esm/src/agent/agent-service-config.d.ts +54 -0
- package/esm/src/agent/agent-service-config.d.ts.map +1 -1
- package/esm/src/agent/agent-service-config.js +24 -0
- package/esm/src/agent/agent-service-registration.d.ts +110 -0
- package/esm/src/agent/agent-service-registration.d.ts.map +1 -0
- package/esm/src/agent/agent-service-registration.js +228 -0
- package/esm/src/agent/agent-service-runtime.d.ts +3 -1
- package/esm/src/agent/agent-service-runtime.d.ts.map +1 -1
- package/esm/src/agent/agent-service-runtime.js +17 -2
- package/esm/src/agent/index.d.ts +1 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/agent/runtime-agent-definition-files.d.ts +5 -0
- package/esm/src/agent/runtime-agent-definition-files.d.ts.map +1 -1
- package/esm/src/agent/runtime-agent-definition-files.js +37 -9
- package/esm/src/agent/veryfront-cloud-agent-service.d.ts +6 -1
- package/esm/src/agent/veryfront-cloud-agent-service.d.ts.map +1 -1
- package/esm/src/agent/veryfront-cloud-agent-service.js +144 -37
- package/esm/src/oauth/providers/base.d.ts +10 -0
- package/esm/src/oauth/providers/base.d.ts.map +1 -1
- package/esm/src/oauth/providers/base.js +32 -1
- 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/src/deno.js +1 -5
- package/src/src/agent/agent-service-config.ts +23 -0
- package/src/src/agent/agent-service-registration.ts +320 -0
- package/src/src/agent/agent-service-runtime.ts +25 -2
- package/src/src/agent/index.ts +15 -0
- package/src/src/agent/runtime-agent-definition-files.ts +52 -9
- package/src/src/agent/veryfront-cloud-agent-service.ts +194 -42
- package/src/src/oauth/providers/base.ts +33 -1
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -40,6 +40,16 @@ export declare class OAuthService extends OAuthProvider {
|
|
|
40
40
|
* tokens. See VULN-AUTH-2.
|
|
41
41
|
*/
|
|
42
42
|
getAccessToken(userId: string): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve `endpoint` against `apiBaseUrl`, validating that absolute URLs
|
|
45
|
+
* share the configured origin.
|
|
46
|
+
*
|
|
47
|
+
* Without this check, a caller that forwards user-controlled data as
|
|
48
|
+
* `endpoint` could cause `fetch()` to issue requests to arbitrary hosts
|
|
49
|
+
* (including cloud metadata services and internal infrastructure). See
|
|
50
|
+
* SEC-003 in the security audit.
|
|
51
|
+
*/
|
|
52
|
+
private resolveEndpointUrl;
|
|
43
53
|
fetch<T>(userId: string, endpoint: string, options?: RequestInit): Promise<T>;
|
|
44
54
|
}
|
|
45
55
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/providers/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACX,MAAM,aAAa,CAAC;AAUrB,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAuB5D,qBAAa,aAAa;IACxB,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACtC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC;gBAEnB,MAAM,EAAE,mBAAmB,EAAE,SAAS,GAAE,SAAkB;IAKtE,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,YAAY,IAAI,OAAO;IAIjB,sBAAsB,CAC1B,OAAO,GAAE,uBAAuB,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACnE,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IA0C9C,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,kBAAkB;YAyCZ,gBAAgB;IAe9B,OAAO,CAAC,4BAA4B;YAQtB,aAAa;IA+BrB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA+BzE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAwBjE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBnD;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,SAAS,CAAC,aAAa,EAAE,kBAAkB,CAAC;IAC5C,SAAS,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;gBAEtB,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS;IAMtF,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEQ,sBAAsB,CAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IAO9C;;;;;;OAMG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/providers/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACX,MAAM,aAAa,CAAC;AAUrB,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAuB5D,qBAAa,aAAa;IACxB,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACtC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC;gBAEnB,MAAM,EAAE,mBAAmB,EAAE,SAAS,GAAE,SAAkB;IAKtE,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,YAAY,IAAI,OAAO;IAIjB,sBAAsB,CAC1B,OAAO,GAAE,uBAAuB,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACnE,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IA0C9C,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,kBAAkB;YAyCZ,gBAAgB;IAe9B,OAAO,CAAC,4BAA4B;YAQtB,aAAa;IA+BrB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA+BzE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAwBjE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBnD;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,SAAS,CAAC,aAAa,EAAE,kBAAkB,CAAC;IAC5C,SAAS,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;gBAEtB,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS;IAMtF,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEQ,sBAAsB,CAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IAO9C;;;;;;OAMG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmB5D;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAuBpB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;CA4BxF"}
|
|
@@ -249,6 +249,37 @@ export class OAuthService extends OAuthProvider {
|
|
|
249
249
|
await this.tokenStore.setTokens(this.serviceId, userId, result.tokens);
|
|
250
250
|
return result.tokens.accessToken;
|
|
251
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Resolve `endpoint` against `apiBaseUrl`, validating that absolute URLs
|
|
254
|
+
* share the configured origin.
|
|
255
|
+
*
|
|
256
|
+
* Without this check, a caller that forwards user-controlled data as
|
|
257
|
+
* `endpoint` could cause `fetch()` to issue requests to arbitrary hosts
|
|
258
|
+
* (including cloud metadata services and internal infrastructure). See
|
|
259
|
+
* SEC-003 in the security audit.
|
|
260
|
+
*/
|
|
261
|
+
resolveEndpointUrl(endpoint) {
|
|
262
|
+
if (!endpoint.startsWith("http")) {
|
|
263
|
+
return `${this.apiBaseUrl}${endpoint}`;
|
|
264
|
+
}
|
|
265
|
+
let target;
|
|
266
|
+
let allowed;
|
|
267
|
+
try {
|
|
268
|
+
target = new URL(endpoint);
|
|
269
|
+
allowed = new URL(this.apiBaseUrl);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
throw INVALID_ARGUMENT.create({
|
|
273
|
+
detail: `Invalid OAuth endpoint URL`,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (target.origin !== allowed.origin) {
|
|
277
|
+
throw INVALID_ARGUMENT.create({
|
|
278
|
+
detail: `OAuth endpoint origin ${target.origin} does not match configured ${allowed.origin}`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return endpoint;
|
|
282
|
+
}
|
|
252
283
|
async fetch(userId, endpoint, options = {}) {
|
|
253
284
|
const token = await this.getAccessToken(userId);
|
|
254
285
|
if (!token) {
|
|
@@ -256,7 +287,7 @@ export class OAuthService extends OAuthProvider {
|
|
|
256
287
|
detail: `Not authenticated with ${this.serviceConfig.displayName}`,
|
|
257
288
|
});
|
|
258
289
|
}
|
|
259
|
-
const url =
|
|
290
|
+
const url = this.resolveEndpointUrl(endpoint);
|
|
260
291
|
const response = await fetch(url, {
|
|
261
292
|
...options,
|
|
262
293
|
headers: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.513";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.513",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"workspace": [
|
|
@@ -284,12 +284,8 @@ export default {
|
|
|
284
284
|
"@opentelemetry/sdk-node": "npm:@opentelemetry/sdk-node@0.217.0",
|
|
285
285
|
"@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@2.7.1",
|
|
286
286
|
"@mdx-js/mdx": "npm:@mdx-js/mdx@3.1.1",
|
|
287
|
-
"@babel/parser": "npm:@babel/parser@7.29.2",
|
|
288
287
|
"gray-matter": "npm:gray-matter@4.0.3",
|
|
289
288
|
"zod": "npm:zod@4.3.6",
|
|
290
|
-
"mdast": "npm:@types/mdast@4.0.3",
|
|
291
|
-
"hast": "npm:@types/hast@3.0.3",
|
|
292
|
-
"unist": "npm:@types/unist@3.0.2",
|
|
293
289
|
"unified": "npm:unified@11.0.5",
|
|
294
290
|
"class-variance-authority": "npm:class-variance-authority@0.7.1",
|
|
295
291
|
"tailwind-merge": "npm:tailwind-merge@3.5.0",
|
|
@@ -10,8 +10,24 @@ function splitAllowedOrigins(value: string): string[] {
|
|
|
10
10
|
|
|
11
11
|
const booleanFlagSchema = z.string().default("false").transform(parseBooleanFlag);
|
|
12
12
|
|
|
13
|
+
const agentServiceRegistrationModeInputSchema = z
|
|
14
|
+
.enum(["auto", "enabled", "disabled", "true", "false"])
|
|
15
|
+
.default("auto")
|
|
16
|
+
.transform((value) => {
|
|
17
|
+
if (value === "true") return "enabled";
|
|
18
|
+
if (value === "false") return "disabled";
|
|
19
|
+
return value;
|
|
20
|
+
});
|
|
21
|
+
|
|
13
22
|
export const agentServiceConfigSchema = z.object({
|
|
14
23
|
VERYFRONT_API_URL: z.string().url().default("https://api.veryfront.com"),
|
|
24
|
+
VERYFRONT_API_TOKEN: z.string().min(1).optional(),
|
|
25
|
+
VERYFRONT_PROJECT_ID: z.string().min(1).optional(),
|
|
26
|
+
VERYFRONT_AGENT_SERVICE_URL: z.string().url().optional(),
|
|
27
|
+
VERYFRONT_AGENT_SERVICE_KEY: z.string().min(1).max(128).optional(),
|
|
28
|
+
VERYFRONT_AGENT_SERVICE_REGISTRATION: agentServiceRegistrationModeInputSchema,
|
|
29
|
+
VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS: z.coerce.number().positive().default(30_000),
|
|
30
|
+
VERYFRONT_AGENT_SERVICE_REGION: z.string().min(1).max(128).optional(),
|
|
15
31
|
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
|
|
16
32
|
PORT: z.coerce.number().default(3001),
|
|
17
33
|
OAUTH_PUBLIC_KEY: z.string().optional(),
|
|
@@ -24,6 +40,13 @@ export const agentServiceConfigSchema = z.object({
|
|
|
24
40
|
}).transform((env) => ({
|
|
25
41
|
VERYFRONT_API_URL: env.VERYFRONT_API_URL,
|
|
26
42
|
VERYFRONT_MCP_URL: `${env.VERYFRONT_API_URL}/mcp`,
|
|
43
|
+
VERYFRONT_API_TOKEN: env.VERYFRONT_API_TOKEN,
|
|
44
|
+
VERYFRONT_PROJECT_ID: env.VERYFRONT_PROJECT_ID,
|
|
45
|
+
VERYFRONT_AGENT_SERVICE_URL: env.VERYFRONT_AGENT_SERVICE_URL,
|
|
46
|
+
VERYFRONT_AGENT_SERVICE_KEY: env.VERYFRONT_AGENT_SERVICE_KEY,
|
|
47
|
+
VERYFRONT_AGENT_SERVICE_REGISTRATION: env.VERYFRONT_AGENT_SERVICE_REGISTRATION,
|
|
48
|
+
VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS: env.VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS,
|
|
49
|
+
VERYFRONT_AGENT_SERVICE_REGION: env.VERYFRONT_AGENT_SERVICE_REGION,
|
|
27
50
|
VERYFRONT_STUDIO_MCP_URL: env.VERYFRONT_STUDIO_MCP_URL,
|
|
28
51
|
VERYFRONT_ENABLE_DURABLE_INVOKE_AGENT: env.VERYFRONT_ENABLE_DURABLE_INVOKE_AGENT,
|
|
29
52
|
VERYFRONT_ENABLE_DURABLE_TASK: env.VERYFRONT_ENABLE_DURABLE_TASK,
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export const agentServiceRegistrationModeSchema = z.enum([
|
|
5
|
+
"auto",
|
|
6
|
+
"enabled",
|
|
7
|
+
"disabled",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export const agentServiceRegistrationConfigSchema = z.object({
|
|
11
|
+
VERYFRONT_API_URL: z.string().url(),
|
|
12
|
+
VERYFRONT_API_TOKEN: z.string().min(1).optional(),
|
|
13
|
+
VERYFRONT_PROJECT_ID: z.string().min(1).optional(),
|
|
14
|
+
VERYFRONT_AGENT_SERVICE_URL: z.string().url().optional(),
|
|
15
|
+
VERYFRONT_AGENT_SERVICE_KEY: z.string().min(1).max(128).optional(),
|
|
16
|
+
VERYFRONT_AGENT_SERVICE_REGISTRATION: agentServiceRegistrationModeSchema,
|
|
17
|
+
VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS: z.number().positive(),
|
|
18
|
+
VERYFRONT_AGENT_SERVICE_REGION: z.string().min(1).max(128).optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const resolvedAgentServiceRegistrationInputSchema = z.object({
|
|
22
|
+
apiUrl: z.string().url(),
|
|
23
|
+
authToken: z.string().min(1),
|
|
24
|
+
serviceName: z.string().min(1).max(128),
|
|
25
|
+
serviceKey: z.string().min(1).max(128),
|
|
26
|
+
scopeKind: z.enum(["global", "project"]),
|
|
27
|
+
projectId: z.string().min(1).optional(),
|
|
28
|
+
agentId: z.string().min(1).max(128).optional(),
|
|
29
|
+
baseUrl: z.string().url(),
|
|
30
|
+
invokeUrl: z.string().url(),
|
|
31
|
+
version: z.string().min(1).max(128).optional(),
|
|
32
|
+
runtime: z.string().min(1).max(128).optional(),
|
|
33
|
+
region: z.string().min(1).max(128).optional(),
|
|
34
|
+
heartbeatIntervalMs: z.number().positive(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const agentPushRuntimeServiceRestSchema = z.object({
|
|
38
|
+
id: z.string().uuid(),
|
|
39
|
+
service_name: z.string(),
|
|
40
|
+
service_key: z.string(),
|
|
41
|
+
scope_kind: z.enum(["global", "project"]),
|
|
42
|
+
scope_key: z.string(),
|
|
43
|
+
project_id: z.string().nullable(),
|
|
44
|
+
agent_id: z.string().nullable(),
|
|
45
|
+
base_url: z.string().url(),
|
|
46
|
+
invoke_url: z.string().url(),
|
|
47
|
+
status: z.enum(["active", "disabled"]),
|
|
48
|
+
capabilities: z.unknown().nullable(),
|
|
49
|
+
metadata: z.unknown().nullable(),
|
|
50
|
+
version: z.string().nullable(),
|
|
51
|
+
runtime: z.string().nullable(),
|
|
52
|
+
region: z.string().nullable(),
|
|
53
|
+
last_heartbeat_at: z.string().nullable(),
|
|
54
|
+
created_at: z.string(),
|
|
55
|
+
updated_at: z.string(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const agentPushRuntimeServiceResponseSchema = z.object({
|
|
59
|
+
service: agentPushRuntimeServiceRestSchema,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const registerAgentPushRuntimeServiceRequestSchema = z.object({
|
|
63
|
+
service_name: z.string().min(1).max(128),
|
|
64
|
+
service_key: z.string().min(1).max(128),
|
|
65
|
+
scope_kind: z.enum(["global", "project"]),
|
|
66
|
+
project_id: z.string().optional(),
|
|
67
|
+
agent_id: z.string().optional(),
|
|
68
|
+
base_url: z.string().url(),
|
|
69
|
+
invoke_url: z.string().url(),
|
|
70
|
+
version: z.string().optional(),
|
|
71
|
+
runtime: z.string().optional(),
|
|
72
|
+
region: z.string().optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export type AgentServiceRegistrationMode = z.infer<typeof agentServiceRegistrationModeSchema>;
|
|
76
|
+
export type AgentServiceRegistrationConfig = z.infer<typeof agentServiceRegistrationConfigSchema>;
|
|
77
|
+
export type ResolvedAgentServiceRegistrationInput = z.infer<
|
|
78
|
+
typeof resolvedAgentServiceRegistrationInputSchema
|
|
79
|
+
>;
|
|
80
|
+
export type AgentPushRuntimeServiceRest = z.infer<typeof agentPushRuntimeServiceRestSchema>;
|
|
81
|
+
export type RegisterAgentPushRuntimeServiceRequest = z.infer<
|
|
82
|
+
typeof registerAgentPushRuntimeServiceRequestSchema
|
|
83
|
+
>;
|
|
84
|
+
|
|
85
|
+
export type AgentServiceRegistrationLogger = {
|
|
86
|
+
info?: (message: string, metadata?: Record<string, unknown>) => void;
|
|
87
|
+
warn?: (message: string, metadata?: Record<string, unknown>) => void;
|
|
88
|
+
error?: (message: string, metadata?: Record<string, unknown>) => void;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type ResolveAgentServiceRegistrationInputOptions = {
|
|
92
|
+
config: AgentServiceRegistrationConfig;
|
|
93
|
+
serviceName: string;
|
|
94
|
+
agentId?: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
runtime?: string;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type AgentServiceRegistrationLifecycle = {
|
|
100
|
+
serviceId: string;
|
|
101
|
+
service: AgentPushRuntimeServiceRest;
|
|
102
|
+
heartbeat: () => Promise<void>;
|
|
103
|
+
stop: () => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type CreateAgentServiceRegistrationLifecycleOptions =
|
|
107
|
+
& ResolvedAgentServiceRegistrationInput
|
|
108
|
+
& {
|
|
109
|
+
fetch?: typeof globalThis.fetch;
|
|
110
|
+
logger?: AgentServiceRegistrationLogger;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function normalizeBaseUrl(value: string): string {
|
|
114
|
+
const url = new URL(value);
|
|
115
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
116
|
+
url.search = "";
|
|
117
|
+
url.hash = "";
|
|
118
|
+
return url.toString().replace(/\/$/, "");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function defaultInvokeUrl(baseUrl: string): string {
|
|
122
|
+
return new URL("/api/runs", baseUrl).toString();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getRegistrationEndpoint(apiUrl: string): string {
|
|
126
|
+
return new URL("/agent-runtimes/push-services", apiUrl).toString();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getHeartbeatEndpoint(apiUrl: string, serviceId: string): string {
|
|
130
|
+
return new URL(`/agent-runtimes/push-services/${serviceId}/heartbeat`, apiUrl).toString();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getErrorMessage(error: unknown): string {
|
|
134
|
+
return error instanceof Error ? error.message : String(error);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function stableServiceKey(input: {
|
|
138
|
+
serviceName: string;
|
|
139
|
+
agentId?: string;
|
|
140
|
+
baseUrl: string;
|
|
141
|
+
scopeKind: "global" | "project";
|
|
142
|
+
projectId?: string;
|
|
143
|
+
}): Promise<string> {
|
|
144
|
+
const keySource = [
|
|
145
|
+
input.serviceName,
|
|
146
|
+
input.agentId ?? "default",
|
|
147
|
+
input.scopeKind,
|
|
148
|
+
input.projectId ?? "global",
|
|
149
|
+
input.baseUrl,
|
|
150
|
+
].join("|");
|
|
151
|
+
const digest = await dntShim.crypto.subtle.digest("SHA-256", new TextEncoder().encode(keySource));
|
|
152
|
+
const hash = Array.from(new Uint8Array(digest))
|
|
153
|
+
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
154
|
+
.join("")
|
|
155
|
+
.slice(0, 32);
|
|
156
|
+
return `${input.serviceName}:${hash}`.slice(0, 128);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function requireExplicitRegistrationValue(
|
|
160
|
+
value: string | undefined,
|
|
161
|
+
envName: string,
|
|
162
|
+
): string {
|
|
163
|
+
if (!value) {
|
|
164
|
+
throw new Error(`${envName} is required when VERYFRONT_AGENT_SERVICE_REGISTRATION=enabled`);
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function resolveAgentServiceRegistrationInput(
|
|
170
|
+
options: ResolveAgentServiceRegistrationInputOptions,
|
|
171
|
+
): Promise<ResolvedAgentServiceRegistrationInput | null> {
|
|
172
|
+
const config = agentServiceRegistrationConfigSchema.parse(options.config);
|
|
173
|
+
const enabled = config.VERYFRONT_AGENT_SERVICE_REGISTRATION === "enabled";
|
|
174
|
+
const token = enabled
|
|
175
|
+
? requireExplicitRegistrationValue(config.VERYFRONT_API_TOKEN, "VERYFRONT_API_TOKEN")
|
|
176
|
+
: config.VERYFRONT_API_TOKEN;
|
|
177
|
+
const serviceUrl = enabled
|
|
178
|
+
? requireExplicitRegistrationValue(
|
|
179
|
+
config.VERYFRONT_AGENT_SERVICE_URL,
|
|
180
|
+
"VERYFRONT_AGENT_SERVICE_URL",
|
|
181
|
+
)
|
|
182
|
+
: config.VERYFRONT_AGENT_SERVICE_URL;
|
|
183
|
+
|
|
184
|
+
if (config.VERYFRONT_AGENT_SERVICE_REGISTRATION === "disabled") {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
if (!token || !serviceUrl) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const scopeKind = config.VERYFRONT_PROJECT_ID ? "project" : "global";
|
|
192
|
+
const baseUrl = normalizeBaseUrl(serviceUrl);
|
|
193
|
+
const serviceKey = config.VERYFRONT_AGENT_SERVICE_KEY ?? await stableServiceKey({
|
|
194
|
+
serviceName: options.serviceName,
|
|
195
|
+
agentId: options.agentId,
|
|
196
|
+
baseUrl,
|
|
197
|
+
scopeKind,
|
|
198
|
+
projectId: config.VERYFRONT_PROJECT_ID,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
return resolvedAgentServiceRegistrationInputSchema.parse({
|
|
202
|
+
apiUrl: config.VERYFRONT_API_URL,
|
|
203
|
+
authToken: token,
|
|
204
|
+
serviceName: options.serviceName,
|
|
205
|
+
serviceKey,
|
|
206
|
+
scopeKind,
|
|
207
|
+
projectId: config.VERYFRONT_PROJECT_ID,
|
|
208
|
+
agentId: options.agentId,
|
|
209
|
+
baseUrl,
|
|
210
|
+
invokeUrl: defaultInvokeUrl(baseUrl),
|
|
211
|
+
version: options.version,
|
|
212
|
+
runtime: options.runtime,
|
|
213
|
+
region: config.VERYFRONT_AGENT_SERVICE_REGION,
|
|
214
|
+
heartbeatIntervalMs: config.VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function readAgentPushRuntimeServiceResponse(
|
|
219
|
+
response: Response,
|
|
220
|
+
): Promise<AgentPushRuntimeServiceRest> {
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
throw new Error(`Agent runtime registration request failed with HTTP ${response.status}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const parsed = agentPushRuntimeServiceResponseSchema.parse(await response.json());
|
|
226
|
+
return parsed.service;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function createHeaders(authToken: string): Headers {
|
|
230
|
+
const headers = new Headers();
|
|
231
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
232
|
+
headers.set("Content-Type", "application/json");
|
|
233
|
+
return headers;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function buildRegistrationRequest(
|
|
237
|
+
input: ResolvedAgentServiceRegistrationInput,
|
|
238
|
+
): RegisterAgentPushRuntimeServiceRequest {
|
|
239
|
+
return registerAgentPushRuntimeServiceRequestSchema.parse({
|
|
240
|
+
service_name: input.serviceName,
|
|
241
|
+
service_key: input.serviceKey,
|
|
242
|
+
scope_kind: input.scopeKind,
|
|
243
|
+
project_id: input.projectId,
|
|
244
|
+
agent_id: input.agentId,
|
|
245
|
+
base_url: input.baseUrl,
|
|
246
|
+
invoke_url: input.invokeUrl,
|
|
247
|
+
version: input.version,
|
|
248
|
+
runtime: input.runtime,
|
|
249
|
+
region: input.region,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function registerAgentPushRuntimeService(
|
|
254
|
+
input: ResolvedAgentServiceRegistrationInput,
|
|
255
|
+
fetchImpl: typeof globalThis.fetch,
|
|
256
|
+
): Promise<AgentPushRuntimeServiceRest> {
|
|
257
|
+
const response = await fetchImpl(getRegistrationEndpoint(input.apiUrl), {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: createHeaders(input.authToken),
|
|
260
|
+
body: JSON.stringify(buildRegistrationRequest(input)),
|
|
261
|
+
});
|
|
262
|
+
return await readAgentPushRuntimeServiceResponse(response);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function heartbeatAgentPushRuntimeService(
|
|
266
|
+
input: { apiUrl: string; authToken: string; serviceId: string },
|
|
267
|
+
fetchImpl: typeof globalThis.fetch,
|
|
268
|
+
): Promise<AgentPushRuntimeServiceRest> {
|
|
269
|
+
const response = await fetchImpl(getHeartbeatEndpoint(input.apiUrl, input.serviceId), {
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers: createHeaders(input.authToken),
|
|
272
|
+
});
|
|
273
|
+
return await readAgentPushRuntimeServiceResponse(response);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export async function createAgentServiceRegistrationLifecycle(
|
|
277
|
+
options: CreateAgentServiceRegistrationLifecycleOptions,
|
|
278
|
+
): Promise<AgentServiceRegistrationLifecycle> {
|
|
279
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
280
|
+
const input = resolvedAgentServiceRegistrationInputSchema.parse(options);
|
|
281
|
+
const service = await registerAgentPushRuntimeService(input, fetchImpl);
|
|
282
|
+
let stopped = false;
|
|
283
|
+
|
|
284
|
+
const heartbeat = async () => {
|
|
285
|
+
if (stopped) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
await heartbeatAgentPushRuntimeService({
|
|
289
|
+
apiUrl: input.apiUrl,
|
|
290
|
+
authToken: input.authToken,
|
|
291
|
+
serviceId: service.id,
|
|
292
|
+
}, fetchImpl);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const interval = dntShim.setInterval(() => {
|
|
296
|
+
void heartbeat().catch((error: unknown) => {
|
|
297
|
+
options.logger?.warn?.("Agent service heartbeat failed", {
|
|
298
|
+
serviceId: service.id,
|
|
299
|
+
error: getErrorMessage(error),
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}, input.heartbeatIntervalMs);
|
|
303
|
+
|
|
304
|
+
options.logger?.info?.("Agent service registered with control plane", {
|
|
305
|
+
serviceId: service.id,
|
|
306
|
+
serviceName: service.service_name,
|
|
307
|
+
scopeKind: service.scope_kind,
|
|
308
|
+
projectId: service.project_id,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
serviceId: service.id,
|
|
313
|
+
service,
|
|
314
|
+
heartbeat,
|
|
315
|
+
stop: () => {
|
|
316
|
+
stopped = true;
|
|
317
|
+
clearInterval(interval);
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
@@ -28,6 +28,7 @@ import type { AgUiResumeValue } from "./ag-ui-tool-shared.js";
|
|
|
28
28
|
import type { RuntimeAgentMarkdownDefinition } from "./runtime-agent-definition.js";
|
|
29
29
|
import {
|
|
30
30
|
type AgentServiceServer,
|
|
31
|
+
type AgentServiceServerLifecycle,
|
|
31
32
|
type NodeAgentServiceServer,
|
|
32
33
|
startAgentServiceServer,
|
|
33
34
|
startNodeAgentServiceServer,
|
|
@@ -110,6 +111,7 @@ export type StartNodeHostedAgentServiceOptions<
|
|
|
110
111
|
bindAddress?: string;
|
|
111
112
|
hardShutdownTimeoutMs?: number;
|
|
112
113
|
signals?: readonly NodeJS.Signals[];
|
|
114
|
+
lifecycle?: AgentServiceServerLifecycle;
|
|
113
115
|
};
|
|
114
116
|
|
|
115
117
|
export type StartNodeAgentServiceOptions<
|
|
@@ -124,6 +126,7 @@ export type StartAgentServiceRuntimeOptions<
|
|
|
124
126
|
bindAddress?: string;
|
|
125
127
|
hardShutdownTimeoutMs?: number;
|
|
126
128
|
signals?: readonly NodeJS.Signals[];
|
|
129
|
+
lifecycle?: AgentServiceServerLifecycle;
|
|
127
130
|
};
|
|
128
131
|
|
|
129
132
|
export type StartNodeHostedAgentServiceResult<
|
|
@@ -152,6 +155,26 @@ function defaultTrace<TResult>(
|
|
|
152
155
|
return operation();
|
|
153
156
|
}
|
|
154
157
|
|
|
158
|
+
function combineAgentServiceLifecycle(
|
|
159
|
+
primary: AgentServiceServerLifecycle,
|
|
160
|
+
secondary: AgentServiceServerLifecycle | undefined,
|
|
161
|
+
): AgentServiceServerLifecycle {
|
|
162
|
+
if (!secondary) {
|
|
163
|
+
return primary;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
setShuttingDown: () => {
|
|
168
|
+
primary.setShuttingDown?.();
|
|
169
|
+
secondary.setShuttingDown?.();
|
|
170
|
+
},
|
|
171
|
+
stop: async () => {
|
|
172
|
+
await primary.stop?.();
|
|
173
|
+
await secondary.stop?.();
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
155
178
|
export function createAgentServiceRuntime<
|
|
156
179
|
TExecution extends object,
|
|
157
180
|
TConfig extends AgentServiceRuntimeConfig = AgentServiceRuntimeConfig,
|
|
@@ -232,7 +255,7 @@ export async function startNodeAgentService<
|
|
|
232
255
|
const nodeServer = await startNodeAgentServiceServer({
|
|
233
256
|
runtime: bundle.runtime,
|
|
234
257
|
serviceName: options.serviceName,
|
|
235
|
-
lifecycle: bundle.lifecycle,
|
|
258
|
+
lifecycle: combineAgentServiceLifecycle(bundle.lifecycle, options.lifecycle),
|
|
236
259
|
port: bundle.config.PORT,
|
|
237
260
|
bindAddress: options.bindAddress,
|
|
238
261
|
signals: options.signals,
|
|
@@ -265,7 +288,7 @@ export async function startAgentServiceRuntime<
|
|
|
265
288
|
const server = await startAgentServiceServer({
|
|
266
289
|
runtime: bundle.runtime,
|
|
267
290
|
serviceName: options.serviceName,
|
|
268
|
-
lifecycle: bundle.lifecycle,
|
|
291
|
+
lifecycle: combineAgentServiceLifecycle(bundle.lifecycle, options.lifecycle),
|
|
269
292
|
port: bundle.config.PORT,
|
|
270
293
|
bindAddress: options.bindAddress,
|
|
271
294
|
signals: options.signals,
|
package/src/src/agent/index.ts
CHANGED
|
@@ -347,6 +347,21 @@ export {
|
|
|
347
347
|
veryfrontMcpServer,
|
|
348
348
|
type VeryfrontMcpServerKind,
|
|
349
349
|
} from "./veryfront-cloud-agent-service.js";
|
|
350
|
+
export {
|
|
351
|
+
type AgentPushRuntimeServiceRest,
|
|
352
|
+
type AgentServiceRegistrationConfig,
|
|
353
|
+
agentServiceRegistrationConfigSchema,
|
|
354
|
+
type AgentServiceRegistrationLifecycle,
|
|
355
|
+
type AgentServiceRegistrationLogger,
|
|
356
|
+
type AgentServiceRegistrationMode,
|
|
357
|
+
createAgentServiceRegistrationLifecycle,
|
|
358
|
+
type CreateAgentServiceRegistrationLifecycleOptions,
|
|
359
|
+
type RegisterAgentPushRuntimeServiceRequest,
|
|
360
|
+
resolveAgentServiceRegistrationInput,
|
|
361
|
+
type ResolveAgentServiceRegistrationInputOptions,
|
|
362
|
+
type ResolvedAgentServiceRegistrationInput,
|
|
363
|
+
resolvedAgentServiceRegistrationInputSchema,
|
|
364
|
+
} from "./agent-service-registration.js";
|
|
350
365
|
export {
|
|
351
366
|
type AgentServiceConfig,
|
|
352
367
|
type AgentServiceConfigInput,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { basename, resolve } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import {
|
|
@@ -19,6 +19,14 @@ export type ResolveRuntimeAgentDefinitionsDirInput = z.infer<
|
|
|
19
19
|
typeof resolveRuntimeAgentDefinitionsDirInputSchema
|
|
20
20
|
>;
|
|
21
21
|
|
|
22
|
+
export const listRuntimeAgentMarkdownDefinitionIdsInputSchema = z.object({
|
|
23
|
+
baseDir: z.string().min(1),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type ListRuntimeAgentMarkdownDefinitionIdsInput = z.infer<
|
|
27
|
+
typeof listRuntimeAgentMarkdownDefinitionIdsInputSchema
|
|
28
|
+
>;
|
|
29
|
+
|
|
22
30
|
export const loadRuntimeAgentMarkdownDefinitionFromFileInputSchema = z.object({
|
|
23
31
|
agentsDir: z.string().min(1),
|
|
24
32
|
id: runtimeAgentDefinitionFileIdSchema,
|
|
@@ -40,27 +48,62 @@ function hasRuntimeAgentDefinitionFile(path: string, fileName: string): boolean
|
|
|
40
48
|
return existsSync(resolve(path, fileName));
|
|
41
49
|
}
|
|
42
50
|
|
|
51
|
+
function getRuntimeAgentDefinitionsDirCandidates(baseDir: string): string[] {
|
|
52
|
+
const firstCandidate = resolve(baseDir, "agents");
|
|
53
|
+
const sourceLayoutCandidate = resolve(baseDir, "../agents");
|
|
54
|
+
const candidates = [
|
|
55
|
+
firstCandidate,
|
|
56
|
+
sourceLayoutCandidate,
|
|
57
|
+
resolve(baseDir, "../../agents"),
|
|
58
|
+
resolve(baseDir, "../../../agents"),
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
return [...new Set(candidates)];
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
export function resolveRuntimeAgentDefinitionsDir(
|
|
44
65
|
input: ResolveRuntimeAgentDefinitionsDirInput,
|
|
45
66
|
): string {
|
|
46
67
|
const parsedInput = resolveRuntimeAgentDefinitionsDirInputSchema.parse(input);
|
|
47
68
|
const fileName = getRuntimeAgentDefinitionFileName(parsedInput);
|
|
48
|
-
const
|
|
69
|
+
const candidates = getRuntimeAgentDefinitionsDirCandidates(parsedInput.baseDir);
|
|
49
70
|
const sourceLayoutCandidate = resolve(parsedInput.baseDir, "../agents");
|
|
50
|
-
const candidates = [
|
|
51
|
-
firstCandidate,
|
|
52
|
-
sourceLayoutCandidate,
|
|
53
|
-
resolve(parsedInput.baseDir, "../../agents"),
|
|
54
|
-
resolve(parsedInput.baseDir, "../../../agents"),
|
|
55
|
-
];
|
|
56
71
|
const fallbackCandidate = basename(parsedInput.baseDir) === "src"
|
|
57
72
|
? sourceLayoutCandidate
|
|
58
|
-
:
|
|
73
|
+
: resolve(parsedInput.baseDir, "agents");
|
|
59
74
|
|
|
60
75
|
return candidates.find((candidate) => hasRuntimeAgentDefinitionFile(candidate, fileName)) ??
|
|
61
76
|
fallbackCandidate;
|
|
62
77
|
}
|
|
63
78
|
|
|
79
|
+
export function listRuntimeAgentMarkdownDefinitionIds(
|
|
80
|
+
input: ListRuntimeAgentMarkdownDefinitionIdsInput,
|
|
81
|
+
): string[] {
|
|
82
|
+
const parsedInput = listRuntimeAgentMarkdownDefinitionIdsInputSchema.parse(input);
|
|
83
|
+
const ids = new Set<string>();
|
|
84
|
+
|
|
85
|
+
for (const dir of getRuntimeAgentDefinitionsDirCandidates(parsedInput.baseDir)) {
|
|
86
|
+
if (!existsSync(dir)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
91
|
+
if (!entry.isFile()) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const parseResult = runtimeAgentDefinitionFileNameSchema.safeParse(entry.name);
|
|
96
|
+
if (!parseResult.success) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
ids.add(parseResult.data.slice(0, -".md".length));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return [...ids].sort((left, right) => left.localeCompare(right));
|
|
105
|
+
}
|
|
106
|
+
|
|
64
107
|
export function resolveRuntimeAgentMarkdownDefinitionFilePath(
|
|
65
108
|
input: LoadRuntimeAgentMarkdownDefinitionFromFileInput,
|
|
66
109
|
): string {
|