@relvo/mcp 0.1.2 → 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.
- package/README.md +90 -5
- package/bin/relvo-mcp.mjs +1 -1
- package/dist/client/agent-run.js +681 -0
- package/dist/client/browser.js +650 -0
- package/dist/client/evm-wallet.js +214 -0
- package/dist/client/index.js +3 -360
- package/dist/client/local-input.js +18 -11
- package/dist/client/local-profile.js +22 -110
- package/dist/client/local-setup.js +0 -1
- package/dist/client/tool-name-hints.js +48 -0
- package/dist/client/wallet-access.js +207 -0
- package/dist/mcp/agent-server.js +237 -0
- package/dist/mcp/context.js +3 -2
- package/dist/mcp/host-schema.js +47 -0
- package/dist/mcp/instructions.js +12 -2
- package/dist/mcp/local-upload.js +12 -4
- package/dist/mcp/operations.js +30 -0
- package/dist/mcp/runtime.js +25 -5
- package/dist/mcp/server.js +191 -37
- package/dist/mcp/version.js +1 -0
- package/dist/schema/accountRequestMethods.js +1 -0
- package/dist/schema/cronValidator.js +150 -0
- package/dist/schema/platformApiKey.js +5 -5
- package/dist/schema/platformBillingConstants.js +3 -0
- package/dist/schema/platformCapabilityFamilies.js +217 -0
- package/dist/schema/platformConnectionCatalog.js +20 -3
- package/dist/schema/platformConnectionPolicy.js +26 -0
- package/dist/schema/platformCustomTools.js +113 -0
- package/dist/schema/platformFunding.js +9 -0
- package/dist/schema/platformHttpConstraints.js +7 -0
- package/dist/schema/platformMcpTools.js +7 -0
- package/dist/schema/platformMetaTools.js +8 -6
- package/dist/schema/platformMonitorSchedule.js +123 -0
- package/dist/schema/platformMonitors.js +159 -0
- package/dist/schema/platformOAuth.js +7 -0
- package/dist/schema/platformPublicExecution.js +53 -15
- package/dist/schema/platformRoutes.js +61 -1
- package/dist/schema/platformSkills.js +1 -1
- package/dist/schema/platformTeamSecretName.js +3 -0
- package/dist/schema/platformTeamSkills.js +49 -0
- package/dist/schema/platformToolAccess.js +13 -0
- package/dist/schema/platformTriggers.js +152 -0
- package/dist/schema/platformWorkflows.js +100 -0
- package/dist/schema/scheduleTimeZone.js +15 -0
- package/dist/schema/toolDiscovery.js +20 -3
- package/dist/schema/toolInputRepair.js +110 -0
- package/dist/schema/toolResultContext.js +1 -1
- package/package.json +1 -1
- package/dist/schema/amazonAds.js +0 -13
- package/dist/schema/oauth2.js +0 -64
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ToolNameHints } from "./tool-name-hints.js";
|
|
3
|
+
import { realtimeVoiceConfigSchema, realtimeVoiceGrantSchema, realtimeVoiceModelsSchema, connectRealtimeVoice } from "./realtime-voice.js";
|
|
4
|
+
export { connectRealtimeVoice, RealtimeVoiceSession, RealtimeVoiceError } from "./realtime-voice.js";
|
|
5
|
+
import { PLATFORM_CONNECTION_CATALOG_LIMITS, platformConnectionCatalogSchema, platformConnectionCatalogQuerySchema } from "../schema/platformConnectionCatalog.js";
|
|
6
|
+
export { PLATFORM_CONNECTION_CATALOG_LIMITS, platformConnectionProviderSchema, platformConnectionCatalogQuerySchema } from "../schema/platformConnectionCatalog.js";
|
|
7
|
+
import { TOOL_RESULT_CONTEXT_MAX_BYTES, toolResultSelectionSchema } from "../schema/toolResultContext.js";
|
|
8
|
+
import { executionReceiptContext } from "./execution-context.js";
|
|
9
|
+
export { executionReceiptContext, executionPageContext } from "./execution-context.js";
|
|
10
|
+
export { UNTRUSTED_CONTENT_RULE, wrapUntrusted } from "../schema/untrustedContent.js";
|
|
11
|
+
export { fitToolResultPage, TOOL_RESULT_CONTEXT_MAX_BYTES, TOOL_RESULT_PAGE_MAX_ITEMS, toolResultSelectionSchema } from "../schema/toolResultContext.js";
|
|
12
|
+
export { withEvidentValues } from "../schema/toolInputRepair.js";
|
|
13
|
+
export { platformJsonObjectSchema } from "../schema/platformPublicExecution.js";
|
|
14
|
+
import { toolDiscoveryInputSchema, toolDiscoveryResultSchema, TOOL_DISCOVERY_REQUEST_TIMEOUT_MS } from "../schema/toolDiscovery.js";
|
|
15
|
+
export { toolDiscoveryInputSchema } from "../schema/toolDiscovery.js";
|
|
16
|
+
export { PLATFORM_MCP_DIRECT_TOOLS_MAX, platformMcpDirectToolNames } from "../schema/platformMetaTools.js";
|
|
17
|
+
import { isRelvoProduct, PLATFORM_API_KEY_PATTERN, PLATFORM_CLIENT_HEADERS, PLATFORM_KEY_PREFIXES, PLATFORM_PRODUCT_NAMES, platformKeyProduct } from "../schema/platformApiKey.js";
|
|
18
|
+
import { readBoundedBody } from "../schema/boundedBody.js";
|
|
19
|
+
import { platformFundingShortfallSchema } from "../schema/platformFunding.js";
|
|
20
|
+
import { PLATFORM_META_ROUTES, PLATFORM_ROUTES, platformToolsPath as route } from "../schema/platformRoutes.js";
|
|
21
|
+
import { platformSkillsBundleSchema, platformSkillSchema } from "../schema/platformSkills.js";
|
|
22
|
+
export { platformSkillsBundleSchema, platformSkillSchema } from "../schema/platformSkills.js";
|
|
23
|
+
import { platformMonitorCreateSchema, platformMonitorSchema, platformMonitorEventsSchema, platformMonitorEventsQuerySchema, platformPopularMonitorSchema, platformMonitorableToolSchema, platformMonitorQuoteSchema, platformMonitorQuoteResultSchema, platformMonitorResumeSchema, PLATFORM_MONITOR_ID } from "../schema/platformMonitors.js";
|
|
24
|
+
export { platformMonitorCreateSchema, platformMonitorEventsQuerySchema, platformMonitorQuoteSchema, platformMonitorResumeSchema, PLATFORM_MONITOR_ID } from "../schema/platformMonitors.js";
|
|
25
|
+
import { platformCustomToolCreateSchema, platformCustomToolNameSchema, platformCustomToolRestoreSchema, platformCustomToolSchema, platformCustomToolSummarySchema, platformCustomToolUpdateSchema, platformCustomToolVersionSchema, PLATFORM_CUSTOM_TOOL_PREFIX } from "../schema/platformCustomTools.js";
|
|
26
|
+
export { platformCustomEndpointDefinitionSchema, platformCustomToolCreateSchema, platformCustomToolDefinitionSchema, platformCustomToolNameSchema, platformCustomToolSlugSchema, platformCustomToolRestoreSchema, platformCustomToolUpdateSchema, PLATFORM_CUSTOM_TOOL_PREFIX } from "../schema/platformCustomTools.js";
|
|
27
|
+
import { platformSuggestionsSchema, platformTeamSkillCreateSchema, platformTeamSkillRestoreSchema, platformTeamSkillSchema, platformTeamSkillSummarySchema, platformTeamSkillUpdateSchema, platformTeamSkillVersionSchema, PLATFORM_TEAM_SKILL_SLUG } from "../schema/platformTeamSkills.js";
|
|
28
|
+
export { platformTeamSkillCreateSchema, platformTeamSkillDefinitionSchema, platformTeamSkillRestoreSchema, platformTeamSkillUpdateSchema, PLATFORM_TEAM_SKILL_SLUG } from "../schema/platformTeamSkills.js";
|
|
29
|
+
import { platformWorkflowCreateSchema, platformWorkflowRunCreateSchema, platformWorkflowRunIdSchema, platformWorkflowRunListQuerySchema, platformWorkflowRunListSchema, platformWorkflowRunSchema, platformWorkflowSchema, platformWorkflowSummarySchema, platformWorkflowUpdateSchema, platformWorkflowVersionSchema, PLATFORM_WORKFLOW_SLUG } from "../schema/platformWorkflows.js";
|
|
30
|
+
export { platformWorkflowCreateSchema, platformWorkflowDefinitionSchema, platformWorkflowRunCreateSchema, platformWorkflowRunIdSchema, platformWorkflowRunListQuerySchema, platformWorkflowSlugSchema, platformWorkflowStepSchema, platformWorkflowUpdateSchema, PLATFORM_WORKFLOW_SLUG } from "../schema/platformWorkflows.js";
|
|
31
|
+
import { platformTriggerCreateSchema, platformTriggerDestinationInputSchema, platformTriggerEventsQuerySchema, platformTriggerEventsSchema, platformTriggerIdSchema, platformTriggerSchema, platformTriggerTypesQuerySchema, platformTriggerTypesSchema, platformTriggerUpdateSchema } from "../schema/platformTriggers.js";
|
|
32
|
+
export { platformTriggerCreateSchema, platformTriggerDestinationInputSchema, platformTriggerEventsQuerySchema, platformTriggerIdSchema, platformTriggerIntervalSchema, platformTriggerTypesQuerySchema, platformTriggerUpdateSchema } from "../schema/platformTriggers.js";
|
|
33
|
+
import { platformMemberAccessUpdateSchema, platformTeamAccessSchema } from "../schema/platformToolAccess.js";
|
|
34
|
+
export { platformMemberAccessUpdateSchema } from "../schema/platformToolAccess.js";
|
|
35
|
+
import { platformInputFileSchema, platformInputUploadSchema, PLATFORM_UPLOAD_MAX_BYTES } from "../schema/platformPublicExecution.js";
|
|
36
|
+
export { platformInputFileSchema, platformInputMimeSchema, platformInputFileMimeSchema, PLATFORM_INPUT_MAX_BYTES } from "../schema/platformPublicExecution.js";
|
|
37
|
+
import { PLATFORM_EXECUTION_MAX_WAIT_SECONDS, PLATFORM_EXECUTION_RESPONSE_MAX_BYTES, platformCatalogSchema, platformCatalogPageQuerySchema, platformCatalogPageSchema, platformQuoteSchema, platformExecutionSchema, platformExecutionContextSchema, platformExecutionResultPageSchema, platformExecutionIdSchema, platformCreateExecutionSchema, platformRecoverExecutionSchema, platformQuoteRequestSchema, platformInvalidArgumentFieldsSchema, platformToolNameSchema, platformArtifactIdSchema, platformArtifactDownloadSchema, platformExecutionListQuerySchema, platformExecutionListSchema } from "../schema/platformPublicExecution.js";
|
|
38
|
+
import { PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR, PlatformExecutionContextBudgetError, platformExecutionResultPageContext } from "../schema/platformPublicExecution.js";
|
|
39
|
+
import { PLATFORM_META_LIMITS, platformMetaSearchSchema, platformMetaSearchResultSchema, platformMetaSchemasSchema, platformMetaSchemasResultSchema, platformMetaConnectionsSchema, platformMetaConnectionsResultSchema, platformMetaExecuteSchema, platformMetaExecuteResultSchema, platformMetaWorkbenchSchema, platformMetaBashSchema, platformMetaRemoteResultSchema } from "../schema/platformMetaTools.js";
|
|
40
|
+
export { PLATFORM_META_LIMITS, platformMetaSearchSchema, platformMetaSchemasSchema, platformMetaConnectionsSchema, platformMetaExecuteSchema, platformMetaWorkbenchSchema, platformMetaBashSchema } from "../schema/platformMetaTools.js";
|
|
41
|
+
export { platformCreateExecutionSchema, platformToolInputSchema, platformQuoteRequestSchema, platformExecutionSchema, platformExecutionIdSchema, platformExecutionContextSchema, platformExecutionResultPageSchema, platformArtifactIdSchema, platformArtifactSchema, platformExecutionListQuerySchema } from "../schema/platformPublicExecution.js";
|
|
42
|
+
export const projectResultSchema = z.object({ data: z.object({ developerOrganizationId: z.string(), developerProjectId: z.string(), name: z.string() }), requestId: z.string() });
|
|
43
|
+
export const usageResultSchema = z.object({ data: z.object({ month: z.string(), requests: z.number().int().nonnegative(), amountCents: z.number().nonnegative(), currency: z.literal("usd") }), requestId: z.string() });
|
|
44
|
+
export const executionResultSchema = z.object({ data: platformExecutionSchema });
|
|
45
|
+
const executionContextResultSchema = z.object({ data: platformExecutionContextSchema });
|
|
46
|
+
const catalogResultSchema = z.object({ data: platformCatalogSchema });
|
|
47
|
+
export const platformToolDiscoverySchema = z.object({
|
|
48
|
+
query: z.string().trim().min(1).max(200).optional().describe("Substring of a tool's name, title, description, provider names or model IDs, ignoring case, whitespace, hyphens and underscores."),
|
|
49
|
+
category: platformCatalogSchema.shape.tools.element.shape.category.optional(),
|
|
50
|
+
provider: z.string().regex(/^[a-z][a-z0-9_]{1,63}$/).optional().describe("Filter discovery by a provider identifier from catalog metadata, such as anyapi or tikhub. Does not pin execution routing."),
|
|
51
|
+
availableOnly: z.boolean().optional().describe("Only tools with a configured provider; check executionEnabled separately."),
|
|
52
|
+
summary: z.boolean().optional().describe("Omit input schemas and examples; inspect a selected tool for those details."),
|
|
53
|
+
}).strict();
|
|
54
|
+
const quoteResultSchema = z.object({ data: platformQuoteSchema });
|
|
55
|
+
const customToolResult = z.object({ data: z.object({ tool: platformCustomToolSchema }) });
|
|
56
|
+
function customToolName(name) {
|
|
57
|
+
const parsed = platformCustomToolNameSchema.safeParse(name.startsWith(PLATFORM_CUSTOM_TOOL_PREFIX) ? name : `${PLATFORM_CUSTOM_TOOL_PREFIX}${name}`);
|
|
58
|
+
if (!parsed.success)
|
|
59
|
+
throw new Error("Provide a custom tool name such as custom__weekly_report.");
|
|
60
|
+
return parsed.data;
|
|
61
|
+
}
|
|
62
|
+
const teamSkillResult = z.object({ data: z.object({ skill: platformTeamSkillSchema }) });
|
|
63
|
+
function teamSkillSlug(slug) {
|
|
64
|
+
if (!PLATFORM_TEAM_SKILL_SLUG.test(slug))
|
|
65
|
+
throw new Error("Provide a team skill slug such as competitor_pricing.");
|
|
66
|
+
return slug;
|
|
67
|
+
}
|
|
68
|
+
const workflowResult = z.object({ data: z.object({ workflow: platformWorkflowSchema }) });
|
|
69
|
+
const workflowRunResult = z.object({ data: z.object({ run: platformWorkflowRunSchema }) });
|
|
70
|
+
function workflowSlug(slug) {
|
|
71
|
+
if (!PLATFORM_WORKFLOW_SLUG.test(slug))
|
|
72
|
+
throw new Error("Provide a workflow slug such as company_people.");
|
|
73
|
+
return slug;
|
|
74
|
+
}
|
|
75
|
+
function workflowRunId(id) {
|
|
76
|
+
if (!platformWorkflowRunIdSchema.safeParse(id).success)
|
|
77
|
+
throw new Error("Provide a workflow run ID such as wfr_….");
|
|
78
|
+
return id;
|
|
79
|
+
}
|
|
80
|
+
const triggerResult = z.object({ data: z.object({ trigger: platformTriggerSchema }) });
|
|
81
|
+
function triggerId(id) {
|
|
82
|
+
if (!platformTriggerIdSchema.safeParse(id).success)
|
|
83
|
+
throw new Error("Provide a trigger ID such as trg_….");
|
|
84
|
+
return id;
|
|
85
|
+
}
|
|
86
|
+
const idempotencySchema = z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/);
|
|
87
|
+
function normalizeToolSearch(value) { return value.toLowerCase().replace(/[\s_-]+/g, ""); }
|
|
88
|
+
const publicErrors = {
|
|
89
|
+
platform_quote_changed: "The priceVersion is stale or the maximum charge is below the current quote. This request was not admitted. Request a new quote for the same arguments/provider, copy its priceVersion and use an authorized maximum covering it. Keep the same idempotency key.",
|
|
90
|
+
platform_idempotency_key_used_for_another_request: "This idempotency key was already used for another request. Keep the original body to retrieve its result.",
|
|
91
|
+
platform_executions_disabled: "Paid executions are paused.",
|
|
92
|
+
platform_provider_not_configured: "This tool's provider is unavailable.",
|
|
93
|
+
platform_provider_own_key_required: "This call runs only on the project's own provider API key, which the project has not added. A person adds it on the console's Connections page; retrying without it fails the same way.",
|
|
94
|
+
platform_provider_not_supported: "This provider does not support the selected tool and arguments. Choose Auto or a compatible provider from the catalog.",
|
|
95
|
+
platform_insufficient_balance_or_spending_limit_reached: "The prepaid balance or monthly spending limit is insufficient.",
|
|
96
|
+
platform_upload_requires_purchase: "Uploading files needs a purchased balance or your own provider key; the free signup credit does not cover it. Add funds in the console, then retry.",
|
|
97
|
+
platform_request_rate_exceeded: "The API key's request limit was reached. Wait before retrying.",
|
|
98
|
+
platform_api_key_suspended: "This API key is suspended for ignoring request limits. Wait for the suspension to end.",
|
|
99
|
+
platform_organization_suspended: "This organization's API access is suspended for ignoring request limits. Wait for the suspension to end.",
|
|
100
|
+
platform_authentication_unavailable: "Account verification is temporarily unavailable. Retry the same request shortly; the key does not need to change.",
|
|
101
|
+
platform_api_key_scope_required: "The API key is missing the required scope.",
|
|
102
|
+
platform_artifact_unavailable: "This output file is unavailable or its retention period has ended.",
|
|
103
|
+
platform_artifact_download_limit_reached: "This output file reached its limit of download URLs for the past 24 hours. Reuse a URL issued in the last five minutes, or try again later.",
|
|
104
|
+
platform_meta_tools_unavailable: "Session meta tools are unavailable on this API deployment.",
|
|
105
|
+
platform_tool_session_required: "Start a tool session with searchMetaTools and retain its session ID.",
|
|
106
|
+
platform_tool_session_unavailable: "This session is expired or unavailable to this project key. Preserve any uncertain purchases for reconciliation.",
|
|
107
|
+
platform_tool_session_limit_reached: "This key has reached its active tool session limit. Reuse an existing session or wait for expiry.",
|
|
108
|
+
platform_tool_session_busy: "This sandbox session has an active execution. Wait on that existing execution; needs_review requires reconciliation.",
|
|
109
|
+
platform_tool_session_state_lost: "This sandbox session has no reusable checkpoint. Preserve uncertain purchases and do not claim earlier state survives.",
|
|
110
|
+
[PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR]: "Execution metadata exceeds the context budget. Retain the execution ID to recover the raw result or select a narrower path.",
|
|
111
|
+
platform_tool_result_selection_invalid: "This result has no such path or fields. Read it with a shorter path, or without path and fields, to see its shape.",
|
|
112
|
+
platform_invalid_tool_request: "The request does not match the quote/execution contract. Check tool, arguments, provider, sessionId, priceVersion and maximumChargeMicros.",
|
|
113
|
+
platform_invalid_tool_arguments: "The arguments do not match this tool's input schema. Inspect the tool's schema and quote again.",
|
|
114
|
+
platform_tool_unavailable: "This tool is not in this project's catalog. Search the catalog again for an available tool.",
|
|
115
|
+
platform_idempotency_key_required: "Paid submissions need an idempotency key of 8-128 letters, digits, '.', '_', ':' or '-'. Retain it for retries.",
|
|
116
|
+
platform_invalid_media_input: "A media input is invalid for this tool. Check its type, size and duration against the tool's schema.",
|
|
117
|
+
platform_media_type_mismatch: "The file content does not match its declared media type.",
|
|
118
|
+
platform_image_reference_required: "This tool needs a reference image as documented in its schema.",
|
|
119
|
+
platform_audio_duration_exceeds_quote: "The audio is longer than the quoted duration. Quote again with the actual duration.",
|
|
120
|
+
platform_input_file_unavailable: "The uploaded input file is unavailable or expired. Upload it again and use the new file ID.",
|
|
121
|
+
platform_upload_quota_exceeded: "This project's daily input upload allowance is used up. Wait before uploading more files.",
|
|
122
|
+
platform_media_source_unavailable: "A referenced media source could not be read. Use a public URL, a current project file or a project artifact.",
|
|
123
|
+
platform_model_tariff_unavailable: "This model has no current price and cannot be quoted. Choose another model.",
|
|
124
|
+
platform_connection_not_configured: "This tool needs a project account connection. Connect the account in the console, then retry.",
|
|
125
|
+
platform_connection_reauthorize: "The connected account must be authorized again in the console before this tool can use it.",
|
|
126
|
+
platform_connection_scope_required: "The connected account lacks a permission this operation needs. Reconnect it with that permission in the console.",
|
|
127
|
+
platform_connection_policy_denied: "This connection's policy does not allow this operation or data range. A connection owner or project admin can change the policy in the console; retrying unchanged fails the same way.",
|
|
128
|
+
platform_approval_required: "This operation needs a team admin's approval on the console's Connections page. After approval, retry the same request.",
|
|
129
|
+
platform_connection_access_denied: "This key or member may not use this connection, or this operation on it. Use a connection from /tools/connections with a tool the tool list shows, or ask a team admin to grant it.",
|
|
130
|
+
platform_tool_access_denied: "This key or member may not use this tool. Add it to the key's tools in the console, ask a team admin to grant it, or use another key.",
|
|
131
|
+
platform_connection_authority_removed: "This key or member no longer has permission to use this connection.",
|
|
132
|
+
platform_connection_required: "No connection this key can use fits this tool. Connect an account in the console, or ask for access to a shared one.",
|
|
133
|
+
platform_connection_choice_required: "More than one connection fits this tool. Pass the connectionId of one from /tools/connections.",
|
|
134
|
+
platform_connection_provider_mismatch: "The selected connection is for a different service than this tool. Choose a connection of the tool's service.",
|
|
135
|
+
platform_connection_resource_mismatch: "The selected resource does not belong to this connected account. Use an ID listed for this connection.",
|
|
136
|
+
platform_connection_priced_operation_required: "X requests run only through X's separately priced tools. Use those instead of the generic request.",
|
|
137
|
+
platform_connection_provider_unavailable: "The connected account's service is temporarily unavailable. Retry later.",
|
|
138
|
+
platform_invalid_json: "The request body is not valid JSON.",
|
|
139
|
+
platform_body_too_large: "The request body is too large.",
|
|
140
|
+
platform_custom_tool_not_found: "No custom tool of that name exists in this project, or this key's member may not use it. List custom tools first.",
|
|
141
|
+
platform_custom_tool_exists: "This project already has a custom tool with that slug. Read it and update it instead, or choose another slug.",
|
|
142
|
+
platform_custom_tool_invalid: "The custom tool definition is invalid. Fix the fields named in details.fields.",
|
|
143
|
+
platform_custom_tool_schema_invalid: "The input schema cannot be used: it must be a JSON Schema object with properties, must compile, and cannot define sourceExecutionIds.",
|
|
144
|
+
platform_custom_tool_version_conflict: "The custom tool changed since it was read. Read it again and apply the edit to its current version (details.version).",
|
|
145
|
+
platform_custom_tool_not_editable: "Only the tool's creator or an organization admin may change this custom tool.",
|
|
146
|
+
platform_custom_tool_endpoints_unavailable: "API endpoints are not available in this deployment.",
|
|
147
|
+
platform_custom_tool_kind_immutable: "A custom tool keeps its kind: a Python tool cannot become an API endpoint or the reverse. Create a new tool instead.",
|
|
148
|
+
platform_custom_tool_endpoint_secrets_required: "Send the secret again for every binding: the base URL moved to another origin, or a binding's saved secret is gone. Saved secrets never travel to a new host.",
|
|
149
|
+
platform_custom_tool_restore_secret_missing: "That version names a binding whose secret is no longer saved. Add the binding again with its secret instead of restoring.",
|
|
150
|
+
platform_custom_tool_secret_ref_forbidden: "Only a team admin or the member who saved that secret may bind it to an endpoint. Use an inline secret, or ask an admin.",
|
|
151
|
+
platform_custom_tool_secret_ref_console_only: "A saved team secret can only be bound from the console, never with an API key. Send an inline secret instead.",
|
|
152
|
+
platform_custom_tool_limit_reached: "This project has reached its custom tool limit. Delete an unused tool first.",
|
|
153
|
+
platform_custom_tool_version_not_found: "That version of the custom tool is not kept. List its versions first.",
|
|
154
|
+
platform_team_skill_not_found: "No team skill with that slug exists in this project. List team skills first.",
|
|
155
|
+
platform_team_skill_exists: "This project already has a team skill with that slug. Read it and update it instead.",
|
|
156
|
+
platform_team_skill_invalid: "The team skill is invalid. Fix the fields named in details.fields.",
|
|
157
|
+
platform_team_skill_version_conflict: "The team skill changed since it was read. Read it again and apply the edit to its current version (details.version).",
|
|
158
|
+
platform_team_skill_not_deletable: "Only the skill's creator or an organization admin may delete this team skill. Update it instead.",
|
|
159
|
+
platform_team_skill_limit_reached: "This project has reached its team skill limit. Merge or delete skills first.",
|
|
160
|
+
platform_team_skill_version_not_found: "That version of the team skill is not kept. List its versions first.",
|
|
161
|
+
platform_workflow_not_found: "No workflow with that slug exists in this project. List workflows first.",
|
|
162
|
+
platform_workflow_exists: "This project already has a workflow with that slug. Read it and update it instead, or choose another slug.",
|
|
163
|
+
platform_workflow_invalid: "The workflow definition is invalid. Fix what details.fields or details.reason names: every tool must be in the project's catalog, and a reference names a declared input or an earlier step.",
|
|
164
|
+
platform_workflow_version_conflict: "The workflow changed since it was read. Read it again and apply the edit to its current version (details.version).",
|
|
165
|
+
platform_workflow_not_editable: "Only the workflow's creator or an organization admin may change this workflow.",
|
|
166
|
+
platform_workflow_limit_reached: "This project has reached its workflow limit. Delete an unused workflow first.",
|
|
167
|
+
platform_workflow_version_not_found: "That version of the workflow is not kept. List its versions first.",
|
|
168
|
+
platform_workflow_inputs_invalid: "The run inputs do not match the workflow's input schema. Fix the inputs named in details.fields.",
|
|
169
|
+
platform_workflow_run_limit_reached: "This project already has the most workflow runs in progress. Wait for one to finish or cancel one.",
|
|
170
|
+
platform_workflow_run_not_found: "No workflow run with that ID exists in this project.",
|
|
171
|
+
platform_workflow_run_owner_required: "Only the member who started the run or an admin may cancel it.",
|
|
172
|
+
platform_workflow_budget_exceeded: "The run stopped before a step whose price no longer fit what was left of maximumChargeMicros. Steps before it were charged; run again with a larger budget.",
|
|
173
|
+
platform_workflow_reference_missing: "The run stopped before a step whose $input or $step reference had no value. Check the earlier step's output and the reference path.",
|
|
174
|
+
platform_workflow_run_cancelled: "The run was cancelled; no later step was bought.",
|
|
175
|
+
platform_workflow_unavailable: "The run could not continue after repeated failures. Start it again.",
|
|
176
|
+
platform_triggers_unavailable: "Triggers are not available in this deployment.",
|
|
177
|
+
platform_trigger_invalid: "The trigger request is invalid. Fix the fields named in details.fields.",
|
|
178
|
+
platform_trigger_not_found: "No trigger with that ID exists in this project. List triggers first.",
|
|
179
|
+
platform_trigger_not_editable: "Only the trigger's creator or an organization admin may change this trigger.",
|
|
180
|
+
platform_trigger_limit_reached: "This project has reached its trigger limit. Delete an unused trigger first.",
|
|
181
|
+
platform_trigger_key_invalid: "The API key the trigger runs as is no longer valid. Create the trigger again with a key that may execute tools.",
|
|
182
|
+
platform_trigger_connection_app_mismatch: "The connection belongs to another app than the tool or trigger type. Use a connection of the same app.",
|
|
183
|
+
platform_trigger_operation_unknown: "That tool is not a read operation of the app. List trigger types for the app and choose one of its operations.",
|
|
184
|
+
platform_trigger_arguments_invalid: "The poll arguments do not match the operation's input schema. Fix the arguments named in details.fields.",
|
|
185
|
+
platform_trigger_items_path_not_found: "itemsPath points at nothing in the operation's response. Fix it or omit it to use the largest list.",
|
|
186
|
+
platform_trigger_type_unknown: "No trigger type with that name exists. List trigger types for the app first.",
|
|
187
|
+
platform_trigger_type_unavailable: "This trigger type cannot be received in this environment yet.",
|
|
188
|
+
platform_trigger_config_invalid: "The config does not match the trigger type's configSchema. Fix the fields named in details.fields.",
|
|
189
|
+
platform_trigger_destination_limit_reached: "This trigger has the most destinations allowed. Remove one first.",
|
|
190
|
+
platform_trigger_destination_not_found: "No destination with that ID exists on this trigger. Read the trigger first.",
|
|
191
|
+
platform_trigger_poll_price_above_maximum: "A poll now costs more than maximumChargeMicrosPerPoll, so the trigger stopped. Raise it or delete the trigger.",
|
|
192
|
+
platform_trigger_poll_failed: "The app's read operation failed, so the trigger stopped. Check the connection and enable the trigger again.",
|
|
193
|
+
platform_trigger_poll_unavailable: "The app's read operation is unavailable, so the trigger stopped. Enable the trigger again later.",
|
|
194
|
+
platform_trigger_registering: "Relvo is still registering the webhook with the app. Read the trigger again shortly.",
|
|
195
|
+
};
|
|
196
|
+
const usd = (micros) => `$${(Number(micros) / 1_000_000).toFixed(micros.length > 4 ? 2 : 4)}`;
|
|
197
|
+
function shortfallText({ reason, requiredMicros, availableMicros }) {
|
|
198
|
+
if (reason === "balance")
|
|
199
|
+
return ` It needs ${usd(requiredMicros)} and the balance has ${usd(availableMicros)}. Top up the balance (relvo wallet top-up, or the console), then retry with the same idempotency key.`;
|
|
200
|
+
const scope = reason === "project_monthly_limit" ? "project's monthly" : reason === "api_key_daily_limit" ? "API key's daily" : reason === "organization_daily_limit" ? "organization's daily" : "organization's monthly";
|
|
201
|
+
const reset = reason.endsWith("_daily_limit") ? " The daily count starts again at 00:00 UTC." : "";
|
|
202
|
+
return ` It needs ${usd(requiredMicros)} and ${usd(availableMicros)} remains of the ${scope} spending limit.${reset} Topping up does not help; a person must raise the limit in the console.`;
|
|
203
|
+
}
|
|
204
|
+
export class PlatformApiError extends Error {
|
|
205
|
+
status;
|
|
206
|
+
code;
|
|
207
|
+
fields;
|
|
208
|
+
shortfall;
|
|
209
|
+
provider;
|
|
210
|
+
scope;
|
|
211
|
+
constructor(status, code, productName = PLATFORM_PRODUCT_NAMES.relvo, fields = [], shortfall, provider, scope) {
|
|
212
|
+
super(`${productName} API request failed (${status}). ${code && Object.hasOwn(publicErrors, code) ? publicErrors[code] : "Check the developer key, project access and limits in the console."}${fields.length ? ` Invalid fields: ${fields.join(", ")}.` : ""}${shortfall ? shortfallText(shortfall) : ""}${provider ? ` Provider key needed: ${provider}.` : ""}${scope ? ` Missing scope: ${scope}; create a key that includes it in the console.` : ""}`);
|
|
213
|
+
this.status = status;
|
|
214
|
+
this.code = code;
|
|
215
|
+
this.fields = fields;
|
|
216
|
+
this.shortfall = shortfall;
|
|
217
|
+
this.provider = provider;
|
|
218
|
+
this.scope = scope;
|
|
219
|
+
this.name = "PlatformApiError";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function contextBudgetFailure(error, id, kind) {
|
|
223
|
+
if (error instanceof PlatformApiError && error.code === PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR)
|
|
224
|
+
throw new PlatformExecutionContextBudgetError(id, kind);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
async function responseJson(response, maximumBytes, productName) {
|
|
228
|
+
const body = await readBoundedBody(response, maximumBytes);
|
|
229
|
+
if (body.status === "missing")
|
|
230
|
+
throw new Error(`The ${productName} API returned an empty response.`);
|
|
231
|
+
if (body.status === "too_large")
|
|
232
|
+
throw new Error(`The ${productName} API response is too large.`);
|
|
233
|
+
try {
|
|
234
|
+
return JSON.parse(new TextDecoder().decode(body.bytes));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
throw new Error(`The ${productName} API returned an invalid response.`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export function hasRelvoEnvironment(env) {
|
|
241
|
+
return env.RELVO_API_URL !== undefined || env.RELVO_API_KEY !== undefined;
|
|
242
|
+
}
|
|
243
|
+
export function configFromEnvironment(env, source) {
|
|
244
|
+
if (hasRelvoEnvironment(env)) {
|
|
245
|
+
const apiUrl = env.RELVO_API_URL, apiKey = env.RELVO_API_KEY;
|
|
246
|
+
if (!apiUrl || !apiKey)
|
|
247
|
+
throw new Error("Set RELVO_API_URL and RELVO_API_KEY from the Relvo console.");
|
|
248
|
+
return { apiUrl, apiKey, source, product: "relvo", local: env.RELVO_ENV === "local" };
|
|
249
|
+
}
|
|
250
|
+
if (!env.NOIMOS_PLATFORM_API_URL || !env.NOIMOS_PLATFORM_API_KEY) {
|
|
251
|
+
throw new Error(env.NOIMOS_PLATFORM_API_URL !== undefined || env.NOIMOS_PLATFORM_API_KEY !== undefined
|
|
252
|
+
? "Set NOIMOS_PLATFORM_API_URL and NOIMOS_PLATFORM_API_KEY from the developer console."
|
|
253
|
+
: "Set RELVO_API_URL and RELVO_API_KEY from the Relvo console.");
|
|
254
|
+
}
|
|
255
|
+
return { apiUrl: env.NOIMOS_PLATFORM_API_URL, apiKey: env.NOIMOS_PLATFORM_API_KEY, source, product: "noimos", local: env.NOIMOS_PLATFORM_ENV === "local" };
|
|
256
|
+
}
|
|
257
|
+
export class PlatformClient {
|
|
258
|
+
config;
|
|
259
|
+
transport;
|
|
260
|
+
toolNameHints = new ToolNameHints();
|
|
261
|
+
async requestJournalScope() {
|
|
262
|
+
const bytes = new TextEncoder().encode(JSON.stringify([this.baseUrl, this.config.apiKey]));
|
|
263
|
+
return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)), value => value.toString(16).padStart(2, "0")).join("");
|
|
264
|
+
}
|
|
265
|
+
baseUrl;
|
|
266
|
+
productName;
|
|
267
|
+
holdsReads;
|
|
268
|
+
constructor(config, transport = (input, init) => fetch(input, init)) {
|
|
269
|
+
this.config = config;
|
|
270
|
+
this.transport = transport;
|
|
271
|
+
const product = config.product ?? platformKeyProduct(config.apiKey);
|
|
272
|
+
this.productName = PLATFORM_PRODUCT_NAMES[product === null || isRelvoProduct(product) ? "relvo" : "noimos"];
|
|
273
|
+
this.holdsReads = product !== null && isRelvoProduct(product);
|
|
274
|
+
const url = new URL(config.apiUrl);
|
|
275
|
+
if (url.username || url.password || url.search || url.hash || (url.protocol !== "https:" && !(config.local && url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))))
|
|
276
|
+
throw new Error(`The ${this.productName} API URL must use HTTPS. Explicit local mode permits loopback HTTP.`);
|
|
277
|
+
if (!PLATFORM_API_KEY_PATTERN.test(config.apiKey) || config.product && platformKeyProduct(config.apiKey) !== config.product)
|
|
278
|
+
throw new Error(`A dedicated ${PLATFORM_KEY_PREFIXES[config.product ?? "relvo"]} developer API key is required.`);
|
|
279
|
+
this.baseUrl = url.href.replace(/\/$/, "");
|
|
280
|
+
}
|
|
281
|
+
async request(path, schema, options) {
|
|
282
|
+
const body = options?.body === undefined ? undefined : JSON.stringify(options.body);
|
|
283
|
+
const upload = path === PLATFORM_ROUTES.upload, meta = PLATFORM_META_ROUTES.includes(path);
|
|
284
|
+
if (body && new TextEncoder().encode(body).byteLength > (upload ? PLATFORM_UPLOAD_MAX_BYTES : meta ? PLATFORM_META_LIMITS.requestBytes : 65536))
|
|
285
|
+
throw new Error(upload ? "The upload exceeds 12 MiB." : meta ? "The meta tool request exceeds 1 MiB." : "The tool request exceeds 64 KiB.");
|
|
286
|
+
let response;
|
|
287
|
+
const timeoutMs = options?.timeoutMs ?? 20000;
|
|
288
|
+
const timeoutMessage = `The ${this.productName} API did not answer within ${Math.round(timeoutMs / 1000)} s. If submitting an execution, retry with the same body and idempotency key.`;
|
|
289
|
+
let timeout;
|
|
290
|
+
try {
|
|
291
|
+
options?.signal?.throwIfAborted();
|
|
292
|
+
timeout = AbortSignal.timeout(timeoutMs);
|
|
293
|
+
response = await this.transport(`${this.baseUrl}${path}`, { method: options?.method ?? "GET", redirect: "error", signal: options?.signal ? AbortSignal.any([timeout, options.signal]) : timeout,
|
|
294
|
+
headers: { authorization: `Bearer ${this.config.apiKey}`, [PLATFORM_CLIENT_HEADERS[this.config.product ?? platformKeyProduct(this.config.apiKey) ?? "relvo"]]: this.config.source, "idempotency-key": options?.idempotencyKey ?? crypto.randomUUID(),
|
|
295
|
+
...(body === undefined ? {} : { "content-type": "application/json" }), ...(options?.prefer ? { prefer: options.prefer } : {}) }, ...(body === undefined ? {} : { body }) });
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
options?.signal?.throwIfAborted();
|
|
299
|
+
throw new Error(timeout?.aborted
|
|
300
|
+
? timeoutMessage
|
|
301
|
+
: `The ${this.productName} API could not be reached. If submitting an execution, retry with the same body and idempotency key.`);
|
|
302
|
+
}
|
|
303
|
+
const maximumBytes = options?.maximumResponseBytes ?? (meta ? PLATFORM_META_LIMITS.contextBytes : PLATFORM_EXECUTION_RESPONSE_MAX_BYTES);
|
|
304
|
+
const payload = await responseJson(response, maximumBytes, this.productName).catch(() => {
|
|
305
|
+
options?.signal?.throwIfAborted();
|
|
306
|
+
if (!response.ok)
|
|
307
|
+
throw new PlatformApiError(response.status, null, this.productName);
|
|
308
|
+
if (timeout?.aborted)
|
|
309
|
+
throw new Error(timeoutMessage);
|
|
310
|
+
throw new Error(`The ${this.productName} API returned an invalid response. If submitting an execution, retain the same body and idempotency key.`);
|
|
311
|
+
});
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
const parsed = z.object({ error: z.object({ message: z.string(), details: z.unknown().optional() }) }).safeParse(payload);
|
|
314
|
+
const named = parsed.success ? [parsed.data.error.message, z.object({ code: z.string() }).safeParse(parsed.data.error.details).data?.code] : [];
|
|
315
|
+
const code = named.find(value => value !== undefined && Object.hasOwn(publicErrors, value)) ?? null;
|
|
316
|
+
const details = parsed.success ? parsed.data.error.details : undefined;
|
|
317
|
+
const fields = code === "platform_invalid_tool_arguments" ? z.object({ fields: platformInvalidArgumentFieldsSchema }).safeParse(details).data?.fields ?? [] : [];
|
|
318
|
+
const shortfall = code === "platform_insufficient_balance_or_spending_limit_reached" ? platformFundingShortfallSchema.safeParse(details).data : undefined;
|
|
319
|
+
const provider = code === "platform_provider_own_key_required" ? z.object({ provider: z.string().regex(/^[a-z0-9]{1,40}$/) }).safeParse(details).data?.provider : undefined;
|
|
320
|
+
const scope = code === "platform_api_key_scope_required" ? z.object({ scope: z.string().regex(/^[a-z]{1,20}:[a-z]{1,20}$/) }).safeParse(details).data?.scope : undefined;
|
|
321
|
+
throw new PlatformApiError(response.status, code, this.productName, fields, shortfall, provider, scope);
|
|
322
|
+
}
|
|
323
|
+
const parsed = schema.safeParse(payload);
|
|
324
|
+
if (!parsed.success)
|
|
325
|
+
throw new Error(`The ${this.productName} API returned an unexpected response. If submitting an execution, retain the same body and idempotency key.`);
|
|
326
|
+
return parsed.data;
|
|
327
|
+
}
|
|
328
|
+
project() { return this.request(route(PLATFORM_ROUTES.project), projectResultSchema); }
|
|
329
|
+
usage() { return this.request(route(PLATFORM_ROUTES.usage), usageResultSchema); }
|
|
330
|
+
connections(input = {}) {
|
|
331
|
+
const { provider, offset, limit } = platformConnectionCatalogQuerySchema.parse(input);
|
|
332
|
+
const query = new URLSearchParams({ offset: String(offset), limit: String(limit) });
|
|
333
|
+
if (provider)
|
|
334
|
+
query.set("provider", provider);
|
|
335
|
+
return this.request(`${route(PLATFORM_ROUTES.connections)}?${query}`, z.object({ data: platformConnectionCatalogSchema }), { maximumResponseBytes: PLATFORM_CONNECTION_CATALOG_LIMITS.responseBytes });
|
|
336
|
+
}
|
|
337
|
+
tools() { return this.request(route(PLATFORM_ROUTES.tools), catalogResultSchema); }
|
|
338
|
+
toolsPage(input = {}) {
|
|
339
|
+
const { offset, limit } = platformCatalogPageQuerySchema.parse(input);
|
|
340
|
+
return this.request(`${route(PLATFORM_ROUTES.tools)}?${new URLSearchParams({ offset: String(offset), limit: String(limit) })}`, z.object({ data: platformCatalogPageSchema }));
|
|
341
|
+
}
|
|
342
|
+
realtimeModels() { return this.request("/tools/realtime/models", z.object({ data: realtimeVoiceModelsSchema })); }
|
|
343
|
+
createRealtimeSession(input, signal) {
|
|
344
|
+
return this.request("/tools/realtime/sessions", z.object({ data: realtimeVoiceGrantSchema }), { method: "POST", body: realtimeVoiceConfigSchema.parse(input), signal });
|
|
345
|
+
}
|
|
346
|
+
async connectVoice(input, options = {}) {
|
|
347
|
+
const { data } = await this.createRealtimeSession(input, options.signal);
|
|
348
|
+
return connectRealtimeVoice(this.baseUrl, data, options);
|
|
349
|
+
}
|
|
350
|
+
async audioTools() {
|
|
351
|
+
const { data } = await this.tools();
|
|
352
|
+
return { data: { ...data, tools: data.tools.filter(tool => /(?:audio|speech|voice|music|sound)/.test(tool.name) || tool.name.startsWith("elevenlabs_")) } };
|
|
353
|
+
}
|
|
354
|
+
generateSpeech(input, idempotencyKey) { return this.createExecution({ ...input, tool: "generate_speech" }, idempotencyKey); }
|
|
355
|
+
transcribeAudio(input, idempotencyKey) { return this.createExecution({ ...input, tool: "transcribe_audio" }, idempotencyKey); }
|
|
356
|
+
translateAudio(input, idempotencyKey) { return this.createExecution({ ...input, tool: "translate_audio" }, idempotencyKey); }
|
|
357
|
+
generateAudioResponse(input, idempotencyKey) { return this.createExecution({ ...input, tool: "generate_audio_response" }, idempotencyKey); }
|
|
358
|
+
skills() { return this.request(PLATFORM_ROUTES.skills, z.object({ data: platformSkillsBundleSchema }), { maximumResponseBytes: 1048640 }); }
|
|
359
|
+
async skill(name) {
|
|
360
|
+
platformSkillSchema.shape.name.parse(name);
|
|
361
|
+
const { data } = await this.skills();
|
|
362
|
+
const skill = data.skills.find((entry) => entry.name === name);
|
|
363
|
+
if (!skill)
|
|
364
|
+
throw new Error(`The requested ${this.productName} guide is unavailable. Use platform_skills to discover current guides.`);
|
|
365
|
+
return { data: { version: data.version, ...skill } };
|
|
366
|
+
}
|
|
367
|
+
uploadInput(input) {
|
|
368
|
+
return this.request(PLATFORM_ROUTES.upload, z.object({ data: platformInputFileSchema }), { method: "POST", body: platformInputUploadSchema.parse(input) });
|
|
369
|
+
}
|
|
370
|
+
async discoverTools(options = {}) {
|
|
371
|
+
const { query, category, provider, availableOnly, summary } = platformToolDiscoverySchema.parse(options);
|
|
372
|
+
const { data } = await this.tools();
|
|
373
|
+
const search = query && normalizeToolSearch(query);
|
|
374
|
+
const tools = data.tools.filter((tool) => (!search || normalizeToolSearch(`${tool.name} ${tool.title ?? ""} ${tool.description} ${(tool.providers ?? []).map(row => row.name).join(" ")} ${(tool.models ?? []).map(row => row.id).join(" ")}`).includes(search)) &&
|
|
375
|
+
(!category || tool.category === category) && (!provider || tool.providers?.some(row => row.id === provider)) && (!availableOnly || tool.available));
|
|
376
|
+
return { data: { ...data, tools: summary ? tools.map(({ inputSchema, example, ...tool }) => tool) : tools } };
|
|
377
|
+
}
|
|
378
|
+
searchTools(options = {}) {
|
|
379
|
+
const credential = this.config.apiKey;
|
|
380
|
+
return this.request(PLATFORM_ROUTES.discover, z.object({ data: toolDiscoveryResultSchema }), { method: "POST", body: toolDiscoveryInputSchema.parse(options), timeoutMs: TOOL_DISCOVERY_REQUEST_TIMEOUT_MS })
|
|
381
|
+
.then(result => { this.toolNameHints.remember(result.data.tools.map(tool => tool.name), credential); return result; });
|
|
382
|
+
}
|
|
383
|
+
searchMetaTools(input) {
|
|
384
|
+
const credential = this.config.apiKey;
|
|
385
|
+
return this.request(PLATFORM_ROUTES.metaSearch, z.object({ data: platformMetaSearchResultSchema }), { method: "POST", body: platformMetaSearchSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs })
|
|
386
|
+
.then(result => { this.toolNameHints.remember(result.data.results.flatMap(group => group.tools.map(tool => tool.name)), credential); return result; });
|
|
387
|
+
}
|
|
388
|
+
getToolSchemas(input) { return this.request(PLATFORM_ROUTES.metaSchemas, z.object({ data: platformMetaSchemasResultSchema }), { method: "POST", body: platformMetaSchemasSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
|
|
389
|
+
manageConnections(input) { return this.request(PLATFORM_ROUTES.metaConnections, z.object({ data: platformMetaConnectionsResultSchema }), { method: "POST", body: platformMetaConnectionsSchema.parse(input) }); }
|
|
390
|
+
multiExecute(input) { return this.request(PLATFORM_ROUTES.metaExecute, z.object({ data: platformMetaExecuteResultSchema }), { method: "POST", body: platformMetaExecuteSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
|
|
391
|
+
remoteWorkbench(input) { return this.request(PLATFORM_ROUTES.metaWorkbench, z.object({ data: platformMetaRemoteResultSchema }), { method: "POST", body: platformMetaWorkbenchSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
|
|
392
|
+
remoteBash(input) { return this.request(PLATFORM_ROUTES.metaBash, z.object({ data: platformMetaRemoteResultSchema }), { method: "POST", body: platformMetaBashSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
|
|
393
|
+
inspectTool(name) {
|
|
394
|
+
platformToolNameSchema.parse(name);
|
|
395
|
+
const schema = platformCatalogSchema.shape.tools.element.extend(platformCatalogSchema.omit({ tools: true }).shape);
|
|
396
|
+
const credential = this.config.apiKey;
|
|
397
|
+
return this.request(route(PLATFORM_ROUTES.definition, { name: encodeURIComponent(name) }), z.object({ data: schema })).catch(error => {
|
|
398
|
+
if (error instanceof PlatformApiError && error.status === 404 && error.code === "platform_tool_unavailable")
|
|
399
|
+
error.message = `Tool is unavailable in the ${this.productName} catalog. ${this.toolNameHints.hint(name, credential) ?? "Run tools to discover supported operations."}`;
|
|
400
|
+
throw error;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
async tool(name) { return this.inspectTool(name); }
|
|
404
|
+
executions(input = {}) {
|
|
405
|
+
const parsed = platformExecutionListQuerySchema.parse(input);
|
|
406
|
+
const query = new URLSearchParams({ limit: String(parsed.limit) });
|
|
407
|
+
if (parsed.before)
|
|
408
|
+
query.set("before", parsed.before);
|
|
409
|
+
if (parsed.status)
|
|
410
|
+
query.set("status", parsed.status);
|
|
411
|
+
if (parsed.tool)
|
|
412
|
+
query.set("tool", parsed.tool);
|
|
413
|
+
return this.request(`${route(PLATFORM_ROUTES.executions)}?${query}`, z.object({ data: platformExecutionListSchema }));
|
|
414
|
+
}
|
|
415
|
+
monitors() { return this.request(route(PLATFORM_ROUTES.monitors), z.object({ data: z.object({ monitors: z.array(platformMonitorSchema) }) })); }
|
|
416
|
+
createMonitor(input) {
|
|
417
|
+
return this.request(route(PLATFORM_ROUTES.monitors), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: platformMonitorCreateSchema.parse(input) });
|
|
418
|
+
}
|
|
419
|
+
deleteMonitor(id) {
|
|
420
|
+
if (!PLATFORM_MONITOR_ID.test(id))
|
|
421
|
+
throw new Error("Provide a valid monitor ID.");
|
|
422
|
+
return this.request(route(PLATFORM_ROUTES.monitorDelete, { id }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
|
|
423
|
+
}
|
|
424
|
+
monitorEvents(input = {}) {
|
|
425
|
+
const { monitorId, ...rest } = input;
|
|
426
|
+
if (monitorId !== undefined && !PLATFORM_MONITOR_ID.test(monitorId))
|
|
427
|
+
throw new Error("Provide a valid monitor ID.");
|
|
428
|
+
const parsed = platformMonitorEventsQuerySchema.parse(rest);
|
|
429
|
+
const query = new URLSearchParams({ limit: String(parsed.limit) });
|
|
430
|
+
if (parsed.after)
|
|
431
|
+
query.set("after", parsed.after);
|
|
432
|
+
const path = monitorId ? route(PLATFORM_ROUTES.monitorEvents, { id: monitorId }) : route(PLATFORM_ROUTES.monitorFeed);
|
|
433
|
+
return this.request(`${path}?${query}`, z.object({ data: platformMonitorEventsSchema }));
|
|
434
|
+
}
|
|
435
|
+
quoteMonitor(input) {
|
|
436
|
+
return this.request(route(PLATFORM_ROUTES.monitorQuote), z.object({ data: z.object({ quote: platformMonitorQuoteResultSchema }) }), { method: "POST", body: platformMonitorQuoteSchema.parse(input) });
|
|
437
|
+
}
|
|
438
|
+
resumeMonitor(id, input = {}) {
|
|
439
|
+
if (!PLATFORM_MONITOR_ID.test(id))
|
|
440
|
+
throw new Error("Provide a valid monitor ID.");
|
|
441
|
+
return this.request(route(PLATFORM_ROUTES.monitorResume, { id }), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: platformMonitorResumeSchema.parse(input) });
|
|
442
|
+
}
|
|
443
|
+
triggers() { return this.request(route(PLATFORM_ROUTES.triggers), z.object({ data: z.object({ triggers: z.array(platformTriggerSchema) }) })); }
|
|
444
|
+
trigger(id) { return this.request(route(PLATFORM_ROUTES.trigger, { id: triggerId(id) }), triggerResult); }
|
|
445
|
+
createTrigger(input) {
|
|
446
|
+
return this.request(route(PLATFORM_ROUTES.triggers), triggerResult, { method: "POST", body: platformTriggerCreateSchema.parse(input) });
|
|
447
|
+
}
|
|
448
|
+
updateTrigger(id, input) {
|
|
449
|
+
return this.request(route(PLATFORM_ROUTES.triggerUpdate, { id: triggerId(id) }), triggerResult, { method: "POST", body: platformTriggerUpdateSchema.parse(input) });
|
|
450
|
+
}
|
|
451
|
+
deleteTrigger(id) {
|
|
452
|
+
return this.request(route(PLATFORM_ROUTES.triggerDelete, { id: triggerId(id) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
|
|
453
|
+
}
|
|
454
|
+
triggerEvents(input = {}) {
|
|
455
|
+
const { triggerId: id, ...rest } = input;
|
|
456
|
+
const parsed = platformTriggerEventsQuerySchema.parse(rest);
|
|
457
|
+
const query = new URLSearchParams({ limit: String(parsed.limit) });
|
|
458
|
+
if (parsed.after)
|
|
459
|
+
query.set("after", parsed.after);
|
|
460
|
+
const path = id === undefined ? route(PLATFORM_ROUTES.triggerFeed) : route(PLATFORM_ROUTES.triggerEvents, { id: triggerId(id) });
|
|
461
|
+
return this.request(`${path}?${query}`, z.object({ data: platformTriggerEventsSchema }));
|
|
462
|
+
}
|
|
463
|
+
triggerTypes(input) {
|
|
464
|
+
const parsed = platformTriggerTypesQuerySchema.parse(input);
|
|
465
|
+
const query = new URLSearchParams({ app: parsed.app, offset: String(parsed.offset), limit: String(parsed.limit) });
|
|
466
|
+
if (parsed.query)
|
|
467
|
+
query.set("query", parsed.query);
|
|
468
|
+
if (parsed.connectionId)
|
|
469
|
+
query.set("connectionId", parsed.connectionId);
|
|
470
|
+
return this.request(`${route(PLATFORM_ROUTES.triggerTypes)}?${query}`, z.object({ data: platformTriggerTypesSchema }));
|
|
471
|
+
}
|
|
472
|
+
addTriggerDestination(id, destination) {
|
|
473
|
+
return this.request(route(PLATFORM_ROUTES.triggerDestinations, { id: triggerId(id) }), triggerResult, { method: "POST", body: platformTriggerDestinationInputSchema.parse(destination) });
|
|
474
|
+
}
|
|
475
|
+
removeTriggerDestination(id, destinationId) {
|
|
476
|
+
if (!/^dst_[A-Za-z0-9_-]{1,100}$/.test(destinationId))
|
|
477
|
+
throw new Error("Provide a destination ID such as dst_….");
|
|
478
|
+
return this.request(route(PLATFORM_ROUTES.triggerDestinationDelete, { id: triggerId(id), destinationId }), triggerResult, { method: "POST", body: {} });
|
|
479
|
+
}
|
|
480
|
+
popularMonitors() { return this.request(route(PLATFORM_ROUTES.monitorsPopular), z.object({ data: z.object({ monitors: z.array(platformPopularMonitorSchema) }) })); }
|
|
481
|
+
customTools() { return this.request(route(PLATFORM_ROUTES.customTools), z.object({ data: z.object({ tools: z.array(platformCustomToolSummarySchema) }) })); }
|
|
482
|
+
customTool(name) { return this.request(route(PLATFORM_ROUTES.customTool, { name: customToolName(name) }), customToolResult); }
|
|
483
|
+
createCustomTool(input) {
|
|
484
|
+
return this.request(route(PLATFORM_ROUTES.customTools), customToolResult, { method: "POST", body: platformCustomToolCreateSchema.parse(input) });
|
|
485
|
+
}
|
|
486
|
+
updateCustomTool(name, input) {
|
|
487
|
+
return this.request(route(PLATFORM_ROUTES.customToolUpdate, { name: customToolName(name) }), customToolResult, { method: "POST", body: platformCustomToolUpdateSchema.parse(input) });
|
|
488
|
+
}
|
|
489
|
+
deleteCustomTool(name) {
|
|
490
|
+
return this.request(route(PLATFORM_ROUTES.customToolDelete, { name: customToolName(name) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
|
|
491
|
+
}
|
|
492
|
+
customToolVersions(name) {
|
|
493
|
+
return this.request(route(PLATFORM_ROUTES.customToolVersions, { name: customToolName(name) }), z.object({ data: z.object({ versions: z.array(platformCustomToolVersionSchema) }) }));
|
|
494
|
+
}
|
|
495
|
+
restoreCustomTool(name, input) {
|
|
496
|
+
return this.request(route(PLATFORM_ROUTES.customToolRestore, { name: customToolName(name) }), customToolResult, { method: "POST", body: platformCustomToolRestoreSchema.parse(input) });
|
|
497
|
+
}
|
|
498
|
+
teamSkills() { return this.request(route(PLATFORM_ROUTES.teamSkills), z.object({ data: z.object({ skills: z.array(platformTeamSkillSummarySchema) }) })); }
|
|
499
|
+
teamSkill(slug) { return this.request(route(PLATFORM_ROUTES.teamSkill, { slug: teamSkillSlug(slug) }), teamSkillResult); }
|
|
500
|
+
createTeamSkill(input) {
|
|
501
|
+
return this.request(route(PLATFORM_ROUTES.teamSkills), teamSkillResult, { method: "POST", body: platformTeamSkillCreateSchema.parse(input) });
|
|
502
|
+
}
|
|
503
|
+
updateTeamSkill(slug, input) {
|
|
504
|
+
return this.request(route(PLATFORM_ROUTES.teamSkillUpdate, { slug: teamSkillSlug(slug) }), teamSkillResult, { method: "POST", body: platformTeamSkillUpdateSchema.parse(input) });
|
|
505
|
+
}
|
|
506
|
+
deleteTeamSkill(slug) {
|
|
507
|
+
return this.request(route(PLATFORM_ROUTES.teamSkillDelete, { slug: teamSkillSlug(slug) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
|
|
508
|
+
}
|
|
509
|
+
teamSkillVersions(slug) {
|
|
510
|
+
return this.request(route(PLATFORM_ROUTES.teamSkillVersions, { slug: teamSkillSlug(slug) }), z.object({ data: z.object({ versions: z.array(platformTeamSkillVersionSchema) }) }));
|
|
511
|
+
}
|
|
512
|
+
restoreTeamSkill(slug, input) {
|
|
513
|
+
return this.request(route(PLATFORM_ROUTES.teamSkillRestore, { slug: teamSkillSlug(slug) }), teamSkillResult, { method: "POST", body: platformTeamSkillRestoreSchema.parse(input) });
|
|
514
|
+
}
|
|
515
|
+
suggestions() { return this.request(route(PLATFORM_ROUTES.suggestions), z.object({ data: platformSuggestionsSchema })); }
|
|
516
|
+
access() { return this.request(route(PLATFORM_ROUTES.access), z.object({ data: platformTeamAccessSchema })); }
|
|
517
|
+
updateMemberAccess(id, input) {
|
|
518
|
+
if (!/^[A-Za-z0-9-]{1,128}$/.test(id))
|
|
519
|
+
throw new Error("Provide a member ID as `access` lists it.");
|
|
520
|
+
return this.request(route(PLATFORM_ROUTES.memberAccess, { id }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: platformMemberAccessUpdateSchema.parse(input) });
|
|
521
|
+
}
|
|
522
|
+
workflows() { return this.request(route(PLATFORM_ROUTES.workflows), z.object({ data: z.object({ workflows: z.array(platformWorkflowSummarySchema) }) })); }
|
|
523
|
+
workflow(slug) { return this.request(route(PLATFORM_ROUTES.workflow, { slug: workflowSlug(slug) }), workflowResult); }
|
|
524
|
+
createWorkflow(input) {
|
|
525
|
+
return this.request(route(PLATFORM_ROUTES.workflows), workflowResult, { method: "POST", body: platformWorkflowCreateSchema.parse(input) });
|
|
526
|
+
}
|
|
527
|
+
updateWorkflow(slug, input) {
|
|
528
|
+
return this.request(route(PLATFORM_ROUTES.workflowUpdate, { slug: workflowSlug(slug) }), workflowResult, { method: "POST", body: platformWorkflowUpdateSchema.parse(input) });
|
|
529
|
+
}
|
|
530
|
+
deleteWorkflow(slug) {
|
|
531
|
+
return this.request(route(PLATFORM_ROUTES.workflowDelete, { slug: workflowSlug(slug) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
|
|
532
|
+
}
|
|
533
|
+
workflowVersions(slug) {
|
|
534
|
+
return this.request(route(PLATFORM_ROUTES.workflowVersions, { slug: workflowSlug(slug) }), z.object({ data: z.object({ versions: z.array(platformWorkflowVersionSchema) }) }));
|
|
535
|
+
}
|
|
536
|
+
runWorkflow(slug, input, idempotencyKey) {
|
|
537
|
+
if (!idempotencySchema.safeParse(idempotencyKey).success)
|
|
538
|
+
throw new Error("Keep an idempotency key of 8–128 letters, digits, periods, underscores, colons or hyphens for this run.");
|
|
539
|
+
return this.request(route(PLATFORM_ROUTES.workflowRuns, { slug: workflowSlug(slug) }), workflowRunResult, { method: "POST", body: platformWorkflowRunCreateSchema.parse(input), idempotencyKey });
|
|
540
|
+
}
|
|
541
|
+
workflowRuns(slug, query = {}) {
|
|
542
|
+
const parsed = platformWorkflowRunListQuerySchema.parse(query), search = new URLSearchParams({ limit: String(parsed.limit), ...(parsed.before ? { before: parsed.before } : {}) });
|
|
543
|
+
return this.request(`${route(PLATFORM_ROUTES.workflowRuns, { slug: workflowSlug(slug) })}?${search}`, z.object({ data: platformWorkflowRunListSchema }));
|
|
544
|
+
}
|
|
545
|
+
workflowRun(id) { return this.request(route(PLATFORM_ROUTES.workflowRun, { id: workflowRunId(id) }), workflowRunResult); }
|
|
546
|
+
cancelWorkflowRun(id) {
|
|
547
|
+
return this.request(route(PLATFORM_ROUTES.workflowRunCancel, { id: workflowRunId(id) }), workflowRunResult, { method: "POST", body: {} });
|
|
548
|
+
}
|
|
549
|
+
monitorableTools() { return this.request(route(PLATFORM_ROUTES.monitorTools), z.object({ data: z.object({ tools: z.array(platformMonitorableToolSchema) }) })); }
|
|
550
|
+
quote(input) {
|
|
551
|
+
const parsed = platformQuoteRequestSchema.safeParse(input);
|
|
552
|
+
if (!parsed.success)
|
|
553
|
+
throw new Error("Provide a tool name, a JSON object of arguments and an optional valid provider identifier.");
|
|
554
|
+
return this.request(route(PLATFORM_ROUTES.quote), quoteResultSchema, { method: "POST", body: parsed.data }).then(value => {
|
|
555
|
+
const receivedAt = Date.now(), expiresAt = Date.parse(value.data.expiresAt);
|
|
556
|
+
return { ...value, timing: { receivedAtUtc: new Date(receivedAt).toISOString(),
|
|
557
|
+
expiresInSeconds: Number.isFinite(expiresAt) ? Math.max(0, Math.floor((expiresAt - receivedAt) / 1000)) : null } };
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
createExecution(input, idempotencyKey) {
|
|
561
|
+
const parsed = platformCreateExecutionSchema.safeParse(input);
|
|
562
|
+
if (!parsed.success)
|
|
563
|
+
throw new Error("Provide a tool, arguments, the quote's priceVersion and maximumChargeMicros, and an optional valid provider identifier.");
|
|
564
|
+
if (!idempotencySchema.safeParse(idempotencyKey).success)
|
|
565
|
+
throw new Error("Keep an idempotency key of 8–128 letters, digits, periods, underscores, colons or hyphens for this execution.");
|
|
566
|
+
return this.request(route(PLATFORM_ROUTES.tool, { tool: parsed.data.tool }), executionResultSchema, { method: "POST", body: parsed.data, idempotencyKey });
|
|
567
|
+
}
|
|
568
|
+
recoverExecution(input) {
|
|
569
|
+
return this.request(route(PLATFORM_ROUTES.executionRecovery), z.object({ data: z.object({ execution: platformExecutionSchema.nullable() }) }), { method: "POST", body: platformRecoverExecutionSchema.parse(input) });
|
|
570
|
+
}
|
|
571
|
+
execution(id, signal, waitMs = 0) {
|
|
572
|
+
if (!platformExecutionIdSchema.safeParse(id).success)
|
|
573
|
+
throw new Error("Provide a valid execution ID.");
|
|
574
|
+
return this.request(route(PLATFORM_ROUTES.execution, { id }), executionResultSchema, { signal, ...this.heldRead(waitMs) });
|
|
575
|
+
}
|
|
576
|
+
executionContext(id, signal, waitMs = 0) {
|
|
577
|
+
platformExecutionIdSchema.parse(id);
|
|
578
|
+
return this.request(route(PLATFORM_ROUTES.executionContext, { id }), executionContextResultSchema, { signal, maximumResponseBytes: TOOL_RESULT_CONTEXT_MAX_BYTES + 65536, ...this.heldRead(waitMs) })
|
|
579
|
+
.then(result => executionReceiptContext(result)).catch(error => contextBudgetFailure(error, id, "execution"));
|
|
580
|
+
}
|
|
581
|
+
heldRead(waitMs) {
|
|
582
|
+
const seconds = this.holdsReads ? Math.min(PLATFORM_EXECUTION_MAX_WAIT_SECONDS, Math.floor(waitMs / 1000)) : 0;
|
|
583
|
+
return seconds > 0 ? { prefer: `wait=${seconds}`, timeoutMs: seconds * 1000 + 20000 } : {};
|
|
584
|
+
}
|
|
585
|
+
readExecutionResult(id, selection = {}) {
|
|
586
|
+
platformExecutionIdSchema.parse(id);
|
|
587
|
+
const body = toolResultSelectionSchema.parse(selection);
|
|
588
|
+
return this.request(route(PLATFORM_ROUTES.executionResult, { id }), z.object({ data: platformExecutionResultPageSchema }), { method: "POST", body, maximumResponseBytes: TOOL_RESULT_CONTEXT_MAX_BYTES + 65536 })
|
|
589
|
+
.then(result => platformExecutionResultPageContext(result, { readWith: "readExecutionResult" }))
|
|
590
|
+
.catch(error => contextBudgetFailure(error, id, "page"));
|
|
591
|
+
}
|
|
592
|
+
waitExecution(id, timeoutMs = 20000, signal) {
|
|
593
|
+
return this.waitForExecution(id, timeoutMs, signal, (executionId, abort, waitMs) => this.execution(executionId, abort, waitMs));
|
|
594
|
+
}
|
|
595
|
+
waitExecutionContext(id, timeoutMs = 20000, signal) {
|
|
596
|
+
return this.waitForExecution(id, timeoutMs, signal, (executionId, abort, waitMs) => this.executionContext(executionId, abort, waitMs));
|
|
597
|
+
}
|
|
598
|
+
async waitForExecution(id, timeoutMs, signal, read) {
|
|
599
|
+
platformExecutionIdSchema.parse(id);
|
|
600
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > 900000)
|
|
601
|
+
throw new Error("Wait must be between 1 and 900 seconds.");
|
|
602
|
+
const controller = new AbortController();
|
|
603
|
+
const timeout = new Error("Execution wait timed out; use the same execution ID to continue reading.");
|
|
604
|
+
const timer = setTimeout(() => controller.abort(timeout), timeoutMs);
|
|
605
|
+
const abort = () => controller.abort(signal?.reason);
|
|
606
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
607
|
+
if (signal?.aborted)
|
|
608
|
+
abort();
|
|
609
|
+
let latest = null;
|
|
610
|
+
const until = Date.now() + timeoutMs;
|
|
611
|
+
try {
|
|
612
|
+
for (;;) {
|
|
613
|
+
controller.signal.throwIfAborted();
|
|
614
|
+
const asked = Date.now();
|
|
615
|
+
latest = (await read(id, controller.signal, Math.max(0, until - asked - 1000))).data;
|
|
616
|
+
if (!["queued", "running", "waiting"].includes(latest.status))
|
|
617
|
+
return { data: latest, id, timedOut: false };
|
|
618
|
+
const pauseMs = this.holdsReads ? Math.max(0, 5000 - (Date.now() - asked)) : 5000;
|
|
619
|
+
if (pauseMs > 0)
|
|
620
|
+
await new Promise((resolve, reject) => {
|
|
621
|
+
const done = () => { controller.signal.removeEventListener("abort", cancelled); resolve(); };
|
|
622
|
+
const pause = setTimeout(done, pauseMs);
|
|
623
|
+
const cancelled = () => { clearTimeout(pause); controller.signal.removeEventListener("abort", cancelled); reject(controller.signal.reason); };
|
|
624
|
+
controller.signal.addEventListener("abort", cancelled, { once: true });
|
|
625
|
+
if (controller.signal.aborted)
|
|
626
|
+
cancelled();
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
catch (error) {
|
|
631
|
+
if (error !== timeout)
|
|
632
|
+
throw error;
|
|
633
|
+
return { data: latest, id, timedOut: true };
|
|
634
|
+
}
|
|
635
|
+
finally {
|
|
636
|
+
clearTimeout(timer);
|
|
637
|
+
signal?.removeEventListener("abort", abort);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
cancelExecution(id) {
|
|
641
|
+
if (!platformExecutionIdSchema.safeParse(id).success)
|
|
642
|
+
throw new Error("Provide a valid execution ID.");
|
|
643
|
+
return this.request(route(PLATFORM_ROUTES.executionCancel, { id }), executionResultSchema, { method: "POST" });
|
|
644
|
+
}
|
|
645
|
+
artifactDownload(id, artifactId) {
|
|
646
|
+
if (!platformExecutionIdSchema.safeParse(id).success || !platformArtifactIdSchema.safeParse(artifactId).success)
|
|
647
|
+
throw new Error("Provide valid execution and artifact IDs.");
|
|
648
|
+
return this.request(route(PLATFORM_ROUTES.artifactDownload, { id, artifact: artifactId }), z.object({ data: platformArtifactDownloadSchema }));
|
|
649
|
+
}
|
|
650
|
+
}
|