@relvo/mcp 0.1.1 → 0.1.3

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.
Files changed (51) hide show
  1. package/README.md +116 -5
  2. package/bin/relvo-mcp.mjs +1 -1
  3. package/dist/client/agent-run.js +681 -0
  4. package/dist/client/browser.js +650 -0
  5. package/dist/client/evm-wallet.js +214 -0
  6. package/dist/client/execution-context.js +6 -3
  7. package/dist/client/index.js +3 -342
  8. package/dist/client/local-input.js +18 -11
  9. package/dist/client/local-profile.js +22 -110
  10. package/dist/client/local-setup.js +0 -1
  11. package/dist/client/tool-name-hints.js +48 -0
  12. package/dist/client/wallet-access.js +207 -0
  13. package/dist/mcp/agent-server.js +237 -0
  14. package/dist/mcp/context.js +70 -0
  15. package/dist/mcp/host-schema.js +47 -0
  16. package/dist/mcp/instructions.js +17 -0
  17. package/dist/mcp/local-upload.js +12 -4
  18. package/dist/mcp/operations.js +30 -0
  19. package/dist/mcp/runtime.js +25 -5
  20. package/dist/mcp/server.js +293 -69
  21. package/dist/mcp/version.js +1 -0
  22. package/dist/schema/accountRequestMethods.js +1 -0
  23. package/dist/schema/cronValidator.js +150 -0
  24. package/dist/schema/platformApiKey.js +5 -5
  25. package/dist/schema/platformBillingConstants.js +3 -0
  26. package/dist/schema/platformCapabilityFamilies.js +217 -0
  27. package/dist/schema/platformConnectionCatalog.js +20 -3
  28. package/dist/schema/platformConnectionPolicy.js +26 -0
  29. package/dist/schema/platformCustomTools.js +113 -0
  30. package/dist/schema/platformFunding.js +9 -0
  31. package/dist/schema/platformHttpConstraints.js +7 -0
  32. package/dist/schema/platformMcpTools.js +7 -0
  33. package/dist/schema/platformMetaTools.js +10 -6
  34. package/dist/schema/platformMonitorSchedule.js +123 -0
  35. package/dist/schema/platformMonitors.js +159 -0
  36. package/dist/schema/platformOAuth.js +7 -0
  37. package/dist/schema/platformPublicExecution.js +72 -25
  38. package/dist/schema/platformRoutes.js +119 -0
  39. package/dist/schema/platformSkills.js +1 -1
  40. package/dist/schema/platformTeamSecretName.js +3 -0
  41. package/dist/schema/platformTeamSkills.js +49 -0
  42. package/dist/schema/platformToolAccess.js +13 -0
  43. package/dist/schema/platformTriggers.js +152 -0
  44. package/dist/schema/platformWorkflows.js +100 -0
  45. package/dist/schema/scheduleTimeZone.js +15 -0
  46. package/dist/schema/toolDiscovery.js +44 -6
  47. package/dist/schema/toolInputRepair.js +110 -0
  48. package/dist/schema/toolResultContext.js +33 -1
  49. package/package.json +1 -1
  50. package/dist/schema/amazonAds.js +0 -13
  51. package/dist/schema/oauth2.js +0 -64
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
- import { platformCatalogSchema, platformCreateExecutionSchema, platformExecutionIdSchema, platformExecutionStatusSchema, platformMicrosSchema, platformQuoteSchema, platformToolNameSchema, platformToolSessionIdSchema, platformOptionalToolSessionIdSchema, PLATFORM_SANDBOX_LIMITS, platformExecutionSchema, platformExecutionContextSchema } from "./platformPublicExecution.js";
3
- import { platformConnectionCatalogSchema, platformConnectionProviderSchema } from "./platformConnectionCatalog.js";
2
+ import { platformCatalogSchema, platformCreateExecutionSchema, platformExecutionIdSchema, platformExecutionStatusSchema, platformMicrosSchema, platformQuoteSchema, platformToolNameSchema, platformToolSessionIdSchema, platformOptionalToolSessionIdSchema, PLATFORM_SANDBOX_LIMITS, platformExecutionSchema, platformExecutionContextSchema, platformRequestIntentSchema, validatePlatformArgumentIdentifiers } from "./platformPublicExecution.js";
3
+ import { platformAnyConnectionProviderSchema, platformConnectionCatalogSchema } from "./platformConnectionCatalog.js";
4
4
  import { toolDiscoveryInputSchema, toolDiscoveryResultSchema } from "./toolDiscovery.js";
5
+ export { PLATFORM_MCP_DIRECT_TOOLS_MAX, platformMcpDirectToolNames } from "./platformMcpTools.js";
5
6
  export const PLATFORM_META_LIMITS = { queries: 4, schemas: 12, batch: 50, concurrency: 4, contextBytes: 24_000,
6
7
  requestBytes: 1_048_576, requestTimeoutMs: 90_000, sessionLifetimeMs: 86_400_000, activeSessions: 100,
7
8
  codeChars: PLATFORM_SANDBOX_LIMITS.code, commandChars: 16_000, sourceExecutions: PLATFORM_SANDBOX_LIMITS.sourceExecutions, timeoutMs: PLATFORM_SANDBOX_LIMITS.timeoutMs };
@@ -10,6 +11,7 @@ export const platformToolSessionSchema = z.object({ id: platformToolSessionIdSch
10
11
  sandbox: z.enum(["new", "ready", "busy", "needs_review", "state_lost"]), activeExecutionId: platformExecutionIdSchema.nullable() });
11
12
  const sessionInput = { sessionId: platformToolSessionIdSchema };
12
13
  export const platformMetaSearchSchema = z.object({ sessionId: platformOptionalToolSessionIdSchema.describe("Continue a returned session. Omit or use an empty string to open a new session."),
14
+ intent: platformRequestIntentSchema,
13
15
  queries: z.array(toolDiscoveryInputSchema).min(1).max(PLATFORM_META_LIMITS.queries)
14
16
  .describe("Independent capability queries, one action per query. Use exact app names when known; omit private account data.") }).strict();
