@alpic-ai/api 0.0.0-dev.gdda900d → 0.0.0-dev.gdddca59
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/dist/index.d.mts +6295 -94
- package/dist/index.mjs +855 -222
- package/package.json +9 -9
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import * as z$1 from "zod/v4";
|
|
3
3
|
import { oc } from "@orpc/contract";
|
|
4
4
|
import ms from "ms";
|
|
5
|
-
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
5
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.1_supports-color@7.2.0_zod@4.5.4/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/auth.js
|
|
6
6
|
/**
|
|
7
7
|
* Reusable URL validation that disallows javascript: scheme
|
|
8
8
|
*/
|
|
@@ -169,11 +169,22 @@ z$1.object({
|
|
|
169
169
|
token: z$1.string(),
|
|
170
170
|
token_type_hint: z$1.string().optional()
|
|
171
171
|
}).strip();
|
|
172
|
-
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region ../domains/dist/index.mjs
|
|
174
|
+
const AI_PLATFORMS = ["chatgpt", "claudeai"];
|
|
175
|
+
const platformSchema = z.enum(AI_PLATFORMS);
|
|
173
176
|
const PLATFORM_LABELS = {
|
|
174
177
|
chatgpt: "ChatGPT",
|
|
175
178
|
claudeai: "Claude.ai"
|
|
176
179
|
};
|
|
180
|
+
const WAF_PLATFORMS = [
|
|
181
|
+
"akamai",
|
|
182
|
+
"cato",
|
|
183
|
+
"cloudflare",
|
|
184
|
+
"fastly"
|
|
185
|
+
];
|
|
186
|
+
const ALLOWED_PLATFORMS = [...AI_PLATFORMS, ...WAF_PLATFORMS];
|
|
187
|
+
const allowedPlatformSchema = z.enum(ALLOWED_PLATFORMS);
|
|
177
188
|
const auditStatusSchema = z.enum([
|
|
178
189
|
"pending",
|
|
179
190
|
"partial",
|
|
@@ -246,6 +257,25 @@ z.object({
|
|
|
246
257
|
clientSecret: z.string(),
|
|
247
258
|
clientScopes: z.array(z.string())
|
|
248
259
|
});
|
|
260
|
+
const creditEntryReasonSchema = z.enum([
|
|
261
|
+
"free_seed",
|
|
262
|
+
"paid_conversion_floor",
|
|
263
|
+
"top_up",
|
|
264
|
+
"usage",
|
|
265
|
+
"admin_adjustment"
|
|
266
|
+
]);
|
|
267
|
+
z.enum(["playground"]);
|
|
268
|
+
z.number().int().min(500);
|
|
269
|
+
const creditActivityEntrySchema = z.object({
|
|
270
|
+
reason: creditEntryReasonSchema,
|
|
271
|
+
day: z.number(),
|
|
272
|
+
amountInNanoUSD: z.number(),
|
|
273
|
+
messageCount: z.number().nullable()
|
|
274
|
+
});
|
|
275
|
+
z.object({
|
|
276
|
+
balanceInNanoUSD: z.number(),
|
|
277
|
+
recentActivity: z.array(creditActivityEntrySchema)
|
|
278
|
+
});
|
|
249
279
|
const deploymentStatusSchema$1 = z.enum([
|
|
250
280
|
"ongoing",
|
|
251
281
|
"deployed",
|
|
@@ -273,34 +303,51 @@ const deploymentSummarySchema = z.object({
|
|
|
273
303
|
createdAt: z.coerce.date(),
|
|
274
304
|
updatedAt: z.coerce.date()
|
|
275
305
|
});
|
|
276
|
-
/**
|
|
277
|
-
* Public environment
|
|
278
|
-
*
|
|
279
|
-
* No OAuth metadata exposed.
|
|
280
|
-
*/
|
|
306
|
+
/** Public environment: answers anonymously and exposes no OAuth metadata. */
|
|
281
307
|
const publicEnvironmentSchema = z.object({
|
|
308
|
+
allowsAnonymous: z.literal(true),
|
|
309
|
+
oAuthMetadata: z.strictObject({})
|
|
310
|
+
});
|
|
311
|
+
/** Protected environment whose external resource metadata URL is returned in the WWW-Authenticate header on the first 401; everything after is handled outside the MCP server scope. */
|
|
312
|
+
const externalResourceMetadataEnvironmentSchema = z.object({
|
|
313
|
+
allowsAnonymous: z.boolean(),
|
|
314
|
+
oAuthMetadata: z.object({ externalResourceMetadataUrl: z.url() }),
|
|
315
|
+
scope: z.string().optional()
|
|
316
|
+
});
|
|
317
|
+
/** Protected environment whose own /.well-known/oauth-protected-resource endpoint points at an external authorization server; everything after is handled outside the MCP server scope. */
|
|
318
|
+
const externalAuthorizationServerEnvironmentSchema = z.object({
|
|
319
|
+
allowsAnonymous: z.boolean(),
|
|
320
|
+
oAuthMetadata: z.object({
|
|
321
|
+
externalAuthorizationServerUrl: z.url(),
|
|
322
|
+
resourceMetadata: OAuthProtectedResourceMetadataSchema.optional()
|
|
323
|
+
}),
|
|
324
|
+
scope: z.string().optional()
|
|
325
|
+
});
|
|
326
|
+
/** Standalone protected environment: the server itself exposes both the /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server endpoints. */
|
|
327
|
+
const standaloneEnvironmentSchema = z.object({
|
|
328
|
+
allowsAnonymous: z.boolean(),
|
|
329
|
+
oAuthMetadata: z.object({
|
|
330
|
+
authorizationServerMetadata: z.union([OAuthMetadataSchema, OpenIdProviderDiscoveryMetadataSchema]),
|
|
331
|
+
resourceMetadata: OAuthProtectedResourceMetadataSchema.optional()
|
|
332
|
+
}),
|
|
333
|
+
scope: z.string().optional()
|
|
334
|
+
});
|
|
335
|
+
/** Protected environment that enforces authentication but advertises no OAuth discovery metadata; clients must obtain credentials out-of-band. */
|
|
336
|
+
const protectedWithoutDiscoveryEnvironmentSchema = z.object({
|
|
337
|
+
allowsAnonymous: z.literal(false),
|
|
338
|
+
oAuthMetadata: z.strictObject({}),
|
|
339
|
+
scope: z.string().optional()
|
|
340
|
+
});
|
|
341
|
+
const legacyPublicEnvironmentSchema = z.object({
|
|
282
342
|
isPublic: z.literal(true),
|
|
283
343
|
oAuthMetadata: z.undefined().optional()
|
|
284
344
|
});
|
|
285
|
-
|
|
286
|
-
* Protected environment with an external resource metadata URL
|
|
287
|
-
*
|
|
288
|
-
* An external resource metadata URL is returned in the WWW-Authenticate header during the first 401 response.
|
|
289
|
-
* Everything that follows is handled outside of the MCP server scope.
|
|
290
|
-
*/
|
|
291
|
-
const externalResourceMetadataEnvironmentSchema = z.object({
|
|
345
|
+
const legacyExternalResourceMetadataEnvironmentSchema = z.object({
|
|
292
346
|
isPublic: z.literal(false),
|
|
293
347
|
oAuthMetadata: z.object({ externalResourceMetadataUrl: z.url() }),
|
|
294
348
|
scope: z.string().optional()
|
|
295
349
|
});
|
|
296
|
-
|
|
297
|
-
* Protected environment with an external authorization server URL
|
|
298
|
-
*
|
|
299
|
-
* The server itself exposes the /.well-known/oauth-protected-resource endpoint.
|
|
300
|
-
* An external authorization server URL is returned when requesting the endpoint.
|
|
301
|
-
* Everything that follows is handled outside of the MCP server scope.
|
|
302
|
-
*/
|
|
303
|
-
const externalAuthorizationServerEnvironmentSchema = z.object({
|
|
350
|
+
const legacyExternalAuthorizationServerEnvironmentSchema = z.object({
|
|
304
351
|
isPublic: z.literal(false),
|
|
305
352
|
oAuthMetadata: z.object({
|
|
306
353
|
externalAuthorizationServerUrl: z.url(),
|
|
@@ -308,13 +355,7 @@ const externalAuthorizationServerEnvironmentSchema = z.object({
|
|
|
308
355
|
}),
|
|
309
356
|
scope: z.string().optional()
|
|
310
357
|
});
|
|
311
|
-
|
|
312
|
-
* Standalone protected environment
|
|
313
|
-
*
|
|
314
|
-
* The server itself exposes the /.well-known/oauth-protected-resource endpoint.
|
|
315
|
-
* The server itself exposes the /.well-known/oauth-authorization-server endpoint.
|
|
316
|
-
*/
|
|
317
|
-
const standaloneEnvironmentSchema = z.object({
|
|
358
|
+
const legacyStandaloneEnvironmentSchema = z.object({
|
|
318
359
|
isPublic: z.literal(false),
|
|
319
360
|
oAuthMetadata: z.object({
|
|
320
361
|
authorizationServerMetadata: z.union([OAuthMetadataSchema, OpenIdProviderDiscoveryMetadataSchema]),
|
|
@@ -322,14 +363,7 @@ const standaloneEnvironmentSchema = z.object({
|
|
|
322
363
|
}),
|
|
323
364
|
scope: z.string().optional()
|
|
324
365
|
});
|
|
325
|
-
|
|
326
|
-
* Protected environment without OAuth discovery
|
|
327
|
-
*
|
|
328
|
-
* The server enforces authentication but exposes no OAuth discovery metadata.
|
|
329
|
-
* Neither /.well-known/oauth-protected-resource nor /.well-known/oauth-authorization-server
|
|
330
|
-
* are advertised. Clients must obtain credentials out-of-band.
|
|
331
|
-
*/
|
|
332
|
-
const protectedWithoutDiscoveryEnvironmentSchema = z.object({
|
|
366
|
+
const legacyProtectedWithoutDiscoveryEnvironmentSchema = z.object({
|
|
333
367
|
isPublic: z.literal(false),
|
|
334
368
|
oAuthMetadata: z.strictObject({}),
|
|
335
369
|
scope: z.string().optional()
|
|
@@ -339,9 +373,13 @@ z.union([
|
|
|
339
373
|
externalResourceMetadataEnvironmentSchema,
|
|
340
374
|
externalAuthorizationServerEnvironmentSchema,
|
|
341
375
|
standaloneEnvironmentSchema,
|
|
342
|
-
protectedWithoutDiscoveryEnvironmentSchema
|
|
376
|
+
protectedWithoutDiscoveryEnvironmentSchema,
|
|
377
|
+
legacyPublicEnvironmentSchema,
|
|
378
|
+
legacyExternalResourceMetadataEnvironmentSchema,
|
|
379
|
+
legacyExternalAuthorizationServerEnvironmentSchema,
|
|
380
|
+
legacyStandaloneEnvironmentSchema,
|
|
381
|
+
legacyProtectedWithoutDiscoveryEnvironmentSchema
|
|
343
382
|
]);
|
|
344
|
-
const allowedPlatformSchema = platformSchema;
|
|
345
383
|
const playgroundModelSchema = z.enum([
|
|
346
384
|
"global.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
347
385
|
"global.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
@@ -381,14 +419,17 @@ z.object({
|
|
|
381
419
|
collectRequestPayload: z.boolean(),
|
|
382
420
|
collectResponsePayload: z.boolean(),
|
|
383
421
|
collectErrorMessage: z.boolean(),
|
|
384
|
-
collectUserData: z.boolean()
|
|
422
|
+
collectUserData: z.boolean(),
|
|
423
|
+
identityTraitKey: z.string().min(1).max(200).nullable(),
|
|
424
|
+
leadEventName: z.string().min(1).max(200).nullable()
|
|
385
425
|
});
|
|
386
|
-
z.object({
|
|
426
|
+
const feedbackSchema = z.object({
|
|
387
427
|
id: z.string(),
|
|
388
428
|
createdAt: z.coerce.date(),
|
|
389
429
|
environmentId: z.string(),
|
|
390
430
|
content: z.string(),
|
|
391
|
-
source: z.enum(["model", "user"])
|
|
431
|
+
source: z.enum(["model", "user"]),
|
|
432
|
+
clientName: z.string().nullable()
|
|
392
433
|
});
|
|
393
434
|
z.object({
|
|
394
435
|
content: z.string(),
|
|
@@ -404,7 +445,7 @@ const intentCategoryColorSchema = z.enum([
|
|
|
404
445
|
"pink",
|
|
405
446
|
"gray"
|
|
406
447
|
]);
|
|
407
|
-
intentCategoryColorSchema.options;
|
|
448
|
+
const INTENT_CATEGORY_COLORS = intentCategoryColorSchema.options;
|
|
408
449
|
const intentCategorySchema = z.object({
|
|
409
450
|
id: z.string(),
|
|
410
451
|
createdAt: z.coerce.date(),
|
|
@@ -412,7 +453,7 @@ const intentCategorySchema = z.object({
|
|
|
412
453
|
name: z.string(),
|
|
413
454
|
color: intentCategoryColorSchema
|
|
414
455
|
});
|
|
415
|
-
z.object({
|
|
456
|
+
const intentCategorySummarySchema = z.object({
|
|
416
457
|
id: z.string(),
|
|
417
458
|
name: z.string(),
|
|
418
459
|
color: intentCategoryColorSchema,
|
|
@@ -424,6 +465,7 @@ z.object({
|
|
|
424
465
|
environmentId: z.string(),
|
|
425
466
|
toolName: z.string(),
|
|
426
467
|
message: z.string(),
|
|
468
|
+
clientName: z.string().nullable(),
|
|
427
469
|
categories: z.array(intentCategorySchema)
|
|
428
470
|
});
|
|
429
471
|
const signalCategorySchema = z.object({
|
|
@@ -482,12 +524,270 @@ z.object({
|
|
|
482
524
|
createdAt: z.coerce.date(),
|
|
483
525
|
updatedAt: z.coerce.date()
|
|
484
526
|
});
|
|
527
|
+
const alpicMcpRequestLogSchema = z.object({
|
|
528
|
+
type: z.literal("mcp_request"),
|
|
529
|
+
method: z.string()
|
|
530
|
+
});
|
|
531
|
+
const alpicMcpErrorLogSchema = z.object({
|
|
532
|
+
type: z.literal("mcp_error"),
|
|
533
|
+
method: z.string(),
|
|
534
|
+
error: z.string()
|
|
535
|
+
});
|
|
536
|
+
z.union([alpicMcpRequestLogSchema, alpicMcpErrorLogSchema]);
|
|
485
537
|
z.enum([
|
|
486
538
|
"INFO",
|
|
487
539
|
"WARNING",
|
|
488
540
|
"DEBUG",
|
|
489
541
|
"ERROR"
|
|
490
542
|
]);
|
|
543
|
+
const LOG_SEARCH_MAX_ESCAPED_LENGTH = 960;
|
|
544
|
+
function escapeLogSearchTerm(search) {
|
|
545
|
+
return [...search].flatMap((character) => [...new TextEncoder().encode(character)]).map((byte) => {
|
|
546
|
+
const character = String.fromCharCode(byte);
|
|
547
|
+
return /^[a-zA-Z0-9]$/.test(character) ? character : `\\x${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
548
|
+
}).join("");
|
|
549
|
+
}
|
|
550
|
+
const logSearchSchema = z.string().max(240, `Search must be 240 characters or fewer`).refine((search) => escapeLogSearchTerm(search).length <= LOG_SEARCH_MAX_ESCAPED_LENGTH, "Search is too complex; shorten it or use fewer special characters");
|
|
551
|
+
/** Only names a vendor actually ships belong here: lookalikes like `claude-code-test` are customers' own scripts. */
|
|
552
|
+
const MCP_CLIENT_GROUPS = [
|
|
553
|
+
{
|
|
554
|
+
id: "claude_code",
|
|
555
|
+
label: "Claude Code",
|
|
556
|
+
vendor: "anthropic",
|
|
557
|
+
rawNames: ["claude-code", "Anthropic/ClaudeCodeDesktop"]
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
id: "claude",
|
|
561
|
+
label: "Claude",
|
|
562
|
+
vendor: "anthropic",
|
|
563
|
+
rawNames: [
|
|
564
|
+
"Anthropic/ClaudeAI",
|
|
565
|
+
"claude-ai",
|
|
566
|
+
"claude",
|
|
567
|
+
"Claude",
|
|
568
|
+
"claude-desktop",
|
|
569
|
+
"claude-desktop-3p"
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: "claude_mobile",
|
|
574
|
+
label: "Claude mobile",
|
|
575
|
+
vendor: "anthropic",
|
|
576
|
+
rawNames: ["claude-ios", "ClaudeAndroid"]
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
id: "claude_cowork",
|
|
580
|
+
label: "Claude Cowork",
|
|
581
|
+
vendor: "anthropic",
|
|
582
|
+
rawNames: ["Anthropic/Cowork"]
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
id: "claude_toolbox",
|
|
586
|
+
label: "Claude Toolbox",
|
|
587
|
+
vendor: "anthropic",
|
|
588
|
+
rawNames: ["Anthropic/Toolbox", "Anthropic/toolbox-bootstrap"]
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
id: "anthropic_api",
|
|
592
|
+
label: "Anthropic API",
|
|
593
|
+
vendor: "anthropic",
|
|
594
|
+
rawNames: ["Anthropic/API"]
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: "anthropic_connector_refresh",
|
|
598
|
+
label: "Anthropic (connector refresh)",
|
|
599
|
+
vendor: "anthropic",
|
|
600
|
+
rawNames: ["Anthropic"]
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: "codex",
|
|
604
|
+
label: "Codex",
|
|
605
|
+
vendor: "openai",
|
|
606
|
+
rawNames: [
|
|
607
|
+
"codex-mcp-client",
|
|
608
|
+
"codex",
|
|
609
|
+
"Codex",
|
|
610
|
+
"codex-cli",
|
|
611
|
+
"openai-mcp (Codex)"
|
|
612
|
+
]
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
id: "chatgpt",
|
|
616
|
+
label: "ChatGPT",
|
|
617
|
+
vendor: "openai",
|
|
618
|
+
rawNames: ["openai-mcp (ChatGPT)"]
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: "openai_responses_api",
|
|
622
|
+
label: "OpenAI Responses API",
|
|
623
|
+
vendor: "openai",
|
|
624
|
+
rawNames: ["openai-mcp (Responses API)"]
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
id: "openai_agent_builder",
|
|
628
|
+
label: "OpenAI Agent Builder",
|
|
629
|
+
vendor: "openai",
|
|
630
|
+
rawNames: ["openai-mcp (Agent Builder)"]
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
id: "openai_other",
|
|
634
|
+
label: "OpenAI Other",
|
|
635
|
+
vendor: "openai",
|
|
636
|
+
rawNames: ["openai-mcp"]
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
id: "cursor",
|
|
640
|
+
label: "Cursor",
|
|
641
|
+
vendor: "cursor",
|
|
642
|
+
rawNames: [
|
|
643
|
+
"cursor-vscode",
|
|
644
|
+
"Cursor",
|
|
645
|
+
"cursor"
|
|
646
|
+
]
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: "cline",
|
|
650
|
+
label: "Cline",
|
|
651
|
+
vendor: "cline",
|
|
652
|
+
rawNames: [
|
|
653
|
+
"Cline",
|
|
654
|
+
"cline",
|
|
655
|
+
"@cline/core"
|
|
656
|
+
]
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
id: "github_copilot",
|
|
660
|
+
label: "GitHub Copilot",
|
|
661
|
+
vendor: "githubCopilot",
|
|
662
|
+
rawNames: [
|
|
663
|
+
"Copilot",
|
|
664
|
+
"github-copilot-developer",
|
|
665
|
+
"Copilot MCP Gateway",
|
|
666
|
+
"Xcode/copilot-xcode"
|
|
667
|
+
]
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
id: "gemini_cli",
|
|
671
|
+
label: "Gemini CLI",
|
|
672
|
+
vendor: "google",
|
|
673
|
+
rawNames: ["gemini-cli-mcp-client", "gemini-cli"]
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: "antigravity",
|
|
677
|
+
label: "Antigravity",
|
|
678
|
+
vendor: "google",
|
|
679
|
+
rawNames: ["antigravity-client"]
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
id: "gemini",
|
|
683
|
+
label: "Gemini",
|
|
684
|
+
vendor: "google",
|
|
685
|
+
rawNames: ["Gemini", "google-ai-edge-gallery"]
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
id: "windsurf",
|
|
689
|
+
label: "Windsurf",
|
|
690
|
+
vendor: "windsurf",
|
|
691
|
+
rawNames: ["Windsurf", "windsurf-client"]
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
id: "mistral",
|
|
695
|
+
label: "Mistral / Le Chat",
|
|
696
|
+
vendor: "mistral",
|
|
697
|
+
rawNames: ["Mistral"],
|
|
698
|
+
rawNamePrefixes: ["connector:custom-mcp:"]
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
id: "n8n",
|
|
702
|
+
label: "n8n",
|
|
703
|
+
vendor: "n8n",
|
|
704
|
+
rawNames: [
|
|
705
|
+
"@n8n/n8n-nodes-langchain.mcpClient",
|
|
706
|
+
"@n8n/n8n-nodes-langchain.mcpClientTool",
|
|
707
|
+
"n8n"
|
|
708
|
+
]
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
id: "alpic_playground",
|
|
712
|
+
label: "Alpic Playground",
|
|
713
|
+
vendor: "alpic",
|
|
714
|
+
rawNames: ["alpic-playground", "playground"]
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
id: "opencode",
|
|
718
|
+
label: "opencode",
|
|
719
|
+
vendor: "opencode",
|
|
720
|
+
rawNames: ["opencode"]
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
id: "zed",
|
|
724
|
+
label: "Zed",
|
|
725
|
+
vendor: "zed",
|
|
726
|
+
rawNames: ["Zed", "zed"]
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
id: "zapier",
|
|
730
|
+
label: "Zapier",
|
|
731
|
+
vendor: "zapier",
|
|
732
|
+
rawNames: ["Zapier", "zapier"]
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
id: "make",
|
|
736
|
+
label: "Make",
|
|
737
|
+
vendor: "make",
|
|
738
|
+
rawNames: ["Make", "make"]
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
id: "librechat",
|
|
742
|
+
label: "LibreChat",
|
|
743
|
+
vendor: "mcp",
|
|
744
|
+
rawNames: ["LibreChat", "librechat"]
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
id: "jan",
|
|
748
|
+
label: "Jan",
|
|
749
|
+
vendor: "mcp",
|
|
750
|
+
rawNames: ["Jan", "jan"]
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
id: "goose",
|
|
754
|
+
label: "goose",
|
|
755
|
+
vendor: "mcp",
|
|
756
|
+
rawNames: ["goose", "Goose"]
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: "mcphost",
|
|
760
|
+
label: "mcphost",
|
|
761
|
+
vendor: "mcp",
|
|
762
|
+
rawNames: ["mcphost"]
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
id: "mcporter",
|
|
766
|
+
label: "mcporter",
|
|
767
|
+
vendor: "mcp",
|
|
768
|
+
rawNames: ["mcporter"]
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
id: "rmcp",
|
|
772
|
+
label: "rmcp",
|
|
773
|
+
vendor: "mcp",
|
|
774
|
+
rawNames: ["rmcp"]
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
id: "unnamed_client",
|
|
778
|
+
label: "Unnamed client",
|
|
779
|
+
vendor: "mcp",
|
|
780
|
+
rawNames: [
|
|
781
|
+
"mcp",
|
|
782
|
+
"unknown",
|
|
783
|
+
""
|
|
784
|
+
]
|
|
785
|
+
}
|
|
786
|
+
];
|
|
787
|
+
/** A source pattern, not a RegExp, because analytics renders the same rule into ClickHouse RE2, which shares this syntax. */
|
|
788
|
+
const MCP_REMOTE_WRAPPER_SUFFIX = String.raw` \(via mcp-remote [^\n]+?\)$`;
|
|
789
|
+
new RegExp(MCP_REMOTE_WRAPPER_SUFFIX);
|
|
790
|
+
new Map(MCP_CLIENT_GROUPS.map((group) => [group.id, group]));
|
|
491
791
|
const runtimeSchema = z.enum([
|
|
492
792
|
"python3.13",
|
|
493
793
|
"python3.14",
|
|
@@ -565,15 +865,7 @@ z.object({
|
|
|
565
865
|
serverFields: serverFieldsSchema$1,
|
|
566
866
|
source: z.enum(["registry", "defaults"])
|
|
567
867
|
});
|
|
568
|
-
/**
|
|
569
|
-
* Returns a variant of a `maxLength`-constrained string that clamps (truncates) instead of
|
|
570
|
-
* rejecting on overflow — used for AI-generated copy where the model routinely overshoots.
|
|
571
|
-
*
|
|
572
|
-
* NOTE: this rebuilds the schema from scratch, carrying over only `maxLength` (as a clamp),
|
|
573
|
-
* `minLength`, and the description. Any other validators on the source field (`.regex()`,
|
|
574
|
-
* `.email()`, custom refinements) are intentionally dropped — this helper is meant for plain
|
|
575
|
-
* free-text length caps only. A field with `null` `maxLength` is returned unchanged.
|
|
576
|
-
*/
|
|
868
|
+
/** Clamps instead of rejecting on max-length overflow (for AI-generated copy that overshoots); intentionally carries over only maxLength, minLength, and description, dropping any other validators. */
|
|
577
869
|
const clampStringField = (field) => {
|
|
578
870
|
const max = field.maxLength;
|
|
579
871
|
if (max === null) return field;
|
|
@@ -581,6 +873,10 @@ const clampStringField = (field) => {
|
|
|
581
873
|
const withMin = field.minLength === null ? clamped : clamped.min(field.minLength);
|
|
582
874
|
return field.description ? withMin.describe(field.description) : withMin;
|
|
583
875
|
};
|
|
876
|
+
const unboundedArrayField = (field) => {
|
|
877
|
+
const unbounded = z.array(field.element);
|
|
878
|
+
return field.description ? unbounded.describe(field.description) : unbounded;
|
|
879
|
+
};
|
|
584
880
|
const toolDefinitionSchema = z.object({
|
|
585
881
|
name: z.string(),
|
|
586
882
|
title: z.string().optional().describe("Human-friendly name for the tool, used in the UI. If not provided, `name` will be used."),
|
|
@@ -593,11 +889,22 @@ const toolDefinitionSchema = z.object({
|
|
|
593
889
|
}).optional()
|
|
594
890
|
});
|
|
595
891
|
const positiveTestCaseSchema = z.object({
|
|
596
|
-
scenario: z.string().describe("Short label for what this case demonstrates."),
|
|
597
|
-
userPrompt: z.string().describe("A realistic, standalone user message a reviewer can paste and run with no prior state or multi-turn setup. Vary phrasing and tone across cases."),
|
|
892
|
+
scenario: z.string().min(1).describe("Short label for what this case demonstrates."),
|
|
893
|
+
userPrompt: z.string().min(1).describe("A realistic, standalone user message a reviewer can paste and run with no prior state or multi-turn setup. Vary phrasing and tone across cases."),
|
|
598
894
|
toolTriggered: z.string().optional().describe("The tool this prompt should invoke."),
|
|
599
895
|
expectedOutput: z.string().optional().describe("What the user should see, including post-interaction behaviour where relevant.")
|
|
600
|
-
}).
|
|
896
|
+
}).describe("Each case: Scenario, User prompt, Tool triggered, Expected output.");
|
|
897
|
+
const positiveTestCaseDraftSchema = positiveTestCaseSchema.partial().extend({
|
|
898
|
+
scenario: z.string().optional(),
|
|
899
|
+
userPrompt: z.string().optional()
|
|
900
|
+
});
|
|
901
|
+
const blankPositiveTestCase = () => ({
|
|
902
|
+
scenario: "",
|
|
903
|
+
userPrompt: "",
|
|
904
|
+
toolTriggered: "",
|
|
905
|
+
expectedOutput: ""
|
|
906
|
+
});
|
|
907
|
+
const toFixedLength = (entries, length, blank) => Array.from({ length }, (_, index) => entries[index] ?? blank());
|
|
601
908
|
const screenshotEntrySchema = z.object({
|
|
602
909
|
prompt: z.string(),
|
|
603
910
|
url: z.url()
|
|
@@ -910,20 +1217,25 @@ const chatgptTranslationLocaleSchema = z.enum([
|
|
|
910
1217
|
"zh-TW"
|
|
911
1218
|
]);
|
|
912
1219
|
const negativeTestCaseSchema = z.object({
|
|
913
|
-
scenario: z.string(),
|
|
914
|
-
userPrompt: z.string()
|
|
915
|
-
}).
|
|
1220
|
+
scenario: z.string().min(1),
|
|
1221
|
+
userPrompt: z.string().min(1)
|
|
1222
|
+
}).describe("Each case: Scenario + User prompt (the plugin should NOT trigger).");
|
|
1223
|
+
const negativeTestCaseDraftSchema = negativeTestCaseSchema.partial().extend({
|
|
1224
|
+
scenario: z.string().optional(),
|
|
1225
|
+
userPrompt: z.string().optional()
|
|
1226
|
+
});
|
|
1227
|
+
const blankNegativeTestCase = () => ({
|
|
1228
|
+
scenario: "",
|
|
1229
|
+
userPrompt: ""
|
|
1230
|
+
});
|
|
1231
|
+
const chatgptSkillSchema = z.object({ name: z.string() }).describe("A skill discovered on the MCP server, by name.");
|
|
916
1232
|
const chatgptToolJustificationSchema = z.object({
|
|
917
1233
|
toolName: z.string(),
|
|
918
1234
|
readOnlyJustification: z.string().describe("One sentence on what the tool reads and returns."),
|
|
919
1235
|
openWorldJustification: z.string().describe("One sentence on whether the tool reaches external systems beyond your own service."),
|
|
920
1236
|
destructiveJustification: z.string().describe("One sentence on whether the tool changes data. For a read-only tool, state the full triad: \"No data is created, modified, or deleted.\"")
|
|
921
1237
|
}).describe("Per-tool justification of each MCP annotation value (one specific sentence per hint).");
|
|
922
|
-
/**
|
|
923
|
-
* Base copy fields, defined once and reused by both the submission form and per-locale
|
|
924
|
-
* translations so the constraints (tagline ≤ 30 chars) never drift. The translation generation
|
|
925
|
-
* path wraps these in {@link clampStringField} since the LLM tends to overshoot the limit.
|
|
926
|
-
*/
|
|
1238
|
+
/** Defined once and reused by both the submission form and per-locale translations so the tagline ≤ 30 chars constraint never drifts. */
|
|
927
1239
|
const chatgptTaglineSchema = z.string().max(30);
|
|
928
1240
|
const chatgptDescriptionSchema = z.string();
|
|
929
1241
|
const chatgptTranslationSchema = z.object({
|
|
@@ -932,34 +1244,32 @@ const chatgptTranslationSchema = z.object({
|
|
|
932
1244
|
description: chatgptDescriptionSchema
|
|
933
1245
|
}).describe("Locale-scoped override for tagline + description. English (US) is the default.");
|
|
934
1246
|
chatgptTranslationSchema.extend({ tagline: clampStringField(chatgptTaglineSchema) });
|
|
935
|
-
/**
|
|
936
|
-
* Field order mirrors the ChatGPT (OpenAI) submission spreadsheet, grouped by section.
|
|
937
|
-
* When adding/removing/reordering fields, keep this in sync with the sheet.
|
|
938
|
-
*/
|
|
1247
|
+
/** Field order mirrors the ChatGPT (OpenAI) submission spreadsheet, grouped by section; keep it in sync with the sheet. */
|
|
939
1248
|
const chatgptSubmissionFormDataSchema = z.object({
|
|
940
1249
|
logoLight: z.string().describe("Logo icon for light mode. Square PNG, no borders or rounded corners (clients apply circular cropping)."),
|
|
941
1250
|
logoDark: z.string().optional().describe("Optional dark-mode logo icon. Square PNG, same specs as the light icon. Leave empty if the light icon also works on dark backgrounds."),
|
|
942
|
-
appName: z.string().describe("The name users will see in ChatGPT and in the
|
|
1251
|
+
appName: z.string().describe("The name users will see in ChatGPT and in the Plugin Directory."),
|
|
943
1252
|
tagline: chatgptTaglineSchema.describe("Plain-language phrase focused on function and user value, 30 chars max. Avoid generic superlatives like \"the best …\"."),
|
|
944
|
-
description: chatgptDescriptionSchema.describe("Clear, engaging description highlighting what the
|
|
1253
|
+
description: chatgptDescriptionSchema.describe("Clear, engaging description highlighting what the plugin does and why people will love it. Appears publicly on the directory page. End it by naming the product categories or intents the plugin does NOT cover, so ChatGPT learns when not to route to it (define exclusions by absent categories or wrong intents, never by naming other brands)."),
|
|
945
1254
|
category: chatgptCategorySchema.describe("Category from the OpenAI taxonomy."),
|
|
946
|
-
developerName: z.string().describe("Developer name shown publicly on the
|
|
1255
|
+
developerName: z.string().describe("Developer name shown publicly on the plugin's directory page."),
|
|
947
1256
|
companyUrl: urlField.describe("Your company's main website URL."),
|
|
948
1257
|
supportChannel: supportChannelSchema.describe("Customer support URL or email address."),
|
|
949
1258
|
privacyPolicyUrl: urlField.describe("URL to your privacy policy."),
|
|
950
|
-
termsOfServiceUrl: urlField.describe("URL to the
|
|
951
|
-
demoRecordingUrl: urlField.describe("URL to a video demonstrating the
|
|
952
|
-
appCommerceAndPurchasing: z.boolean().describe("Checkbox: 'My
|
|
1259
|
+
termsOfServiceUrl: urlField.describe("URL to the plugin's terms of service."),
|
|
1260
|
+
demoRecordingUrl: urlField.describe("URL to a video demonstrating the plugin, recorded via OpenAI Developer Mode. Cover all main use cases on web, iOS, and Android."),
|
|
1261
|
+
appCommerceAndPurchasing: z.boolean().describe("Checkbox: 'My plugin links or directs users out of ChatGPT to make purchases.' Defaults to false. Verify the plugin does not offer digital goods."),
|
|
953
1262
|
serverUrl: urlField.describe("URL of the MCP server."),
|
|
954
1263
|
authentication: chatgptAuthenticationSchema.describe("OAuth 2.0 or No auth. OAuth configuration is auto-discovered from the MCP server's metadata."),
|
|
955
1264
|
tools: z.array(toolDefinitionSchema).describe("List of tools exposed by the MCP server (auto-populated from the production server's manifest)."),
|
|
956
1265
|
toolJustifications: z.array(chatgptToolJustificationSchema).describe("Per-tool justification of `readOnlyHint`, `openWorldHint`, and `destructiveHint` annotation values. One short, specific sentence per hint describing what the tool actually does."),
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1266
|
+
skills: z.array(chatgptSkillSchema).optional().describe("Skills the server exposes over MCP (auto-populated from the production server's manifest). Left unset when the server does not advertise the skills extension, which is a different state from advertising it and exposing none."),
|
|
1267
|
+
testCases: z.array(positiveTestCaseSchema).length(5).describe("Exactly 5 positive test cases. Each: Scenario, User prompt, Tool triggered, Expected output. Collectively cover the full capability across different flow stages (initial search or browse, market or locale handling, post-interaction detail, an action ChatGPT performs on the user's behalf, and a conversion or secondary tool) rather than five variations of the same first step."),
|
|
1268
|
+
negativeTestCases: z.array(negativeTestCaseSchema).length(3).describe("Exactly 3 negative test cases: prompts where the plugin should NOT trigger but the model might think it is relevant. These become production routing metadata that teaches ChatGPT when to stay silent, so pick tight near-misses: a product category the plugin does not carry, the right brand or domain but an intent the plugin does not serve, or a boundary with a sibling plugin."),
|
|
1269
|
+
screenshots: z.array(screenshotEntrySchema.extend({ prompt: z.string().max(75, { error: "Screenshot prompts are limited to 75 characters." }) })).max(3).describe("In-plugin screenshots paired with the user prompt that produced them (each prompt is 75 characters max). Widget plugins must show the widget UI; non-widget plugins show the model response. Min 1, max 3 entries, all public. Images: 706px wide, 400–860px tall, PNG."),
|
|
960
1270
|
translations: z.array(chatgptTranslationSchema).describe("Per-locale translation of tagline + description. English (US) is the default."),
|
|
961
1271
|
allowedCountries: chatgptAllowedCountriesSchema.describe("Availability: all supported countries, or an allow-list / block-list of specific ones (ISO alpha-2)."),
|
|
962
|
-
releaseNotes: z.string().describe("Publicly displayed on the
|
|
1272
|
+
releaseNotes: z.string().describe("Publicly displayed on the plugin details page.")
|
|
963
1273
|
});
|
|
964
1274
|
const chatgptSubmissionDraftSchema = chatgptSubmissionFormDataSchema.partial().extend({
|
|
965
1275
|
tagline: z.string().optional(),
|
|
@@ -970,23 +1280,35 @@ const chatgptSubmissionDraftSchema = chatgptSubmissionFormDataSchema.partial().e
|
|
|
970
1280
|
demoRecordingUrl: z.string().optional(),
|
|
971
1281
|
serverUrl: z.string().optional(),
|
|
972
1282
|
screenshots: z.array(screenshotDraftEntrySchema).optional(),
|
|
1283
|
+
testCases: z.array(positiveTestCaseDraftSchema).optional(),
|
|
1284
|
+
negativeTestCases: z.array(negativeTestCaseDraftSchema).optional(),
|
|
973
1285
|
translations: z.array(chatgptTranslationSchema.extend({ tagline: z.string() })).optional()
|
|
974
1286
|
});
|
|
975
|
-
|
|
1287
|
+
/** Source of truth for the fields `SubmissionPrefillService.prefill({ platform: "chatgpt" })` can populate. */
|
|
1288
|
+
const chatgptPrefillFormDataSchema = chatgptSubmissionFormDataSchema.pick({
|
|
976
1289
|
appName: true,
|
|
977
1290
|
developerName: true,
|
|
978
1291
|
serverUrl: true,
|
|
979
1292
|
tools: true,
|
|
1293
|
+
skills: true,
|
|
980
1294
|
authentication: true
|
|
981
1295
|
});
|
|
982
|
-
|
|
1296
|
+
/** Source of truth for `SubmissionPrefillService.generate({ platform: "chatgpt" })`. */
|
|
1297
|
+
const chatgptAiGeneratedFormDataSchema = chatgptSubmissionFormDataSchema.pick({
|
|
983
1298
|
tagline: true,
|
|
984
1299
|
description: true,
|
|
985
1300
|
category: true,
|
|
986
1301
|
testCases: true,
|
|
987
1302
|
negativeTestCases: true,
|
|
988
1303
|
toolJustifications: true
|
|
989
|
-
}).extend({
|
|
1304
|
+
}).extend({
|
|
1305
|
+
testCases: unboundedArrayField(chatgptSubmissionFormDataSchema.shape.testCases),
|
|
1306
|
+
negativeTestCases: unboundedArrayField(chatgptSubmissionFormDataSchema.shape.negativeTestCases)
|
|
1307
|
+
}).extend({
|
|
1308
|
+
tagline: clampStringField(chatgptSubmissionFormDataSchema.shape.tagline),
|
|
1309
|
+
testCases: z.array(positiveTestCaseDraftSchema).transform((cases) => toFixedLength(cases, 5, blankPositiveTestCase)),
|
|
1310
|
+
negativeTestCases: z.array(negativeTestCaseDraftSchema).transform((cases) => toFixedLength(cases, 3, blankNegativeTestCase))
|
|
1311
|
+
});
|
|
990
1312
|
const claudeCategorySchema = z.enum([
|
|
991
1313
|
"Productivity",
|
|
992
1314
|
"Communication",
|
|
@@ -1032,11 +1354,7 @@ const claudePolicyAcknowledgementSchema = z.enum([
|
|
|
1032
1354
|
"My connector does not collect conversation data beyond what is needed for its function.",
|
|
1033
1355
|
"Public documentation for this connector will be live by the publish date."
|
|
1034
1356
|
]);
|
|
1035
|
-
/**
|
|
1036
|
-
* Field order mirrors the claude.ai directory submission wizard
|
|
1037
|
-
* (`claude.ai/admin-settings/directory/submissions/new`), grouped by step.
|
|
1038
|
-
* When adding/removing/reordering fields, keep this in sync with the wizard.
|
|
1039
|
-
*/
|
|
1357
|
+
/** Field order mirrors the claude.ai directory submission wizard (`claude.ai/admin-settings/directory/submissions/new`), grouped by step; keep it in sync with the wizard. */
|
|
1040
1358
|
const claudeSubmissionFormDataSchema = z.object({
|
|
1041
1359
|
serverUrl: z.url().describe(`The full https:// URL where your MCP server is hosted and reachable. In the claude.ai wizard this is read live
|
|
1042
1360
|
from the custom connector you select — the connection is used to verify the URL and the tool list at submission time.
|
|
@@ -1113,7 +1431,8 @@ const claudeSubmissionDraftSchema = claudeSubmissionFormDataSchema.partial().ext
|
|
|
1113
1431
|
categories: z.array(claudeCategorySchema).optional(),
|
|
1114
1432
|
screenshots: z.array(screenshotDraftEntrySchema).optional()
|
|
1115
1433
|
});
|
|
1116
|
-
|
|
1434
|
+
/** Source of truth for the fields `SubmissionPrefillService.prefill({ platform: "claudeai" })` can populate. */
|
|
1435
|
+
const claudePrefillFormDataSchema = claudeSubmissionFormDataSchema.pick({
|
|
1117
1436
|
serverUrl: true,
|
|
1118
1437
|
name: true,
|
|
1119
1438
|
slug: true,
|
|
@@ -1128,39 +1447,93 @@ claudeSubmissionFormDataSchema.pick({
|
|
|
1128
1447
|
connectionRequirements: claudeSubmissionFormDataSchema.shape.connectionRequirements.optional(),
|
|
1129
1448
|
testInstructions: claudeSubmissionFormDataSchema.shape.testInstructions.optional()
|
|
1130
1449
|
});
|
|
1131
|
-
/**
|
|
1132
|
-
* Narrative fields the LLM-backed generation step produces for Claude submissions.
|
|
1133
|
-
* This schema accepts the slightly loose shape returned by AI generation, then normalizes it into
|
|
1134
|
-
* the strict form shape. Keep transforms here rather than in callers so generation leniency is
|
|
1135
|
-
* colocated with the Claude contract.
|
|
1136
|
-
*/
|
|
1450
|
+
/** Loose shape returned by AI generation for Claude narrative fields; transforms live here so generation leniency stays colocated with the Claude contract. */
|
|
1137
1451
|
const claudeAiGeneratedFormDataRawSchema = z.object({
|
|
1138
1452
|
oneLiner: claudeSubmissionFormDataSchema.shape.oneLiner,
|
|
1139
1453
|
description: claudeSubmissionFormDataSchema.shape.description,
|
|
1140
1454
|
categories: z.array(claudeCategorySchema).describe(claudeCategoryDescription),
|
|
1141
1455
|
primaryUseCases: claudeSubmissionFormDataSchema.shape.primaryUseCases
|
|
1142
1456
|
});
|
|
1143
|
-
claudeAiGeneratedFormDataRawSchema.extend({
|
|
1457
|
+
const claudeAiGeneratedFormDataSchema = claudeAiGeneratedFormDataRawSchema.extend({
|
|
1144
1458
|
oneLiner: clampStringField(claudeSubmissionFormDataSchema.shape.oneLiner),
|
|
1145
1459
|
categories: claudeAiGeneratedFormDataRawSchema.shape.categories.transform((categories) => categories.slice(0, 5))
|
|
1146
1460
|
});
|
|
1461
|
+
function provenanceByField(prefillShape, aiGeneratedShape) {
|
|
1462
|
+
const byField = {};
|
|
1463
|
+
for (const field of Object.keys(prefillShape)) byField[field] = "prefilled";
|
|
1464
|
+
for (const field of Object.keys(aiGeneratedShape)) byField[field] = "ai-generated";
|
|
1465
|
+
return byField;
|
|
1466
|
+
}
|
|
1467
|
+
provenanceByField(claudePrefillFormDataSchema.shape, claudeAiGeneratedFormDataSchema.shape), provenanceByField(chatgptPrefillFormDataSchema.shape, chatgptAiGeneratedFormDataSchema.shape);
|
|
1468
|
+
const urlVerificationOutcomeSchema = z.enum([
|
|
1469
|
+
"pass",
|
|
1470
|
+
"fail",
|
|
1471
|
+
"unverified"
|
|
1472
|
+
]);
|
|
1473
|
+
const VERIFIABLE_URL_FIELDS = {
|
|
1474
|
+
chatgpt: ["privacyPolicyUrl", "termsOfServiceUrl"],
|
|
1475
|
+
claudeai: ["privacyPolicy"]
|
|
1476
|
+
};
|
|
1477
|
+
const verifiableUrlFieldSchema = z.enum([...VERIFIABLE_URL_FIELDS.chatgpt, ...VERIFIABLE_URL_FIELDS.claudeai]);
|
|
1478
|
+
const policyDisclosureSchema = z.enum([
|
|
1479
|
+
"dataCategories",
|
|
1480
|
+
"purposesOfUse",
|
|
1481
|
+
"recipients",
|
|
1482
|
+
"retention"
|
|
1483
|
+
]);
|
|
1484
|
+
const urlVerificationFieldResultSchema = z.object({
|
|
1485
|
+
field: verifiableUrlFieldSchema,
|
|
1486
|
+
url: z.string(),
|
|
1487
|
+
outcome: urlVerificationOutcomeSchema,
|
|
1488
|
+
reason: z.string(),
|
|
1489
|
+
concerns: z.array(z.string()).optional(),
|
|
1490
|
+
scrapedWith: z.enum(["fetch", "firecrawl"]).optional()
|
|
1491
|
+
});
|
|
1492
|
+
const urlVerificationSchema = z.object({
|
|
1493
|
+
verifiedAt: z.coerce.date(),
|
|
1494
|
+
results: z.array(urlVerificationFieldResultSchema)
|
|
1495
|
+
});
|
|
1496
|
+
const pageKindSchema = z.enum([
|
|
1497
|
+
"expectedDocument",
|
|
1498
|
+
"errorPage",
|
|
1499
|
+
"loginWall",
|
|
1500
|
+
"parkedDomain",
|
|
1501
|
+
"comingSoon",
|
|
1502
|
+
"marketingPage",
|
|
1503
|
+
"unrelatedDocument"
|
|
1504
|
+
]);
|
|
1505
|
+
z.object({
|
|
1506
|
+
pageKind: pageKindSchema.describe("What the page actually is. Use 'expectedDocument' only when it is genuinely the document the field claims."),
|
|
1507
|
+
reason: z.string().max(200).describe("One sentence naming what the page is, addressed to the developer who entered the URL."),
|
|
1508
|
+
missingDisclosures: z.array(policyDisclosureSchema).describe("For a privacy policy only: which of the four required disclosures the text does not cover. Empty for every other page kind.")
|
|
1509
|
+
});
|
|
1147
1510
|
const submissionMetaFieldsSchema = z.object({
|
|
1148
1511
|
id: z.string(),
|
|
1149
1512
|
environmentId: z.string(),
|
|
1150
1513
|
auditId: z.string().nullable(),
|
|
1514
|
+
urlVerification: urlVerificationSchema.nullable().default(null),
|
|
1151
1515
|
createdAt: z.coerce.date(),
|
|
1152
1516
|
updatedAt: z.coerce.date()
|
|
1153
1517
|
});
|
|
1154
1518
|
const claudeSubmissionSchemaInternal = submissionMetaFieldsSchema.extend({
|
|
1155
1519
|
platform: z.literal("claudeai"),
|
|
1156
|
-
formData: claudeSubmissionDraftSchema
|
|
1520
|
+
formData: claudeSubmissionDraftSchema,
|
|
1521
|
+
prefillSnapshot: claudeSubmissionDraftSchema.nullable().default(null)
|
|
1157
1522
|
});
|
|
1158
1523
|
const chatgptSubmissionSchemaInternal = submissionMetaFieldsSchema.extend({
|
|
1159
1524
|
platform: z.literal("chatgpt"),
|
|
1160
|
-
formData: chatgptSubmissionDraftSchema
|
|
1525
|
+
formData: chatgptSubmissionDraftSchema,
|
|
1526
|
+
prefillSnapshot: chatgptSubmissionDraftSchema.nullable().default(null)
|
|
1161
1527
|
});
|
|
1162
|
-
z.discriminatedUnion("platform", [claudeSubmissionSchemaInternal, chatgptSubmissionSchemaInternal]);
|
|
1528
|
+
const submissionSchemaInternal = z.discriminatedUnion("platform", [claudeSubmissionSchemaInternal, chatgptSubmissionSchemaInternal]);
|
|
1163
1529
|
z.union([claudeSubmissionDraftSchema, chatgptSubmissionDraftSchema]);
|
|
1530
|
+
/** Discriminated on `platform`, which pins the shape of `formData`: a Claude submission cannot carry chatgpt-only fields, and vice-versa. */
|
|
1531
|
+
const submissionSchema = submissionSchemaInternal;
|
|
1532
|
+
z.literal([
|
|
1533
|
+
100,
|
|
1534
|
+
75,
|
|
1535
|
+
50
|
|
1536
|
+
]);
|
|
1164
1537
|
const subscriptionPlanSchema = z.enum([
|
|
1165
1538
|
"pro",
|
|
1166
1539
|
"business",
|
|
@@ -1172,17 +1545,70 @@ const customerCreditsSchema = z.object({
|
|
|
1172
1545
|
expiresAt: z.number().nullable(),
|
|
1173
1546
|
currency: z.string()
|
|
1174
1547
|
});
|
|
1175
|
-
const customerUsageSchema = z.object({
|
|
1176
|
-
amountInCents: z.number(),
|
|
1177
|
-
currency: z.string(),
|
|
1178
|
-
periodEnd: z.number()
|
|
1179
|
-
});
|
|
1180
1548
|
const partnerDiscountSchema = z.object({ endsAt: z.number() });
|
|
1181
1549
|
z.object({
|
|
1182
1550
|
customerCredits: customerCreditsSchema.nullable(),
|
|
1183
|
-
customerUsage: customerUsageSchema.nullable(),
|
|
1184
1551
|
partnerDiscount: partnerDiscountSchema.nullable()
|
|
1185
1552
|
});
|
|
1553
|
+
const metricsQueueEventSchema = z.object({
|
|
1554
|
+
ts: z.number().int(),
|
|
1555
|
+
method: z.string(),
|
|
1556
|
+
tool: z.string().nullable(),
|
|
1557
|
+
client: z.string(),
|
|
1558
|
+
sessionId: z.string().nullable(),
|
|
1559
|
+
durationMs: z.number().int(),
|
|
1560
|
+
outputTokens: z.number().int().nullable(),
|
|
1561
|
+
errorType: z.enum([
|
|
1562
|
+
"mcp_error",
|
|
1563
|
+
"tool_error",
|
|
1564
|
+
"timeout"
|
|
1565
|
+
]).nullable(),
|
|
1566
|
+
errorCode: z.number().int().nullable()
|
|
1567
|
+
});
|
|
1568
|
+
z.object({
|
|
1569
|
+
v: z.literal(1),
|
|
1570
|
+
envId: z.string(),
|
|
1571
|
+
events: z.array(metricsQueueEventSchema)
|
|
1572
|
+
});
|
|
1573
|
+
z.object({ v: z.number() });
|
|
1574
|
+
const telemetrySignalSchema = z.enum([
|
|
1575
|
+
"logs",
|
|
1576
|
+
"metrics",
|
|
1577
|
+
"traces"
|
|
1578
|
+
]);
|
|
1579
|
+
const otlpProtocolSchema = z.enum(["http/protobuf", "http/json"]);
|
|
1580
|
+
const otlpEndpointSchema = z.string().trim().min(1).max(2048);
|
|
1581
|
+
const OTLP_HEADER_VALUE_MAX_LENGTH = 2048;
|
|
1582
|
+
const otlpHeaderNameSchema = z.string().trim().min(1, "Enter a header name").max(128).regex(/^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/, "Use letters, digits and !#$%&'*+-.^_`|~ only");
|
|
1583
|
+
const otlpHeaderValueSchema = z.string().trim().max(OTLP_HEADER_VALUE_MAX_LENGTH).refine((value) => !/[\r\n]/.test(value), "Line breaks are not allowed in a header value");
|
|
1584
|
+
const telemetryDestinationHeaderSchema = z.object({
|
|
1585
|
+
id: z.string(),
|
|
1586
|
+
name: otlpHeaderNameSchema,
|
|
1587
|
+
value: otlpHeaderValueSchema,
|
|
1588
|
+
isSecret: z.boolean()
|
|
1589
|
+
});
|
|
1590
|
+
const otlpPushFailureReasonSchema = z.enum([
|
|
1591
|
+
"credentials",
|
|
1592
|
+
"wrong-path",
|
|
1593
|
+
"wrong-protocol",
|
|
1594
|
+
"invalid-payload",
|
|
1595
|
+
"partial-rejection",
|
|
1596
|
+
"blocked-target",
|
|
1597
|
+
"timeout",
|
|
1598
|
+
"unreachable",
|
|
1599
|
+
"receiver-error",
|
|
1600
|
+
"unexpected-status"
|
|
1601
|
+
]);
|
|
1602
|
+
const telemetryDestinationStatusSchema = z.enum(["active", "suspended"]);
|
|
1603
|
+
z.object({
|
|
1604
|
+
id: z.string(),
|
|
1605
|
+
signal: telemetrySignalSchema,
|
|
1606
|
+
endpoint: otlpEndpointSchema,
|
|
1607
|
+
protocol: otlpProtocolSchema,
|
|
1608
|
+
headers: z.array(telemetryDestinationHeaderSchema),
|
|
1609
|
+
status: telemetryDestinationStatusSchema,
|
|
1610
|
+
lastFailureReason: otlpPushFailureReasonSchema.nullable()
|
|
1611
|
+
});
|
|
1186
1612
|
//#endregion
|
|
1187
1613
|
//#region src/auth-scope.ts
|
|
1188
1614
|
/** Accepts an API key or OAuth access token (default for most routes). */
|
|
@@ -1225,7 +1651,10 @@ const RESERVED_KEYS = [
|
|
|
1225
1651
|
"BUILD_ARG_START_COMMAND",
|
|
1226
1652
|
"ALPIC_HOST",
|
|
1227
1653
|
"ALPIC_CUSTOM_DOMAINS",
|
|
1228
|
-
"ALPIC_INTENT_META_KEY"
|
|
1654
|
+
"ALPIC_INTENT_META_KEY",
|
|
1655
|
+
"ALPIC_FEEDBACK_META_KEY",
|
|
1656
|
+
"ALPIC_EVENTS_META_KEY",
|
|
1657
|
+
"ALPIC_IDENTIFY_META_KEY"
|
|
1229
1658
|
];
|
|
1230
1659
|
const environmentVariableSchema = z.object({
|
|
1231
1660
|
key: z.string().min(2, "Key must be at least 2 characters").regex(/^[a-zA-Z]([a-zA-Z0-9_])+$/, "Key must start with a letter and contain only letters, numbers, and underscores").refine((key) => !RESERVED_KEYS.includes(key), "This key is reserved and cannot be used as an environment variable key"),
|
|
@@ -1278,6 +1707,14 @@ const serverFieldsSchema = z.object({
|
|
|
1278
1707
|
});
|
|
1279
1708
|
//#endregion
|
|
1280
1709
|
//#region src/api.contract.ts
|
|
1710
|
+
const isValidIanaTimeZone = (timeZone) => {
|
|
1711
|
+
try {
|
|
1712
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
1713
|
+
return true;
|
|
1714
|
+
} catch {
|
|
1715
|
+
return false;
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1281
1718
|
const deploymentStatusSchema = z.enum([
|
|
1282
1719
|
"ongoing",
|
|
1283
1720
|
"deployed",
|
|
@@ -1306,21 +1743,20 @@ const deploymentSchema = z.object({
|
|
|
1306
1743
|
isCurrent: z.boolean(),
|
|
1307
1744
|
deploymentPageUrl: z.url().nullable()
|
|
1308
1745
|
});
|
|
1309
|
-
const
|
|
1746
|
+
const isValidTimeInput = (value) => {
|
|
1310
1747
|
if (ms(value) !== void 0) return true;
|
|
1311
1748
|
return !Number.isNaN(new Date(value).getTime());
|
|
1312
1749
|
};
|
|
1313
|
-
const
|
|
1750
|
+
const timeInputSchema = z.string().refine(isValidTimeInput, { message: "Invalid time. Use relative (1h, 30m, 2d) or ISO 8601 format." });
|
|
1751
|
+
const base = oc.errors({ BAD_REQUEST: {} });
|
|
1752
|
+
const createEnvironmentContractV1 = base.route({
|
|
1314
1753
|
path: "/v1/environments",
|
|
1315
1754
|
method: "POST",
|
|
1316
1755
|
summary: "Create an environment",
|
|
1317
1756
|
description: "Create an environment for a project",
|
|
1318
1757
|
tags: ["environments"],
|
|
1319
1758
|
successDescription: "The environment has been created successfully"
|
|
1320
|
-
}).errors({
|
|
1321
|
-
NOT_FOUND: {},
|
|
1322
|
-
BAD_REQUEST: {}
|
|
1323
|
-
}).input(z.object({
|
|
1759
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1324
1760
|
projectId: z.string().describe("The ID of the project to create the environment for"),
|
|
1325
1761
|
name: z.string().min(1).describe("The name of the environment"),
|
|
1326
1762
|
sourceBranch: z.string().min(1).describe("The branch used to build the environment"),
|
|
@@ -1333,7 +1769,7 @@ const createEnvironmentContractV1 = oc.route({
|
|
|
1333
1769
|
createdAt: z.coerce.date(),
|
|
1334
1770
|
projectId: z.string().describe("The ID of the project the environment belongs to")
|
|
1335
1771
|
}));
|
|
1336
|
-
const getEnvironmentContractV1 =
|
|
1772
|
+
const getEnvironmentContractV1 = base.route({
|
|
1337
1773
|
path: "/v1/environments/{environmentId}",
|
|
1338
1774
|
method: "GET",
|
|
1339
1775
|
summary: "Get an environment",
|
|
@@ -1349,6 +1785,14 @@ const getEnvironmentContractV1 = oc.route({
|
|
|
1349
1785
|
createdAt: z.coerce.date(),
|
|
1350
1786
|
projectId: z.string()
|
|
1351
1787
|
}));
|
|
1788
|
+
const deleteEnvironmentContractV1 = base.route({
|
|
1789
|
+
path: "/v1/environments/{environmentId}",
|
|
1790
|
+
method: "DELETE",
|
|
1791
|
+
summary: "Delete an environment",
|
|
1792
|
+
description: "All environments can be deleted except the production environment. This operation is not reversible: associated logs, analytics, and metrics are deleted as well.",
|
|
1793
|
+
tags: ["environments"],
|
|
1794
|
+
successDescription: "The environment has been deleted successfully"
|
|
1795
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ environmentId: z.string().describe("The ID of the environment") })).output(z.object({ success: z.literal(true) }));
|
|
1352
1796
|
const domainSchema = z.object({
|
|
1353
1797
|
domain: z.string(),
|
|
1354
1798
|
status: z.enum([
|
|
@@ -1390,7 +1834,7 @@ const projectOutputSchema = z.object({
|
|
|
1390
1834
|
productionEnvironment: productionEnvironmentSchema.nullable(),
|
|
1391
1835
|
environments: z.array(environmentSchema)
|
|
1392
1836
|
});
|
|
1393
|
-
const getProjectContractV1 =
|
|
1837
|
+
const getProjectContractV1 = base.route({
|
|
1394
1838
|
path: "/v1/projects/{projectId}",
|
|
1395
1839
|
method: "GET",
|
|
1396
1840
|
summary: "Get a project",
|
|
@@ -1398,18 +1842,15 @@ const getProjectContractV1 = oc.route({
|
|
|
1398
1842
|
tags: ["projects"],
|
|
1399
1843
|
successDescription: "The project details"
|
|
1400
1844
|
}).errors({ NOT_FOUND: {} }).input(z.object({ projectId: z.string().describe("The ID of the project") })).output(projectOutputSchema);
|
|
1401
|
-
const listProjectsContractV1 =
|
|
1845
|
+
const listProjectsContractV1 = base.route({
|
|
1402
1846
|
path: "/v1/projects",
|
|
1403
1847
|
method: "GET",
|
|
1404
1848
|
summary: "List projects",
|
|
1405
1849
|
description: "List all projects for a team",
|
|
1406
1850
|
tags: ["projects"],
|
|
1407
1851
|
successDescription: "The list of projects"
|
|
1408
|
-
}).errors({
|
|
1409
|
-
|
|
1410
|
-
BAD_REQUEST: {}
|
|
1411
|
-
}).input(z.object({ teamId: z.string().optional() }).optional()).output(z.array(projectOutputSchema));
|
|
1412
|
-
const createProjectContractV1 = oc.route({
|
|
1852
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ teamId: z.string().optional() }).optional()).output(z.array(projectOutputSchema));
|
|
1853
|
+
const createProjectContractV1 = base.route({
|
|
1413
1854
|
path: "/v1/projects",
|
|
1414
1855
|
method: "POST",
|
|
1415
1856
|
summary: "Create a project",
|
|
@@ -1418,7 +1859,6 @@ const createProjectContractV1 = oc.route({
|
|
|
1418
1859
|
successDescription: "The project has been created successfully"
|
|
1419
1860
|
}).errors({
|
|
1420
1861
|
NOT_FOUND: {},
|
|
1421
|
-
BAD_REQUEST: {},
|
|
1422
1862
|
FORBIDDEN: {}
|
|
1423
1863
|
}).input(z.object({
|
|
1424
1864
|
teamId: z.string().optional(),
|
|
@@ -1459,7 +1899,7 @@ const environmentVariableOutputSchema = z.object({
|
|
|
1459
1899
|
isSecret: z.boolean(),
|
|
1460
1900
|
createdAt: z.coerce.date()
|
|
1461
1901
|
});
|
|
1462
|
-
const listEnvironmentVariablesContractV1 =
|
|
1902
|
+
const listEnvironmentVariablesContractV1 = base.route({
|
|
1463
1903
|
path: "/v1/environments/{environmentId}/environment-variables",
|
|
1464
1904
|
method: "GET",
|
|
1465
1905
|
summary: "List environment variables",
|
|
@@ -1467,48 +1907,39 @@ const listEnvironmentVariablesContractV1 = oc.route({
|
|
|
1467
1907
|
tags: ["environments"],
|
|
1468
1908
|
successDescription: "The list of environment variables"
|
|
1469
1909
|
}).errors({ NOT_FOUND: {} }).input(z.object({ environmentId: z.string().describe("The ID of the environment") })).output(z.array(environmentVariableOutputSchema));
|
|
1470
|
-
const createEnvironmentVariablesContractV1 =
|
|
1910
|
+
const createEnvironmentVariablesContractV1 = base.route({
|
|
1471
1911
|
path: "/v1/environments/{environmentId}/environment-variables",
|
|
1472
1912
|
method: "POST",
|
|
1473
1913
|
summary: "Add environment variables",
|
|
1474
1914
|
description: "Add one or more environment variables to an environment",
|
|
1475
1915
|
tags: ["environments"],
|
|
1476
1916
|
successDescription: "The environment variables have been added successfully"
|
|
1477
|
-
}).errors({
|
|
1478
|
-
NOT_FOUND: {},
|
|
1479
|
-
BAD_REQUEST: {}
|
|
1480
|
-
}).input(z.object({
|
|
1917
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1481
1918
|
environmentId: z.string().describe("The ID of the environment"),
|
|
1482
1919
|
environmentVariables: environmentVariablesSchema
|
|
1483
1920
|
})).output(z.object({ success: z.literal(true) }));
|
|
1484
|
-
const updateEnvironmentVariableContractV1 =
|
|
1921
|
+
const updateEnvironmentVariableContractV1 = base.route({
|
|
1485
1922
|
path: "/v1/environment-variables/{environmentVariableId}",
|
|
1486
1923
|
method: "PATCH",
|
|
1487
1924
|
summary: "Update an environment variable",
|
|
1488
1925
|
description: "Update an environment variable by ID",
|
|
1489
1926
|
tags: ["environments"],
|
|
1490
1927
|
successDescription: "The environment variable has been updated successfully"
|
|
1491
|
-
}).errors({
|
|
1492
|
-
NOT_FOUND: {},
|
|
1493
|
-
BAD_REQUEST: {}
|
|
1494
|
-
}).input(z.object({
|
|
1928
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1495
1929
|
environmentVariableId: z.string().describe("The ID of the environment variable"),
|
|
1496
1930
|
key: environmentVariableSchema.shape.key,
|
|
1497
1931
|
value: environmentVariableSchema.shape.value.optional(),
|
|
1498
1932
|
isSecret: environmentVariableSchema.shape.isSecret.optional()
|
|
1499
1933
|
})).output(z.object({ success: z.literal(true) }));
|
|
1500
|
-
const deleteEnvironmentVariableContractV1 =
|
|
1934
|
+
const deleteEnvironmentVariableContractV1 = base.route({
|
|
1501
1935
|
path: "/v1/environment-variables/{environmentVariableId}",
|
|
1502
1936
|
method: "DELETE",
|
|
1503
1937
|
summary: "Delete an environment variable",
|
|
1504
1938
|
description: "Delete an environment variable by ID",
|
|
1505
1939
|
tags: ["environments"],
|
|
1506
1940
|
successDescription: "The environment variable has been deleted successfully"
|
|
1507
|
-
}).errors({
|
|
1508
|
-
|
|
1509
|
-
BAD_REQUEST: {}
|
|
1510
|
-
}).input(z.object({ environmentVariableId: z.string().describe("The ID of the environment variable") })).output(z.object({ success: z.literal(true) }));
|
|
1511
|
-
const deleteProjectContractV1 = oc.route({
|
|
1941
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ environmentVariableId: z.string().describe("The ID of the environment variable") })).output(z.object({ success: z.literal(true) }));
|
|
1942
|
+
const deleteProjectContractV1 = base.route({
|
|
1512
1943
|
path: "/v1/projects/{projectId}",
|
|
1513
1944
|
method: "DELETE",
|
|
1514
1945
|
summary: "Delete a project",
|
|
@@ -1516,7 +1947,7 @@ const deleteProjectContractV1 = oc.route({
|
|
|
1516
1947
|
tags: ["projects"],
|
|
1517
1948
|
successDescription: "The project has been deleted successfully"
|
|
1518
1949
|
}).errors({ NOT_FOUND: {} }).input(z.object({ projectId: z.string().describe("The ID of the project") })).output(z.object({ success: z.literal(true) }));
|
|
1519
|
-
const updateProjectContractV1 =
|
|
1950
|
+
const updateProjectContractV1 = base.route({
|
|
1520
1951
|
path: "/v1/projects/{projectId}",
|
|
1521
1952
|
method: "PATCH",
|
|
1522
1953
|
summary: "Update a project",
|
|
@@ -1525,14 +1956,13 @@ const updateProjectContractV1 = oc.route({
|
|
|
1525
1956
|
successDescription: "The project has been updated successfully"
|
|
1526
1957
|
}).errors({
|
|
1527
1958
|
NOT_FOUND: {},
|
|
1528
|
-
BAD_REQUEST: {},
|
|
1529
1959
|
FORBIDDEN: {}
|
|
1530
1960
|
}).input(z.object({
|
|
1531
1961
|
projectId: z.string().describe("The ID of the project"),
|
|
1532
1962
|
name: z.string().min(1).max(100).optional().describe("The new name for the project"),
|
|
1533
1963
|
sourceRepository: z.string().nullable().optional().describe("The source repository to connect to the project")
|
|
1534
1964
|
})).output(projectOutputSchema);
|
|
1535
|
-
const deployEnvironmentContractV1 =
|
|
1965
|
+
const deployEnvironmentContractV1 = base.route({
|
|
1536
1966
|
path: "/v1/environments/{environmentId}/deploy",
|
|
1537
1967
|
method: "POST",
|
|
1538
1968
|
summary: "Deploy an environment",
|
|
@@ -1541,27 +1971,25 @@ const deployEnvironmentContractV1 = oc.route({
|
|
|
1541
1971
|
successDescription: "The environment has been deployed successfully"
|
|
1542
1972
|
}).errors({
|
|
1543
1973
|
NOT_FOUND: {},
|
|
1544
|
-
|
|
1974
|
+
CONFLICT: {}
|
|
1545
1975
|
}).input(z.object({
|
|
1546
1976
|
environmentId: z.string().describe("The ID of the environment to deploy"),
|
|
1547
|
-
token: z.string().describe("The token to identify the source archive").optional()
|
|
1977
|
+
token: z.string().describe("The token to identify the source archive").optional(),
|
|
1978
|
+
settings: buildSettingsSchema.extend({ transport: transportSchema }).optional().describe("Project settings detected by the client from the source. When provided, the server skips downloading and extracting the source archive to detect them.")
|
|
1548
1979
|
})).output(deploymentSchema);
|
|
1549
|
-
const uploadDeploymentArtifactContractV1 =
|
|
1980
|
+
const uploadDeploymentArtifactContractV1 = base.route({
|
|
1550
1981
|
path: "/v1/deployments/upload",
|
|
1551
1982
|
method: "POST",
|
|
1552
1983
|
summary: "Upload deployment artifacts",
|
|
1553
1984
|
description: "Return a presigned S3 URL to upload a deployment artifact",
|
|
1554
1985
|
tags: ["deployments"],
|
|
1555
1986
|
successDescription: "The presigned upload URL has been generated successfully"
|
|
1556
|
-
}).errors({
|
|
1557
|
-
NOT_FOUND: {},
|
|
1558
|
-
BAD_REQUEST: {}
|
|
1559
|
-
}).input(z.object({ teamId: z.string().optional() }).optional()).output(z.object({
|
|
1987
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ teamId: z.string().optional() }).optional()).output(z.object({
|
|
1560
1988
|
uploadUrl: z.url().describe("Presigned S3 URL to upload the source archive with HTTP PUT"),
|
|
1561
1989
|
token: z.string().describe("Token to identify the source archive"),
|
|
1562
1990
|
expiresAt: z.coerce.date().describe("Expiration date of the presigned URL")
|
|
1563
1991
|
}));
|
|
1564
|
-
const listDeploymentsContractV1 =
|
|
1992
|
+
const listDeploymentsContractV1 = base.route({
|
|
1565
1993
|
path: "/v1/projects/{projectId}/deployments",
|
|
1566
1994
|
method: "GET",
|
|
1567
1995
|
summary: "List project deployments",
|
|
@@ -1573,7 +2001,7 @@ const listDeploymentsContractV1 = oc.route({
|
|
|
1573
2001
|
status: z.array(deploymentStatusSchema).optional().describe("Filter by one or more statuses"),
|
|
1574
2002
|
environmentId: z.string().optional().describe("Filter by environment ID")
|
|
1575
2003
|
})).output(z.array(deploymentSchema));
|
|
1576
|
-
const getDeploymentContractV1 =
|
|
2004
|
+
const getDeploymentContractV1 = base.route({
|
|
1577
2005
|
path: "/v1/deployments/{deploymentId}",
|
|
1578
2006
|
method: "GET",
|
|
1579
2007
|
summary: "Get a deployment",
|
|
@@ -1581,20 +2009,32 @@ const getDeploymentContractV1 = oc.route({
|
|
|
1581
2009
|
tags: ["deployments"],
|
|
1582
2010
|
successDescription: "The deployment details"
|
|
1583
2011
|
}).errors({ NOT_FOUND: {} }).input(z.object({ deploymentId: z.string().describe("The ID of the deployment") })).output(deploymentSchema);
|
|
1584
|
-
const
|
|
2012
|
+
const logEntrySchema = z.object({
|
|
2013
|
+
timestamp: z.coerce.date(),
|
|
2014
|
+
type: z.enum([
|
|
2015
|
+
"START",
|
|
2016
|
+
"END",
|
|
2017
|
+
"INFO",
|
|
2018
|
+
"ERROR",
|
|
2019
|
+
"WARNING",
|
|
2020
|
+
"DEBUG"
|
|
2021
|
+
]),
|
|
2022
|
+
requestId: z.string(),
|
|
2023
|
+
content: z.string().optional(),
|
|
2024
|
+
method: z.string().optional(),
|
|
2025
|
+
durationInMs: z.number().optional()
|
|
2026
|
+
});
|
|
2027
|
+
const getLogsContractV1 = base.route({
|
|
1585
2028
|
path: "/v1/environments/{environmentId}/logs",
|
|
1586
2029
|
method: "GET",
|
|
1587
2030
|
summary: "Get logs",
|
|
1588
2031
|
description: "Get logs for an environment",
|
|
1589
2032
|
tags: ["environments"],
|
|
1590
2033
|
successDescription: "The logs"
|
|
1591
|
-
}).errors({
|
|
1592
|
-
NOT_FOUND: {},
|
|
1593
|
-
BAD_REQUEST: {}
|
|
1594
|
-
}).input(z.object({
|
|
2034
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1595
2035
|
environmentId: z.string().describe("The ID of the environment"),
|
|
1596
|
-
since:
|
|
1597
|
-
until:
|
|
2036
|
+
since: timeInputSchema.optional().describe("Start time — ISO 8601 (2024-01-01T00:00:00Z) or relative (1h, 30m, 2d)"),
|
|
2037
|
+
until: timeInputSchema.optional().describe("End time — ISO 8601 or relative"),
|
|
1598
2038
|
limit: z.coerce.number().int().min(1).max(1e3).default(1e3).describe("Maximum number of log entries to return."),
|
|
1599
2039
|
level: z.array(z.enum([
|
|
1600
2040
|
"INFO",
|
|
@@ -1602,37 +2042,20 @@ const getLogsContractV1 = oc.route({
|
|
|
1602
2042
|
"WARNING",
|
|
1603
2043
|
"DEBUG"
|
|
1604
2044
|
])).optional().describe("Filter by log level"),
|
|
1605
|
-
search:
|
|
2045
|
+
search: logSearchSchema.optional().describe("Term to search for in log content"),
|
|
1606
2046
|
nextToken: z.string().optional().describe("Pagination token from a previous response")
|
|
1607
2047
|
})).output(z.object({
|
|
1608
|
-
logs: z.array(
|
|
1609
|
-
timestamp: z.coerce.date(),
|
|
1610
|
-
type: z.enum([
|
|
1611
|
-
"START",
|
|
1612
|
-
"END",
|
|
1613
|
-
"INFO",
|
|
1614
|
-
"ERROR",
|
|
1615
|
-
"WARNING",
|
|
1616
|
-
"DEBUG"
|
|
1617
|
-
]),
|
|
1618
|
-
requestId: z.string(),
|
|
1619
|
-
content: z.string().optional(),
|
|
1620
|
-
method: z.string().optional(),
|
|
1621
|
-
durationInMs: z.number().optional()
|
|
1622
|
-
})),
|
|
2048
|
+
logs: z.array(logEntrySchema),
|
|
1623
2049
|
nextToken: z.string().nullable()
|
|
1624
2050
|
}));
|
|
1625
|
-
const getLatestLogsContractV1 =
|
|
2051
|
+
const getLatestLogsContractV1 = base.route({
|
|
1626
2052
|
path: "/v1/environments/{environmentId}/latest-logs",
|
|
1627
2053
|
method: "GET",
|
|
1628
2054
|
summary: "Get latest logs",
|
|
1629
2055
|
description: "Get the N most recent logs for an environment",
|
|
1630
2056
|
tags: ["environments"],
|
|
1631
2057
|
successDescription: "The latest logs"
|
|
1632
|
-
}).errors({
|
|
1633
|
-
NOT_FOUND: {},
|
|
1634
|
-
BAD_REQUEST: {}
|
|
1635
|
-
}).input(z.object({
|
|
2058
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1636
2059
|
environmentId: z.string().describe("The ID of the environment"),
|
|
1637
2060
|
limit: z.coerce.number().int().min(1).max(1e3).default(100).describe("Number of most recent log entries to return"),
|
|
1638
2061
|
level: z.array(z.enum([
|
|
@@ -1641,23 +2064,9 @@ const getLatestLogsContractV1 = oc.route({
|
|
|
1641
2064
|
"WARNING",
|
|
1642
2065
|
"DEBUG"
|
|
1643
2066
|
])).optional().describe("Filter by log level"),
|
|
1644
|
-
search:
|
|
1645
|
-
})).output(z.object({ logs: z.array(
|
|
1646
|
-
|
|
1647
|
-
type: z.enum([
|
|
1648
|
-
"START",
|
|
1649
|
-
"END",
|
|
1650
|
-
"INFO",
|
|
1651
|
-
"ERROR",
|
|
1652
|
-
"WARNING",
|
|
1653
|
-
"DEBUG"
|
|
1654
|
-
]),
|
|
1655
|
-
requestId: z.string(),
|
|
1656
|
-
content: z.string().optional(),
|
|
1657
|
-
method: z.string().optional(),
|
|
1658
|
-
durationInMs: z.number().optional()
|
|
1659
|
-
})) }));
|
|
1660
|
-
const getDeploymentLogsContractV1 = oc.route({
|
|
2067
|
+
search: logSearchSchema.optional().describe("Term to search for in log content")
|
|
2068
|
+
})).output(z.object({ logs: z.array(logEntrySchema) }));
|
|
2069
|
+
const getDeploymentLogsContractV1 = base.route({
|
|
1661
2070
|
path: "/v1/deployments/{deploymentId}/logs",
|
|
1662
2071
|
method: "GET",
|
|
1663
2072
|
summary: "Get deployment logs",
|
|
@@ -1684,22 +2093,19 @@ const analyticsTimeSeriesSchema = z.object({
|
|
|
1684
2093
|
output_token_mean: z.array(analyticsDataPointSchema),
|
|
1685
2094
|
task_count: z.array(analyticsDataPointSchema)
|
|
1686
2095
|
});
|
|
1687
|
-
const getProjectAnalyticsContractV1 =
|
|
2096
|
+
const getProjectAnalyticsContractV1 = base.route({
|
|
1688
2097
|
path: "/v1/analytics/{projectId}",
|
|
1689
2098
|
method: "GET",
|
|
1690
2099
|
summary: "Get project analytics",
|
|
1691
2100
|
description: "Get analytics data for a project over a time range",
|
|
1692
2101
|
tags: ["analytics"],
|
|
1693
2102
|
successDescription: "The project analytics data"
|
|
1694
|
-
}).errors({
|
|
1695
|
-
NOT_FOUND: {},
|
|
1696
|
-
BAD_REQUEST: {}
|
|
1697
|
-
}).input(z.object({
|
|
2103
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1698
2104
|
projectId: z.string().describe("The ID of the project"),
|
|
1699
2105
|
startTimestamp: z.coerce.number().describe("Start timestamp in milliseconds"),
|
|
1700
2106
|
endTimestamp: z.coerce.number().describe("End timestamp in milliseconds"),
|
|
1701
2107
|
timeZone: z.string().describe("IANA timezone (e.g. Europe/Paris)")
|
|
1702
|
-
}).refine(({ startTimestamp, endTimestamp }) => endTimestamp - startTimestamp > 0, { message: "End date must be after start date" }).refine(({ startTimestamp, endTimestamp }) => endTimestamp - startTimestamp <=
|
|
2108
|
+
}).refine(({ startTimestamp, endTimestamp }) => endTimestamp - startTimestamp > 0, { message: "End date must be after start date" }).refine(({ startTimestamp, endTimestamp }) => endTimestamp - startTimestamp <= 26784e5, { message: "Date range must be less than 1 month" }).refine(({ timeZone }) => isValidIanaTimeZone(timeZone), { message: "Time zone must be a valid IANA timezone" })).output(z.object({
|
|
1703
2109
|
metadata: z.object({
|
|
1704
2110
|
startTimestamp: z.number(),
|
|
1705
2111
|
endTimestamp: z.number(),
|
|
@@ -1709,7 +2115,132 @@ const getProjectAnalyticsContractV1 = oc.route({
|
|
|
1709
2115
|
}),
|
|
1710
2116
|
timeSeries: analyticsTimeSeriesSchema
|
|
1711
2117
|
}));
|
|
1712
|
-
const
|
|
2118
|
+
const paginatedInsightsInputShape = {
|
|
2119
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2120
|
+
since: timeInputSchema.default("7d").describe("Start time — ISO 8601 (2024-01-01T00:00:00Z) or relative (1h, 30m, 2d)"),
|
|
2121
|
+
until: timeInputSchema.optional().describe("End time — ISO 8601 or relative"),
|
|
2122
|
+
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
2123
|
+
offset: z.coerce.number().int().min(0).default(0),
|
|
2124
|
+
search: z.string().trim().min(1).max(200).optional()
|
|
2125
|
+
};
|
|
2126
|
+
const listIntentsContractV1 = base.route({
|
|
2127
|
+
path: "/v1/environments/{environmentId}/intents",
|
|
2128
|
+
method: "GET",
|
|
2129
|
+
summary: "List intents",
|
|
2130
|
+
description: "List grouped user intents for an environment over a time range",
|
|
2131
|
+
tags: ["insights"],
|
|
2132
|
+
successDescription: "The grouped user intents"
|
|
2133
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2134
|
+
...paginatedInsightsInputShape,
|
|
2135
|
+
tools: z.array(z.string().trim().min(1)).min(1).max(100).optional(),
|
|
2136
|
+
clientIds: z.array(z.string().min(1)).min(1).max(100).optional(),
|
|
2137
|
+
categoryIds: z.array(z.string().trim().min(1)).min(1).max(100).optional(),
|
|
2138
|
+
sortDir: z.enum(["asc", "desc"]).default("desc")
|
|
2139
|
+
})).output(z.object({
|
|
2140
|
+
intents: z.array(z.object({
|
|
2141
|
+
id: z.string(),
|
|
2142
|
+
intentIds: z.array(z.string()).min(1),
|
|
2143
|
+
environmentId: z.string(),
|
|
2144
|
+
message: z.string(),
|
|
2145
|
+
toolName: z.string(),
|
|
2146
|
+
createdAt: z.coerce.date(),
|
|
2147
|
+
occurrenceCount: z.number().int().positive(),
|
|
2148
|
+
clientName: z.string().nullable(),
|
|
2149
|
+
clientId: z.string().nullable(),
|
|
2150
|
+
categories: z.array(z.object({
|
|
2151
|
+
id: z.string(),
|
|
2152
|
+
name: z.string(),
|
|
2153
|
+
color: intentCategoryColorSchema
|
|
2154
|
+
}))
|
|
2155
|
+
})),
|
|
2156
|
+
totalCount: z.number().int().nonnegative()
|
|
2157
|
+
}));
|
|
2158
|
+
const listFeedbacksContractV1 = base.route({
|
|
2159
|
+
path: "/v1/environments/{environmentId}/feedbacks",
|
|
2160
|
+
method: "GET",
|
|
2161
|
+
summary: "List feedbacks",
|
|
2162
|
+
description: "List user feedback for an environment over a time range",
|
|
2163
|
+
tags: ["insights"],
|
|
2164
|
+
successDescription: "The user feedback"
|
|
2165
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2166
|
+
...paginatedInsightsInputShape,
|
|
2167
|
+
clientIds: z.array(z.string().min(1)).min(1).max(100).optional()
|
|
2168
|
+
})).output(z.object({
|
|
2169
|
+
feedbacks: z.array(feedbackSchema.extend({ clientId: z.string().nullable() })),
|
|
2170
|
+
totalCount: z.number().int().nonnegative()
|
|
2171
|
+
}));
|
|
2172
|
+
const intentCategoryNameSchema = z.string().trim().min(1).max(100).describe("The category name");
|
|
2173
|
+
const categorizableIntentIdsSchema = z.array(z.string().trim().min(1)).min(1).max(100).describe("IDs of the intents to categorize");
|
|
2174
|
+
const listIntentCategoriesContractV1 = base.route({
|
|
2175
|
+
path: "/v1/environments/{environmentId}/intent-categories",
|
|
2176
|
+
method: "GET",
|
|
2177
|
+
summary: "List intent categories",
|
|
2178
|
+
description: "List intent categories for an environment, including how many intents each category is linked to",
|
|
2179
|
+
tags: ["insights"],
|
|
2180
|
+
successDescription: "The intent categories"
|
|
2181
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ environmentId: z.string().describe("The ID of the environment") })).output(z.array(intentCategorySummarySchema));
|
|
2182
|
+
const createIntentCategoryContractV1 = base.route({
|
|
2183
|
+
path: "/v1/environments/{environmentId}/intent-categories",
|
|
2184
|
+
method: "POST",
|
|
2185
|
+
summary: "Create an intent category",
|
|
2186
|
+
description: "Create an intent category, optionally linking it to existing intents",
|
|
2187
|
+
tags: ["insights"],
|
|
2188
|
+
successDescription: "The created intent category"
|
|
2189
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2190
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2191
|
+
name: intentCategoryNameSchema,
|
|
2192
|
+
color: intentCategoryColorSchema.optional().describe("The category color; derived from the name when omitted"),
|
|
2193
|
+
intentIds: categorizableIntentIdsSchema.optional().describe("IDs of intents to link to the new category")
|
|
2194
|
+
})).output(intentCategorySchema);
|
|
2195
|
+
const updateIntentCategoryContractV1 = base.route({
|
|
2196
|
+
path: "/v1/environments/{environmentId}/intent-categories/{categoryId}",
|
|
2197
|
+
method: "PATCH",
|
|
2198
|
+
summary: "Update an intent category",
|
|
2199
|
+
description: "Rename or recolor an intent category",
|
|
2200
|
+
tags: ["insights"],
|
|
2201
|
+
successDescription: "The updated intent category"
|
|
2202
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2203
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2204
|
+
categoryId: z.string().describe("The ID of the category"),
|
|
2205
|
+
name: intentCategoryNameSchema.optional(),
|
|
2206
|
+
color: intentCategoryColorSchema.optional().describe("The category color")
|
|
2207
|
+
}).refine(({ name, color }) => name !== void 0 || color !== void 0, { message: "At least one of name or color must be provided" })).output(intentCategorySchema);
|
|
2208
|
+
const deleteIntentCategoryContractV1 = base.route({
|
|
2209
|
+
path: "/v1/environments/{environmentId}/intent-categories/{categoryId}",
|
|
2210
|
+
method: "DELETE",
|
|
2211
|
+
summary: "Delete an intent category",
|
|
2212
|
+
description: "Delete an intent category; its intents are unlinked but not deleted",
|
|
2213
|
+
tags: ["insights"],
|
|
2214
|
+
successDescription: "The intent category has been deleted successfully"
|
|
2215
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2216
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2217
|
+
categoryId: z.string().describe("The ID of the category")
|
|
2218
|
+
})).output(z.object({ success: z.literal(true) }));
|
|
2219
|
+
const linkIntentCategoryContractV1 = base.route({
|
|
2220
|
+
path: "/v1/environments/{environmentId}/intent-categories/{categoryId}/link",
|
|
2221
|
+
method: "POST",
|
|
2222
|
+
summary: "Link an intent category",
|
|
2223
|
+
description: "Link an intent category to intents; already linked intents are ignored",
|
|
2224
|
+
tags: ["insights"],
|
|
2225
|
+
successDescription: "The intents have been linked successfully"
|
|
2226
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2227
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2228
|
+
categoryId: z.string().describe("The ID of the category"),
|
|
2229
|
+
intentIds: categorizableIntentIdsSchema
|
|
2230
|
+
})).output(z.object({ success: z.literal(true) }));
|
|
2231
|
+
const unlinkIntentCategoryContractV1 = base.route({
|
|
2232
|
+
path: "/v1/environments/{environmentId}/intent-categories/{categoryId}/unlink",
|
|
2233
|
+
method: "POST",
|
|
2234
|
+
summary: "Unlink an intent category",
|
|
2235
|
+
description: "Unlink an intent category from intents; intents without the category are ignored",
|
|
2236
|
+
tags: ["insights"],
|
|
2237
|
+
successDescription: "The intents have been unlinked successfully"
|
|
2238
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2239
|
+
environmentId: z.string().describe("The ID of the environment"),
|
|
2240
|
+
categoryId: z.string().describe("The ID of the category"),
|
|
2241
|
+
intentIds: categorizableIntentIdsSchema
|
|
2242
|
+
})).output(z.object({ success: z.literal(true) }));
|
|
2243
|
+
const listTeamsContractV1 = base.route({
|
|
1713
2244
|
path: "/v1/teams",
|
|
1714
2245
|
method: "GET",
|
|
1715
2246
|
summary: "List teams",
|
|
@@ -1722,7 +2253,22 @@ const listTeamsContractV1 = oc.route({
|
|
|
1722
2253
|
createdAt: z.coerce.date(),
|
|
1723
2254
|
hasStripeAccount: z.boolean()
|
|
1724
2255
|
})));
|
|
1725
|
-
const
|
|
2256
|
+
const getTeamUsageContractV1 = base.route({
|
|
2257
|
+
path: "/v1/teams/{teamId}/usage",
|
|
2258
|
+
method: "GET",
|
|
2259
|
+
summary: "Get team usage",
|
|
2260
|
+
description: "Get the MCP request usage of a team for its current billing window. A team that has made no request in the window reports zero.",
|
|
2261
|
+
tags: ["teams"],
|
|
2262
|
+
successDescription: "The current billing window usage"
|
|
2263
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ teamId: z.string().describe("The ID of the team") })).output(z.object({
|
|
2264
|
+
teamId: z.string(),
|
|
2265
|
+
plan: subscriptionPlanSchema.nullable().describe("The plan in force for the window, null on the Free plan"),
|
|
2266
|
+
periodStart: z.coerce.date().describe("Start of the current billing window"),
|
|
2267
|
+
periodEnd: z.coerce.date().describe("End of the current billing window"),
|
|
2268
|
+
requestCount: z.number().int().describe("MCP requests settled during the current billing window"),
|
|
2269
|
+
includedRequests: z.number().int().nullable().describe("Requests included in the plan for one window, null when the allowance is contract-specific")
|
|
2270
|
+
}));
|
|
2271
|
+
const getTunnelTicketContractV1 = base.route({
|
|
1726
2272
|
path: "/v1/tunnels/ticket",
|
|
1727
2273
|
method: "GET",
|
|
1728
2274
|
summary: "Get a tunnel ticket",
|
|
@@ -1734,16 +2280,13 @@ const getTunnelTicketContractV1 = oc.route({
|
|
|
1734
2280
|
ticket: z.string().describe("The signed tunnel ticket"),
|
|
1735
2281
|
tunnelHost: z.string().describe("The tunnel host to connect to")
|
|
1736
2282
|
}));
|
|
1737
|
-
const publishServerContractV1 =
|
|
2283
|
+
const publishServerContractV1 = base.route({
|
|
1738
2284
|
path: "/v1/distribution/publish",
|
|
1739
2285
|
method: "POST",
|
|
1740
2286
|
summary: "Publish a server to the MCP registry",
|
|
1741
2287
|
tags: ["distribution"],
|
|
1742
2288
|
successDescription: "The server has been published successfully"
|
|
1743
|
-
}).errors({
|
|
1744
|
-
NOT_FOUND: {},
|
|
1745
|
-
BAD_REQUEST: {}
|
|
1746
|
-
}).input(z.object({
|
|
2289
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1747
2290
|
projectId: z.string(),
|
|
1748
2291
|
domain: z.string(),
|
|
1749
2292
|
title: z.string().min(1).max(100),
|
|
@@ -1752,17 +2295,14 @@ const publishServerContractV1 = oc.route({
|
|
|
1752
2295
|
iconSrc: z.url().max(255).optional(),
|
|
1753
2296
|
dryRun: z.boolean().optional()
|
|
1754
2297
|
})).output(z.object({ serverFields: serverFieldsSchema }));
|
|
1755
|
-
const getServerInfoContractV1 =
|
|
2298
|
+
const getServerInfoContractV1 = base.route({
|
|
1756
2299
|
path: "/v1/distribution/get",
|
|
1757
2300
|
method: "GET",
|
|
1758
2301
|
summary: "Get server info",
|
|
1759
2302
|
description: "Get info about a server",
|
|
1760
2303
|
tags: ["distribution"],
|
|
1761
2304
|
successDescription: "The server info"
|
|
1762
|
-
}).errors({
|
|
1763
|
-
NOT_FOUND: {},
|
|
1764
|
-
BAD_REQUEST: {}
|
|
1765
|
-
}).input(z.object({
|
|
2305
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1766
2306
|
projectId: z.string(),
|
|
1767
2307
|
domain: z.string()
|
|
1768
2308
|
})).output(z.object({ serverFields: serverFieldsSchema }));
|
|
@@ -1781,7 +2321,7 @@ const playgroundOutputSchema = z.object({
|
|
|
1781
2321
|
isPlaygroundEnabled: z.boolean(),
|
|
1782
2322
|
serverMetadata: playgroundServerMetadataOutputSchema.nullable()
|
|
1783
2323
|
});
|
|
1784
|
-
const getPlaygroundContractV1 =
|
|
2324
|
+
const getPlaygroundContractV1 = base.route({
|
|
1785
2325
|
path: "/v1/environments/{environmentId}/playground",
|
|
1786
2326
|
method: "GET",
|
|
1787
2327
|
summary: "Get playground configuration",
|
|
@@ -1789,17 +2329,14 @@ const getPlaygroundContractV1 = oc.route({
|
|
|
1789
2329
|
tags: ["environments"],
|
|
1790
2330
|
successDescription: "The playground configuration"
|
|
1791
2331
|
}).errors({ NOT_FOUND: {} }).input(z.object({ environmentId: z.string().describe("The ID of the environment") })).output(playgroundOutputSchema);
|
|
1792
|
-
const upsertPlaygroundContractV1 =
|
|
2332
|
+
const upsertPlaygroundContractV1 = base.route({
|
|
1793
2333
|
path: "/v1/environments/{environmentId}/playground",
|
|
1794
2334
|
method: "PUT",
|
|
1795
2335
|
summary: "Update playground configuration",
|
|
1796
2336
|
description: "Update the playground configuration for an environment. All fields are optional — only provided fields are updated.",
|
|
1797
2337
|
tags: ["environments"],
|
|
1798
2338
|
successDescription: "The updated playground configuration"
|
|
1799
|
-
}).errors({
|
|
1800
|
-
NOT_FOUND: {},
|
|
1801
|
-
BAD_REQUEST: {}
|
|
1802
|
-
}).input(z.object({
|
|
2339
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
1803
2340
|
environmentId: z.string().describe("The ID of the environment"),
|
|
1804
2341
|
isPlaygroundEnabled: z.boolean().optional(),
|
|
1805
2342
|
name: z.string().min(1).max(100).optional(),
|
|
@@ -1807,7 +2344,7 @@ const upsertPlaygroundContractV1 = oc.route({
|
|
|
1807
2344
|
headers: z.array(playgroundHeaderSchema).optional(),
|
|
1808
2345
|
examplePrompts: z.array(playgroundExamplePromptSchema).max(5).optional()
|
|
1809
2346
|
})).output(playgroundOutputSchema);
|
|
1810
|
-
const createBeaconContractV1 =
|
|
2347
|
+
const createBeaconContractV1 = base.route({
|
|
1811
2348
|
path: "/v1/beacon/audits",
|
|
1812
2349
|
method: "POST",
|
|
1813
2350
|
summary: "Create a beacon audit",
|
|
@@ -1816,7 +2353,6 @@ const createBeaconContractV1 = oc.route({
|
|
|
1816
2353
|
successDescription: "The audit has been created"
|
|
1817
2354
|
}).errors({
|
|
1818
2355
|
NOT_FOUND: {},
|
|
1819
|
-
BAD_REQUEST: {},
|
|
1820
2356
|
DNS_RESOLUTION_FAILED: { status: 400 }
|
|
1821
2357
|
}).input(z.object({
|
|
1822
2358
|
targetUrl: z.url().describe("The HTTPS URL of the MCP server to audit"),
|
|
@@ -1824,7 +2360,7 @@ const createBeaconContractV1 = oc.route({
|
|
|
1824
2360
|
projectId: z.string().optional().describe("The project ID to associate the audit with"),
|
|
1825
2361
|
excludeCategories: z.array(checkCategorySchema).optional().describe("Check categories to exclude from the audit")
|
|
1826
2362
|
})).output(z.object({ id: z.string() }));
|
|
1827
|
-
const getBeaconContractV1 =
|
|
2363
|
+
const getBeaconContractV1 = base.route({
|
|
1828
2364
|
path: "/v1/beacon/audits/{auditId}",
|
|
1829
2365
|
method: "GET",
|
|
1830
2366
|
summary: "Get a beacon audit",
|
|
@@ -1839,9 +2375,73 @@ const getBeaconContractV1 = oc.route({
|
|
|
1839
2375
|
createdAt: z.coerce.date(),
|
|
1840
2376
|
report: auditReportWithScreenshotsSchema.nullable()
|
|
1841
2377
|
}));
|
|
2378
|
+
const PLATFORM_DESCRIPTION = "The directory the package targets: `chatgpt` for the OpenAI App Directory, `claudeai` for the Anthropic MCP Directory";
|
|
2379
|
+
const submissionIdSchema = z.string().describe("The ID of the submission package");
|
|
2380
|
+
const claudeSubmissionUpdateSchema = claudeSubmissionDraftSchema.strict();
|
|
2381
|
+
const chatgptSubmissionUpdateSchema = chatgptSubmissionDraftSchema.strict();
|
|
2382
|
+
const createSubmissionContractV1 = base.route({
|
|
2383
|
+
path: "/v1/submissions",
|
|
2384
|
+
method: "POST",
|
|
2385
|
+
summary: "Create a submission package",
|
|
2386
|
+
description: "Create a submission package for an environment. Alpic reads the environment's live MCP server and prefills every field it can infer, so the response already carries the populated package. The environment must have a successful deployment and must not require authentication.",
|
|
2387
|
+
tags: ["submissions"],
|
|
2388
|
+
successDescription: "The created submission package, with its prefilled fields"
|
|
2389
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2390
|
+
environmentId: z.string().describe("The ID of the environment to build the package from"),
|
|
2391
|
+
platform: platformSchema.describe(PLATFORM_DESCRIPTION)
|
|
2392
|
+
})).output(submissionSchema);
|
|
2393
|
+
const listSubmissionsContractV1 = base.route({
|
|
2394
|
+
path: "/v1/projects/{projectId}/submissions",
|
|
2395
|
+
method: "GET",
|
|
2396
|
+
summary: "List submission packages",
|
|
2397
|
+
description: "List the submission packages of a project, grouped by directory and most recently updated first, optionally narrowed to one environment or one directory",
|
|
2398
|
+
tags: ["submissions"],
|
|
2399
|
+
successDescription: "The list of submission packages"
|
|
2400
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({
|
|
2401
|
+
projectId: z.string().describe("The ID of the project"),
|
|
2402
|
+
environmentId: z.string().optional().describe("Filter by environment ID"),
|
|
2403
|
+
platform: platformSchema.optional().describe(PLATFORM_DESCRIPTION)
|
|
2404
|
+
})).output(z.array(submissionSchema));
|
|
2405
|
+
const getSubmissionContractV1 = base.route({
|
|
2406
|
+
path: "/v1/submissions/{submissionId}",
|
|
2407
|
+
method: "GET",
|
|
2408
|
+
summary: "Get a submission package",
|
|
2409
|
+
description: "Get a submission package by ID, including its form data and the prefill it started from",
|
|
2410
|
+
tags: ["submissions"],
|
|
2411
|
+
successDescription: "The submission package"
|
|
2412
|
+
}).errors({ NOT_FOUND: {} }).input(z.object({ submissionId: submissionIdSchema })).output(submissionSchema);
|
|
2413
|
+
const updateSubmissionContractV1 = base.route({
|
|
2414
|
+
path: "/v1/submissions/{submissionId}",
|
|
2415
|
+
method: "PUT",
|
|
2416
|
+
summary: "Update a submission package",
|
|
2417
|
+
description: "Update the form data of a submission package. Only the fields you send are changed; the rest are left as they are. Unknown field names are rejected rather than ignored, so a typo fails the call. `platform` must match the package's own platform.",
|
|
2418
|
+
tags: ["submissions"],
|
|
2419
|
+
successDescription: "The updated submission package"
|
|
2420
|
+
}).errors({ NOT_FOUND: {} }).input(z.discriminatedUnion("platform", [z.object({
|
|
2421
|
+
submissionId: submissionIdSchema,
|
|
2422
|
+
platform: z.literal("claudeai").describe(PLATFORM_DESCRIPTION),
|
|
2423
|
+
formData: claudeSubmissionUpdateSchema
|
|
2424
|
+
}), z.object({
|
|
2425
|
+
submissionId: submissionIdSchema,
|
|
2426
|
+
platform: z.literal("chatgpt").describe(PLATFORM_DESCRIPTION),
|
|
2427
|
+
formData: chatgptSubmissionUpdateSchema
|
|
2428
|
+
})])).output(submissionSchema);
|
|
1842
2429
|
const contract = {
|
|
1843
|
-
teams: {
|
|
2430
|
+
teams: {
|
|
2431
|
+
list: { v1: listTeamsContractV1 },
|
|
2432
|
+
getUsage: { v1: getTeamUsageContractV1 }
|
|
2433
|
+
},
|
|
1844
2434
|
analytics: { get: { v1: getProjectAnalyticsContractV1 } },
|
|
2435
|
+
intents: { list: { v1: listIntentsContractV1 } },
|
|
2436
|
+
intentCategories: {
|
|
2437
|
+
list: { v1: listIntentCategoriesContractV1 },
|
|
2438
|
+
create: { v1: createIntentCategoryContractV1 },
|
|
2439
|
+
update: { v1: updateIntentCategoryContractV1 },
|
|
2440
|
+
delete: { v1: deleteIntentCategoryContractV1 },
|
|
2441
|
+
link: { v1: linkIntentCategoryContractV1 },
|
|
2442
|
+
unlink: { v1: unlinkIntentCategoryContractV1 }
|
|
2443
|
+
},
|
|
2444
|
+
feedbacks: { list: { v1: listFeedbacksContractV1 } },
|
|
1845
2445
|
deployments: {
|
|
1846
2446
|
list: { v1: listDeploymentsContractV1 },
|
|
1847
2447
|
get: { v1: getDeploymentContractV1 },
|
|
@@ -1851,6 +2451,7 @@ const contract = {
|
|
|
1851
2451
|
environments: {
|
|
1852
2452
|
create: { v1: createEnvironmentContractV1 },
|
|
1853
2453
|
get: { v1: getEnvironmentContractV1 },
|
|
2454
|
+
delete: { v1: deleteEnvironmentContractV1 },
|
|
1854
2455
|
deploy: { v1: deployEnvironmentContractV1 },
|
|
1855
2456
|
getLogs: { v1: getLogsContractV1 },
|
|
1856
2457
|
getLatestLogs: { v1: getLatestLogsContractV1 }
|
|
@@ -1880,7 +2481,39 @@ const contract = {
|
|
|
1880
2481
|
beacon: {
|
|
1881
2482
|
create: { v1: createBeaconContractV1 },
|
|
1882
2483
|
get: { v1: getBeaconContractV1 }
|
|
2484
|
+
},
|
|
2485
|
+
submissions: {
|
|
2486
|
+
create: { v1: createSubmissionContractV1 },
|
|
2487
|
+
list: { v1: listSubmissionsContractV1 },
|
|
2488
|
+
get: { v1: getSubmissionContractV1 },
|
|
2489
|
+
update: { v1: updateSubmissionContractV1 }
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
const { uploadArtifact: _discard, ...deploymentsForDocs } = contract.deployments;
|
|
2493
|
+
const contractForDocs = {
|
|
2494
|
+
...contract,
|
|
2495
|
+
deployments: deploymentsForDocs
|
|
2496
|
+
};
|
|
2497
|
+
/** Shared by the served `openapi.json` and the snapshot test that guards it, so the two cannot drift. */
|
|
2498
|
+
const OPENAPI_DOC_OPTIONS = {
|
|
2499
|
+
info: {
|
|
2500
|
+
title: "Alpic API",
|
|
2501
|
+
version: "1.0.0"
|
|
2502
|
+
},
|
|
2503
|
+
servers: [{ url: "https://api.alpic.ai" }],
|
|
2504
|
+
security: [{ bearerAuth: [] }],
|
|
2505
|
+
components: { securitySchemes: { bearerAuth: {
|
|
2506
|
+
type: "http",
|
|
2507
|
+
description: "Bearer token for authentication. Use an API key from the API Keys page in team settings, or an OAuth access token.",
|
|
2508
|
+
scheme: "bearer"
|
|
2509
|
+
} } },
|
|
2510
|
+
commonSchemas: {
|
|
2511
|
+
Submission: { schema: submissionSchema },
|
|
2512
|
+
ClaudeSubmissionFormData: { schema: claudeSubmissionDraftSchema },
|
|
2513
|
+
ChatgptSubmissionFormData: { schema: chatgptSubmissionDraftSchema },
|
|
2514
|
+
ClaudeSubmissionFormDataUpdate: { schema: claudeSubmissionUpdateSchema },
|
|
2515
|
+
ChatgptSubmissionFormDataUpdate: { schema: chatgptSubmissionUpdateSchema }
|
|
1883
2516
|
}
|
|
1884
2517
|
};
|
|
1885
2518
|
//#endregion
|
|
1886
|
-
export { AUTH_SCOPE_ANY, AUTH_SCOPE_USER, PLATFORM_LABELS, buildSettingsSchema, contract, createEnvironmentContractV1, deploymentStatusSchema, environmentVariableSchema, environmentVariablesSchema, playgroundExamplePromptSchema, playgroundHeaderSchema, runtimeSchema, serverFieldsSchema, transportSchema, updateEnvironmentVariableSchema };
|
|
2519
|
+
export { AI_PLATFORMS, AUTH_SCOPE_ANY, AUTH_SCOPE_USER, INTENT_CATEGORY_COLORS, OPENAPI_DOC_OPTIONS, PLATFORM_LABELS, buildSettingsSchema, chatgptSubmissionUpdateSchema, claudeSubmissionUpdateSchema, contract, contractForDocs, createEnvironmentContractV1, deploymentStatusSchema, environmentVariableSchema, environmentVariablesSchema, intentCategoryColorSchema, playgroundExamplePromptSchema, playgroundHeaderSchema, runtimeSchema, serverFieldsSchema, transportSchema, updateEnvironmentVariableSchema };
|