@ganju/utils 0.0.6 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attachment.d.ts.map +1 -1
- package/dist/chunking.d.ts.map +1 -1
- package/dist/chunking.js +9 -3
- package/dist/coerce.d.ts.map +1 -1
- package/dist/coerce.js +4 -1
- package/dist/constants.d.ts +27 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +157 -22
- package/dist/containmentToken.d.ts +25 -0
- package/dist/containmentToken.d.ts.map +1 -0
- package/dist/containmentToken.js +81 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +4 -1
- package/dist/getEnv.d.ts.map +1 -1
- package/dist/index.d.ts +34 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/llmBaseUrl.d.ts +39 -0
- package/dist/llmBaseUrl.d.ts.map +1 -0
- package/dist/llmBaseUrl.js +65 -0
- package/dist/localizeZodIssue.d.ts.map +1 -1
- package/dist/localizeZodIssue.js +6 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/parseHttpError.d.ts.map +1 -1
- package/dist/schema.d.ts +2 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +32 -8
- package/dist/sources.d.ts.map +1 -1
- package/dist/vendorError.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/attachment.ts +2 -1
- package/src/chunking.ts +9 -3
- package/src/coerce.ts +4 -1
- package/src/constants.ts +178 -22
- package/src/containmentToken.ts +146 -0
- package/src/crypto.ts +6 -2
- package/src/getEnv.ts +1 -4
- package/src/index.ts +16 -0
- package/src/llmBaseUrl.ts +65 -0
- package/src/localizeZodIssue.ts +6 -0
- package/src/oauth.ts +1 -3
- package/src/parseHttpError.ts +3 -1
- package/src/schema.ts +63 -11
- package/src/slackSend.ts +10 -10
- package/src/sources.ts +7 -2
- package/src/vendorError.ts +4 -1
package/src/schema.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
import { constants } from './constants';
|
|
4
|
+
import { isAllowedLlmBaseUrl, requiresLlmBaseUrl } from './llmBaseUrl';
|
|
4
5
|
import { isReservedSlug, isValidSlugFormat } from './slug';
|
|
5
6
|
import { isReservedToolName } from './reservedToolName';
|
|
6
7
|
import { slugifyTitle } from './slugifyTitle';
|
|
@@ -572,11 +573,33 @@ const ORGANIZATION_LIST_LLM = z.object({
|
|
|
572
573
|
organizationId: z.uuid()
|
|
573
574
|
});
|
|
574
575
|
|
|
575
|
-
|
|
576
|
+
// The two rules live in `llmBaseUrl.ts`, because the update controller applies
|
|
577
|
+
// them again to the merged row — the only place both halves are visible. Here
|
|
578
|
+
// they are just the field validator and the cross-field check.
|
|
579
|
+
const LLM_BASE_URL = z
|
|
580
|
+
.url()
|
|
581
|
+
.max(500)
|
|
582
|
+
.refine(isAllowedLlmBaseUrl, constants.LLM_BASE_URL_INVALID_MESSAGE);
|
|
583
|
+
|
|
584
|
+
const BASE_URL_REQUIRED = {
|
|
585
|
+
message: constants.LLM_BASE_URL_REQUIRED_MESSAGE,
|
|
586
|
+
path: ['baseUrl']
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const hasBaseUrlWhenRequired = (
|
|
590
|
+
provider: string | undefined,
|
|
591
|
+
baseUrl: unknown
|
|
592
|
+
) => !requiresLlmBaseUrl(provider) || !!baseUrl;
|
|
593
|
+
|
|
594
|
+
// The base-URL rule is a cross-field check, and zod cannot `.omit()` from a
|
|
595
|
+
// schema that already carries one. So the shape stays a plain object — the
|
|
596
|
+
// `_VIEW` variants below derive from it — and the refinement is applied last,
|
|
597
|
+
// to each schema in turn, which also keeps the two in step.
|
|
598
|
+
const ORGANIZATION_CREATE_LLM_SHAPE = z.object({
|
|
576
599
|
name: z.string().min(1).max(200),
|
|
577
600
|
provider: z.enum(constants.LLM_PROVIDERS),
|
|
578
601
|
model: z.string().min(1).max(200),
|
|
579
|
-
baseUrl:
|
|
602
|
+
baseUrl: LLM_BASE_URL.optional().or(z.literal('')),
|
|
580
603
|
apiKey: z.string().min(1).max(500),
|
|
581
604
|
systemPrompt: z.string().max(10000).optional(),
|
|
582
605
|
config: z.record(z.string(), z.any()).optional(),
|
|
@@ -584,12 +607,21 @@ const ORGANIZATION_CREATE_LLM = z.object({
|
|
|
584
607
|
organizationId: z.uuid()
|
|
585
608
|
});
|
|
586
609
|
|
|
587
|
-
const
|
|
610
|
+
const ORGANIZATION_CREATE_LLM = ORGANIZATION_CREATE_LLM_SHAPE.refine(
|
|
611
|
+
v => hasBaseUrlWhenRequired(v.provider, v.baseUrl),
|
|
612
|
+
BASE_URL_REQUIRED
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
// An update is a partial: an omitted field keeps its stored value, so the
|
|
616
|
+
// base-URL rule can only be checked here when the caller sends the provider and
|
|
617
|
+
// the base URL together. The controller re-checks it against the merged row,
|
|
618
|
+
// which is the only place both halves are known.
|
|
619
|
+
const ORGANIZATION_UPDATE_LLM_SHAPE = z.object({
|
|
588
620
|
llmId: z.uuid(),
|
|
589
621
|
name: z.string().min(1).max(200).optional(),
|
|
590
622
|
provider: z.enum(constants.LLM_PROVIDERS).optional(),
|
|
591
623
|
model: z.string().min(1).max(200).optional(),
|
|
592
|
-
baseUrl:
|
|
624
|
+
baseUrl: LLM_BASE_URL.optional().or(z.literal('')).nullable(),
|
|
593
625
|
apiKey: z.string().min(1).max(500).optional(),
|
|
594
626
|
systemPrompt: z.string().max(10000).optional().nullable(),
|
|
595
627
|
config: z.record(z.string(), z.any()).optional().nullable(),
|
|
@@ -597,6 +629,21 @@ const ORGANIZATION_UPDATE_LLM = z.object({
|
|
|
597
629
|
organizationId: z.uuid()
|
|
598
630
|
});
|
|
599
631
|
|
|
632
|
+
const refineUpdateBaseUrl = <
|
|
633
|
+
T extends z.ZodType<{ provider?: string; baseUrl?: unknown }>
|
|
634
|
+
>(
|
|
635
|
+
schema: T
|
|
636
|
+
) =>
|
|
637
|
+
schema.refine(
|
|
638
|
+
v =>
|
|
639
|
+
v.baseUrl === undefined || hasBaseUrlWhenRequired(v.provider, v.baseUrl),
|
|
640
|
+
BASE_URL_REQUIRED
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
const ORGANIZATION_UPDATE_LLM = refineUpdateBaseUrl(
|
|
644
|
+
ORGANIZATION_UPDATE_LLM_SHAPE
|
|
645
|
+
);
|
|
646
|
+
|
|
600
647
|
const ORGANIZATION_REMOVE_LLM = z.object({
|
|
601
648
|
llmId: z.uuid(),
|
|
602
649
|
userId: z.uuid(),
|
|
@@ -649,16 +696,21 @@ const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
|
|
|
649
696
|
projectId: true
|
|
650
697
|
});
|
|
651
698
|
|
|
652
|
-
const ORGANIZATION_CREATE_LLM_VIEW =
|
|
699
|
+
const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM_SHAPE.omit({
|
|
653
700
|
userId: true,
|
|
654
701
|
organizationId: true
|
|
655
|
-
})
|
|
702
|
+
}).refine(
|
|
703
|
+
v => hasBaseUrlWhenRequired(v.provider, v.baseUrl),
|
|
704
|
+
BASE_URL_REQUIRED
|
|
705
|
+
);
|
|
656
706
|
|
|
657
|
-
const ORGANIZATION_UPDATE_LLM_VIEW =
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
707
|
+
const ORGANIZATION_UPDATE_LLM_VIEW = refineUpdateBaseUrl(
|
|
708
|
+
ORGANIZATION_UPDATE_LLM_SHAPE.omit({
|
|
709
|
+
llmId: true,
|
|
710
|
+
userId: true,
|
|
711
|
+
organizationId: true
|
|
712
|
+
})
|
|
713
|
+
);
|
|
662
714
|
|
|
663
715
|
// `channel.config` is an open bag, so this stays loose — it only pins down the
|
|
664
716
|
// keys we actually read, and passes everything else through untouched.
|
package/src/slackSend.ts
CHANGED
|
@@ -18,17 +18,17 @@ export interface SlackSendRequest {
|
|
|
18
18
|
operation: SlackOperation;
|
|
19
19
|
|
|
20
20
|
// chat.postMessage / files.completeUploadExternal share these
|
|
21
|
-
channel: string;
|
|
22
|
-
threadTs?: string;
|
|
21
|
+
channel: string; // channel ID (C…/G…/D…) or name (#general)
|
|
22
|
+
threadTs?: string; // reply in-thread when set
|
|
23
23
|
|
|
24
24
|
// post-message
|
|
25
|
-
text?: string;
|
|
26
|
-
mrkdwn?: boolean;
|
|
27
|
-
blocks?: unknown[];
|
|
25
|
+
text?: string; // required for post-message
|
|
26
|
+
mrkdwn?: boolean; // defaults to true on Slack's side
|
|
27
|
+
blocks?: unknown[]; // optional Block Kit payload
|
|
28
28
|
|
|
29
29
|
// upload-file
|
|
30
|
-
title?: string;
|
|
31
|
-
initialComment?: string;
|
|
30
|
+
title?: string; // shown in the Slack file viewer
|
|
31
|
+
initialComment?: string; // message body posted alongside the file
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Wire protocol for sending a PROXIED (remote MCP) resource as a Slack file.
|
|
@@ -59,7 +59,7 @@ export interface SlackSendResponse {
|
|
|
59
59
|
// file id. Empty string when Slack returns ok:true with no id (shouldn't
|
|
60
60
|
// happen, but defensively typed).
|
|
61
61
|
id: string;
|
|
62
|
-
channel?: string;
|
|
63
|
-
ts?: string;
|
|
64
|
-
permalink?: string;
|
|
62
|
+
channel?: string; // resolved channel id
|
|
63
|
+
ts?: string; // message timestamp (post-message only)
|
|
64
|
+
permalink?: string; // file permalink (upload-file only)
|
|
65
65
|
}
|
package/src/sources.ts
CHANGED
|
@@ -30,7 +30,9 @@ export interface ResourceUrlContext {
|
|
|
30
30
|
* nothing to a browser. Formatting it as a web link produced a dead button in
|
|
31
31
|
* whichever channel the answer went to.
|
|
32
32
|
*/
|
|
33
|
-
export const isDownloadableSource = (
|
|
33
|
+
export const isDownloadableSource = (
|
|
34
|
+
sourceType: Source['sourceType']
|
|
35
|
+
): boolean =>
|
|
34
36
|
sourceType === constants.RESOURCE_SOURCE_TYPE_FILE ||
|
|
35
37
|
sourceType === constants.RESOURCE_SOURCE_TYPE_CUSTOM_CODE;
|
|
36
38
|
|
|
@@ -79,7 +81,10 @@ export const formatSourcesAsButtons = (
|
|
|
79
81
|
source.pageNumber
|
|
80
82
|
);
|
|
81
83
|
return {
|
|
82
|
-
text: truncateLabel(
|
|
84
|
+
text: truncateLabel(
|
|
85
|
+
`${position} ${label}${pageSuffix}`,
|
|
86
|
+
maxLabelLength
|
|
87
|
+
),
|
|
83
88
|
url
|
|
84
89
|
};
|
|
85
90
|
}
|
package/src/vendorError.ts
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
// Error message turns a perfectly diagnosable failure into "[object Object]",
|
|
8
8
|
// which is what every one of these call sites did until a real Gmail rejection
|
|
9
9
|
// was put through them.
|
|
10
|
-
export const describeVendorError = (
|
|
10
|
+
export const describeVendorError = (
|
|
11
|
+
body: unknown,
|
|
12
|
+
fallback: string
|
|
13
|
+
): string => {
|
|
11
14
|
const error = (body as { error?: unknown } | null | undefined)?.error;
|
|
12
15
|
if (typeof error === 'string' && error) return error;
|
|
13
16
|
if (error && typeof error === 'object') {
|