15
17
  export const platformMetaSearchResultSchema = z.object({ session: platformToolSessionSchema,
@@ -20,15 +22,16 @@ export const platformMetaSchemasSchema = z.object({ ...sessionInput,
20
22
  export const platformMetaSchemasResultSchema = z.object({ session: platformToolSessionSchema,
21
23
  tools: platformCatalogSchema.shape.tools, remaining: z.array(platformToolNameSchema) });
22
24
  export const platformMetaConnectionsSchema = z.object({ ...sessionInput,
23
- providers: z.array(platformConnectionProviderSchema).max(platformConnectionProviderSchema.options.length).default([])
24
- .describe("OAuth providers to inspect. Empty means all supported providers. Data-provider API keys are managed by Relvo.") }).strict();
25
+ providers: z.array(platformAnyConnectionProviderSchema).max(32).default([])
26
+ .describe("Providers to inspect: OAuth providers or connected apps as app:<id> (e.g. app:airtable). Empty means every connection. Data-provider API keys are managed by Relvo.") }).strict();
25
27
  export const platformMetaConnectionsResultSchema = z.object({ session: platformToolSessionSchema,
26
28
  developerOrganizationId: z.string(), developerProjectId: z.string(),
27
29
  manageUrl: z.url(), connections: platformConnectionCatalogSchema.shape.connections,
28
- total: z.number().int().nonnegative(), providers: z.array(z.object({ provider: platformConnectionProviderSchema,
30
+ total: z.number().int().nonnegative(), providers: z.array(z.object({ provider: platformAnyConnectionProviderSchema,
29
31
  status: z.enum(["connected", "reauth_required", "not_connected"]) })),
30
32
  instruction: z.string() });
31
- const item = platformCreateExecutionSchema.omit({ sessionId: true }).extend({ idempotencyKey: z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/) }).strict();
33
+ const item = z.object(platformCreateExecutionSchema.shape).omit({ sessionId: true }).extend({ idempotencyKey: z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/) }).strict()
34
+ .superRefine((value, context) => validatePlatformArgumentIdentifiers(value.tool, value.arguments, context));
32
35
  export const platformMetaExecuteSchema = z.object({ ...sessionInput,
33
36
  maximumChargeMicros: platformMicrosSchema.describe("Accepted total reservation ceiling across this batch, in addition to each accepted quote."),
34
37
  tools: z.array(item).min(1).max(PLATFORM_META_LIMITS.batch)
@@ -46,6 +49,7 @@ export const platformMetaExecuteResultSchema = z.object({ session: platformToolS
46
49
  errorCode: z.string().optional() })),
47
50
  instruction: z.string() });
48
51
  const remote = { ...sessionInput, timeoutMs: z.number().int().min(1000).max(PLATFORM_META_LIMITS.timeoutMs).default(30_000),
52
+ intent: platformRequestIntentSchema,
49
53
  sourceExecutionIds: z.array(platformExecutionIdSchema).max(PLATFORM_META_LIMITS.sourceExecutions).default([])
50
54
  .refine(ids => new Set(ids).size === ids.length, "Source execution IDs must be unique"),
51
55
  quote: platformQuoteSchema,
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ import { isCronTooFrequent, isValidCron, parseCron } from "./cronValidator.js";
3
+ import { ScheduleTimeZone } from "./scheduleTimeZone.js";
4
+ export const PLATFORM_MONITOR_INTERVALS = { "15m": 900, "1h": 3600, "1d": 86_400 };
5
+ export const platformMonitorIntervalSchema = z.enum(["15m", "1h", "1d"]);
6
+ export const PLATFORM_MONITOR_DEFAULT_INTERVAL = "1h";
7
+ export const PLATFORM_MONITOR_DEFAULT_TIME_ZONE = "UTC";
8
+ export const PLATFORM_MONITOR_MIN_CRON_MINUTES = 15;
9
+ export const PLATFORM_MONITOR_CRON_CHARS = 100;
10
+ const cronText = z.string().trim().max(PLATFORM_MONITOR_CRON_CHARS)
11
+ .refine(isValidCron, "platform_monitor_cron_invalid")
12
+ .refine(cron => !isCronTooFrequent(cron, PLATFORM_MONITOR_MIN_CRON_MINUTES), "platform_monitor_cron_too_frequent");
13
+ export const platformMonitorCronSchema = z.union([z.literal(""), cronText]);
14
+ export const platformMonitorTimeZoneSchema = z.union([z.literal(""), ScheduleTimeZone]);
15
+ export const normalizeMonitorCron = (cron) => cron.trim().split(/\s+/).map(field => field.toUpperCase()).join(" ");
16
+ export class PlatformMonitorScheduleError extends Error {
17
+ }
18
+ export function resolvePlatformMonitorSchedule(input, after = Date.now()) {
19
+ if (!input.cron) {
20
+ if (input.timeZone)
21
+ throw new PlatformMonitorScheduleError("platform_monitor_time_zone_needs_cron");
22
+ return { interval: input.interval ?? PLATFORM_MONITOR_DEFAULT_INTERVAL };
23
+ }
24
+ if (input.interval)
25
+ throw new PlatformMonitorScheduleError("platform_monitor_schedule_conflict");
26
+ const zone = ScheduleTimeZone.safeParse(input.timeZone || PLATFORM_MONITOR_DEFAULT_TIME_ZONE);
27
+ if (!zone.success)
28
+ throw new PlatformMonitorScheduleError("platform_monitor_time_zone_invalid");
29
+ const cron = normalizeMonitorCron(input.cron), timeZone = zone.data;
30
+ if (nextCronFire(cron, timeZone, after) === null)
31
+ throw new PlatformMonitorScheduleError("platform_monitor_cron_never_runs");
32
+ return { cron, timeZone };
33
+ }
34
+ const MINUTE = 60_000, HOUR = 3_600_000, DAY = 86_400_000;
35
+ const SEARCH_DAYS = 366 * 8;
36
+ const formatters = new Map();
37
+ function offsetAt(timeZone, at) {
38
+ let format = formatters.get(timeZone);
39
+ if (!format) {
40
+ format = new Intl.DateTimeFormat("en-US", { timeZone, hourCycle: "h23", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric" });
41
+ formatters.set(timeZone, format);
42
+ }
43
+ const part = {};
44
+ for (const { type, value } of format.formatToParts(at))
45
+ if (type !== "literal")
46
+ part[type] = Number(value);
47
+ return Date.UTC(part.year, part.month - 1, part.day, part.hour, part.minute, part.second) - Math.floor(at / 1000) * 1000;
48
+ }
49
+ function instantOf(timeZone, wall) {
50
+ const before = offsetAt(timeZone, wall - DAY), after = offsetAt(timeZone, wall + DAY);
51
+ const candidates = [...new Set([wall - before, wall - after])].sort((a, b) => a - b);
52
+ const valid = candidates.filter(at => offsetAt(timeZone, at) === wall - at);
53
+ if (valid.length)
54
+ return valid[0];
55
+ let low = candidates[0], high = candidates[candidates.length - 1];
56
+ if (low === high)
57
+ return low;
58
+ const lowOffset = offsetAt(timeZone, low);
59
+ while (high - low > MINUTE) {
60
+ const mid = low + Math.floor((high - low) / (2 * MINUTE)) * MINUTE;
61
+ if (offsetAt(timeZone, mid) === lowOffset)
62
+ low = mid;
63
+ else
64
+ high = mid;
65
+ }
66
+ return high;
67
+ }
68
+ const dayMatches = (parsed, year, month, day) => {
69
+ if (!parsed.months.has(month))
70
+ return false;
71
+ const dom = parsed.daysOfMonth.has(day), dow = parsed.daysOfWeek.has(new Date(Date.UTC(year, month - 1, day)).getUTCDay());
72
+ return parsed.dayRestrictionOr ? dom || dow : dom && dow;
73
+ };
74
+ const ascending = (values) => [...values].sort((a, b) => a - b);
75
+ export function nextCronFire(cron, timeZone, after) {
76
+ const parsed = parseCron(cron);
77
+ if (!parsed)
78
+ return null;
79
+ const hours = ascending(parsed.hours), minutes = ascending(parsed.minutes);
80
+ const wallAfter = after + offsetAt(timeZone, after);
81
+ const first = new Date(wallAfter);
82
+ const start = Date.UTC(first.getUTCFullYear(), first.getUTCMonth(), first.getUTCDate()) - DAY;
83
+ for (let index = 0; index < SEARCH_DAYS; index++) {
84
+ const dayStart = start + index * DAY, date = new Date(dayStart);
85
+ if (!dayMatches(parsed, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()))
86
+ continue;
87
+ for (const hour of hours)
88
+ for (const minute of minutes) {
89
+ const wall = dayStart + hour * HOUR + minute * MINUTE;
90
+ if (wall < wallAfter - 6 * HOUR)
91
+ continue;
92
+ const at = instantOf(timeZone, wall);
93
+ if (at > after)
94
+ return at;
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+ export function upcomingCronFires(cron, timeZone, after, count) {
100
+ const fires = [];
101
+ for (let at = after; fires.length < count;) {
102
+ const next = nextCronFire(cron, timeZone, at);
103
+ if (next === null)
104
+ break;
105
+ fires.push(next);
106
+ at = next;
107
+ }
108
+ return fires;
109
+ }
110
+ export function monitorRunsPerMonth(schedule) {
111
+ if (!("cron" in schedule))
112
+ return Math.round((365 / 12) * 86_400 / PLATFORM_MONITOR_INTERVALS[schedule.interval]);
113
+ const parsed = parseCron(schedule.cron);
114
+ if (!parsed)
115
+ return 0;
116
+ let days = 0;
117
+ for (let index = 0; index < 365; index++) {
118
+ const date = new Date(Date.UTC(2026, 0, 1 + index));
119
+ if (dayMatches(parsed, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()))
120
+ days++;
121
+ }
122
+ return Math.round(days * parsed.hours.size * parsed.minutes.size / 12);
123
+ }
@@ -0,0 +1,159 @@
1
+ import { z } from "zod";
2
+ import { projectScopeSchema, platformJsonObjectSchema, platformMicrosSchema } from "./platformPublicExecution.js";
3
+ import { platformMonitorCronSchema, platformMonitorIntervalSchema, platformMonitorTimeZoneSchema } from "./platformMonitorSchedule.js";
4
+ export { PLATFORM_MONITOR_INTERVALS, platformMonitorIntervalSchema } from "./platformMonitorSchedule.js";
5
+ export const PLATFORM_MONITOR_LIMITS = {
6
+ perProject: 25,
7
+ nameChars: 80,
8
+ eventsPage: 100,
9
+ eventItemBytes: 8_192,
10
+ snapshotItems: 500,
11
+ retentionDays: 30,
12
+ popularMinimumOrganizations: 3,
13
+ popularPage: 50,
14
+ };
15
+ export const PLATFORM_MONITOR_DESTINATION_LIMITS = {
16
+ perMonitor: 5,
17
+ emailRecipients: 5,
18
+ labelChars: 80,
19
+ urlChars: 512,
20
+ digestEvents: 10,
21
+ };
22
+ export const PLATFORM_MONITOR_EMAIL_PRICE_MICROS = 900;
23
+ export const platformMonitorDestinationKindSchema = z.enum(["webhook", "slack", "discord", "email"]);
24
+ const httpsUrl = (value) => {
25
+ try {
26
+ const url = new URL(value);
27
+ return url.protocol === "https:" && !url.username && !url.password && url.hash === "" ? url : null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ };
33
+ export const isPlatformWebhookUrl = (value) => {
34
+ const url = httpsUrl(value);
35
+ return Boolean(url && url.port === "");
36
+ };
37
+ export const isPlatformSlackWebhookUrl = (value) => {
38
+ const url = httpsUrl(value);
39
+ return Boolean(url && url.hostname === "hooks.slack.com" && url.port === "" && url.search === "" && /^\/services\/[A-Za-z0-9]+\/[A-Za-z0-9]+\/[A-Za-z0-9]+$/.test(url.pathname));
40
+ };
41
+ export const isPlatformDiscordWebhookUrl = (value) => {
42
+ const url = httpsUrl(value);
43
+ return Boolean(url && ["discord.com", "discordapp.com"].includes(url.hostname) && url.port === "" && url.search === "" &&
44
+ /^\/api(\/v\d{1,2})?\/webhooks\/\d{1,25}\/[A-Za-z0-9_-]{1,200}$/.test(url.pathname));
45
+ };
46
+ const destinationUrl = z.string().trim().max(PLATFORM_MONITOR_DESTINATION_LIMITS.urlChars);
47
+ const destinationLabel = z.string().trim().max(PLATFORM_MONITOR_DESTINATION_LIMITS.labelChars).default("");
48
+ const memberId = z.string().min(1).max(64);
49
+ export const platformMonitorDestinationInputSchema = z.discriminatedUnion("kind", [
50
+ z.object({ kind: z.literal("webhook"), label: destinationLabel, url: destinationUrl.refine(isPlatformWebhookUrl, "platform_monitor_destination_url_invalid") }).strict(),
51
+ z.object({ kind: z.literal("slack"), label: destinationLabel, url: destinationUrl.refine(isPlatformSlackWebhookUrl, "platform_monitor_destination_url_invalid") }).strict(),
52
+ z.object({ kind: z.literal("discord"), label: destinationLabel, url: destinationUrl.refine(isPlatformDiscordWebhookUrl, "platform_monitor_destination_url_invalid") }).strict(),
53
+ z.object({ kind: z.literal("email"), label: destinationLabel,
54
+ memberIds: z.array(memberId).min(1).max(PLATFORM_MONITOR_DESTINATION_LIMITS.emailRecipients) }).strict(),
55
+ ]);
56
+ export const platformMonitorDestinationSchema = z.object({
57
+ id: z.string().regex(/^dst_[A-Za-z0-9_-]{1,100}$/),
58
+ kind: platformMonitorDestinationKindSchema,
59
+ label: z.string(),
60
+ target: z.string(),
61
+ memberIds: z.array(z.string()),
62
+ createdAt: z.string(),
63
+ lastDeliveredAt: z.string().nullable(),
64
+ lastStatus: z.enum(["ok", "failed"]).nullable(),
65
+ lastError: z.string().nullable(),
66
+ signingSecret: z.string().optional(),
67
+ });
68
+ export const platformMonitorRecipientSchema = z.object({ memberId, email: z.string(), name: z.string() });
69
+ const toolName = z.string().regex(/^[a-z][a-z0-9_]{1,63}$/);
70
+ export const PLATFORM_MONITOR_ID = /^mon_[A-Za-z0-9_-]{1,100}$/;
71
+ const monitorId = z.string().regex(PLATFORM_MONITOR_ID);
72
+ export const platformMonitorCreateSchema = z.object({
73
+ tool: toolName,
74
+ arguments: platformJsonObjectSchema.default({}),
75
+ provider: z.string().trim().max(64).default(""),
76
+ interval: platformMonitorIntervalSchema.optional().describe("Run every 15m, 1h (default) or 1d. Omit when using cron."),
77
+ cron: platformMonitorCronSchema.optional().describe("5-field cron for fixed times, at least 15 minutes between runs, for example '0 9 * * 1-5' (weekdays 09:00). Omit when using interval."),
78
+ timeZone: platformMonitorTimeZoneSchema.optional().describe("IANA time zone the cron is read in, for example Asia/Tokyo; UTC when omitted. Only with cron."),
79
+ name: z.string().trim().max(PLATFORM_MONITOR_LIMITS.nameChars).default(""),
80
+ maximumChargeMicrosPerRun: platformMicrosSchema.default(0),
81
+ destinations: z.array(platformMonitorDestinationInputSchema).max(PLATFORM_MONITOR_DESTINATION_LIMITS.perMonitor).default([]),
82
+ }).strict();
83
+ export const platformMonitorPoolSchema = z.object({
84
+ chargeCeilingMicros: platformMicrosSchema,
85
+ members: z.number().int().min(0),
86
+ shareMicros: platformMicrosSchema,
87
+ });
88
+ export const platformMonitorQuoteSchema = platformMonitorCreateSchema.pick({ tool: true, arguments: true, provider: true, interval: true, cron: true, timeZone: true }).strict();
89
+ export const platformMonitorQuoteResultSchema = platformMonitorPoolSchema.extend({
90
+ joined: z.boolean(),
91
+ payers: z.number().int().min(1),
92
+ });
93
+ export const platformMonitorResumeSchema = z.object({ maximumChargeMicrosPerRun: platformMicrosSchema.optional() }).strict();
94
+ export const platformMonitorSchema = z.object({
95
+ id: monitorId,
96
+ name: z.string(),
97
+ tool: toolName,
98
+ provider: z.string(),
99
+ arguments: platformJsonObjectSchema,
100
+ interval: platformMonitorIntervalSchema.nullable(),
101
+ cron: z.string().nullable().default(null),
102
+ timeZone: z.string().nullable().default(null),
103
+ subscribers: z.number().int().min(1),
104
+ lastCheckedAt: z.string().nullable(),
105
+ lastChangedAt: z.string().nullable(),
106
+ nextCheckAt: z.string().nullable(),
107
+ lastError: z.string().nullable(),
108
+ createdAt: z.string(),
109
+ maximumChargeMicrosPerRun: platformMicrosSchema,
110
+ pool: platformMonitorPoolSchema,
111
+ status: z.enum(["active", "suspended"]),
112
+ suspendedReason: z.string().nullable(),
113
+ destinations: z.array(platformMonitorDestinationSchema).default([]),
114
+ });
115
+ const changeValue = z.union([z.string(), z.number(), z.boolean(), z.null()]);
116
+ export const platformMonitorEventSchema = z.object({
117
+ id: z.string().regex(/^\d{1,20}$/),
118
+ monitorId: monitorId,
119
+ kind: z.enum(["added", "changed", "removed"]),
120
+ itemKey: z.string(),
121
+ title: z.string(),
122
+ url: z.string(),
123
+ item: platformJsonObjectSchema,
124
+ changes: z.array(z.object({ field: z.string(), before: changeValue, after: changeValue })),
125
+ detectedAt: z.string(),
126
+ });
127
+ export const platformMonitorEventsQuerySchema = z.object({
128
+ after: z.string().regex(/^\d{1,20}$/).optional(),
129
+ limit: z.coerce.number().int().min(1).max(PLATFORM_MONITOR_LIMITS.eventsPage).default(50),
130
+ }).strict();
131
+ export const platformMonitorEventsSchema = z.object({ events: z.array(platformMonitorEventSchema), nextAfter: z.string().nullable() });
132
+ export const platformPopularMonitorSchema = z.object({
133
+ tool: toolName, provider: z.string(), arguments: platformJsonObjectSchema,
134
+ interval: platformMonitorIntervalSchema.nullable(), cron: z.string().nullable().default(null), timeZone: z.string().nullable().default(null),
135
+ organizations: z.number().int().min(1),
136
+ chargeCeilingMicros: platformMicrosSchema,
137
+ });
138
+ export const platformMonitorableToolSchema = z.object({ name: toolName, provider: z.string(), chargeCeilingMicros: platformMicrosSchema });
139
+ export function platformMonitorWire(monitor) {
140
+ const { cron, timeZone, destinations, ...rest } = monitor;
141
+ return { ...rest, ...(cron == null ? {} : { cron }), ...(timeZone == null ? {} : { timeZone }), ...(destinations?.length ? { destinations } : {}) };
142
+ }
143
+ export const platformMonitorContracts = {
144
+ listDeveloperMonitors: { input: projectScopeSchema.strict(), output: z.object({ monitors: z.array(platformMonitorSchema) }).strict() },
145
+ createDeveloperMonitor: { input: projectScopeSchema.extend({ monitor: platformMonitorCreateSchema }).strict(), output: z.object({ monitor: platformMonitorSchema }).strict() },
146
+ deleteDeveloperMonitor: { input: projectScopeSchema.extend({ monitorId }).strict(), output: z.object({ success: z.literal(true) }).strict() },
147
+ quoteDeveloperMonitor: { input: projectScopeSchema.extend({ monitor: platformMonitorQuoteSchema }).strict(), output: z.object({ quote: platformMonitorQuoteResultSchema }).strict() },
148
+ resumeDeveloperMonitor: { input: projectScopeSchema.extend({ monitorId }).merge(platformMonitorResumeSchema).strict(), output: z.object({ monitor: platformMonitorSchema }).strict() },
149
+ listDeveloperMonitorEvents: {
150
+ input: projectScopeSchema.extend({ monitorId: monitorId.optional(), after: platformMonitorEventsQuerySchema.shape.after,
151
+ limit: z.number().int().min(1).max(PLATFORM_MONITOR_LIMITS.eventsPage).default(50) }).strict(),
152
+ output: platformMonitorEventsSchema,
153
+ },
154
+ addDeveloperMonitorDestination: { input: projectScopeSchema.extend({ monitorId, destination: platformMonitorDestinationInputSchema }).strict(), output: z.object({ monitor: platformMonitorSchema }).strict() },
155
+ removeDeveloperMonitorDestination: { input: projectScopeSchema.extend({ monitorId, destinationId: platformMonitorDestinationSchema.shape.id }).strict(), output: z.object({ monitor: platformMonitorSchema }).strict() },
156
+ listDeveloperMonitorRecipients: { input: projectScopeSchema.strict(), output: z.object({ recipients: z.array(platformMonitorRecipientSchema) }).strict() },
157
+ listPopularMonitors: { input: projectScopeSchema.strict(), output: z.object({ monitors: z.array(platformPopularMonitorSchema) }).strict() },
158
+ listMonitorableTools: { input: projectScopeSchema.strict(), output: z.object({ tools: z.array(platformMonitorableToolSchema) }).strict() },
159
+ };
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ export const platformOAuthScopeAlternativesSchema = z.array(z.array(z.string().min(1).max(256)).max(128)).min(1).max(128);
3
+ export const platformOAuthPermissionSchema = z.object({
4
+ status: z.enum(["available", "missing_scopes", "unknown_grant", "unknown_requirements", "not_applicable"]),
5
+ required: platformOAuthScopeAlternativesSchema.optional(),
6
+ missing: z.array(z.string()).optional(),
7
+ });
@@ -1,16 +1,24 @@
1
1
  import { z } from "zod";
2
- import { TOOL_RESULT_CONTEXT_MAX_BYTES, toolResultContextSchema, toolResultPageSchema } from "./toolResultContext.js";
2
+ import { fitToolResultPage, TOOL_RESULT_CONTEXT_MAX_BYTES, toolResultContextSchema, toolResultPageSchema } from "./toolResultContext.js";
3
+ import { platformOAuthPermissionSchema } from "./platformOAuth.js";
3
4
  export const PLATFORM_EXECUTION_MAX_STEPS = 20;
5
+ export const PLATFORM_EXECUTION_MAX_WAIT_SECONDS = 25;
4
6
  export const PLATFORM_STEP_OUTPUT_MAX_BYTES = 1_048_576;
5
7
  export const PLATFORM_EXECUTION_RESPONSE_MAX_BYTES = PLATFORM_EXECUTION_MAX_STEPS * PLATFORM_STEP_OUTPUT_MAX_BYTES + 65_536;
6
8
  export const platformJsonObjectSchema = z.record(z.string(), z.json());
9
+ export const organizationScopeSchema = z.object({ developerOrganizationId: z.string().min(1).max(128) });
10
+ export const projectScopeSchema = organizationScopeSchema.extend({ developerProjectId: z.string().min(1).max(128) });
7
11
  export const developerKeyScopeSchema = z.enum(["project:read", "usage:read", "tools:read", "tools:execute", "executions:read"]);
12
+ export const developerKeyAnyScopeSchema = z.enum([...developerKeyScopeSchema.options, "access:write"]);
8
13
  export const PLATFORM_INPUT_MAX_BYTES = 12 * 1024 * 1024;
14
+ export const PLATFORM_FILE_CHUNK_MAX_BYTES = 32 * 1024 * 1024;
9
15
  export const PLATFORM_UPLOAD_MAX_BYTES = 16 * 1024 * 1024 + 4096;
10
16
  export const platformInputFileIdSchema = z.string().regex(/^f_[a-f0-9]{64}$/);
11
17
  export const platformInputMimeSchema = z.enum(["image/jpeg", "image/png", "image/webp", "audio/wav", "audio/mpeg", "video/mp4", "video/quicktime"]);
12
- export const platformInputUploadSchema = z.object({ mimeType: platformInputMimeSchema, data: z.string().min(4).max(16 * 1024 * 1024) }).strict();
13
- export const platformInputFileSchema = z.object({ id: platformInputFileIdSchema, mimeType: platformInputMimeSchema,
18
+ export const platformInputFileMimeSchema = z.string().max(255).regex(/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/);
19
+ export const platformInputUploadSchema = z.object({ mimeType: platformInputFileMimeSchema, data: z.string().min(4).max(16 * 1024 * 1024),
20
+ purpose: z.literal("file").optional().describe("file stores opaque bytes for provider uploads, including GIF, documents, archives and local upload chunks. Omit for validated AI media.") }).strict();
21
+ export const platformInputFileSchema = z.object({ id: platformInputFileIdSchema, mimeType: platformInputFileMimeSchema,
14
22
  sizeBytes: z.number().int().positive().max(PLATFORM_INPUT_MAX_BYTES), sha256: z.string().regex(/^[a-f0-9]{64}$/), expiresAt: z.iso.datetime() });
15
23
  export const platformMicrosSchema = z.number().int().min(0).max(1_000_000_000_000);
16
24
  export const platformToolNameSchema = z.string().regex(/^[a-z][a-z0-9_]{1,63}$/);
@@ -21,30 +29,55 @@ export const PLATFORM_SANDBOX_LIMITS = { code: 64000, sourceExecutions: 10, time
21
29
  export const platformProviderChoiceSchema = z.string().trim().regex(/^(?:[a-z][a-z0-9_]{0,63})?$/)
22
30
  .transform(value => value === "" || value === "auto" ? undefined : value).optional()
23
31
  .describe("Omit, use auto, or use an empty string for automatic routing. A catalog provider ID strictly selects that compatible provider; no other provider is substituted.");
32
+ export const platformRelevanceCheckSchema = z.enum(["", "auto", "light"])
33
+ .transform(value => value === "light" ? "light" : undefined).optional()
34
+ .describe("Auto result filtering: auto (default) or light, a cheaper check that keeps rows it cannot decide.");
35
+ export const platformInvalidArgumentFieldsSchema = z.array(z.string().max(200).regex(/^[\w.-]*$/)).max(50);
36
+ export const PLATFORM_REQUEST_INTENT_MAX_CHARS = 1000;
37
+ export const platformRequestIntentSchema = z.string().trim().max(PLATFORM_REQUEST_INTENT_MAX_CHARS).transform(value => value || undefined).optional()
38
+ .describe("Brief user goal without secrets or private content. Empty means omitted.");
24
39
  export const platformExecutionStatusSchema = z.enum(["queued", "running", "waiting", "succeeded", "failed", "cancelled", "needs_review"]);
25
- export const platformFundingSchema = z.enum(["relvo", "toolhub", "provider", "mixed"])
26
- .transform(value => value === "toolhub" ? "relvo" : value);
27
- const platformModelFundingSchema = z.enum(["relvo", "toolhub", "provider"])
28
- .transform(value => value === "toolhub" ? "relvo" : value);
40
+ export const platformFundingSchema = z.enum(["relvo", "provider", "mixed"]);
41
+ const platformModelFundingSchema = z.enum(["relvo", "provider"]);
29
42
  const workspaceArgumentNames = new Set(["workspaceid", "workspaceids", "teamid", "provideraccountid", "provideraccountids", "datasetid", "datasetids", "knowledgebaseid", "sessionid", "skillid"]);
30
- const standaloneArgumentsSchema = z.record(z.string(), z.unknown()).pipe(platformJsonObjectSchema).superRefine((value, context) => {
31
- for (const key of Object.keys(value))
32
- if (workspaceArgumentNames.has(key.replaceAll("_", "").toLowerCase())) {
33
- context.addIssue({ code: "custom", path: [key], message: "Workspace-owned data is unavailable on the standalone Platform." });
43
+ export function validatePlatformArgumentIdentifiers(tool, input, context, field = "arguments") {
44
+ if (tool?.startsWith("custom__"))
45
+ return;
46
+ const nativeFields = tool?.startsWith("tikhub_") ? ["session_id"]
47
+ : tool?.startsWith("brightdata_") ? ["dataset_id"]
48
+ : tool?.startsWith("firecrawl_") ? ["dataset_id", "session_id", "team_id"] : [];
49
+ for (const key of Object.keys(input))
50
+ if (workspaceArgumentNames.has(key.replaceAll("_", "").toLowerCase()) && !nativeFields.includes(key)) {
51
+ context.addIssue({ code: "custom", path: [field, key], message: "Workspace-owned data is unavailable on the standalone Platform." });
34
52
  }
35
- }).describe("Self-contained tool arguments: prompts, public URLs or public account identifiers. Web workspace identifiers are not supported.");
53
+ }
54
+ const standaloneArgumentsSchema = z.record(z.string(), z.unknown()).pipe(platformJsonObjectSchema)
55
+ .describe("Self-contained tool arguments: prompts, public URLs or public account identifiers. Web workspace identifiers are not supported.");
36
56
  export const platformToolInputSchema = z.object({ tool: platformToolNameSchema.describe("Tool name from the standalone Platform catalog."), arguments: standaloneArgumentsSchema,
37
57
  provider: platformProviderChoiceSchema,
38
- sessionId: platformOptionalToolSessionIdSchema.describe("Relvo tool session returned by platform_search_tools. Omit or use an empty string for standalone executions.") }).strict();
58
+ relevanceCheck: platformRelevanceCheckSchema,
59
+ intent: platformRequestIntentSchema,
60
+ sessionId: platformOptionalToolSessionIdSchema.describe("Relvo tool session returned by platform_search_tools. Omit or use an empty string for standalone executions.") }).strict()
61
+ .superRefine((value, context) => validatePlatformArgumentIdentifiers(value.tool, value.arguments, context));
62
+ export const platformQuoteRequestSchema = platformToolInputSchema.extend({
63
+ maximumChargeMicros: platformMicrosSchema.optional()
64
+ .describe("Optional cap: an Auto quote above it returns the best plan within it as plan; execute with plan's fields."),
65
+ }).strict();
66
+ export const platformRunRequestHashSchema = z.string().regex(/^[a-f0-9]{64}$/);
67
+ export const platformRecoverExecutionSchema = z.object({ idempotencyKey: z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/),
68
+ requestHash: platformRunRequestHashSchema.optional() }).strict();
39
69
  export const platformCreateExecutionSchema = platformToolInputSchema.extend({
40
70
  maximumChargeMicros: platformMicrosSchema,
41
71
  priceVersion: z.string().min(1).max(100),
72
+ runRequestHash: platformRunRequestHashSchema.optional(),
42
73
  }).strict();
43
74
  export const platformQuoteSchema = z.object({
44
75
  tool: platformToolNameSchema, priceVersion: z.string(), currency: z.literal("usd"),
45
76
  minimumChargeMicros: platformMicrosSchema, maximumChargeMicros: platformMicrosSchema,
77
+ lowestChargeMicros: platformMicrosSchema.optional(),
46
78
  expiresAt: z.string(),
47
79
  funding: platformFundingSchema.optional(), externalBillingProviders: z.array(z.string()).optional(),
80
+ plan: z.object({ provider: z.string().optional(), relevanceCheck: z.literal("light").optional() }).strict().optional(),
48
81
  });
49
82
  export const platformExecutionSchema = z.object({
50
83
  id: platformExecutionIdSchema, tool: platformToolNameSchema, status: platformExecutionStatusSchema, cancelRequested: z.boolean(),
@@ -88,6 +121,9 @@ export function platformExecutionResultPageContext(result, options) {
88
121
  if (executionContextBytes(result) <= maximumBytes)
89
122
  return result;
90
123
  const { executionId, page } = result.data;
124
+ const fitted = { data: { executionId, page: fitToolResultPage(page, candidate => executionContextBytes({ data: { executionId, page: candidate } }) <= maximumBytes) } };
125
+ if (executionContextBytes(fitted) <= maximumBytes)
126
+ return fitted;
91
127
  const compact = { data: platformExecutionResultPageSchema.parse({ executionId, page: { ...page,
92
128
  context: { ...page.context, offloaded: true, preview: null, reference: { ...page.context.reference,
93
129
  executionId, readWith: options.readWith, path: page.path, offset: page.offset } },
@@ -108,20 +144,31 @@ export const platformExecutionListSchema = z.object({
108
144
  });
109
145
  export const platformArtifactIdSchema = z.string().regex(/^a_[A-Za-z0-9_-]{1,100}$/);
110
146
  export const platformArtifactSchema = z.object({
111
- id: platformArtifactIdSchema, mimeType: z.enum(["image/jpeg", "image/png", "image/webp", "audio/wav", "audio/mpeg", "audio/flac", "audio/l16", "audio/mulaw", "audio/alaw", "video/mp4", "video/quicktime", "model/gltf-binary", "application/zip"]),
147
+ id: platformArtifactIdSchema, mimeType: z.enum(["image/jpeg", "image/png", "image/webp", "image/svg+xml", "audio/wav", "audio/mpeg", "audio/flac", "audio/l16", "audio/mulaw", "audio/alaw", "video/mp4", "video/quicktime", "model/gltf-binary", "application/zip"]),
112
148
  sizeBytes: z.number().int().positive().max(64 * 1024 * 1024), sha256: z.string().regex(/^[a-f0-9]{64}$/),
113
149
  generation: z.string().regex(/^\d{1,30}$/), expiresAt: z.iso.datetime(),
114
150
  });
115
151
  export const platformArtifactDownloadSchema = z.object({ url: z.url().refine((value) => { const url = new URL(value); return url.protocol === "https:" && !url.username && !url.password; }), expiresAt: z.iso.datetime() });
116
- export const platformCatalogSchema = z.object({
117
- priceVersion: z.string(), currency: z.literal("usd"), executionEnabled: z.boolean(),
118
- tools: z.array(z.object({
119
- name: platformToolNameSchema, title: z.string().optional(), description: z.string(), category: z.enum(["research", "seo", "social", "generation", "enrichment"]),
120
- available: z.boolean(), inputSchema: platformJsonObjectSchema, example: platformJsonObjectSchema,
121
- providers: z.array(z.object({ id: z.string(), name: z.string(), available: z.boolean(),
122
- role: z.enum(["primary", "fallback", "alternative"]), condition: z.string().optional() })).optional(),
123
- models: z.array(z.object({ id: z.string(), available: z.boolean(), example: platformJsonObjectSchema, priceDescription: z.string(), funding: platformModelFundingSchema.optional() })).optional(),
124
- price: z.object({ minimumChargeMicros: platformMicrosSchema, unit: z.string(), unitPriceMicros: platformMicrosSchema.nullable(), unitPriceCeilingMicros: platformMicrosSchema.optional(), description: z.string(),
125
- funding: platformFundingSchema.optional(), externalBillingProviders: z.array(z.string()).optional() }),
126
- })),
152
+ export const platformUnavailableReasonSchema = z.enum(["setup", "own_key", "connection", "permission", "retired"]);
153
+ export const platformCatalogToolSchema = z.object({
154
+ name: platformToolNameSchema, title: z.string().optional(), description: z.string(), category: z.enum(["research", "seo", "social", "generation", "enrichment", "finance", "news"]),
155
+ available: z.boolean(), unavailable: platformUnavailableReasonSchema.optional(), inputSchema: platformJsonObjectSchema, example: platformJsonObjectSchema,
156
+ permission: platformOAuthPermissionSchema.optional(),
157
+ providers: z.array(z.object({ id: z.string(), name: z.string(), available: z.boolean(), unavailable: platformUnavailableReasonSchema.optional(),
158
+ role: z.enum(["primary", "fallback", "alternative"]), condition: z.string().optional() })).optional(),
159
+ models: z.array(z.object({ id: z.string(), available: z.boolean(), unavailable: platformUnavailableReasonSchema.optional(), example: platformJsonObjectSchema, priceDescription: z.string(), funding: platformModelFundingSchema.optional() })).optional(),
160
+ price: z.object({ minimumChargeMicros: platformMicrosSchema, unit: z.string(), unitPriceMicros: platformMicrosSchema.nullable(), unitPriceCeilingMicros: platformMicrosSchema.optional(), description: z.string(),
161
+ unitsPerRequest: z.object({ minimum: z.number().int().positive(), maximum: z.number().int().positive() }).refine(range => range.minimum <= range.maximum).optional(),
162
+ funding: platformFundingSchema.optional(), externalBillingProviders: z.array(z.string()).optional() }),
127
163
  });
164
+ const platformCatalogHeader = { priceVersion: z.string(), currency: z.literal("usd"), executionEnabled: z.boolean() };
165
+ export const platformCatalogSchema = z.object({ ...platformCatalogHeader, tools: z.array(platformCatalogToolSchema) });
166
+ export const PLATFORM_CATALOG_PAGE_MAX = 50;
167
+ export const PLATFORM_READ_UNIT_BYTES = 262_144;
168
+ export const platformCatalogPageQuerySchema = z.object({
169
+ offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0),
170
+ limit: z.number().int().min(1).max(PLATFORM_CATALOG_PAGE_MAX).default(PLATFORM_CATALOG_PAGE_MAX),
171
+ }).strict();
172
+ export const platformCatalogPageSchema = platformCatalogSchema.extend({ total: z.number().int().min(0), nextOffset: z.number().int().min(0).nullable() });
173
+ export const platformCatalogSummarySchema = z.object({ ...platformCatalogHeader, tools: z.array(platformCatalogToolSchema.omit({ inputSchema: true })) });
174
+ export const platformCatalogToolDetailSchema = z.object({ ...platformCatalogHeader, tool: platformCatalogToolSchema });
@@ -0,0 +1,119 @@
1
+ const TOOL = "[a-z][a-z0-9_]{1,63}";
2
+ const EXECUTION = "[A-Za-z0-9_-]{1,128}";
3
+ const ARTIFACT = "a_[A-Za-z0-9_-]{1,100}";
4
+ const MONITOR = "mon_[A-Za-z0-9_-]{1,100}";
5
+ const CUSTOM_TOOL = "custom__[a-z][a-z0-9_]{1,39}";
6
+ const TEAM_SKILL = "[a-z][a-z0-9_]{1,39}";
7
+ const WORKFLOW = "[a-z][a-z0-9_]{1,39}";
8
+ const WORKFLOW_RUN = "wfr_[A-Za-z0-9_-]{1,100}";
9
+ const TRIGGER = "trg_[A-Za-z0-9_-]{1,100}";
10
+ const MEMBER = "[A-Za-z0-9-]{1,128}";
11
+ export const PLATFORM_ROUTES = {
12
+ tools: "/v1/tools",
13
+ connections: "/v1/connections",
14
+ quote: "/v1/quote",
15
+ project: "/v1/project",
16
+ usage: "/v1/usage",
17
+ executions: "/v1/executions",
18
+ executionRecovery: "/v1/executions/recover",
19
+ execution: `/v1/executions/:id{${EXECUTION}}`,
20
+ executionContext: `/v1/executions/:id{${EXECUTION}}/context`,
21
+ executionResult: `/v1/executions/:id{${EXECUTION}}/result`,
22
+ executionCancel: `/v1/executions/:id{${EXECUTION}}/cancel`,
23
+ artifactDownload: `/v1/executions/:id{${EXECUTION}}/artifacts/:artifact{${ARTIFACT}}/download`,
24
+ monitors: "/v1/monitors",
25
+ monitorTools: "/v1/monitors/tools",
26
+ monitorsPopular: "/v1/monitors/popular",
27
+ monitorQuote: "/v1/monitors/quote",
28
+ monitorResume: `/v1/monitors/:id{${MONITOR}}/resume`,
29
+ monitor: `/v1/monitors/:id{${MONITOR}}`,
30
+ monitorDelete: `/v1/monitors/:id{${MONITOR}}/delete`,
31
+ monitorEvents: `/v1/monitors/:id{${MONITOR}}/events`,
32
+ monitorDestinations: `/v1/monitors/:id{${MONITOR}}/destinations`,
33
+ monitorDestinationDelete: `/v1/monitors/:id{${MONITOR}}/destinations/:destinationId{dst_[A-Za-z0-9_-]{1,100}}/delete`,
34
+ monitorRecipients: "/v1/monitor-recipients",
35
+ monitorFeed: "/v1/monitor-events",
36
+ customTools: "/v1/custom-tools",
37
+ customTool: `/v1/custom-tools/:name{${CUSTOM_TOOL}}`,
38
+ customToolUpdate: `/v1/custom-tools/:name{${CUSTOM_TOOL}}/update`,
39
+ customToolDelete: `/v1/custom-tools/:name{${CUSTOM_TOOL}}/delete`,
40
+ customToolVersions: `/v1/custom-tools/:name{${CUSTOM_TOOL}}/versions`,
41
+ customToolRestore: `/v1/custom-tools/:name{${CUSTOM_TOOL}}/restore`,
42
+ teamSkills: "/v1/team-skills",
43
+ teamSkill: `/v1/team-skills/:slug{${TEAM_SKILL}}`,
44
+ teamSkillUpdate: `/v1/team-skills/:slug{${TEAM_SKILL}}/update`,
45
+ teamSkillDelete: `/v1/team-skills/:slug{${TEAM_SKILL}}/delete`,
46
+ teamSkillVersions: `/v1/team-skills/:slug{${TEAM_SKILL}}/versions`,
47
+ teamSkillRestore: `/v1/team-skills/:slug{${TEAM_SKILL}}/restore`,
48
+ workflows: "/v1/workflows",
49
+ workflow: `/v1/workflows/:slug{${WORKFLOW}}`,
50
+ workflowUpdate: `/v1/workflows/:slug{${WORKFLOW}}/update`,
51
+ workflowDelete: `/v1/workflows/:slug{${WORKFLOW}}/delete`,
52
+ workflowVersions: `/v1/workflows/:slug{${WORKFLOW}}/versions`,
53
+ workflowRuns: `/v1/workflows/:slug{${WORKFLOW}}/runs`,
54
+ workflowRun: `/v1/workflow-runs/:id{${WORKFLOW_RUN}}`,
55
+ workflowRunCancel: `/v1/workflow-runs/:id{${WORKFLOW_RUN}}/cancel`,
56
+ triggers: "/v1/triggers",
57
+ trigger: `/v1/triggers/:id{${TRIGGER}}`,
58
+ triggerUpdate: `/v1/triggers/:id{${TRIGGER}}/update`,
59
+ triggerDelete: `/v1/triggers/:id{${TRIGGER}}/delete`,
60
+ triggerEvents: `/v1/triggers/:id{${TRIGGER}}/events`,
61
+ triggerDestinations: `/v1/triggers/:id{${TRIGGER}}/destinations`,
62
+ triggerDestinationDelete: `/v1/triggers/:id{${TRIGGER}}/destinations/:destinationId{dst_[A-Za-z0-9_-]{1,100}}/delete`,
63
+ triggerFeed: "/v1/trigger-events",
64
+ triggerTypes: "/v1/trigger-types",
65
+ suggestions: "/v1/suggestions",
66
+ access: "/v1/access",
67
+ memberAccess: `/v1/access/members/:id{${MEMBER}}/update`,
68
+ definition: `/tools/definition/:name{${TOOL}}`,
69
+ tool: `/tools/:tool{${TOOL}}`,
70
+ discover: "/tools/discover",
71
+ upload: "/tools/upload",
72
+ skills: "/tools/skills",
73
+ metaSearch: "/tools/meta/search",
74
+ metaSchemas: "/tools/meta/schemas",
75
+ metaConnections: "/tools/meta/connections",
76
+ metaExecute: "/tools/meta/execute",
77
+ metaWorkbench: "/tools/meta/workbench",
78
+ metaBash: "/tools/meta/bash",
79
+ };
80
+ export const PLATFORM_TRIGGER_INGRESS_ROUTES = {
81
+ endpoint: "/v1/trigger-ingress/:endpoint{tep_[a-f0-9]{32}}",
82
+ app: "/v1/trigger-ingress/app/:app{[a-z0-9][a-z0-9_]{0,63}}",
83
+ };
84
+ export const PLATFORM_WALLET_ROUTES = {
85
+ signIn: "/v1/wallet/sign-in",
86
+ topUp: "/v1/wallet/top-up",
87
+ };
88
+ export const PLATFORM_META_ROUTES = [PLATFORM_ROUTES.metaSearch, PLATFORM_ROUTES.metaSchemas, PLATFORM_ROUTES.metaConnections,
89
+ PLATFORM_ROUTES.metaExecute, PLATFORM_ROUTES.metaWorkbench, PLATFORM_ROUTES.metaBash];
90
+ export const PLATFORM_BRIDGE_ROUTES = [
91
+ PLATFORM_ROUTES.tools, PLATFORM_ROUTES.connections, PLATFORM_ROUTES.quote, PLATFORM_ROUTES.project, PLATFORM_ROUTES.usage,
92
+ PLATFORM_ROUTES.executions, PLATFORM_ROUTES.executionRecovery, PLATFORM_ROUTES.execution, PLATFORM_ROUTES.executionContext, PLATFORM_ROUTES.executionResult,
93
+ PLATFORM_ROUTES.executionCancel, PLATFORM_ROUTES.artifactDownload, PLATFORM_ROUTES.definition, PLATFORM_ROUTES.tool,
94
+ ];
95
+ const ALIASED = "(?:project|usage|quote|executions|connections)(?:\\/|$)";
96
+ const TOOLS_ALIASES = new RegExp(`^\\/tools\\/${ALIASED}`), V1_ALIASED = new RegExp(`^\\/v1\\/(?=${ALIASED})`);
97
+ export function platformRoutePath(pathname) {
98
+ if (pathname === "/tools")
99
+ return PLATFORM_ROUTES.tools;
100
+ return TOOLS_ALIASES.test(pathname) ? pathname.replace(/^\/tools\//, "/v1/") : pathname;
101
+ }
102
+ const PARAMETER = /:([A-Za-z]+)\{((?:[^{}]|\{[^{}]*\})*)\}/g;
103
+ export function platformToolsPath(route, values = {}) {
104
+ const path = route === PLATFORM_ROUTES.tools ? "/tools" : route.replace(V1_ALIASED, "/tools/");
105
+ return path.replace(PARAMETER, (_, name) => values[name] ?? `{${name}}`);
106
+ }
107
+ function routeExpression(pattern) {
108
+ let source = "", last = 0;
109
+ for (const match of pattern.matchAll(PARAMETER)) {
110
+ source += escapeLiteral(pattern.slice(last, match.index)) + `(?:${match[2]})`;
111
+ last = match.index + match[0].length;
112
+ }
113
+ return source + escapeLiteral(pattern.slice(last));
114
+ }
115
+ const escapeLiteral = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
116
+ const BRIDGE_PATH = new RegExp(`^(?:${PLATFORM_BRIDGE_ROUTES.map(routeExpression).join("|")})$`);
117
+ export function isPlatformBridgePath(pathname) {
118
+ return BRIDGE_PATH.test(platformRoutePath(pathname));
119
+ }
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export const PLATFORM_SKILLS_MAX_BYTES = 1_048_576;
3
3
  export const PLATFORM_SKILL_MAX_BYTES = 65_536;
4
- export const platformSkillSchema = z.object({ name: z.string().regex(/^(?:relvo|toolhub)(?:-[a-z0-9]+)*$/).max(64),
4
+ export const platformSkillSchema = z.object({ name: z.string().regex(/^relvo(?:-[a-z0-9]+)*$/).max(64),
5
5
  description: z.string().min(1).max(1000), sha256: z.string().regex(/^[a-f0-9]{64}$/),
6
6
  content: z.string().min(1).max(PLATFORM_SKILL_MAX_BYTES).refine(value => new TextEncoder().encode(value).byteLength <= PLATFORM_SKILL_MAX_BYTES) }).strict();
7
7
  export const platformSkillsBundleSchema = z.object({ version: z.string().regex(/^[a-f0-9]{64}$/),
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ export const PLATFORM_TEAM_SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
3
+ export const platformTeamSecretNameSchema = z.string().regex(PLATFORM_TEAM_SECRET_NAME).describe("Letters, digits and underscores, not starting with a digit, e.g. STRIPE_API_KEY.");