@insforge/mcp 1.2.6-memory.2 → 1.2.7
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 +21 -0
- package/dist/{chunk-43OCZIXR.js → chunk-Z3FXBI3Z.js} +1349 -761
- package/dist/http-server.js +2087 -130
- package/dist/index.js +3 -2
- package/package.json +32 -7
- package/server.json +29 -6
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/shared/tools.ts
|
|
4
|
-
import
|
|
5
|
-
import fetch2 from "node-fetch";
|
|
6
|
-
import { promises as fs } from "fs";
|
|
7
|
-
import { exec } from "child_process";
|
|
8
|
-
import { promisify } from "util";
|
|
9
|
-
import { tmpdir } from "os";
|
|
10
|
-
import archiver from "archiver";
|
|
3
|
+
// src/shared/tools/index.ts
|
|
4
|
+
import fetch7 from "node-fetch";
|
|
11
5
|
|
|
12
6
|
// src/shared/response-handler.ts
|
|
7
|
+
function isErrorResponse(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null) return false;
|
|
9
|
+
const v = value;
|
|
10
|
+
const hasMessage = typeof v.message === "string";
|
|
11
|
+
const hasError = typeof v.error === "string";
|
|
12
|
+
const hasStatusCode = typeof v.statusCode === "number";
|
|
13
|
+
const hasNextAction = !("nextAction" in v) || typeof v.nextAction === "string";
|
|
14
|
+
return (hasMessage || hasError || hasStatusCode) && hasNextAction;
|
|
15
|
+
}
|
|
13
16
|
async function handleApiResponse(response) {
|
|
14
17
|
const responseData = await response.json();
|
|
15
18
|
if (!response.ok) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
if (isErrorResponse(responseData)) {
|
|
20
|
+
let fullMessage = responseData.message ?? responseData.error ?? "Unknown error";
|
|
21
|
+
if (typeof responseData.nextAction === "string" && responseData.nextAction.length > 0) {
|
|
22
|
+
fullMessage += `. ${responseData.nextAction}`;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(fullMessage);
|
|
20
25
|
}
|
|
21
|
-
throw new Error(
|
|
26
|
+
throw new Error("Unknown error");
|
|
22
27
|
}
|
|
23
28
|
return responseData;
|
|
24
29
|
}
|
|
30
|
+
function hasMessageProperty(value) {
|
|
31
|
+
return typeof value === "object" && value !== null && "message" in value && typeof value.message === "string";
|
|
32
|
+
}
|
|
25
33
|
function formatSuccessMessage(operation, data) {
|
|
26
|
-
if (data
|
|
34
|
+
if (hasMessageProperty(data)) {
|
|
27
35
|
return `${data.message}
|
|
28
36
|
${JSON.stringify(data, null, 2)}`;
|
|
29
37
|
}
|
|
@@ -64,6 +72,10 @@ var UsageTracker = class {
|
|
|
64
72
|
}
|
|
65
73
|
};
|
|
66
74
|
|
|
75
|
+
// src/shared/tools/docs.ts
|
|
76
|
+
import { z as z25 } from "zod";
|
|
77
|
+
import fetch2 from "node-fetch";
|
|
78
|
+
|
|
67
79
|
// node_modules/@insforge/shared-schemas/dist/database.schema.js
|
|
68
80
|
import { z } from "zod";
|
|
69
81
|
var ColumnType;
|
|
@@ -382,6 +394,12 @@ var storageBucketSchema = z5.object({
|
|
|
382
394
|
public: z5.boolean(),
|
|
383
395
|
createdAt: z5.string()
|
|
384
396
|
});
|
|
397
|
+
var storageConfigSchema = z5.object({
|
|
398
|
+
id: z5.string().uuid(),
|
|
399
|
+
maxFileSizeMb: z5.number().int().positive(),
|
|
400
|
+
createdAt: z5.string(),
|
|
401
|
+
updatedAt: z5.string()
|
|
402
|
+
});
|
|
385
403
|
|
|
386
404
|
// node_modules/@insforge/shared-schemas/dist/storage-api.schema.js
|
|
387
405
|
import { z as z6 } from "zod";
|
|
@@ -428,6 +446,9 @@ var confirmUploadRequestSchema = z6.object({
|
|
|
428
446
|
contentType: z6.string().optional(),
|
|
429
447
|
etag: z6.string().optional()
|
|
430
448
|
});
|
|
449
|
+
var updateStorageConfigRequestSchema = z6.object({
|
|
450
|
+
maxFileSizeMb: z6.number().int().min(1, "Must be at least 1 MB").max(200, "Must be at most 200 MB")
|
|
451
|
+
});
|
|
431
452
|
|
|
432
453
|
// node_modules/@insforge/shared-schemas/dist/auth.schema.js
|
|
433
454
|
import { z as z7 } from "zod";
|
|
@@ -485,6 +506,7 @@ var oAuthConfigSchema = z7.object({
|
|
|
485
506
|
updatedAt: z7.string()
|
|
486
507
|
// PostgreSQL timestamp
|
|
487
508
|
});
|
|
509
|
+
var allowedRedirectUrlsRegex = /^(?:(?:https?:\/\/)(?:(?:\*\.)?[^\s/:?#]+|\[[0-9A-Fa-f:.]+\])(?::\d+)?(?:\/[^\s]*)?|(?!(?:https?|javascript|data|file|vbscript):)[a-zA-Z][a-zA-Z0-9+.-]*:(?:\/\/[^\s/]+(?:\/[^\s]*)?|\/[^\s]*))$/i;
|
|
488
510
|
var authConfigSchema = z7.object({
|
|
489
511
|
id: z7.string().uuid(),
|
|
490
512
|
requireEmailVerification: z7.boolean(),
|
|
@@ -495,7 +517,7 @@ var authConfigSchema = z7.object({
|
|
|
495
517
|
requireSpecialChar: z7.boolean(),
|
|
496
518
|
verifyEmailMethod: verificationMethodSchema,
|
|
497
519
|
resetPasswordMethod: verificationMethodSchema,
|
|
498
|
-
|
|
520
|
+
allowedRedirectUrls: z7.array(z7.string().regex(allowedRedirectUrlsRegex, { message: "Invalid URL or wildcard URL" })).optional().nullable(),
|
|
499
521
|
createdAt: z7.string(),
|
|
500
522
|
// PostgreSQL timestamp
|
|
501
523
|
updatedAt: z7.string()
|
|
@@ -511,6 +533,16 @@ var tokenPayloadSchema = z7.object({
|
|
|
511
533
|
exp: z7.number().optional()
|
|
512
534
|
// Expiration
|
|
513
535
|
});
|
|
536
|
+
var customOAuthKeySchema = z7.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Key must contain only lowercase letters, numbers, hyphens, and underscores");
|
|
537
|
+
var customOAuthConfigSchema = z7.object({
|
|
538
|
+
id: z7.string().uuid(),
|
|
539
|
+
key: customOAuthKeySchema,
|
|
540
|
+
name: z7.string().min(1),
|
|
541
|
+
discoveryEndpoint: z7.string().url(),
|
|
542
|
+
clientId: z7.string().min(1),
|
|
543
|
+
createdAt: z7.string(),
|
|
544
|
+
updatedAt: z7.string()
|
|
545
|
+
});
|
|
514
546
|
|
|
515
547
|
// node_modules/@insforge/shared-schemas/dist/auth-api.schema.js
|
|
516
548
|
import { z as z8 } from "zod";
|
|
@@ -521,12 +553,16 @@ var paginationSchema = z8.object({
|
|
|
521
553
|
var createUserRequestSchema = z8.object({
|
|
522
554
|
email: emailSchema,
|
|
523
555
|
password: passwordSchema,
|
|
524
|
-
name: nameSchema.optional()
|
|
556
|
+
name: nameSchema.optional(),
|
|
557
|
+
redirectTo: z8.string().url().optional()
|
|
525
558
|
});
|
|
526
559
|
var createSessionRequestSchema = z8.object({
|
|
527
560
|
email: emailSchema,
|
|
528
561
|
password: passwordSchema
|
|
529
562
|
});
|
|
563
|
+
var refreshSessionRequestSchema = z8.object({
|
|
564
|
+
refreshToken: z8.string().min(1, "refreshToken is required")
|
|
565
|
+
});
|
|
530
566
|
var exchangeAdminSessionRequestSchema = z8.object({
|
|
531
567
|
code: z8.string()
|
|
532
568
|
});
|
|
@@ -540,20 +576,20 @@ var updateProfileRequestSchema = z8.object({
|
|
|
540
576
|
profile: z8.record(z8.unknown())
|
|
541
577
|
});
|
|
542
578
|
var sendVerificationEmailRequestSchema = z8.object({
|
|
543
|
-
email: emailSchema
|
|
579
|
+
email: emailSchema,
|
|
580
|
+
redirectTo: z8.string().url().optional()
|
|
544
581
|
});
|
|
545
582
|
var verifyEmailRequestSchema = z8.object({
|
|
546
|
-
email: emailSchema
|
|
547
|
-
otp: z8.string().
|
|
548
|
-
}).refine((data) => data.email || data.otp, {
|
|
549
|
-
message: "Either email or otp must be provided"
|
|
583
|
+
email: emailSchema,
|
|
584
|
+
otp: z8.string().regex(/^\d{6}$/, "OTP code must be a 6-digit numeric code")
|
|
550
585
|
});
|
|
551
586
|
var sendResetPasswordEmailRequestSchema = z8.object({
|
|
552
|
-
email: emailSchema
|
|
587
|
+
email: emailSchema,
|
|
588
|
+
redirectTo: z8.string().url().optional()
|
|
553
589
|
});
|
|
554
590
|
var exchangeResetPasswordTokenRequestSchema = z8.object({
|
|
555
591
|
email: emailSchema,
|
|
556
|
-
code: z8.string().
|
|
592
|
+
code: z8.string().regex(/^\d{6}$/, "Reset password code must be a 6-digit numeric code")
|
|
557
593
|
});
|
|
558
594
|
var resetPasswordRequestSchema = z8.object({
|
|
559
595
|
newPassword: passwordSchema,
|
|
@@ -563,25 +599,31 @@ var createUserResponseSchema = z8.object({
|
|
|
563
599
|
user: userSchema.optional(),
|
|
564
600
|
accessToken: z8.string().nullable(),
|
|
565
601
|
requireEmailVerification: z8.boolean().optional(),
|
|
566
|
-
|
|
567
|
-
|
|
602
|
+
csrfToken: z8.string().nullable().optional(),
|
|
603
|
+
refreshToken: z8.string().optional()
|
|
604
|
+
// For mobile/desktop clients (no cookies)
|
|
568
605
|
});
|
|
569
606
|
var createSessionResponseSchema = z8.object({
|
|
570
607
|
user: userSchema,
|
|
571
608
|
accessToken: z8.string(),
|
|
572
|
-
|
|
573
|
-
|
|
609
|
+
csrfToken: z8.string().nullable().optional(),
|
|
610
|
+
refreshToken: z8.string().optional()
|
|
611
|
+
// For mobile/desktop clients (no cookies)
|
|
574
612
|
});
|
|
575
613
|
var verifyEmailResponseSchema = z8.object({
|
|
576
614
|
user: userSchema,
|
|
577
615
|
accessToken: z8.string(),
|
|
578
|
-
|
|
579
|
-
|
|
616
|
+
csrfToken: z8.string().nullable().optional(),
|
|
617
|
+
refreshToken: z8.string().optional()
|
|
618
|
+
// For mobile/desktop clients (no cookies)
|
|
580
619
|
});
|
|
581
620
|
var refreshSessionResponseSchema = z8.object({
|
|
582
621
|
accessToken: z8.string(),
|
|
583
622
|
user: userSchema,
|
|
584
|
-
csrfToken: z8.string()
|
|
623
|
+
csrfToken: z8.string().optional(),
|
|
624
|
+
// For web clients (cookie-based)
|
|
625
|
+
refreshToken: z8.string().optional()
|
|
626
|
+
// For mobile/desktop clients (no cookies)
|
|
585
627
|
});
|
|
586
628
|
var exchangeResetPasswordTokenResponseSchema = z8.object({
|
|
587
629
|
token: z8.string(),
|
|
@@ -627,6 +669,18 @@ var updateOAuthConfigRequestSchema = oAuthConfigSchema.omit({
|
|
|
627
669
|
}).extend({
|
|
628
670
|
clientSecret: z8.string().optional()
|
|
629
671
|
}).partial();
|
|
672
|
+
var pkceRegex = /^[A-Za-z0-9._~-]+$/;
|
|
673
|
+
var oAuthInitRequestSchema = z8.object({
|
|
674
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
675
|
+
redirect_uri: z8.string().url().optional(),
|
|
676
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
677
|
+
code_challenge: z8.string().min(43, "Code challenge must be at least 43 characters").max(128, "Code challenge must be at most 128 characters").regex(pkceRegex, "Code challenge must be base64url encoded")
|
|
678
|
+
});
|
|
679
|
+
var oAuthCodeExchangeRequestSchema = z8.object({
|
|
680
|
+
code: z8.string().min(1, "Exchange code is required"),
|
|
681
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
682
|
+
code_verifier: z8.string().min(43, "Code verifier must be at least 43 characters").max(128, "Code verifier must be at most 128 characters").regex(pkceRegex, "Code verifier must be base64url encoded")
|
|
683
|
+
});
|
|
630
684
|
var listOAuthConfigsResponseSchema = z8.object({
|
|
631
685
|
data: z8.array(oAuthConfigSchema),
|
|
632
686
|
count: z8.number()
|
|
@@ -638,11 +692,12 @@ var updateAuthConfigRequestSchema = authConfigSchema.omit({
|
|
|
638
692
|
}).partial();
|
|
639
693
|
var getPublicAuthConfigResponseSchema = z8.object({
|
|
640
694
|
oAuthProviders: z8.array(oAuthProvidersSchema),
|
|
695
|
+
customOAuthProviders: z8.array(customOAuthKeySchema),
|
|
641
696
|
...authConfigSchema.omit({
|
|
642
697
|
id: true,
|
|
643
698
|
updatedAt: true,
|
|
644
699
|
createdAt: true,
|
|
645
|
-
|
|
700
|
+
allowedRedirectUrls: true
|
|
646
701
|
}).shape
|
|
647
702
|
});
|
|
648
703
|
var authErrorResponseSchema = z8.object({
|
|
@@ -651,6 +706,16 @@ var authErrorResponseSchema = z8.object({
|
|
|
651
706
|
statusCode: z8.number().int(),
|
|
652
707
|
nextActions: z8.string().optional()
|
|
653
708
|
});
|
|
709
|
+
var createCustomOAuthConfigRequestSchema = customOAuthConfigSchema.omit({ id: true, createdAt: true, updatedAt: true }).extend({
|
|
710
|
+
clientSecret: z8.string().min(1, "Client secret is required")
|
|
711
|
+
});
|
|
712
|
+
var updateCustomOAuthConfigRequestSchema = customOAuthConfigSchema.omit({ id: true, key: true, createdAt: true, updatedAt: true }).extend({
|
|
713
|
+
clientSecret: z8.string().min(1).optional()
|
|
714
|
+
}).partial();
|
|
715
|
+
var listCustomOAuthConfigsResponseSchema = z8.object({
|
|
716
|
+
data: z8.array(customOAuthConfigSchema),
|
|
717
|
+
count: z8.number()
|
|
718
|
+
});
|
|
654
719
|
|
|
655
720
|
// node_modules/@insforge/shared-schemas/dist/metadata.schema.js
|
|
656
721
|
import { z as z11 } from "zod";
|
|
@@ -680,6 +745,9 @@ var realtimeMessageSchema = z9.object({
|
|
|
680
745
|
whDeliveredCount: z9.number().int().min(0),
|
|
681
746
|
createdAt: z9.string().datetime()
|
|
682
747
|
});
|
|
748
|
+
var realtimeConfigSchema = z9.object({
|
|
749
|
+
retentionDays: z9.number().int().positive().nullable()
|
|
750
|
+
});
|
|
683
751
|
var subscribeChannelPayloadSchema = z9.object({
|
|
684
752
|
channel: z9.string().min(1)
|
|
685
753
|
// The resolved channel instance, e.g., "order:123"
|
|
@@ -765,7 +833,8 @@ var messageStatsResponseSchema = z10.object({
|
|
|
765
833
|
topEvents: z10.array(z10.object({
|
|
766
834
|
eventName: z10.string(),
|
|
767
835
|
count: z10.number().int().min(0)
|
|
768
|
-
}))
|
|
836
|
+
})),
|
|
837
|
+
retentionDays: realtimeConfigSchema.shape.retentionDays
|
|
769
838
|
});
|
|
770
839
|
var rlsPolicySchema = z10.object({
|
|
771
840
|
policyName: z10.string(),
|
|
@@ -845,6 +914,9 @@ var databasePasswordInfoSchema = z11.object({
|
|
|
845
914
|
var apiKeyResponseSchema = z11.object({
|
|
846
915
|
apiKey: z11.string()
|
|
847
916
|
});
|
|
917
|
+
var projectIdResponseSchema = z11.object({
|
|
918
|
+
projectId: z11.string().nullable()
|
|
919
|
+
});
|
|
848
920
|
|
|
849
921
|
// node_modules/@insforge/shared-schemas/dist/ai.schema.js
|
|
850
922
|
import { z as z12 } from "zod";
|
|
@@ -919,13 +991,83 @@ var audioContentSchema = z13.object({
|
|
|
919
991
|
format: z13.enum(["wav", "mp3", "aiff", "aac", "ogg", "flac", "m4a"])
|
|
920
992
|
})
|
|
921
993
|
});
|
|
922
|
-
var
|
|
994
|
+
var fileContentSchema = z13.object({
|
|
995
|
+
type: z13.literal("file"),
|
|
996
|
+
file: z13.object({
|
|
997
|
+
// Filename with extension (e.g., "document.pdf")
|
|
998
|
+
filename: z13.string(),
|
|
999
|
+
// File data can be:
|
|
1000
|
+
// - Public URL: "https://example.com/document.pdf"
|
|
1001
|
+
// - Base64 data URL: "data:application/pdf;base64,..."
|
|
1002
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1003
|
+
file_data: z13.string()
|
|
1004
|
+
})
|
|
1005
|
+
});
|
|
1006
|
+
var contentSchema = z13.union([
|
|
1007
|
+
textContentSchema,
|
|
1008
|
+
imageContentSchema,
|
|
1009
|
+
audioContentSchema,
|
|
1010
|
+
fileContentSchema
|
|
1011
|
+
]);
|
|
1012
|
+
var toolFunctionSchema = z13.object({
|
|
1013
|
+
name: z13.string(),
|
|
1014
|
+
description: z13.string().optional(),
|
|
1015
|
+
parameters: z13.record(z13.unknown()).optional()
|
|
1016
|
+
});
|
|
1017
|
+
var toolSchema = z13.object({
|
|
1018
|
+
type: z13.literal("function"),
|
|
1019
|
+
function: toolFunctionSchema
|
|
1020
|
+
});
|
|
1021
|
+
var toolChoiceSchema = z13.union([
|
|
1022
|
+
z13.enum(["auto", "none", "required"]),
|
|
1023
|
+
z13.object({
|
|
1024
|
+
type: z13.literal("function"),
|
|
1025
|
+
function: z13.object({ name: z13.string() })
|
|
1026
|
+
})
|
|
1027
|
+
]);
|
|
1028
|
+
var toolCallSchema = z13.object({
|
|
1029
|
+
id: z13.string(),
|
|
1030
|
+
type: z13.literal("function"),
|
|
1031
|
+
function: z13.object({
|
|
1032
|
+
name: z13.string(),
|
|
1033
|
+
arguments: z13.string()
|
|
1034
|
+
})
|
|
1035
|
+
});
|
|
923
1036
|
var chatMessageSchema = z13.object({
|
|
924
|
-
role: z13.enum(["user", "assistant", "system"]),
|
|
1037
|
+
role: z13.enum(["user", "assistant", "system", "tool"]),
|
|
925
1038
|
// New format: content can be string or array of content parts (OpenAI-compatible)
|
|
926
|
-
content: z13.union([z13.string(), z13.array(contentSchema)]),
|
|
1039
|
+
content: z13.union([z13.string(), z13.array(contentSchema)]).nullable(),
|
|
927
1040
|
// Legacy format: separate images field (deprecated but supported for backward compatibility)
|
|
928
|
-
images: z13.array(z13.object({ url: z13.string() })).optional()
|
|
1041
|
+
images: z13.array(z13.object({ url: z13.string() })).optional(),
|
|
1042
|
+
// Tool calls made by the assistant
|
|
1043
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1044
|
+
tool_calls: z13.array(toolCallSchema).optional(),
|
|
1045
|
+
// Tool call ID for tool response messages
|
|
1046
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1047
|
+
tool_call_id: z13.string().optional()
|
|
1048
|
+
});
|
|
1049
|
+
var webSearchPluginSchema = z13.object({
|
|
1050
|
+
enabled: z13.boolean(),
|
|
1051
|
+
// Engine selection:
|
|
1052
|
+
// - "native": Always use provider's built-in web search (OpenAI, Anthropic, Perplexity, xAI)
|
|
1053
|
+
// - "exa": Use Exa's search API
|
|
1054
|
+
// - undefined: Auto-select (native if available, otherwise Exa)
|
|
1055
|
+
engine: z13.enum(["native", "exa"]).optional(),
|
|
1056
|
+
// Maximum number of search results (1-10, default: 5)
|
|
1057
|
+
maxResults: z13.number().min(1).max(10).optional(),
|
|
1058
|
+
// Custom prompt for attaching search results to the message
|
|
1059
|
+
searchPrompt: z13.string().optional()
|
|
1060
|
+
});
|
|
1061
|
+
var fileParserPluginSchema = z13.object({
|
|
1062
|
+
enabled: z13.boolean(),
|
|
1063
|
+
pdf: z13.object({
|
|
1064
|
+
// PDF processing engine:
|
|
1065
|
+
// - "pdf-text": Best for well-structured PDFs with clear text content (Free)
|
|
1066
|
+
// - "mistral-ocr": Best for scanned documents or PDFs with images ($2 per 1,000 pages)
|
|
1067
|
+
// - "native": Only available for models that support file input natively (charged as input tokens)
|
|
1068
|
+
// If not specified, defaults to native if available, otherwise mistral-ocr
|
|
1069
|
+
engine: z13.enum(["pdf-text", "mistral-ocr", "native"]).optional()
|
|
1070
|
+
}).optional()
|
|
929
1071
|
});
|
|
930
1072
|
var chatCompletionRequestSchema = z13.object({
|
|
931
1073
|
model: z13.string(),
|
|
@@ -933,10 +1075,52 @@ var chatCompletionRequestSchema = z13.object({
|
|
|
933
1075
|
temperature: z13.number().min(0).max(2).optional(),
|
|
934
1076
|
maxTokens: z13.number().positive().optional(),
|
|
935
1077
|
topP: z13.number().min(0).max(1).optional(),
|
|
936
|
-
stream: z13.boolean().optional()
|
|
1078
|
+
stream: z13.boolean().optional(),
|
|
1079
|
+
// Web Search: Incorporate relevant web search results into the response
|
|
1080
|
+
// Results are returned in the annotations field
|
|
1081
|
+
webSearch: webSearchPluginSchema.optional(),
|
|
1082
|
+
// File Parser: Configure PDF processing for file content in messages
|
|
1083
|
+
// When files are included in messages, this controls how PDFs are parsed
|
|
1084
|
+
fileParser: fileParserPluginSchema.optional(),
|
|
1085
|
+
// Thinking/Reasoning mode: Enable extended reasoning capabilities
|
|
1086
|
+
// Appends ":thinking" to the model ID for chain-of-thought reasoning
|
|
1087
|
+
thinking: z13.boolean().optional(),
|
|
1088
|
+
// Tool calling: Define functions the AI can call
|
|
1089
|
+
tools: z13.array(toolSchema).optional(),
|
|
1090
|
+
// Tool choice: Control whether/which tool is called ('auto', 'none', 'required', or specific function)
|
|
1091
|
+
toolChoice: toolChoiceSchema.optional(),
|
|
1092
|
+
// Parallel tool calls: Allow the model to call multiple tools in parallel
|
|
1093
|
+
parallelToolCalls: z13.boolean().optional()
|
|
1094
|
+
});
|
|
1095
|
+
var urlCitationAnnotationSchema = z13.object({
|
|
1096
|
+
type: z13.literal("url_citation"),
|
|
1097
|
+
urlCitation: z13.object({
|
|
1098
|
+
url: z13.string(),
|
|
1099
|
+
title: z13.string().optional(),
|
|
1100
|
+
content: z13.string().optional(),
|
|
1101
|
+
// Character indices in the response text where this citation applies
|
|
1102
|
+
startIndex: z13.number().optional(),
|
|
1103
|
+
endIndex: z13.number().optional()
|
|
1104
|
+
})
|
|
1105
|
+
});
|
|
1106
|
+
var fileAnnotationSchema = z13.object({
|
|
1107
|
+
type: z13.literal("file"),
|
|
1108
|
+
file: z13.object({
|
|
1109
|
+
filename: z13.string(),
|
|
1110
|
+
// Parsed content from the PDF (used for caching)
|
|
1111
|
+
parsedContent: z13.string().optional(),
|
|
1112
|
+
// Additional metadata from the parser
|
|
1113
|
+
metadata: z13.record(z13.unknown()).optional()
|
|
1114
|
+
})
|
|
937
1115
|
});
|
|
1116
|
+
var annotationSchema = z13.union([urlCitationAnnotationSchema, fileAnnotationSchema]);
|
|
938
1117
|
var chatCompletionResponseSchema = z13.object({
|
|
939
1118
|
text: z13.string(),
|
|
1119
|
+
// Tool calls from the assistant (present when the model invokes tools)
|
|
1120
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1121
|
+
tool_calls: z13.array(toolCallSchema).optional(),
|
|
1122
|
+
// Annotations from web search or file parsing (can be URL citations or file annotations)
|
|
1123
|
+
annotations: z13.array(annotationSchema).optional(),
|
|
940
1124
|
metadata: z13.object({
|
|
941
1125
|
model: z13.string(),
|
|
942
1126
|
usage: z13.object({
|
|
@@ -946,6 +1130,30 @@ var chatCompletionResponseSchema = z13.object({
|
|
|
946
1130
|
}).optional()
|
|
947
1131
|
}).optional()
|
|
948
1132
|
});
|
|
1133
|
+
var embeddingsRequestSchema = z13.object({
|
|
1134
|
+
model: z13.string(),
|
|
1135
|
+
input: z13.union([z13.string(), z13.array(z13.string())]),
|
|
1136
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1137
|
+
encoding_format: z13.enum(["float", "base64"]).optional(),
|
|
1138
|
+
dimensions: z13.number().int().min(0).optional()
|
|
1139
|
+
});
|
|
1140
|
+
var embeddingObjectSchema = z13.object({
|
|
1141
|
+
object: z13.literal("embedding"),
|
|
1142
|
+
// Embedding can be number[] (float format) or string (base64 format)
|
|
1143
|
+
embedding: z13.union([z13.array(z13.number()), z13.string()]),
|
|
1144
|
+
index: z13.number()
|
|
1145
|
+
});
|
|
1146
|
+
var embeddingsResponseSchema = z13.object({
|
|
1147
|
+
object: z13.literal("list"),
|
|
1148
|
+
data: z13.array(embeddingObjectSchema),
|
|
1149
|
+
metadata: z13.object({
|
|
1150
|
+
model: z13.string(),
|
|
1151
|
+
usage: z13.object({
|
|
1152
|
+
promptTokens: z13.number().optional(),
|
|
1153
|
+
totalTokens: z13.number().optional()
|
|
1154
|
+
}).optional()
|
|
1155
|
+
}).optional()
|
|
1156
|
+
});
|
|
949
1157
|
var imageGenerationRequestSchema = z13.object({
|
|
950
1158
|
model: z13.string(),
|
|
951
1159
|
prompt: z13.string(),
|
|
@@ -974,7 +1182,10 @@ var aiModelSchema = z13.object({
|
|
|
974
1182
|
outputModality: z13.array(modalitySchema).min(1),
|
|
975
1183
|
provider: z13.string(),
|
|
976
1184
|
modelId: z13.string(),
|
|
977
|
-
|
|
1185
|
+
inputPrice: z13.number().min(0).optional(),
|
|
1186
|
+
// Price per million tokens in USD
|
|
1187
|
+
outputPrice: z13.number().min(0).optional()
|
|
1188
|
+
// Price per million tokens in USD
|
|
978
1189
|
});
|
|
979
1190
|
var createAIConfigurationRequestSchema = aiConfigurationSchema.omit({
|
|
980
1191
|
id: true
|
|
@@ -997,6 +1208,15 @@ var getAIUsageSummaryRequestSchema = z13.object({
|
|
|
997
1208
|
startDate: z13.string().datetime().optional(),
|
|
998
1209
|
endDate: z13.string().datetime().optional()
|
|
999
1210
|
});
|
|
1211
|
+
var keySourceSchema = z13.enum(["byok", "cloud", "env", "unconfigured"]);
|
|
1212
|
+
var gatewayConfigResponseSchema = z13.object({
|
|
1213
|
+
keySource: keySourceSchema,
|
|
1214
|
+
hasByokKey: z13.boolean(),
|
|
1215
|
+
maskedKey: z13.string().optional()
|
|
1216
|
+
});
|
|
1217
|
+
var setGatewayBYOKKeyRequestSchema = z13.object({
|
|
1218
|
+
apiKey: z13.string().min(1, "API key is required")
|
|
1219
|
+
});
|
|
1000
1220
|
|
|
1001
1221
|
// node_modules/@insforge/shared-schemas/dist/logs.schema.js
|
|
1002
1222
|
import { z as z14 } from "zod";
|
|
@@ -1027,6 +1247,16 @@ var logStatsSchema = z14.object({
|
|
|
1027
1247
|
count: z14.number(),
|
|
1028
1248
|
lastActivity: z14.string()
|
|
1029
1249
|
});
|
|
1250
|
+
var buildLogEntrySchema = z14.object({
|
|
1251
|
+
level: z14.string(),
|
|
1252
|
+
message: z14.string()
|
|
1253
|
+
});
|
|
1254
|
+
var getBuildLogsResponseSchema = z14.object({
|
|
1255
|
+
deploymentId: z14.string(),
|
|
1256
|
+
status: z14.enum(["pending", "success", "failed"]),
|
|
1257
|
+
logs: z14.array(buildLogEntrySchema),
|
|
1258
|
+
createdAt: z14.string()
|
|
1259
|
+
});
|
|
1030
1260
|
|
|
1031
1261
|
// node_modules/@insforge/shared-schemas/dist/logs-api.schema.js
|
|
1032
1262
|
import { z as z15 } from "zod";
|
|
@@ -1102,7 +1332,19 @@ var listFunctionsResponseSchema = z17.object({
|
|
|
1102
1332
|
functions: z17.array(functionSchema),
|
|
1103
1333
|
runtime: z17.object({
|
|
1104
1334
|
status: z17.enum(["running", "unavailable"])
|
|
1105
|
-
})
|
|
1335
|
+
}),
|
|
1336
|
+
deploymentUrl: z17.string().nullable().optional()
|
|
1337
|
+
});
|
|
1338
|
+
var deploymentResultSchema = z17.object({
|
|
1339
|
+
id: z17.string(),
|
|
1340
|
+
status: z17.enum(["success", "failed"]),
|
|
1341
|
+
url: z17.string().nullable(),
|
|
1342
|
+
buildLogs: z17.array(z17.string()).optional()
|
|
1343
|
+
});
|
|
1344
|
+
var functionResponseSchema = z17.object({
|
|
1345
|
+
success: z17.boolean(),
|
|
1346
|
+
function: functionSchema,
|
|
1347
|
+
deployment: deploymentResultSchema.nullable().optional()
|
|
1106
1348
|
});
|
|
1107
1349
|
|
|
1108
1350
|
// node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js
|
|
@@ -1142,6 +1384,9 @@ var showContactModalEventSchema = z18.object({
|
|
|
1142
1384
|
var showConnectOverlayEventSchema = z18.object({
|
|
1143
1385
|
type: z18.literal("SHOW_CONNECT_OVERLAY")
|
|
1144
1386
|
});
|
|
1387
|
+
var showPlanModalEventSchema = z18.object({
|
|
1388
|
+
type: z18.literal("SHOW_PLAN_MODAL")
|
|
1389
|
+
});
|
|
1145
1390
|
var authorizationCodeEventSchema = z18.object({
|
|
1146
1391
|
type: z18.literal("AUTHORIZATION_CODE"),
|
|
1147
1392
|
code: z18.string()
|
|
@@ -1150,6 +1395,51 @@ var routeChangeEventSchema = z18.object({
|
|
|
1150
1395
|
type: z18.literal("ROUTE_CHANGE"),
|
|
1151
1396
|
path: z18.string()
|
|
1152
1397
|
});
|
|
1398
|
+
var requestProjectInfoEventSchema = z18.object({
|
|
1399
|
+
type: z18.literal("REQUEST_PROJECT_INFO")
|
|
1400
|
+
});
|
|
1401
|
+
var projectInfoEventSchema = z18.object({
|
|
1402
|
+
type: z18.literal("PROJECT_INFO"),
|
|
1403
|
+
name: z18.string(),
|
|
1404
|
+
instanceType: z18.string(),
|
|
1405
|
+
region: z18.string(),
|
|
1406
|
+
latestVersion: z18.string().optional()
|
|
1407
|
+
});
|
|
1408
|
+
var requestInstanceInfoEventSchema = z18.object({
|
|
1409
|
+
type: z18.literal("REQUEST_INSTANCE_INFO")
|
|
1410
|
+
});
|
|
1411
|
+
var instanceInfoEventSchema = z18.object({
|
|
1412
|
+
type: z18.literal("INSTANCE_INFO"),
|
|
1413
|
+
currentInstanceType: z18.string(),
|
|
1414
|
+
planName: z18.string(),
|
|
1415
|
+
computeCredits: z18.number(),
|
|
1416
|
+
currentOrgComputeCost: z18.number(),
|
|
1417
|
+
instanceTypes: z18.array(z18.object({
|
|
1418
|
+
id: z18.string(),
|
|
1419
|
+
name: z18.string(),
|
|
1420
|
+
cpu: z18.string(),
|
|
1421
|
+
ram: z18.string(),
|
|
1422
|
+
pricePerHour: z18.number(),
|
|
1423
|
+
pricePerMonth: z18.number()
|
|
1424
|
+
})),
|
|
1425
|
+
projects: z18.array(z18.object({
|
|
1426
|
+
name: z18.string(),
|
|
1427
|
+
instanceType: z18.string(),
|
|
1428
|
+
monthlyCost: z18.number(),
|
|
1429
|
+
isCurrent: z18.boolean(),
|
|
1430
|
+
status: z18.string()
|
|
1431
|
+
}))
|
|
1432
|
+
});
|
|
1433
|
+
var requestInstanceTypeChangeEventSchema = z18.object({
|
|
1434
|
+
type: z18.literal("REQUEST_INSTANCE_TYPE_CHANGE"),
|
|
1435
|
+
instanceType: z18.string()
|
|
1436
|
+
});
|
|
1437
|
+
var instanceTypeChangeResultEventSchema = z18.object({
|
|
1438
|
+
type: z18.literal("INSTANCE_TYPE_CHANGE_RESULT"),
|
|
1439
|
+
success: z18.boolean(),
|
|
1440
|
+
instanceType: z18.string().optional(),
|
|
1441
|
+
error: z18.string().optional()
|
|
1442
|
+
});
|
|
1153
1443
|
var cloudEventSchema = z18.discriminatedUnion("type", [
|
|
1154
1444
|
appRouteChangeEventSchema,
|
|
1155
1445
|
authSuccessEventSchema,
|
|
@@ -1161,8 +1451,15 @@ var cloudEventSchema = z18.discriminatedUnion("type", [
|
|
|
1161
1451
|
navigateToUsageSchema,
|
|
1162
1452
|
showContactModalEventSchema,
|
|
1163
1453
|
showConnectOverlayEventSchema,
|
|
1454
|
+
showPlanModalEventSchema,
|
|
1164
1455
|
authorizationCodeEventSchema,
|
|
1165
|
-
routeChangeEventSchema
|
|
1456
|
+
routeChangeEventSchema,
|
|
1457
|
+
requestProjectInfoEventSchema,
|
|
1458
|
+
projectInfoEventSchema,
|
|
1459
|
+
requestInstanceInfoEventSchema,
|
|
1460
|
+
instanceInfoEventSchema,
|
|
1461
|
+
requestInstanceTypeChangeEventSchema,
|
|
1462
|
+
instanceTypeChangeResultEventSchema
|
|
1166
1463
|
]);
|
|
1167
1464
|
|
|
1168
1465
|
// node_modules/@insforge/shared-schemas/dist/docs.schema.js
|
|
@@ -1198,8 +1495,6 @@ var docTypeSchema = z19.enum([
|
|
|
1198
1495
|
"storage-sdk",
|
|
1199
1496
|
"functions-sdk",
|
|
1200
1497
|
"ai-integration-sdk",
|
|
1201
|
-
"auth-components-react",
|
|
1202
|
-
"auth-components-nextjs",
|
|
1203
1498
|
"real-time",
|
|
1204
1499
|
"deployment"
|
|
1205
1500
|
]).describe(`
|
|
@@ -1209,8 +1504,6 @@ var docTypeSchema = z19.enum([
|
|
|
1209
1504
|
"storage-sdk" (file storage),
|
|
1210
1505
|
"functions-sdk" (edge functions),
|
|
1211
1506
|
"auth-sdk" (direct SDK methods for custom auth flows),
|
|
1212
|
-
"auth-components-react" (authentication components for React+Vite applications),
|
|
1213
|
-
"auth-components-nextjs" (authentication components for Next.js applications),
|
|
1214
1507
|
"ai-integration-sdk" (AI features),
|
|
1215
1508
|
"real-time" (real-time pub/sub through WebSockets),
|
|
1216
1509
|
"deployment" (deploy frontend applications via MCP tool)
|
|
@@ -1295,109 +1588,182 @@ var listDeploymentsResponseSchema = z22.object({
|
|
|
1295
1588
|
total: z22.number()
|
|
1296
1589
|
})
|
|
1297
1590
|
});
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
}
|
|
1337
|
-
return true;
|
|
1338
|
-
}
|
|
1339
|
-
async function fetchBackendVersion(apiBaseUrl) {
|
|
1340
|
-
const response = await fetch2(`${apiBaseUrl}/api/health`, {
|
|
1341
|
-
method: "GET",
|
|
1342
|
-
headers: {
|
|
1343
|
-
"Content-Type": "application/json"
|
|
1591
|
+
var deploymentEnvVarSchema = z22.object({
|
|
1592
|
+
id: z22.string(),
|
|
1593
|
+
// Vercel env var ID (needed for delete/get)
|
|
1594
|
+
key: z22.string(),
|
|
1595
|
+
type: z22.enum(["plain", "encrypted", "secret", "sensitive", "system"]),
|
|
1596
|
+
updatedAt: z22.number().optional()
|
|
1597
|
+
// Unix timestamp (milliseconds)
|
|
1598
|
+
});
|
|
1599
|
+
var deploymentEnvVarWithValueSchema = z22.object({
|
|
1600
|
+
id: z22.string(),
|
|
1601
|
+
key: z22.string(),
|
|
1602
|
+
value: z22.string(),
|
|
1603
|
+
type: z22.enum(["plain", "encrypted", "secret", "sensitive", "system"]),
|
|
1604
|
+
updatedAt: z22.number().optional()
|
|
1605
|
+
});
|
|
1606
|
+
var listEnvVarsResponseSchema = z22.object({
|
|
1607
|
+
envVars: z22.array(deploymentEnvVarSchema)
|
|
1608
|
+
});
|
|
1609
|
+
var getEnvVarResponseSchema = z22.object({
|
|
1610
|
+
envVar: deploymentEnvVarWithValueSchema
|
|
1611
|
+
});
|
|
1612
|
+
var upsertEnvVarRequestSchema = z22.object({
|
|
1613
|
+
key: z22.string().trim().min(1, "key is required"),
|
|
1614
|
+
value: z22.string()
|
|
1615
|
+
});
|
|
1616
|
+
var upsertEnvVarsRequestSchema = z22.object({
|
|
1617
|
+
envVars: z22.array(upsertEnvVarRequestSchema).min(1)
|
|
1618
|
+
}).superRefine(({ envVars }, ctx) => {
|
|
1619
|
+
const firstSeenByKey = /* @__PURE__ */ new Map();
|
|
1620
|
+
envVars.forEach((envVar, index) => {
|
|
1621
|
+
const existingIndex = firstSeenByKey.get(envVar.key);
|
|
1622
|
+
if (existingIndex !== void 0) {
|
|
1623
|
+
ctx.addIssue({
|
|
1624
|
+
code: z22.ZodIssueCode.custom,
|
|
1625
|
+
message: "duplicate environment variable key",
|
|
1626
|
+
path: ["envVars", index, "key"]
|
|
1627
|
+
});
|
|
1628
|
+
return;
|
|
1344
1629
|
}
|
|
1630
|
+
firstSeenByKey.set(envVar.key, index);
|
|
1345
1631
|
});
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1632
|
+
});
|
|
1633
|
+
var upsertEnvVarResponseSchema = z22.object({
|
|
1634
|
+
success: z22.literal(true),
|
|
1635
|
+
message: z22.string()
|
|
1636
|
+
});
|
|
1637
|
+
var upsertEnvVarsResponseSchema = z22.object({
|
|
1638
|
+
success: z22.literal(true),
|
|
1639
|
+
message: z22.string(),
|
|
1640
|
+
count: z22.number().int().positive()
|
|
1641
|
+
});
|
|
1642
|
+
var deleteEnvVarResponseSchema = z22.object({
|
|
1643
|
+
success: z22.literal(true),
|
|
1644
|
+
message: z22.string()
|
|
1645
|
+
});
|
|
1646
|
+
var updateSlugRequestSchema = z22.object({
|
|
1647
|
+
slug: z22.string().trim().min(3, "slug must be at least 3 characters").max(63, "slug must be at most 63 characters").regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "slug must be lowercase alphanumeric with hyphens, not starting or ending with hyphen").nullable()
|
|
1648
|
+
});
|
|
1649
|
+
var updateSlugResponseSchema = z22.object({
|
|
1650
|
+
success: z22.boolean(),
|
|
1651
|
+
slug: z22.string().nullable(),
|
|
1652
|
+
domain: z22.string().nullable()
|
|
1653
|
+
});
|
|
1654
|
+
var deploymentMetadataResponseSchema = z22.object({
|
|
1655
|
+
currentDeploymentId: z22.string().uuid().nullable(),
|
|
1656
|
+
defaultDomainUrl: z22.string().nullable(),
|
|
1657
|
+
customDomainUrl: z22.string().nullable()
|
|
1658
|
+
});
|
|
1659
|
+
var domainVerificationRecordSchema = z22.object({
|
|
1660
|
+
type: z22.string(),
|
|
1661
|
+
domain: z22.string(),
|
|
1662
|
+
value: z22.string()
|
|
1663
|
+
});
|
|
1664
|
+
var customDomainSchema = z22.object({
|
|
1665
|
+
domain: z22.string(),
|
|
1666
|
+
apexDomain: z22.string(),
|
|
1667
|
+
verified: z22.boolean(),
|
|
1668
|
+
misconfigured: z22.boolean(),
|
|
1669
|
+
verification: z22.array(domainVerificationRecordSchema),
|
|
1670
|
+
cnameTarget: z22.string().nullable(),
|
|
1671
|
+
aRecordValue: z22.string().nullable()
|
|
1672
|
+
});
|
|
1673
|
+
var addCustomDomainRequestSchema = z22.object({
|
|
1674
|
+
domain: z22.string().trim().min(1, "Domain is required").regex(/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i, "Invalid domain format (e.g. myapp.com or www.myapp.com)").refine((domain) => !domain.toLowerCase().endsWith(".insforge.site"), {
|
|
1675
|
+
message: "Domains ending with .insforge.site are reserved by InsForge"
|
|
1676
|
+
})
|
|
1677
|
+
});
|
|
1678
|
+
var listCustomDomainsResponseSchema = z22.object({
|
|
1679
|
+
domains: z22.array(customDomainSchema)
|
|
1680
|
+
});
|
|
1681
|
+
|
|
1682
|
+
// node_modules/@insforge/shared-schemas/dist/schedules.schema.js
|
|
1683
|
+
import { z as z23 } from "zod";
|
|
1684
|
+
var scheduleSchema = z23.object({
|
|
1685
|
+
id: z23.string().uuid(),
|
|
1686
|
+
name: z23.string(),
|
|
1687
|
+
cronSchedule: z23.string(),
|
|
1688
|
+
functionUrl: z23.string().url(),
|
|
1689
|
+
httpMethod: z23.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
1690
|
+
// Optional HTTP headers to include when invoking the scheduled function
|
|
1691
|
+
headers: z23.record(z23.string()).nullable(),
|
|
1692
|
+
// Body payload for the scheduled invocation. Can be a JSON object or a raw string.
|
|
1693
|
+
body: z23.union([z23.string(), z23.record(z23.unknown())]).nullable(),
|
|
1694
|
+
// cron_job_id is a BIGINT in postgres, which node-pg returns as a string.
|
|
1695
|
+
cronJobId: z23.string().nullable(),
|
|
1696
|
+
lastExecutedAt: z23.string().datetime().nullable(),
|
|
1697
|
+
// Whether the cron job is currently active (has a scheduled cron job)
|
|
1698
|
+
isActive: z23.boolean().default(true),
|
|
1699
|
+
// Next scheduled run time in ISO format (nullable if cron expression invalid)
|
|
1700
|
+
nextRun: z23.string().datetime().nullable(),
|
|
1701
|
+
createdAt: z23.string().datetime(),
|
|
1702
|
+
updatedAt: z23.string().datetime()
|
|
1703
|
+
});
|
|
1704
|
+
var scheduleLogSchema = z23.object({
|
|
1705
|
+
id: z23.string().uuid(),
|
|
1706
|
+
scheduleId: z23.string().uuid(),
|
|
1707
|
+
executedAt: z23.string().datetime(),
|
|
1708
|
+
statusCode: z23.number().int(),
|
|
1709
|
+
success: z23.boolean(),
|
|
1710
|
+
durationMs: z23.number().int(),
|
|
1711
|
+
message: z23.string().nullable()
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
// node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js
|
|
1715
|
+
import { z as z24 } from "zod";
|
|
1716
|
+
var cronScheduleSchema = z24.string().refine((value) => {
|
|
1717
|
+
const parts = value.split(" ");
|
|
1718
|
+
return parts.length === 5 || parts.length === 6;
|
|
1719
|
+
}, { message: 'Invalid cron schedule format. Use 5 or 6 parts (e.g., "* * * * *").' });
|
|
1720
|
+
var createScheduleRequestSchema = z24.object({
|
|
1721
|
+
name: z24.string().min(3, "Schedule name must be at least 3 characters long"),
|
|
1722
|
+
cronSchedule: cronScheduleSchema,
|
|
1723
|
+
functionUrl: z24.string().url("The function URL must be a valid URL."),
|
|
1724
|
+
httpMethod: z24.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
1725
|
+
headers: z24.record(z24.string()).optional().describe("Header values can reference secrets using ${{secrets.KEY_NAME}} syntax."),
|
|
1726
|
+
body: z24.record(z24.unknown()).optional().describe("The JSON body to send with the request.")
|
|
1727
|
+
});
|
|
1728
|
+
var updateScheduleRequestSchema = z24.object({
|
|
1729
|
+
name: z24.string().min(3, "Schedule name must be at least 3 characters long").optional(),
|
|
1730
|
+
cronSchedule: cronScheduleSchema.optional(),
|
|
1731
|
+
functionUrl: z24.string().url("The function URL must be a valid URL.").optional(),
|
|
1732
|
+
httpMethod: z24.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional(),
|
|
1733
|
+
headers: z24.record(z24.string()).optional().describe("Header values can reference secrets using ${{secrets.KEY_NAME}} syntax."),
|
|
1734
|
+
body: z24.record(z24.unknown()).optional().describe("The JSON body to send with the request."),
|
|
1735
|
+
isActive: z24.boolean().optional().describe("Enable or disable the schedule.")
|
|
1736
|
+
});
|
|
1737
|
+
var listSchedulesResponseSchema = z24.array(scheduleSchema);
|
|
1738
|
+
var executionLogResponseSchema = scheduleLogSchema;
|
|
1739
|
+
var listExecutionLogsResponseSchema = z24.object({
|
|
1740
|
+
logs: z24.array(executionLogResponseSchema),
|
|
1741
|
+
totalCount: z24.number().int().nonnegative(),
|
|
1742
|
+
limit: z24.number().int().positive(),
|
|
1743
|
+
offset: z24.number().int().nonnegative()
|
|
1744
|
+
});
|
|
1745
|
+
var createScheduleResponseSchema = z24.object({
|
|
1746
|
+
id: z24.string().uuid(),
|
|
1747
|
+
cronJobId: z24.string(),
|
|
1748
|
+
message: z24.string()
|
|
1749
|
+
});
|
|
1750
|
+
var updateScheduleResponseSchema = z24.object({
|
|
1751
|
+
id: z24.string().uuid(),
|
|
1752
|
+
cronJobId: z24.string().optional(),
|
|
1753
|
+
message: z24.string()
|
|
1754
|
+
});
|
|
1755
|
+
var deleteScheduleResponseSchema = z24.object({
|
|
1756
|
+
message: z24.string()
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
// src/shared/tools/docs.ts
|
|
1760
|
+
function registerDocsTools(ctx) {
|
|
1761
|
+
const { API_BASE_URL, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
|
|
1394
1762
|
const fetchDocumentation = async (docType) => {
|
|
1395
1763
|
try {
|
|
1396
1764
|
const response = await fetch2(`${API_BASE_URL}/api/docs/${docType}`, {
|
|
1397
1765
|
method: "GET",
|
|
1398
|
-
headers: {
|
|
1399
|
-
"Content-Type": "application/json"
|
|
1400
|
-
}
|
|
1766
|
+
headers: { "Content-Type": "application/json" }
|
|
1401
1767
|
});
|
|
1402
1768
|
if (response.status === 404) {
|
|
1403
1769
|
throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
|
|
@@ -1407,55 +1773,47 @@ async function registerInsforgeTools(server, config = {}) {
|
|
|
1407
1773
|
let content = result.content;
|
|
1408
1774
|
content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
|
|
1409
1775
|
content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
|
|
1776
|
+
content = content.replace(/https:\/\/your-app\.insforge\.app/g, API_BASE_URL);
|
|
1410
1777
|
return content;
|
|
1411
1778
|
}
|
|
1412
1779
|
throw new Error("Invalid response format from documentation endpoint");
|
|
1413
1780
|
} catch (error) {
|
|
1414
1781
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1415
|
-
throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}
|
|
1782
|
+
throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`, { cause: error });
|
|
1416
1783
|
}
|
|
1417
1784
|
};
|
|
1418
|
-
const
|
|
1785
|
+
const fetchSDKDocumentation = async (feature, language) => {
|
|
1419
1786
|
try {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
---
|
|
1436
|
-
\u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
|
|
1437
|
-
${context}`
|
|
1438
|
-
});
|
|
1787
|
+
const response = await fetch2(`${API_BASE_URL}/api/docs/${feature}/${language}`, {
|
|
1788
|
+
method: "GET",
|
|
1789
|
+
headers: { "Content-Type": "application/json" }
|
|
1790
|
+
});
|
|
1791
|
+
if (response.status === 404) {
|
|
1792
|
+
throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
|
|
1793
|
+
}
|
|
1794
|
+
const result = await handleApiResponse(response);
|
|
1795
|
+
if (result && typeof result === "object" && "content" in result) {
|
|
1796
|
+
let content = result.content;
|
|
1797
|
+
content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
|
|
1798
|
+
content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
|
|
1799
|
+
content = content.replace(/https:\/\/your-app\.insforge\.app/g, API_BASE_URL);
|
|
1800
|
+
return content;
|
|
1439
1801
|
}
|
|
1802
|
+
throw new Error("Invalid response format from documentation endpoint");
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1805
|
+
throw new Error(`Unable to retrieve ${feature}-${language} documentation: ${errMsg}`, { cause: error });
|
|
1440
1806
|
}
|
|
1441
|
-
return response;
|
|
1442
1807
|
};
|
|
1443
1808
|
registerTool(
|
|
1444
1809
|
"fetch-docs",
|
|
1445
1810
|
'Fetch Insforge documentation. Use "instructions" for essential backend setup (MANDATORY FIRST), or select specific SDK docs for database, auth, storage, functions, or AI integration.',
|
|
1446
|
-
{
|
|
1447
|
-
docType: docTypeSchema
|
|
1448
|
-
},
|
|
1811
|
+
{ docType: docTypeSchema },
|
|
1449
1812
|
withUsageTracking("fetch-docs", async ({ docType }) => {
|
|
1450
1813
|
try {
|
|
1451
1814
|
const content = await fetchDocumentation(docType);
|
|
1452
1815
|
return await addBackgroundContext({
|
|
1453
|
-
content: [
|
|
1454
|
-
{
|
|
1455
|
-
type: "text",
|
|
1456
|
-
text: content
|
|
1457
|
-
}
|
|
1458
|
-
]
|
|
1816
|
+
content: [{ type: "text", text: content }]
|
|
1459
1817
|
});
|
|
1460
1818
|
} catch (error) {
|
|
1461
1819
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -1468,7 +1826,38 @@ ${context}`
|
|
|
1468
1826
|
};
|
|
1469
1827
|
}
|
|
1470
1828
|
return {
|
|
1471
|
-
content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
|
|
1829
|
+
content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }],
|
|
1830
|
+
isError: true
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
})
|
|
1834
|
+
);
|
|
1835
|
+
registerTool(
|
|
1836
|
+
"fetch-sdk-docs",
|
|
1837
|
+
`Fetch Insforge SDK documentation for a specific feature and language combination.
|
|
1838
|
+
|
|
1839
|
+
Supported features: ${sdkFeatureSchema.options.join(", ")}
|
|
1840
|
+
Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
|
|
1841
|
+
{ sdkFeature: sdkFeatureSchema, sdkLanguage: sdkLanguageSchema },
|
|
1842
|
+
withUsageTracking("fetch-sdk-docs", async ({ sdkFeature, sdkLanguage }) => {
|
|
1843
|
+
try {
|
|
1844
|
+
const content = await fetchSDKDocumentation(sdkFeature, sdkLanguage);
|
|
1845
|
+
return await addBackgroundContext({
|
|
1846
|
+
content: [{ type: "text", text: content }]
|
|
1847
|
+
});
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1850
|
+
if (errMsg.includes("404") || errMsg.toLowerCase().includes("not found")) {
|
|
1851
|
+
return {
|
|
1852
|
+
content: [{
|
|
1853
|
+
type: "text",
|
|
1854
|
+
text: `Documentation for "${sdkFeature}-${sdkLanguage}" is not available. This is likely because your backend version is too old and doesn't support this documentation endpoint yet. This won't affect the functionality of the tools - they will still work correctly.`
|
|
1855
|
+
}]
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
return {
|
|
1859
|
+
content: [{ type: "text", text: `Error fetching ${sdkFeature}-${sdkLanguage} documentation: ${errMsg}` }],
|
|
1860
|
+
isError: true
|
|
1472
1861
|
};
|
|
1473
1862
|
}
|
|
1474
1863
|
})
|
|
@@ -1477,7 +1866,7 @@ ${context}`
|
|
|
1477
1866
|
"get-anon-key",
|
|
1478
1867
|
"Generate an anonymous JWT token that never expires. Requires admin API key. Use this for client-side applications that need public access.",
|
|
1479
1868
|
{
|
|
1480
|
-
apiKey:
|
|
1869
|
+
apiKey: z25.string().optional().describe("API key for authentication (optional if provided via --api_key)")
|
|
1481
1870
|
},
|
|
1482
1871
|
withUsageTracking("get-anon-key", async ({ apiKey }) => {
|
|
1483
1872
|
try {
|
|
@@ -1491,61 +1880,74 @@ ${context}`
|
|
|
1491
1880
|
});
|
|
1492
1881
|
const result = await handleApiResponse(response);
|
|
1493
1882
|
return await addBackgroundContext({
|
|
1494
|
-
content: [
|
|
1495
|
-
{
|
|
1496
|
-
type: "text",
|
|
1497
|
-
text: formatSuccessMessage("Anonymous token generated", result)
|
|
1498
|
-
}
|
|
1499
|
-
]
|
|
1883
|
+
content: [{ type: "text", text: formatSuccessMessage("Anonymous token generated", result) }]
|
|
1500
1884
|
});
|
|
1501
1885
|
} catch (error) {
|
|
1502
1886
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1503
1887
|
return {
|
|
1504
|
-
content: [
|
|
1505
|
-
{
|
|
1506
|
-
type: "text",
|
|
1507
|
-
text: `Error generating anonymous token: ${errMsg}`
|
|
1508
|
-
}
|
|
1509
|
-
],
|
|
1888
|
+
content: [{ type: "text", text: `Error generating anonymous token: ${errMsg}` }],
|
|
1510
1889
|
isError: true
|
|
1511
1890
|
};
|
|
1512
1891
|
}
|
|
1513
1892
|
})
|
|
1514
1893
|
);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/shared/tools/database.ts
|
|
1897
|
+
import { z as z26 } from "zod";
|
|
1898
|
+
import fetch3 from "node-fetch";
|
|
1899
|
+
import { promises as fs } from "fs";
|
|
1900
|
+
import { execFile } from "child_process";
|
|
1901
|
+
import { promisify } from "util";
|
|
1902
|
+
import { tmpdir } from "os";
|
|
1903
|
+
import { join, basename } from "path";
|
|
1904
|
+
import FormData from "form-data";
|
|
1905
|
+
|
|
1906
|
+
// src/shared/tools/utils.ts
|
|
1907
|
+
var shellEsc = (s) => `'${s.replace(/'/g, "'\\''")}'`;
|
|
1908
|
+
|
|
1909
|
+
// src/shared/tools/database.ts
|
|
1910
|
+
var execFileAsync = promisify(execFile);
|
|
1911
|
+
function isAnonTokenResponse(obj) {
|
|
1912
|
+
return typeof obj === "object" && obj !== null && "accessToken" in obj && typeof obj.accessToken === "string" && obj.accessToken.length > 0;
|
|
1913
|
+
}
|
|
1914
|
+
function isBulkUpsertApiResult(obj) {
|
|
1915
|
+
if (typeof obj !== "object" || obj === null || !("success" in obj) || !("rowsAffected" in obj) || !("totalRecords" in obj) || !("table" in obj)) {
|
|
1916
|
+
return false;
|
|
1917
|
+
}
|
|
1918
|
+
const value = obj;
|
|
1919
|
+
if (typeof value.success !== "boolean" || typeof value.rowsAffected !== "number" || typeof value.totalRecords !== "number" || typeof value.table !== "string" || value.table.length === 0) {
|
|
1920
|
+
return false;
|
|
1921
|
+
}
|
|
1922
|
+
if (value.message !== void 0 && typeof value.message !== "string") {
|
|
1923
|
+
return false;
|
|
1924
|
+
}
|
|
1925
|
+
return true;
|
|
1926
|
+
}
|
|
1927
|
+
function registerDatabaseTools(ctx) {
|
|
1928
|
+
const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
|
|
1515
1929
|
registerTool(
|
|
1516
1930
|
"get-table-schema",
|
|
1517
1931
|
"Returns the detailed schema(including RLS, indexes, constraints, etc.) of a specific table",
|
|
1518
1932
|
{
|
|
1519
|
-
apiKey:
|
|
1520
|
-
tableName:
|
|
1933
|
+
apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
1934
|
+
tableName: z26.string().describe("Name of the table")
|
|
1521
1935
|
},
|
|
1522
1936
|
withUsageTracking("get-table-schema", async ({ apiKey, tableName }) => {
|
|
1523
1937
|
try {
|
|
1524
1938
|
const actualApiKey = getApiKey(apiKey);
|
|
1525
|
-
const response = await
|
|
1939
|
+
const response = await fetch3(`${API_BASE_URL}/api/metadata/${encodeURIComponent(tableName)}`, {
|
|
1526
1940
|
method: "GET",
|
|
1527
|
-
headers: {
|
|
1528
|
-
"x-api-key": actualApiKey
|
|
1529
|
-
}
|
|
1941
|
+
headers: { "x-api-key": actualApiKey }
|
|
1530
1942
|
});
|
|
1531
1943
|
const result = await handleApiResponse(response);
|
|
1532
1944
|
return await addBackgroundContext({
|
|
1533
|
-
content: [
|
|
1534
|
-
{
|
|
1535
|
-
type: "text",
|
|
1536
|
-
text: formatSuccessMessage("Schema retrieved", result)
|
|
1537
|
-
}
|
|
1538
|
-
]
|
|
1945
|
+
content: [{ type: "text", text: formatSuccessMessage("Schema retrieved", result) }]
|
|
1539
1946
|
});
|
|
1540
1947
|
} catch (error) {
|
|
1541
1948
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1542
1949
|
return {
|
|
1543
|
-
content: [
|
|
1544
|
-
{
|
|
1545
|
-
type: "text",
|
|
1546
|
-
text: `Error getting table schema: ${errMsg}`
|
|
1547
|
-
}
|
|
1548
|
-
],
|
|
1950
|
+
content: [{ type: "text", text: `Error getting table schema: ${errMsg}` }],
|
|
1549
1951
|
isError: true
|
|
1550
1952
|
};
|
|
1551
1953
|
}
|
|
@@ -1555,37 +1957,25 @@ ${context}`
|
|
|
1555
1957
|
"get-backend-metadata",
|
|
1556
1958
|
"Index all backend metadata",
|
|
1557
1959
|
{
|
|
1558
|
-
apiKey:
|
|
1960
|
+
apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)")
|
|
1559
1961
|
},
|
|
1560
1962
|
withUsageTracking("get-backend-metadata", async ({ apiKey }) => {
|
|
1561
1963
|
try {
|
|
1562
1964
|
const actualApiKey = getApiKey(apiKey);
|
|
1563
|
-
const response = await
|
|
1965
|
+
const response = await fetch3(`${API_BASE_URL}/api/metadata?mcp=true`, {
|
|
1564
1966
|
method: "GET",
|
|
1565
|
-
headers: {
|
|
1566
|
-
"x-api-key": actualApiKey
|
|
1567
|
-
}
|
|
1967
|
+
headers: { "x-api-key": actualApiKey }
|
|
1568
1968
|
});
|
|
1569
1969
|
const metadata = await handleApiResponse(response);
|
|
1570
1970
|
return await addBackgroundContext({
|
|
1571
|
-
content: [
|
|
1572
|
-
{
|
|
1573
|
-
type: "text",
|
|
1574
|
-
text: `Backend metadata:
|
|
1971
|
+
content: [{ type: "text", text: `Backend metadata:
|
|
1575
1972
|
|
|
1576
|
-
${JSON.stringify(metadata, null, 2)}`
|
|
1577
|
-
}
|
|
1578
|
-
]
|
|
1973
|
+
${JSON.stringify(metadata, null, 2)}` }]
|
|
1579
1974
|
});
|
|
1580
1975
|
} catch (error) {
|
|
1581
1976
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1582
1977
|
return {
|
|
1583
|
-
content: [
|
|
1584
|
-
{
|
|
1585
|
-
type: "text",
|
|
1586
|
-
text: `Error retrieving backend metadata: ${errMsg}`
|
|
1587
|
-
}
|
|
1588
|
-
],
|
|
1978
|
+
content: [{ type: "text", text: `Error retrieving backend metadata: ${errMsg}` }],
|
|
1589
1979
|
isError: true
|
|
1590
1980
|
};
|
|
1591
1981
|
}
|
|
@@ -1595,17 +1985,14 @@ ${JSON.stringify(metadata, null, 2)}`
|
|
|
1595
1985
|
"run-raw-sql",
|
|
1596
1986
|
"Execute raw SQL query with optional parameters. Admin access required. Use with caution as it can modify data directly.",
|
|
1597
1987
|
{
|
|
1598
|
-
apiKey:
|
|
1988
|
+
apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
1599
1989
|
...rawSQLRequestSchema.shape
|
|
1600
1990
|
},
|
|
1601
1991
|
withUsageTracking("run-raw-sql", async ({ apiKey, query, params }) => {
|
|
1602
1992
|
try {
|
|
1603
1993
|
const actualApiKey = getApiKey(apiKey);
|
|
1604
|
-
const requestBody = {
|
|
1605
|
-
|
|
1606
|
-
params: params || []
|
|
1607
|
-
};
|
|
1608
|
-
const response = await fetch2(`${API_BASE_URL}/api/database/advance/rawsql`, {
|
|
1994
|
+
const requestBody = { query, params: params || [] };
|
|
1995
|
+
const response = await fetch3(`${API_BASE_URL}/api/database/advance/rawsql`, {
|
|
1609
1996
|
method: "POST",
|
|
1610
1997
|
headers: {
|
|
1611
1998
|
"x-api-key": actualApiKey,
|
|
@@ -1615,75 +2002,103 @@ ${JSON.stringify(metadata, null, 2)}`
|
|
|
1615
2002
|
});
|
|
1616
2003
|
const result = await handleApiResponse(response);
|
|
1617
2004
|
return await addBackgroundContext({
|
|
1618
|
-
content: [
|
|
1619
|
-
{
|
|
1620
|
-
type: "text",
|
|
1621
|
-
text: formatSuccessMessage("SQL query executed", result)
|
|
1622
|
-
}
|
|
1623
|
-
]
|
|
2005
|
+
content: [{ type: "text", text: formatSuccessMessage("SQL query executed", result) }]
|
|
1624
2006
|
});
|
|
1625
2007
|
} catch (error) {
|
|
1626
2008
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1627
2009
|
return {
|
|
1628
|
-
content: [
|
|
1629
|
-
{
|
|
1630
|
-
type: "text",
|
|
1631
|
-
text: `Error executing SQL query: ${errMsg}`
|
|
1632
|
-
}
|
|
1633
|
-
],
|
|
2010
|
+
content: [{ type: "text", text: `Error executing SQL query: ${errMsg}` }],
|
|
1634
2011
|
isError: true
|
|
1635
2012
|
};
|
|
1636
2013
|
}
|
|
1637
2014
|
})
|
|
1638
2015
|
);
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2016
|
+
if (isRemote) {
|
|
2017
|
+
registerTool(
|
|
2018
|
+
"download-template",
|
|
2019
|
+
"CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Fetches configuration and returns a command for you to run locally to scaffold a starter template.",
|
|
2020
|
+
{
|
|
2021
|
+
frame: z26.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
|
|
2022
|
+
projectName: z26.string().optional().describe('Name for the project directory (optional, defaults to "insforge-{frame}")')
|
|
2023
|
+
},
|
|
2024
|
+
withUsageTracking("download-template", async ({ frame, projectName }) => {
|
|
2025
|
+
try {
|
|
2026
|
+
const response = await fetch3(`${API_BASE_URL}/api/auth/tokens/anon`, {
|
|
2027
|
+
method: "POST",
|
|
2028
|
+
headers: {
|
|
2029
|
+
"x-api-key": getApiKey(),
|
|
2030
|
+
"Content-Type": "application/json"
|
|
2031
|
+
}
|
|
2032
|
+
});
|
|
2033
|
+
const anonResult = await handleApiResponse(response);
|
|
2034
|
+
if (!isAnonTokenResponse(anonResult)) {
|
|
2035
|
+
throw new Error("Failed to retrieve anon key from backend");
|
|
1653
2036
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
2037
|
+
const anonKey = anonResult.accessToken;
|
|
2038
|
+
const rawDir = projectName || `insforge-${frame}`;
|
|
2039
|
+
if (!rawDir || rawDir === "." || rawDir === ".." || /[/\\]/.test(rawDir) || !/^[\w.-]+$/.test(rawDir)) {
|
|
2040
|
+
throw new Error("projectName must be a single directory name using only letters, numbers, hyphens, underscores, and dots");
|
|
2041
|
+
}
|
|
2042
|
+
const targetDir = rawDir;
|
|
2043
|
+
const instructions = `Template configuration ready. Please run the following command in your project's parent directory:
|
|
2044
|
+
|
|
2045
|
+
\`\`\`bash
|
|
2046
|
+
npx create-insforge-app ${shellEsc(targetDir)} --frame ${frame} --base-url ${shellEsc(API_BASE_URL)} --anon-key ${shellEsc(anonKey)}
|
|
2047
|
+
\`\`\`
|
|
2048
|
+
|
|
2049
|
+
After the command completes, \`cd ${shellEsc(targetDir)}\` and start developing.`;
|
|
2050
|
+
return {
|
|
2051
|
+
content: [{ type: "text", text: instructions }]
|
|
2052
|
+
};
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2055
|
+
return {
|
|
2056
|
+
content: [{ type: "text", text: `Error preparing template: ${errMsg}` }],
|
|
2057
|
+
isError: true
|
|
2058
|
+
};
|
|
1659
2059
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
2060
|
+
})
|
|
2061
|
+
);
|
|
2062
|
+
} else {
|
|
2063
|
+
registerTool(
|
|
2064
|
+
"download-template",
|
|
2065
|
+
"CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Download pre-configured starter template to a temporary directory. After download, you MUST copy files to current directory using the provided command.",
|
|
2066
|
+
{
|
|
2067
|
+
frame: z26.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
|
|
2068
|
+
projectName: z26.string().optional().describe('Name for the project directory (optional, defaults to "insforge-{frame}")')
|
|
2069
|
+
},
|
|
2070
|
+
withUsageTracking("download-template", async ({ frame, projectName }) => {
|
|
1664
2071
|
try {
|
|
1665
|
-
const
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
2072
|
+
const response = await fetch3(`${API_BASE_URL}/api/auth/tokens/anon`, {
|
|
2073
|
+
method: "POST",
|
|
2074
|
+
headers: {
|
|
2075
|
+
"x-api-key": getApiKey(),
|
|
2076
|
+
"Content-Type": "application/json"
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
const anonResult = await handleApiResponse(response);
|
|
2080
|
+
if (!isAnonTokenResponse(anonResult)) {
|
|
2081
|
+
throw new Error("Failed to retrieve anon key from backend");
|
|
1669
2082
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
2083
|
+
const anonKey = anonResult.accessToken;
|
|
2084
|
+
const rawDir = projectName || `insforge-${frame}`;
|
|
2085
|
+
if (!rawDir || rawDir === "." || rawDir === ".." || /[/\\]/.test(rawDir) || !/^[\w.-]+$/.test(rawDir)) {
|
|
2086
|
+
throw new Error("projectName must be a single directory name using only letters, numbers, hyphens, underscores, and dots");
|
|
2087
|
+
}
|
|
2088
|
+
const targetDir = rawDir;
|
|
2089
|
+
const workspaceBase = await fs.mkdtemp(join(tmpdir(), "insforge-template-"));
|
|
2090
|
+
const templatePath = join(workspaceBase, targetDir);
|
|
2091
|
+
console.error(`[download-template] Target path: ${templatePath}`);
|
|
2092
|
+
await execFileAsync(
|
|
2093
|
+
"npx",
|
|
2094
|
+
["create-insforge-app", targetDir, "--frame", frame, "--base-url", API_BASE_URL, "--anon-key", anonKey, "--skip-install"],
|
|
2095
|
+
{ maxBuffer: 10 * 1024 * 1024, cwd: workspaceBase }
|
|
2096
|
+
);
|
|
2097
|
+
const frameName = frame === "nextjs" ? "Next.js" : "React";
|
|
2098
|
+
return await addBackgroundContext({
|
|
2099
|
+
content: [{
|
|
1685
2100
|
type: "text",
|
|
1686
|
-
text: `\u2705
|
|
2101
|
+
text: `\u2705 ${frameName} template downloaded successfully
|
|
1687
2102
|
|
|
1688
2103
|
\u{1F4C1} Template Location: ${templatePath}
|
|
1689
2104
|
|
|
@@ -1695,43 +2110,38 @@ You MUST copy ALL files (INCLUDING HIDDEN FILES like .env, .gitignore, etc.) fro
|
|
|
1695
2110
|
Copy all files from: ${templatePath}
|
|
1696
2111
|
To: Your current project directory
|
|
1697
2112
|
`
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
})
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
isError: true
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
})
|
|
1714
|
-
);
|
|
2113
|
+
}]
|
|
2114
|
+
});
|
|
2115
|
+
} catch (error) {
|
|
2116
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2117
|
+
return {
|
|
2118
|
+
content: [{ type: "text", text: `Error downloading template: ${errMsg}` }],
|
|
2119
|
+
isError: true
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
})
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
1715
2125
|
registerTool(
|
|
1716
2126
|
"bulk-upsert",
|
|
1717
2127
|
"Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
|
|
1718
2128
|
{
|
|
1719
|
-
apiKey:
|
|
2129
|
+
apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
1720
2130
|
...bulkUpsertRequestSchema.shape,
|
|
1721
|
-
filePath:
|
|
2131
|
+
filePath: z26.string().describe("Path to CSV or JSON file containing data to import")
|
|
1722
2132
|
},
|
|
1723
2133
|
withUsageTracking("bulk-upsert", async ({ apiKey, table, filePath, upsertKey }) => {
|
|
1724
2134
|
try {
|
|
1725
2135
|
const actualApiKey = getApiKey(apiKey);
|
|
1726
2136
|
const fileBuffer = await fs.readFile(filePath);
|
|
1727
|
-
const fileName = filePath
|
|
2137
|
+
const fileName = basename(filePath) || "data.csv";
|
|
1728
2138
|
const formData = new FormData();
|
|
1729
2139
|
formData.append("file", fileBuffer, fileName);
|
|
1730
2140
|
formData.append("table", table);
|
|
1731
2141
|
if (upsertKey) {
|
|
1732
2142
|
formData.append("upsertKey", upsertKey);
|
|
1733
2143
|
}
|
|
1734
|
-
const response = await
|
|
2144
|
+
const response = await fetch3(`${API_BASE_URL}/api/database/advance/bulk-upsert`, {
|
|
1735
2145
|
method: "POST",
|
|
1736
2146
|
headers: {
|
|
1737
2147
|
"x-api-key": actualApiKey,
|
|
@@ -1739,47 +2149,50 @@ To: Your current project directory
|
|
|
1739
2149
|
},
|
|
1740
2150
|
body: formData
|
|
1741
2151
|
});
|
|
1742
|
-
const
|
|
1743
|
-
|
|
2152
|
+
const rawResult = await handleApiResponse(response);
|
|
2153
|
+
if (!isBulkUpsertApiResult(rawResult)) {
|
|
2154
|
+
throw new Error("Unexpected response format from bulk-upsert endpoint");
|
|
2155
|
+
}
|
|
2156
|
+
const message = rawResult.success ? `Successfully processed ${rawResult.rowsAffected} of ${rawResult.totalRecords} records into table "${rawResult.table}"` : rawResult.message || "Bulk upsert operation completed";
|
|
1744
2157
|
return await addBackgroundContext({
|
|
1745
|
-
content: [
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
}
|
|
1756
|
-
]
|
|
2158
|
+
content: [{
|
|
2159
|
+
type: "text",
|
|
2160
|
+
text: formatSuccessMessage("Bulk upsert completed", {
|
|
2161
|
+
message,
|
|
2162
|
+
table: rawResult.table,
|
|
2163
|
+
rowsAffected: rawResult.rowsAffected,
|
|
2164
|
+
totalRecords: rawResult.totalRecords,
|
|
2165
|
+
errors: rawResult.errors
|
|
2166
|
+
})
|
|
2167
|
+
}]
|
|
1757
2168
|
});
|
|
1758
2169
|
} catch (error) {
|
|
1759
2170
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1760
2171
|
return {
|
|
1761
|
-
content: [
|
|
1762
|
-
{
|
|
1763
|
-
type: "text",
|
|
1764
|
-
text: `Error performing bulk upsert: ${errMsg}`
|
|
1765
|
-
}
|
|
1766
|
-
],
|
|
2172
|
+
content: [{ type: "text", text: `Error performing bulk upsert: ${errMsg}` }],
|
|
1767
2173
|
isError: true
|
|
1768
2174
|
};
|
|
1769
2175
|
}
|
|
1770
2176
|
})
|
|
1771
2177
|
);
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// src/shared/tools/storage.ts
|
|
2181
|
+
import { z as z27 } from "zod";
|
|
2182
|
+
import fetch4 from "node-fetch";
|
|
2183
|
+
function registerStorageTools(ctx) {
|
|
2184
|
+
const { API_BASE_URL, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
|
|
1772
2185
|
registerTool(
|
|
1773
2186
|
"create-bucket",
|
|
1774
2187
|
"Create new storage bucket",
|
|
1775
2188
|
{
|
|
1776
|
-
apiKey:
|
|
2189
|
+
apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
1777
2190
|
...createBucketRequestSchema.shape
|
|
1778
2191
|
},
|
|
1779
2192
|
withUsageTracking("create-bucket", async ({ apiKey, bucketName, isPublic }) => {
|
|
1780
2193
|
try {
|
|
1781
2194
|
const actualApiKey = getApiKey(apiKey);
|
|
1782
|
-
const response = await
|
|
2195
|
+
const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
|
|
1783
2196
|
method: "POST",
|
|
1784
2197
|
headers: {
|
|
1785
2198
|
"x-api-key": actualApiKey,
|
|
@@ -1789,22 +2202,12 @@ To: Your current project directory
|
|
|
1789
2202
|
});
|
|
1790
2203
|
const result = await handleApiResponse(response);
|
|
1791
2204
|
return await addBackgroundContext({
|
|
1792
|
-
content: [
|
|
1793
|
-
{
|
|
1794
|
-
type: "text",
|
|
1795
|
-
text: formatSuccessMessage("Bucket created", result)
|
|
1796
|
-
}
|
|
1797
|
-
]
|
|
2205
|
+
content: [{ type: "text", text: formatSuccessMessage("Bucket created", result) }]
|
|
1798
2206
|
});
|
|
1799
2207
|
} catch (error) {
|
|
1800
2208
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1801
2209
|
return {
|
|
1802
|
-
content: [
|
|
1803
|
-
{
|
|
1804
|
-
type: "text",
|
|
1805
|
-
text: `Error creating bucket: ${errMsg}`
|
|
1806
|
-
}
|
|
1807
|
-
],
|
|
2210
|
+
content: [{ type: "text", text: `Error creating bucket: ${errMsg}` }],
|
|
1808
2211
|
isError: true
|
|
1809
2212
|
};
|
|
1810
2213
|
}
|
|
@@ -1813,33 +2216,23 @@ To: Your current project directory
|
|
|
1813
2216
|
registerTool(
|
|
1814
2217
|
"list-buckets",
|
|
1815
2218
|
"Lists all storage buckets",
|
|
1816
|
-
{
|
|
1817
|
-
|
|
2219
|
+
{
|
|
2220
|
+
apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)")
|
|
2221
|
+
},
|
|
2222
|
+
withUsageTracking("list-buckets", async ({ apiKey }) => {
|
|
1818
2223
|
try {
|
|
1819
|
-
const response = await
|
|
2224
|
+
const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
|
|
1820
2225
|
method: "GET",
|
|
1821
|
-
headers: {
|
|
1822
|
-
"x-api-key": getApiKey()
|
|
1823
|
-
}
|
|
2226
|
+
headers: { "x-api-key": getApiKey(apiKey) }
|
|
1824
2227
|
});
|
|
1825
2228
|
const result = await handleApiResponse(response);
|
|
1826
2229
|
return await addBackgroundContext({
|
|
1827
|
-
content: [
|
|
1828
|
-
{
|
|
1829
|
-
type: "text",
|
|
1830
|
-
text: formatSuccessMessage("Buckets retrieved", result)
|
|
1831
|
-
}
|
|
1832
|
-
]
|
|
2230
|
+
content: [{ type: "text", text: formatSuccessMessage("Buckets retrieved", result) }]
|
|
1833
2231
|
});
|
|
1834
2232
|
} catch (error) {
|
|
1835
2233
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1836
2234
|
return {
|
|
1837
|
-
content: [
|
|
1838
|
-
{
|
|
1839
|
-
type: "text",
|
|
1840
|
-
text: `Error listing buckets: ${errMsg}`
|
|
1841
|
-
}
|
|
1842
|
-
],
|
|
2235
|
+
content: [{ type: "text", text: `Error listing buckets: ${errMsg}` }],
|
|
1843
2236
|
isError: true
|
|
1844
2237
|
};
|
|
1845
2238
|
}
|
|
@@ -1849,541 +2242,736 @@ To: Your current project directory
|
|
|
1849
2242
|
"delete-bucket",
|
|
1850
2243
|
"Deletes a storage bucket",
|
|
1851
2244
|
{
|
|
1852
|
-
apiKey:
|
|
1853
|
-
|
|
2245
|
+
apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2246
|
+
// Reuse the same bucket name validation as create-bucket
|
|
2247
|
+
bucketName: createBucketRequestSchema.shape.bucketName
|
|
1854
2248
|
},
|
|
1855
2249
|
withUsageTracking("delete-bucket", async ({ apiKey, bucketName }) => {
|
|
1856
2250
|
try {
|
|
1857
2251
|
const actualApiKey = getApiKey(apiKey);
|
|
1858
|
-
const response = await
|
|
2252
|
+
const response = await fetch4(`${API_BASE_URL}/api/storage/buckets/${encodeURIComponent(bucketName)}`, {
|
|
1859
2253
|
method: "DELETE",
|
|
1860
|
-
headers: {
|
|
1861
|
-
"x-api-key": actualApiKey
|
|
1862
|
-
}
|
|
2254
|
+
headers: { "x-api-key": actualApiKey }
|
|
1863
2255
|
});
|
|
1864
2256
|
const result = await handleApiResponse(response);
|
|
1865
2257
|
return await addBackgroundContext({
|
|
1866
|
-
content: [
|
|
1867
|
-
{
|
|
1868
|
-
type: "text",
|
|
1869
|
-
text: formatSuccessMessage("Bucket deleted", result)
|
|
1870
|
-
}
|
|
1871
|
-
]
|
|
2258
|
+
content: [{ type: "text", text: formatSuccessMessage("Bucket deleted", result) }]
|
|
1872
2259
|
});
|
|
1873
2260
|
} catch (error) {
|
|
1874
2261
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1875
2262
|
return {
|
|
1876
|
-
content: [
|
|
1877
|
-
{
|
|
1878
|
-
type: "text",
|
|
1879
|
-
text: `Error deleting bucket: ${errMsg}`
|
|
1880
|
-
}
|
|
1881
|
-
],
|
|
2263
|
+
content: [{ type: "text", text: `Error deleting bucket: ${errMsg}` }],
|
|
1882
2264
|
isError: true
|
|
1883
2265
|
};
|
|
1884
2266
|
}
|
|
1885
2267
|
})
|
|
1886
2268
|
);
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
// src/shared/tools/functions.ts
|
|
2272
|
+
import { z as z28 } from "zod";
|
|
2273
|
+
import fetch5 from "node-fetch";
|
|
2274
|
+
import { promises as fs2 } from "fs";
|
|
2275
|
+
function registerFunctionTools(ctx) {
|
|
2276
|
+
const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
|
|
2277
|
+
if (isRemote) {
|
|
2278
|
+
registerTool(
|
|
2279
|
+
"create-function",
|
|
2280
|
+
"Create a new edge function that runs in Deno runtime",
|
|
2281
|
+
{
|
|
2282
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2283
|
+
...uploadFunctionRequestSchema.omit({ code: true }).shape,
|
|
2284
|
+
code: z28.string().describe(
|
|
2285
|
+
"The function code as a string. Must export: module.exports = async function(request) { return new Response(...) }"
|
|
2286
|
+
)
|
|
2287
|
+
},
|
|
2288
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2289
|
+
withUsageTracking("create-function", async (args) => {
|
|
1899
2290
|
try {
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2291
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions`, {
|
|
2292
|
+
method: "POST",
|
|
2293
|
+
headers: {
|
|
2294
|
+
"Content-Type": "application/json",
|
|
2295
|
+
"x-api-key": getApiKey(args.apiKey)
|
|
2296
|
+
},
|
|
2297
|
+
body: JSON.stringify({
|
|
2298
|
+
slug: args.slug,
|
|
2299
|
+
name: args.name,
|
|
2300
|
+
code: args.code,
|
|
2301
|
+
description: args.description,
|
|
2302
|
+
status: args.status
|
|
2303
|
+
})
|
|
2304
|
+
});
|
|
2305
|
+
const result = await handleApiResponse(response);
|
|
2306
|
+
return await addBackgroundContext({
|
|
2307
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' created successfully`, result) }]
|
|
2308
|
+
});
|
|
2309
|
+
} catch (error) {
|
|
2310
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2311
|
+
return {
|
|
2312
|
+
content: [{ type: "text", text: `Error creating function: ${errMsg}` }],
|
|
2313
|
+
isError: true
|
|
2314
|
+
};
|
|
1905
2315
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2316
|
+
})
|
|
2317
|
+
);
|
|
2318
|
+
} else {
|
|
2319
|
+
registerTool(
|
|
2320
|
+
"create-function",
|
|
2321
|
+
"Create a new edge function that runs in Deno runtime. The code must be written to a file first for version control",
|
|
2322
|
+
{
|
|
2323
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2324
|
+
...uploadFunctionRequestSchema.omit({ code: true }).shape,
|
|
2325
|
+
codeFile: z28.string().describe(
|
|
2326
|
+
"Path to JavaScript file containing the function code. Must export: module.exports = async function(request) { return new Response(...) }"
|
|
2327
|
+
)
|
|
2328
|
+
},
|
|
2329
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2330
|
+
withUsageTracking("create-function", async (args) => {
|
|
2331
|
+
try {
|
|
2332
|
+
let code;
|
|
2333
|
+
try {
|
|
2334
|
+
code = await fs2.readFile(args.codeFile, "utf-8");
|
|
2335
|
+
} catch (fileError) {
|
|
2336
|
+
throw new Error(
|
|
2337
|
+
`Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`,
|
|
2338
|
+
{ cause: fileError }
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions`, {
|
|
2342
|
+
method: "POST",
|
|
2343
|
+
headers: {
|
|
2344
|
+
"Content-Type": "application/json",
|
|
2345
|
+
"x-api-key": getApiKey(args.apiKey)
|
|
2346
|
+
},
|
|
2347
|
+
body: JSON.stringify({
|
|
2348
|
+
slug: args.slug,
|
|
2349
|
+
name: args.name,
|
|
2350
|
+
code,
|
|
2351
|
+
description: args.description,
|
|
2352
|
+
status: args.status
|
|
2353
|
+
})
|
|
2354
|
+
});
|
|
2355
|
+
const result = await handleApiResponse(response);
|
|
2356
|
+
return await addBackgroundContext({
|
|
2357
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' created successfully from ${args.codeFile}`, result) }]
|
|
2358
|
+
});
|
|
2359
|
+
} catch (error) {
|
|
2360
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2361
|
+
return {
|
|
2362
|
+
content: [{ type: "text", text: `Error creating function: ${errMsg}` }],
|
|
2363
|
+
isError: true
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
})
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
1946
2369
|
registerTool(
|
|
1947
2370
|
"get-function",
|
|
1948
2371
|
"Get details of a specific edge function including its code",
|
|
1949
2372
|
{
|
|
1950
|
-
|
|
2373
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2374
|
+
slug: functionSchema.shape.slug.describe("The slug identifier of the function")
|
|
1951
2375
|
},
|
|
2376
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1952
2377
|
withUsageTracking("get-function", async (args) => {
|
|
1953
2378
|
try {
|
|
1954
|
-
const response = await
|
|
2379
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
|
|
1955
2380
|
method: "GET",
|
|
1956
|
-
headers: {
|
|
1957
|
-
"x-api-key": getApiKey()
|
|
1958
|
-
}
|
|
2381
|
+
headers: { "x-api-key": getApiKey(args.apiKey) }
|
|
1959
2382
|
});
|
|
1960
2383
|
const result = await handleApiResponse(response);
|
|
1961
2384
|
return await addBackgroundContext({
|
|
1962
|
-
content: [
|
|
1963
|
-
{
|
|
1964
|
-
type: "text",
|
|
1965
|
-
text: formatSuccessMessage(`Edge function '${args.slug}' details`, result)
|
|
1966
|
-
}
|
|
1967
|
-
]
|
|
2385
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' details`, result) }]
|
|
1968
2386
|
});
|
|
1969
2387
|
} catch (error) {
|
|
1970
2388
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1971
2389
|
return {
|
|
1972
|
-
content: [
|
|
1973
|
-
{
|
|
1974
|
-
type: "text",
|
|
1975
|
-
text: `Error getting function: ${errMsg}`
|
|
1976
|
-
}
|
|
1977
|
-
],
|
|
2390
|
+
content: [{ type: "text", text: `Error getting function: ${errMsg}` }],
|
|
1978
2391
|
isError: true
|
|
1979
2392
|
};
|
|
1980
2393
|
}
|
|
1981
2394
|
})
|
|
1982
2395
|
);
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2396
|
+
if (isRemote) {
|
|
2397
|
+
registerTool(
|
|
2398
|
+
"update-function",
|
|
2399
|
+
"Update an existing edge function code or metadata",
|
|
2400
|
+
{
|
|
2401
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2402
|
+
slug: functionSchema.shape.slug.describe("The slug identifier of the function to update"),
|
|
2403
|
+
...updateFunctionRequestSchema.omit({ code: true }).shape,
|
|
2404
|
+
code: z28.string().optional().describe(
|
|
2405
|
+
"The new function code as a string. Must export: module.exports = async function(request) { return new Response(...) }"
|
|
2406
|
+
)
|
|
2407
|
+
},
|
|
2408
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2409
|
+
withUsageTracking("update-function", async (args) => {
|
|
2410
|
+
try {
|
|
2411
|
+
const updateData = {};
|
|
2412
|
+
if (args.name) updateData.name = args.name;
|
|
2413
|
+
if (args.code) updateData.code = args.code;
|
|
2414
|
+
if (args.description !== void 0) updateData.description = args.description;
|
|
2415
|
+
if (args.status) updateData.status = args.status;
|
|
2416
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
|
|
2417
|
+
method: "PUT",
|
|
2418
|
+
headers: {
|
|
2419
|
+
"Content-Type": "application/json",
|
|
2420
|
+
"x-api-key": getApiKey(args.apiKey)
|
|
2421
|
+
},
|
|
2422
|
+
body: JSON.stringify(updateData)
|
|
2423
|
+
});
|
|
2424
|
+
const result = await handleApiResponse(response);
|
|
2425
|
+
return await addBackgroundContext({
|
|
2426
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' updated successfully`, result) }]
|
|
2427
|
+
});
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2430
|
+
return {
|
|
2431
|
+
content: [{ type: "text", text: `Error updating function: ${errMsg}` }],
|
|
2432
|
+
isError: true
|
|
2433
|
+
};
|
|
1998
2434
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2435
|
+
})
|
|
2436
|
+
);
|
|
2437
|
+
} else {
|
|
2438
|
+
registerTool(
|
|
2439
|
+
"update-function",
|
|
2440
|
+
"Update an existing edge function code or metadata",
|
|
2441
|
+
{
|
|
2442
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2443
|
+
slug: functionSchema.shape.slug.describe("The slug identifier of the function to update"),
|
|
2444
|
+
...updateFunctionRequestSchema.omit({ code: true }).shape,
|
|
2445
|
+
codeFile: z28.string().optional().describe(
|
|
2446
|
+
"Path to JavaScript file containing the new function code. Must export: module.exports = async function(request) { return new Response(...) }"
|
|
2447
|
+
)
|
|
2448
|
+
},
|
|
2449
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2450
|
+
withUsageTracking("update-function", async (args) => {
|
|
2451
|
+
try {
|
|
2452
|
+
const updateData = {};
|
|
2453
|
+
if (args.name) updateData.name = args.name;
|
|
2454
|
+
if (args.codeFile) {
|
|
2455
|
+
try {
|
|
2456
|
+
updateData.code = await fs2.readFile(args.codeFile, "utf-8");
|
|
2457
|
+
} catch (fileError) {
|
|
2458
|
+
throw new Error(
|
|
2459
|
+
`Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`,
|
|
2460
|
+
{ cause: fileError }
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2006
2463
|
}
|
|
2464
|
+
if (args.description !== void 0) updateData.description = args.description;
|
|
2465
|
+
if (args.status) updateData.status = args.status;
|
|
2466
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
|
|
2467
|
+
method: "PUT",
|
|
2468
|
+
headers: {
|
|
2469
|
+
"Content-Type": "application/json",
|
|
2470
|
+
"x-api-key": getApiKey(args.apiKey)
|
|
2471
|
+
},
|
|
2472
|
+
body: JSON.stringify(updateData)
|
|
2473
|
+
});
|
|
2474
|
+
const result = await handleApiResponse(response);
|
|
2475
|
+
const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
|
|
2476
|
+
return await addBackgroundContext({
|
|
2477
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' updated successfully${fileInfo}`, result) }]
|
|
2478
|
+
});
|
|
2479
|
+
} catch (error) {
|
|
2480
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2481
|
+
return {
|
|
2482
|
+
content: [{ type: "text", text: `Error updating function: ${errMsg}` }],
|
|
2483
|
+
isError: true
|
|
2484
|
+
};
|
|
2007
2485
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
if (args.status) {
|
|
2012
|
-
updateData.status = args.status;
|
|
2013
|
-
}
|
|
2014
|
-
const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
|
|
2015
|
-
method: "PUT",
|
|
2016
|
-
headers: {
|
|
2017
|
-
"Content-Type": "application/json",
|
|
2018
|
-
"x-api-key": getApiKey()
|
|
2019
|
-
},
|
|
2020
|
-
body: JSON.stringify(updateData)
|
|
2021
|
-
});
|
|
2022
|
-
const result = await handleApiResponse(response);
|
|
2023
|
-
const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
|
|
2024
|
-
return await addBackgroundContext({
|
|
2025
|
-
content: [
|
|
2026
|
-
{
|
|
2027
|
-
type: "text",
|
|
2028
|
-
text: formatSuccessMessage(
|
|
2029
|
-
`Edge function '${args.slug}' updated successfully${fileInfo}`,
|
|
2030
|
-
result
|
|
2031
|
-
)
|
|
2032
|
-
}
|
|
2033
|
-
]
|
|
2034
|
-
});
|
|
2035
|
-
} catch (error) {
|
|
2036
|
-
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2037
|
-
return {
|
|
2038
|
-
content: [
|
|
2039
|
-
{
|
|
2040
|
-
type: "text",
|
|
2041
|
-
text: `Error updating function: ${errMsg}`
|
|
2042
|
-
}
|
|
2043
|
-
],
|
|
2044
|
-
isError: true
|
|
2045
|
-
};
|
|
2046
|
-
}
|
|
2047
|
-
})
|
|
2048
|
-
);
|
|
2486
|
+
})
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2049
2489
|
registerTool(
|
|
2050
2490
|
"delete-function",
|
|
2051
2491
|
"Delete an edge function permanently",
|
|
2052
2492
|
{
|
|
2053
|
-
|
|
2493
|
+
apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2494
|
+
slug: functionSchema.shape.slug.describe("The slug identifier of the function to delete")
|
|
2054
2495
|
},
|
|
2496
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2055
2497
|
withUsageTracking("delete-function", async (args) => {
|
|
2056
2498
|
try {
|
|
2057
|
-
const response = await
|
|
2499
|
+
const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
|
|
2058
2500
|
method: "DELETE",
|
|
2059
|
-
headers: {
|
|
2060
|
-
"x-api-key": getApiKey()
|
|
2061
|
-
}
|
|
2501
|
+
headers: { "x-api-key": getApiKey(args.apiKey) }
|
|
2062
2502
|
});
|
|
2063
2503
|
const result = await handleApiResponse(response);
|
|
2064
2504
|
return await addBackgroundContext({
|
|
2065
|
-
content: [
|
|
2066
|
-
{
|
|
2067
|
-
type: "text",
|
|
2068
|
-
text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result)
|
|
2069
|
-
}
|
|
2070
|
-
]
|
|
2505
|
+
content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result) }]
|
|
2071
2506
|
});
|
|
2072
2507
|
} catch (error) {
|
|
2073
2508
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2074
2509
|
return {
|
|
2075
|
-
content: [
|
|
2076
|
-
{
|
|
2077
|
-
type: "text",
|
|
2078
|
-
text: `Error deleting function: ${errMsg}`
|
|
2079
|
-
}
|
|
2080
|
-
],
|
|
2510
|
+
content: [{ type: "text", text: `Error deleting function: ${errMsg}` }],
|
|
2081
2511
|
isError: true
|
|
2082
2512
|
};
|
|
2083
2513
|
}
|
|
2084
2514
|
})
|
|
2085
2515
|
);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
// src/shared/tools/deployment.ts
|
|
2519
|
+
import { z as z29 } from "zod";
|
|
2520
|
+
import fetch6 from "node-fetch";
|
|
2521
|
+
import { promises as fs3, createWriteStream, createReadStream } from "fs";
|
|
2522
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
2523
|
+
import { join as join2 } from "path";
|
|
2524
|
+
import archiver from "archiver";
|
|
2525
|
+
import FormData2 from "form-data";
|
|
2526
|
+
function isCreateDeploymentResponse(obj) {
|
|
2527
|
+
if (typeof obj !== "object" || obj === null) {
|
|
2528
|
+
return false;
|
|
2529
|
+
}
|
|
2530
|
+
const value = obj;
|
|
2531
|
+
const idOk = "id" in value && (typeof value.id === "string" || typeof value.id === "number");
|
|
2532
|
+
const urlOk = "uploadUrl" in value && typeof value.uploadUrl === "string" && value.uploadUrl.length > 0;
|
|
2533
|
+
const fieldsOk = "uploadFields" in value && typeof value.uploadFields === "object" && value.uploadFields !== null;
|
|
2534
|
+
return idOk && urlOk && fieldsOk;
|
|
2535
|
+
}
|
|
2536
|
+
function registerDeploymentTools(ctx) {
|
|
2537
|
+
const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
|
|
2086
2538
|
registerTool(
|
|
2087
2539
|
"get-container-logs",
|
|
2088
2540
|
"Get latest logs from a specific container/service. Use this to help debug problems with your app.",
|
|
2089
2541
|
{
|
|
2090
|
-
apiKey:
|
|
2091
|
-
source:
|
|
2092
|
-
limit:
|
|
2542
|
+
apiKey: z29.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2543
|
+
source: z29.enum(["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs"]).describe("Log source to retrieve"),
|
|
2544
|
+
limit: z29.number().optional().default(20).describe("Number of logs to return (default: 20)")
|
|
2093
2545
|
},
|
|
2094
2546
|
withUsageTracking("get-container-logs", async ({ apiKey, source, limit }) => {
|
|
2095
2547
|
try {
|
|
2096
2548
|
const actualApiKey = getApiKey(apiKey);
|
|
2097
2549
|
const queryParams = new URLSearchParams();
|
|
2098
2550
|
if (limit) queryParams.append("limit", limit.toString());
|
|
2099
|
-
let response = await
|
|
2551
|
+
let response = await fetch6(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
|
|
2100
2552
|
method: "GET",
|
|
2101
|
-
headers: {
|
|
2102
|
-
"x-api-key": actualApiKey
|
|
2103
|
-
}
|
|
2553
|
+
headers: { "x-api-key": actualApiKey }
|
|
2104
2554
|
});
|
|
2105
2555
|
if (response.status === 404) {
|
|
2106
|
-
response = await
|
|
2556
|
+
response = await fetch6(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
|
|
2107
2557
|
method: "GET",
|
|
2108
|
-
headers: {
|
|
2109
|
-
"x-api-key": actualApiKey
|
|
2110
|
-
}
|
|
2558
|
+
headers: { "x-api-key": actualApiKey }
|
|
2111
2559
|
});
|
|
2112
2560
|
}
|
|
2113
2561
|
const result = await handleApiResponse(response);
|
|
2114
2562
|
return await addBackgroundContext({
|
|
2115
|
-
content: [
|
|
2116
|
-
{
|
|
2117
|
-
type: "text",
|
|
2118
|
-
text: formatSuccessMessage(`Latest logs from ${source}`, result)
|
|
2119
|
-
}
|
|
2120
|
-
]
|
|
2563
|
+
content: [{ type: "text", text: formatSuccessMessage(`Latest logs from ${source}`, result) }]
|
|
2121
2564
|
});
|
|
2122
2565
|
} catch (error) {
|
|
2123
2566
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2124
2567
|
return {
|
|
2125
|
-
content: [
|
|
2126
|
-
{
|
|
2127
|
-
type: "text",
|
|
2128
|
-
text: `Error retrieving container logs: ${errMsg}`
|
|
2129
|
-
}
|
|
2130
|
-
],
|
|
2568
|
+
content: [{ type: "text", text: `Error retrieving container logs: ${errMsg}` }],
|
|
2131
2569
|
isError: true
|
|
2132
2570
|
};
|
|
2133
2571
|
}
|
|
2134
2572
|
})
|
|
2135
2573
|
);
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
try {
|
|
2145
|
-
const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
|
|
2146
|
-
if (!isAbsolutePath) {
|
|
2147
|
-
return {
|
|
2148
|
-
content: [
|
|
2149
|
-
{
|
|
2150
|
-
type: "text",
|
|
2151
|
-
text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
|
|
2152
|
-
}
|
|
2153
|
-
],
|
|
2154
|
-
isError: true
|
|
2155
|
-
};
|
|
2156
|
-
}
|
|
2574
|
+
if (isRemote) {
|
|
2575
|
+
registerTool(
|
|
2576
|
+
"create-deployment",
|
|
2577
|
+
"Prepare a deployment by creating a presigned upload URL. Returns shell commands for the agent to execute locally: zip the source directory and upload to cloud storage. After uploading, call the start-deployment tool to trigger the build.",
|
|
2578
|
+
{
|
|
2579
|
+
sourceDirectory: z29.string().describe('Absolute path to the source directory containing files to deploy (e.g., /Users/name/project). Do not use relative paths like "."')
|
|
2580
|
+
},
|
|
2581
|
+
withUsageTracking("create-deployment", async ({ sourceDirectory }) => {
|
|
2157
2582
|
try {
|
|
2158
|
-
const
|
|
2159
|
-
if (!
|
|
2583
|
+
const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
|
|
2584
|
+
if (!isAbsolutePath) {
|
|
2160
2585
|
return {
|
|
2161
|
-
content: [
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
}
|
|
2166
|
-
],
|
|
2586
|
+
content: [{
|
|
2587
|
+
type: "text",
|
|
2588
|
+
text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
|
|
2589
|
+
}],
|
|
2167
2590
|
isError: true
|
|
2168
2591
|
};
|
|
2169
2592
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
text: `Error: Directory "${sourceDirectory}" does not exist or is not accessible. Please verify the path is correct.`
|
|
2176
|
-
}
|
|
2177
|
-
],
|
|
2178
|
-
isError: true
|
|
2179
|
-
};
|
|
2180
|
-
}
|
|
2181
|
-
const resolvedSourceDir = sourceDirectory;
|
|
2182
|
-
const createResponse = await fetch2(`${API_BASE_URL}/api/deployments`, {
|
|
2183
|
-
method: "POST",
|
|
2184
|
-
headers: {
|
|
2185
|
-
"x-api-key": getApiKey(),
|
|
2186
|
-
"Content-Type": "application/json"
|
|
2187
|
-
}
|
|
2188
|
-
});
|
|
2189
|
-
const createResult = await handleApiResponse(createResponse);
|
|
2190
|
-
const { id: deploymentId, uploadUrl, uploadFields } = createResult;
|
|
2191
|
-
const zipBuffer = await new Promise((resolve, reject) => {
|
|
2192
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
2193
|
-
const chunks = [];
|
|
2194
|
-
archive.on("data", (chunk) => chunks.push(chunk));
|
|
2195
|
-
archive.on("end", () => resolve(Buffer.concat(chunks)));
|
|
2196
|
-
archive.on("error", (err) => reject(err));
|
|
2197
|
-
const excludePatterns = [
|
|
2198
|
-
"node_modules",
|
|
2199
|
-
".git",
|
|
2200
|
-
".next",
|
|
2201
|
-
".env",
|
|
2202
|
-
".env.local",
|
|
2203
|
-
"dist",
|
|
2204
|
-
"build",
|
|
2205
|
-
".DS_Store"
|
|
2206
|
-
];
|
|
2207
|
-
archive.directory(resolvedSourceDir, false, (entry) => {
|
|
2208
|
-
const normalizedName = entry.name.replace(/\\/g, "/");
|
|
2209
|
-
for (const pattern of excludePatterns) {
|
|
2210
|
-
if (normalizedName.startsWith(pattern + "/") || normalizedName === pattern || normalizedName.endsWith("/" + pattern) || normalizedName.includes("/" + pattern + "/")) {
|
|
2211
|
-
return false;
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
if (normalizedName.endsWith(".log")) {
|
|
2215
|
-
return false;
|
|
2593
|
+
const createResponse = await fetch6(`${API_BASE_URL}/api/deployments`, {
|
|
2594
|
+
method: "POST",
|
|
2595
|
+
headers: {
|
|
2596
|
+
"x-api-key": getApiKey(),
|
|
2597
|
+
"Content-Type": "application/json"
|
|
2216
2598
|
}
|
|
2217
|
-
return entry;
|
|
2218
2599
|
});
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
const uploadResponse = await fetch2(uploadUrl, {
|
|
2230
|
-
method: "POST",
|
|
2231
|
-
body: uploadFormData,
|
|
2232
|
-
headers: uploadFormData.getHeaders()
|
|
2233
|
-
});
|
|
2234
|
-
if (!uploadResponse.ok) {
|
|
2235
|
-
const uploadError = await uploadResponse.text();
|
|
2236
|
-
throw new Error(`Failed to upload zip file: ${uploadError}`);
|
|
2237
|
-
}
|
|
2238
|
-
const startBody = {};
|
|
2239
|
-
if (projectSettings) startBody.projectSettings = projectSettings;
|
|
2240
|
-
if (envVars) startBody.envVars = envVars;
|
|
2241
|
-
if (meta) startBody.meta = meta;
|
|
2242
|
-
const startResponse = await fetch2(`${API_BASE_URL}/api/deployments/${deploymentId}/start`, {
|
|
2243
|
-
method: "POST",
|
|
2244
|
-
headers: {
|
|
2245
|
-
"x-api-key": getApiKey(),
|
|
2246
|
-
"Content-Type": "application/json"
|
|
2247
|
-
},
|
|
2248
|
-
body: JSON.stringify(startBody)
|
|
2249
|
-
});
|
|
2250
|
-
const startResult = await handleApiResponse(startResponse);
|
|
2251
|
-
return await addBackgroundContext({
|
|
2252
|
-
content: [
|
|
2253
|
-
{
|
|
2254
|
-
type: "text",
|
|
2255
|
-
text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
|
|
2256
|
-
}
|
|
2257
|
-
]
|
|
2258
|
-
});
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2261
|
-
return {
|
|
2262
|
-
content: [
|
|
2263
|
-
{
|
|
2264
|
-
type: "text",
|
|
2265
|
-
text: `Error creating deployment: ${errMsg}`
|
|
2266
|
-
}
|
|
2267
|
-
],
|
|
2268
|
-
isError: true
|
|
2269
|
-
};
|
|
2270
|
-
}
|
|
2271
|
-
})
|
|
2272
|
-
);
|
|
2273
|
-
registerTool(
|
|
2274
|
-
"store-conversation",
|
|
2275
|
-
`IMPORTANT: Call this tool automatically when a task or conversation is complete to save it for future reference.
|
|
2600
|
+
const createResult = await handleApiResponse(createResponse);
|
|
2601
|
+
if (!isCreateDeploymentResponse(createResult)) {
|
|
2602
|
+
throw new Error("Unexpected response format from deployments endpoint");
|
|
2603
|
+
}
|
|
2604
|
+
const { id: deploymentId, uploadUrl, uploadFields } = createResult;
|
|
2605
|
+
const esc = shellEsc;
|
|
2606
|
+
const curlFields = Object.entries(uploadFields).map(([key, value]) => `-F ${esc(`${key}=${value}`)}`).join(" \\\n ");
|
|
2607
|
+
const escapedDir = esc(sourceDirectory);
|
|
2608
|
+
const tmpZip = `/tmp/insforge-deploy-${deploymentId}.zip`;
|
|
2609
|
+
const instructions = `Deployment prepared successfully. Deployment ID: ${deploymentId}
|
|
2276
2610
|
|
|
2277
|
-
|
|
2611
|
+
Please execute the following commands locally, then call the \`start-deployment\` tool:
|
|
2278
2612
|
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2613
|
+
## Step 1: Zip the source directory
|
|
2614
|
+
\`\`\`bash
|
|
2615
|
+
cd ${escapedDir} && zip -r ${tmpZip} . -x "node_modules/*" ".git/*" ".next/*" ".env" ".env.local" "dist/*" "build/*" ".DS_Store" "*.log"
|
|
2616
|
+
\`\`\`
|
|
2617
|
+
|
|
2618
|
+
## Step 2: Upload the zip file
|
|
2619
|
+
\`\`\`bash
|
|
2620
|
+
curl -X POST ${esc(uploadUrl)} ${curlFields} -F 'file=@${tmpZip};type=application/zip'
|
|
2621
|
+
\`\`\`
|
|
2622
|
+
|
|
2623
|
+
## Step 3: Clean up
|
|
2624
|
+
\`\`\`bash
|
|
2625
|
+
rm /tmp/insforge-deploy-${deploymentId}.zip
|
|
2626
|
+
\`\`\`
|
|
2627
|
+
|
|
2628
|
+
## Step 4: Trigger the build
|
|
2629
|
+
Call the \`start-deployment\` tool with deploymentId: "${deploymentId}"
|
|
2630
|
+
|
|
2631
|
+
Run each step in order. If any step fails, do not proceed to the next step.`;
|
|
2632
|
+
return {
|
|
2633
|
+
content: [{ type: "text", text: instructions }]
|
|
2634
|
+
};
|
|
2635
|
+
} catch (error) {
|
|
2636
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2637
|
+
return {
|
|
2638
|
+
content: [{ type: "text", text: `Error preparing deployment: ${errMsg}` }],
|
|
2639
|
+
isError: true
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
})
|
|
2643
|
+
);
|
|
2644
|
+
registerTool(
|
|
2645
|
+
"start-deployment",
|
|
2646
|
+
"Trigger a deployment build after uploading source code. Use this after executing the upload commands from create-deployment.",
|
|
2647
|
+
{
|
|
2648
|
+
deploymentId: z29.string().describe("The deployment ID returned by create-deployment"),
|
|
2649
|
+
...startDeploymentRequestSchema.shape
|
|
2650
|
+
},
|
|
2651
|
+
withUsageTracking("start-deployment", async ({ deploymentId, projectSettings, envVars, meta }) => {
|
|
2296
2652
|
try {
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2653
|
+
const startBody = {};
|
|
2654
|
+
if (projectSettings) startBody.projectSettings = projectSettings;
|
|
2655
|
+
if (envVars) startBody.envVars = envVars;
|
|
2656
|
+
if (meta) startBody.meta = meta;
|
|
2657
|
+
const startResponse = await fetch6(`${API_BASE_URL}/api/deployments/${encodeURIComponent(deploymentId)}/start`, {
|
|
2299
2658
|
method: "POST",
|
|
2300
2659
|
headers: {
|
|
2301
|
-
"x-api-key":
|
|
2660
|
+
"x-api-key": getApiKey(),
|
|
2302
2661
|
"Content-Type": "application/json"
|
|
2303
2662
|
},
|
|
2304
|
-
body: JSON.stringify(
|
|
2663
|
+
body: JSON.stringify(startBody)
|
|
2305
2664
|
});
|
|
2306
|
-
const
|
|
2665
|
+
const startResult = await handleApiResponse(startResponse);
|
|
2307
2666
|
return await addBackgroundContext({
|
|
2308
|
-
content: [
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
}
|
|
2313
|
-
]
|
|
2667
|
+
content: [{
|
|
2668
|
+
type: "text",
|
|
2669
|
+
text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
|
|
2670
|
+
}]
|
|
2314
2671
|
});
|
|
2315
2672
|
} catch (error) {
|
|
2316
2673
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2317
2674
|
return {
|
|
2318
|
-
content: [
|
|
2319
|
-
{
|
|
2320
|
-
type: "text",
|
|
2321
|
-
text: `Error storing conversation: ${errMsg}`
|
|
2322
|
-
}
|
|
2323
|
-
],
|
|
2675
|
+
content: [{ type: "text", text: `Error starting deployment: ${errMsg}` }],
|
|
2324
2676
|
isError: true
|
|
2325
2677
|
};
|
|
2326
2678
|
}
|
|
2327
|
-
}
|
|
2328
|
-
)
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
|
|
2339
|
-
query: z23.string().describe("Search query to find similar conversations"),
|
|
2340
|
-
limit: z23.number().int().positive().max(100).optional().default(10).describe("Maximum number of results to return (default: 10)"),
|
|
2341
|
-
threshold: z23.number().min(0).max(1).optional().default(0).describe("Minimum similarity threshold 0-1 (default: 0)"),
|
|
2342
|
-
metadataFilter: z23.record(z23.unknown()).optional().describe("Filter conversations by metadata (JSONB containment)"),
|
|
2343
|
-
embeddingModel: z23.string().optional().describe("Embedding model for the query (uses project default if not specified)")
|
|
2344
|
-
},
|
|
2345
|
-
withUsageTracking(
|
|
2346
|
-
"search-conversations",
|
|
2347
|
-
async ({ apiKey, query, limit, threshold, metadataFilter, embeddingModel }) => {
|
|
2679
|
+
})
|
|
2680
|
+
);
|
|
2681
|
+
} else {
|
|
2682
|
+
registerTool(
|
|
2683
|
+
"create-deployment",
|
|
2684
|
+
"Deploy source code from a directory. This tool zips files, uploads to cloud storage, and triggers deployment with optional environment variables and project settings.",
|
|
2685
|
+
{
|
|
2686
|
+
sourceDirectory: z29.string().describe('Absolute path to the source directory containing files to deploy (e.g., /Users/name/project or C:\\Users\\name\\project). Do not use relative paths like "."'),
|
|
2687
|
+
...startDeploymentRequestSchema.shape
|
|
2688
|
+
},
|
|
2689
|
+
withUsageTracking("create-deployment", async ({ sourceDirectory, projectSettings, envVars, meta }) => {
|
|
2348
2690
|
try {
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2691
|
+
const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
|
|
2692
|
+
if (!isAbsolutePath) {
|
|
2693
|
+
return {
|
|
2694
|
+
content: [{
|
|
2695
|
+
type: "text",
|
|
2696
|
+
text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
|
|
2697
|
+
}],
|
|
2698
|
+
isError: true
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
try {
|
|
2702
|
+
const stats = await fs3.stat(sourceDirectory);
|
|
2703
|
+
if (!stats.isDirectory()) {
|
|
2704
|
+
return {
|
|
2705
|
+
content: [{
|
|
2706
|
+
type: "text",
|
|
2707
|
+
text: `Error: "${sourceDirectory}" is not a directory. Please provide a path to a directory containing the source code.`
|
|
2708
|
+
}],
|
|
2709
|
+
isError: true
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
} catch {
|
|
2713
|
+
return {
|
|
2714
|
+
content: [{
|
|
2715
|
+
type: "text",
|
|
2716
|
+
text: `Error: Directory "${sourceDirectory}" does not exist or is not accessible. Please verify the path is correct.`
|
|
2717
|
+
}],
|
|
2718
|
+
isError: true
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
const resolvedSourceDir = sourceDirectory;
|
|
2722
|
+
const createResponse = await fetch6(`${API_BASE_URL}/api/deployments`, {
|
|
2723
|
+
method: "POST",
|
|
2724
|
+
headers: {
|
|
2725
|
+
"x-api-key": getApiKey(),
|
|
2726
|
+
"Content-Type": "application/json"
|
|
2727
|
+
}
|
|
2728
|
+
});
|
|
2729
|
+
const createResult = await handleApiResponse(createResponse);
|
|
2730
|
+
if (!isCreateDeploymentResponse(createResult)) {
|
|
2731
|
+
throw new Error("Unexpected response format from deployments endpoint");
|
|
2732
|
+
}
|
|
2733
|
+
const { id: deploymentId, uploadUrl, uploadFields } = createResult;
|
|
2734
|
+
const tmpZipPath = join2(tmpdir2(), `insforge-deploy-${deploymentId}.zip`);
|
|
2735
|
+
try {
|
|
2736
|
+
await new Promise((resolve, reject) => {
|
|
2737
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
2738
|
+
const output = createWriteStream(tmpZipPath);
|
|
2739
|
+
output.on("close", resolve);
|
|
2740
|
+
output.on("error", reject);
|
|
2741
|
+
archive.on("error", reject);
|
|
2742
|
+
const excludePatterns = ["node_modules", ".git", ".next", ".env", ".env.local", "dist", "build", ".DS_Store"];
|
|
2743
|
+
archive.directory(resolvedSourceDir, false, (entry) => {
|
|
2744
|
+
const normalizedName = entry.name.replace(/\\/g, "/");
|
|
2745
|
+
for (const pattern of excludePatterns) {
|
|
2746
|
+
if (normalizedName.startsWith(pattern + "/") || normalizedName === pattern || normalizedName.endsWith("/" + pattern) || normalizedName.includes("/" + pattern + "/")) {
|
|
2747
|
+
return false;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
if (normalizedName.endsWith(".log")) return false;
|
|
2751
|
+
return entry;
|
|
2752
|
+
});
|
|
2753
|
+
archive.pipe(output);
|
|
2754
|
+
archive.finalize();
|
|
2755
|
+
});
|
|
2756
|
+
const { size: zipSize } = await fs3.stat(tmpZipPath);
|
|
2757
|
+
const uploadFormData = new FormData2();
|
|
2758
|
+
for (const [key, value] of Object.entries(uploadFields)) {
|
|
2759
|
+
uploadFormData.append(key, value);
|
|
2760
|
+
}
|
|
2761
|
+
uploadFormData.append("file", createReadStream(tmpZipPath), {
|
|
2762
|
+
filename: "deployment.zip",
|
|
2763
|
+
contentType: "application/zip",
|
|
2764
|
+
knownLength: zipSize
|
|
2765
|
+
});
|
|
2766
|
+
const uploadResponse = await fetch6(uploadUrl, {
|
|
2767
|
+
method: "POST",
|
|
2768
|
+
body: uploadFormData,
|
|
2769
|
+
headers: uploadFormData.getHeaders()
|
|
2770
|
+
});
|
|
2771
|
+
if (!uploadResponse.ok) {
|
|
2772
|
+
const uploadError = await uploadResponse.text();
|
|
2773
|
+
throw new Error(`Failed to upload zip file: ${uploadError}`);
|
|
2774
|
+
}
|
|
2775
|
+
} finally {
|
|
2776
|
+
await fs3.rm(tmpZipPath, { force: true }).catch(() => void 0);
|
|
2777
|
+
}
|
|
2778
|
+
const startBody = {};
|
|
2779
|
+
if (projectSettings) startBody.projectSettings = projectSettings;
|
|
2780
|
+
if (envVars) startBody.envVars = envVars;
|
|
2781
|
+
if (meta) startBody.meta = meta;
|
|
2782
|
+
const startResponse = await fetch6(`${API_BASE_URL}/api/deployments/${encodeURIComponent(deploymentId)}/start`, {
|
|
2351
2783
|
method: "POST",
|
|
2352
2784
|
headers: {
|
|
2353
|
-
"x-api-key":
|
|
2785
|
+
"x-api-key": getApiKey(),
|
|
2354
2786
|
"Content-Type": "application/json"
|
|
2355
2787
|
},
|
|
2356
|
-
body: JSON.stringify(
|
|
2788
|
+
body: JSON.stringify(startBody)
|
|
2357
2789
|
});
|
|
2358
|
-
const
|
|
2790
|
+
const startResult = await handleApiResponse(startResponse);
|
|
2359
2791
|
return await addBackgroundContext({
|
|
2360
|
-
content: [
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
}
|
|
2365
|
-
]
|
|
2792
|
+
content: [{
|
|
2793
|
+
type: "text",
|
|
2794
|
+
text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
|
|
2795
|
+
}]
|
|
2366
2796
|
});
|
|
2367
2797
|
} catch (error) {
|
|
2368
2798
|
const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2369
2799
|
return {
|
|
2370
|
-
content: [
|
|
2371
|
-
{
|
|
2372
|
-
type: "text",
|
|
2373
|
-
text: `Error searching conversations: ${errMsg}`
|
|
2374
|
-
}
|
|
2375
|
-
],
|
|
2800
|
+
content: [{ type: "text", text: `Error creating deployment: ${errMsg}` }],
|
|
2376
2801
|
isError: true
|
|
2377
2802
|
};
|
|
2378
2803
|
}
|
|
2804
|
+
})
|
|
2805
|
+
);
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
// src/shared/tools/index.ts
|
|
2810
|
+
var TOOL_VERSION_REQUIREMENTS = {
|
|
2811
|
+
// Schedule tools - require backend v1.1.1+
|
|
2812
|
+
// 'upsert-schedule': { minVersion: '1.1.1' },
|
|
2813
|
+
// 'delete-schedule': { minVersion: '1.1.1' },
|
|
2814
|
+
// 'get-schedules': { minVersion: '1.1.1' },
|
|
2815
|
+
// 'get-schedule-logs': { minVersion: '1.1.1' },
|
|
2816
|
+
"create-deployment": { minVersion: "1.4.7" },
|
|
2817
|
+
"fetch-sdk-docs": { minVersion: "1.5.1" }
|
|
2818
|
+
// Example of a deprecated tool (uncomment when needed):
|
|
2819
|
+
// 'legacy-tool': { minVersion: '1.0.0', maxVersion: '1.5.0' },
|
|
2820
|
+
};
|
|
2821
|
+
var LOCAL_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
2822
|
+
"bulk-upsert"
|
|
2823
|
+
// Requires reading local data file (filePath is required)
|
|
2824
|
+
]);
|
|
2825
|
+
function compareVersions(v1, v2) {
|
|
2826
|
+
const clean1 = v1.replace(/^v/, "").split("-")[0];
|
|
2827
|
+
const clean2 = v2.replace(/^v/, "").split("-")[0];
|
|
2828
|
+
const parts1 = clean1.split(".").map(Number);
|
|
2829
|
+
const parts2 = clean2.split(".").map(Number);
|
|
2830
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
2831
|
+
const part1 = parts1[i] || 0;
|
|
2832
|
+
const part2 = parts2[i] || 0;
|
|
2833
|
+
if (part1 > part2) return 1;
|
|
2834
|
+
if (part1 < part2) return -1;
|
|
2835
|
+
}
|
|
2836
|
+
return 0;
|
|
2837
|
+
}
|
|
2838
|
+
function shouldRegisterTool(toolName, backendVersion) {
|
|
2839
|
+
const requirement = TOOL_VERSION_REQUIREMENTS[toolName];
|
|
2840
|
+
if (!requirement) return true;
|
|
2841
|
+
const { minVersion, maxVersion } = requirement;
|
|
2842
|
+
if (minVersion && compareVersions(backendVersion, minVersion) < 0) return false;
|
|
2843
|
+
if (maxVersion && compareVersions(backendVersion, maxVersion) > 0) return false;
|
|
2844
|
+
return true;
|
|
2845
|
+
}
|
|
2846
|
+
async function fetchBackendVersion(apiBaseUrl) {
|
|
2847
|
+
const controller = new AbortController();
|
|
2848
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
2849
|
+
try {
|
|
2850
|
+
const response = await fetch7(`${apiBaseUrl}/api/health`, {
|
|
2851
|
+
method: "GET",
|
|
2852
|
+
headers: { "Content-Type": "application/json" },
|
|
2853
|
+
signal: controller.signal
|
|
2854
|
+
});
|
|
2855
|
+
if (!response.ok) {
|
|
2856
|
+
throw new Error(`Health check failed with status ${response.status}`);
|
|
2857
|
+
}
|
|
2858
|
+
const health = await response.json();
|
|
2859
|
+
if (!health.version || typeof health.version !== "string") {
|
|
2860
|
+
throw new Error("Health check returned invalid version field");
|
|
2861
|
+
}
|
|
2862
|
+
return health.version;
|
|
2863
|
+
} catch (error) {
|
|
2864
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2865
|
+
throw new Error(`Health check timed out after 10s \u2014 is the backend running at ${apiBaseUrl}?`, { cause: error });
|
|
2866
|
+
}
|
|
2867
|
+
throw error;
|
|
2868
|
+
} finally {
|
|
2869
|
+
clearTimeout(timeoutId);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
async function registerInsforgeTools(server, config = {}) {
|
|
2873
|
+
const GLOBAL_API_KEY = config.apiKey || process.env.API_KEY || "";
|
|
2874
|
+
const API_BASE_URL = config.apiBaseUrl || process.env.API_BASE_URL || "http://localhost:7130";
|
|
2875
|
+
const isRemote = config.mode === "remote";
|
|
2876
|
+
const usageTracker = new UsageTracker(API_BASE_URL, GLOBAL_API_KEY);
|
|
2877
|
+
let backendVersion;
|
|
2878
|
+
try {
|
|
2879
|
+
backendVersion = await fetchBackendVersion(API_BASE_URL);
|
|
2880
|
+
console.error(`Backend version: ${backendVersion}`);
|
|
2881
|
+
} catch (error) {
|
|
2882
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2883
|
+
console.error(`Failed to fetch backend version: ${msg}`);
|
|
2884
|
+
throw new Error(`Cannot initialize tools: backend at ${API_BASE_URL} is unreachable. ${msg}`, { cause: error });
|
|
2885
|
+
}
|
|
2886
|
+
let toolCount = 0;
|
|
2887
|
+
const registerTool = (toolName, ...args) => {
|
|
2888
|
+
if (isRemote && LOCAL_ONLY_TOOLS.has(toolName)) {
|
|
2889
|
+
console.error(`Skipping tool '${toolName}': requires local filesystem (remote mode)`);
|
|
2890
|
+
return false;
|
|
2891
|
+
}
|
|
2892
|
+
if (shouldRegisterTool(toolName, backendVersion)) {
|
|
2893
|
+
server.tool(toolName, ...args);
|
|
2894
|
+
toolCount++;
|
|
2895
|
+
return true;
|
|
2896
|
+
} else {
|
|
2897
|
+
const req = TOOL_VERSION_REQUIREMENTS[toolName];
|
|
2898
|
+
const reason = req?.minVersion && compareVersions(backendVersion, req.minVersion) < 0 ? `requires backend >= ${req.minVersion}` : `deprecated after backend ${req?.maxVersion}`;
|
|
2899
|
+
console.error(`Skipping tool '${toolName}': ${reason} (current: ${backendVersion})`);
|
|
2900
|
+
return false;
|
|
2901
|
+
}
|
|
2902
|
+
};
|
|
2903
|
+
async function trackToolUsage(toolName, success = true) {
|
|
2904
|
+
if (GLOBAL_API_KEY) {
|
|
2905
|
+
await usageTracker.trackUsage(toolName, success).catch((err) => {
|
|
2906
|
+
console.error(`Failed to track usage for '${toolName}':`, err);
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
function withUsageTracking(toolName, handler) {
|
|
2911
|
+
return async (...args) => {
|
|
2912
|
+
try {
|
|
2913
|
+
const result = await handler(...args);
|
|
2914
|
+
const isStructuredError = result !== null && typeof result === "object" && "isError" in result && result["isError"] === true;
|
|
2915
|
+
void trackToolUsage(toolName, !isStructuredError);
|
|
2916
|
+
return result;
|
|
2917
|
+
} catch (error) {
|
|
2918
|
+
void trackToolUsage(toolName, false);
|
|
2919
|
+
throw error;
|
|
2920
|
+
}
|
|
2921
|
+
};
|
|
2922
|
+
}
|
|
2923
|
+
const getApiKey = (toolApiKey) => {
|
|
2924
|
+
const apiKey = toolApiKey?.trim() || GLOBAL_API_KEY;
|
|
2925
|
+
if (!apiKey) {
|
|
2926
|
+
throw new Error("API key is required. Pass --api_key when starting the MCP server.");
|
|
2927
|
+
}
|
|
2928
|
+
return apiKey;
|
|
2929
|
+
};
|
|
2930
|
+
const addBackgroundContext = async (response) => {
|
|
2931
|
+
const isLegacyVersion = compareVersions(backendVersion, "1.1.7") < 0;
|
|
2932
|
+
if (isLegacyVersion) {
|
|
2933
|
+
try {
|
|
2934
|
+
const docResponse = await fetch7(`${API_BASE_URL}/api/docs/instructions`, {
|
|
2935
|
+
method: "GET",
|
|
2936
|
+
headers: { "Content-Type": "application/json" }
|
|
2937
|
+
});
|
|
2938
|
+
if (docResponse.ok) {
|
|
2939
|
+
const result = await handleApiResponse(docResponse);
|
|
2940
|
+
if (result && typeof result === "object" && "content" in result) {
|
|
2941
|
+
response.content.push({
|
|
2942
|
+
type: "text",
|
|
2943
|
+
text: `
|
|
2944
|
+
|
|
2945
|
+
---
|
|
2946
|
+
\u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
|
|
2947
|
+
${result.content}`
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
console.error("Failed to fetch insforge-instructions.md:", error);
|
|
2379
2953
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2954
|
+
}
|
|
2955
|
+
return response;
|
|
2956
|
+
};
|
|
2957
|
+
const ctx = {
|
|
2958
|
+
API_BASE_URL,
|
|
2959
|
+
isRemote,
|
|
2960
|
+
registerTool,
|
|
2961
|
+
withUsageTracking,
|
|
2962
|
+
getApiKey,
|
|
2963
|
+
addBackgroundContext
|
|
2964
|
+
};
|
|
2965
|
+
registerDocsTools(ctx);
|
|
2966
|
+
registerDatabaseTools(ctx);
|
|
2967
|
+
registerStorageTools(ctx);
|
|
2968
|
+
registerFunctionTools(ctx);
|
|
2969
|
+
registerDeploymentTools(ctx);
|
|
2382
2970
|
return {
|
|
2383
2971
|
apiKey: GLOBAL_API_KEY,
|
|
2384
2972
|
apiBaseUrl: API_BASE_URL,
|
|
2385
|
-
|
|
2386
|
-
|
|
2973
|
+
backendVersion,
|
|
2974
|
+
toolCount
|
|
2387
2975
|
};
|
|
2388
2976
|
}
|
|
2389
2977
|
|