@fgv/ts-extras 5.0.2 → 5.1.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.js +6 -2
- package/dist/index.js +5 -1
- package/dist/packlets/ai-assist/apiClient.js +484 -0
- package/dist/packlets/ai-assist/converters.js +121 -0
- package/dist/packlets/ai-assist/index.js +10 -0
- package/dist/packlets/ai-assist/model.js +90 -0
- package/dist/packlets/ai-assist/registry.js +145 -0
- package/dist/packlets/ai-assist/toolFormats.js +160 -0
- package/dist/packlets/crypto-utils/constants.js +48 -0
- package/dist/packlets/crypto-utils/converters.js +155 -0
- package/dist/packlets/crypto-utils/directEncryptionProvider.js +86 -0
- package/dist/packlets/crypto-utils/encryptedFile.js +161 -0
- package/dist/packlets/crypto-utils/index.browser.js +41 -0
- package/dist/packlets/crypto-utils/index.js +41 -0
- package/dist/packlets/crypto-utils/keystore/converters.js +84 -0
- package/dist/packlets/crypto-utils/keystore/index.js +31 -0
- package/dist/packlets/crypto-utils/keystore/keyStore.js +758 -0
- package/dist/packlets/crypto-utils/keystore/model.js +64 -0
- package/dist/packlets/crypto-utils/model.js +39 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js +159 -0
- package/dist/packlets/experimental/formatter.js +1 -1
- package/dist/packlets/mustache/index.js +23 -0
- package/dist/packlets/mustache/interfaces.js +25 -0
- package/dist/packlets/mustache/mustacheTemplate.js +242 -0
- package/dist/packlets/record-jar/recordJarHelpers.js +1 -1
- package/dist/packlets/yaml/converters.js +46 -0
- package/dist/packlets/yaml/index.js +23 -0
- package/dist/packlets/zip-file-tree/index.js +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +43 -2
- package/dist/packlets/zip-file-tree/zipFileTreeWriter.js +40 -0
- package/dist/ts-extras.d.ts +1990 -112
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts +3 -1
- package/lib/index.browser.js +6 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.js +9 -1
- package/lib/packlets/ai-assist/apiClient.d.ts +60 -0
- package/lib/packlets/ai-assist/apiClient.js +488 -0
- package/lib/packlets/ai-assist/converters.d.ts +55 -0
- package/lib/packlets/ai-assist/converters.js +124 -0
- package/lib/packlets/ai-assist/index.d.ts +10 -0
- package/lib/packlets/ai-assist/index.js +33 -0
- package/lib/packlets/ai-assist/model.d.ts +222 -0
- package/lib/packlets/ai-assist/model.js +95 -0
- package/lib/packlets/ai-assist/registry.d.ts +25 -0
- package/lib/packlets/ai-assist/registry.js +150 -0
- package/lib/packlets/ai-assist/toolFormats.d.ts +44 -0
- package/lib/packlets/ai-assist/toolFormats.js +166 -0
- package/lib/packlets/crypto-utils/constants.d.ts +26 -0
- package/lib/packlets/crypto-utils/constants.js +51 -0
- package/lib/packlets/crypto-utils/converters.d.ts +58 -0
- package/lib/packlets/crypto-utils/converters.js +192 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts +69 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.js +90 -0
- package/lib/packlets/crypto-utils/encryptedFile.d.ts +88 -0
- package/lib/packlets/crypto-utils/encryptedFile.js +201 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts +14 -0
- package/lib/packlets/crypto-utils/index.browser.js +91 -0
- package/lib/packlets/crypto-utils/index.d.ts +15 -0
- package/lib/packlets/crypto-utils/index.js +88 -0
- package/lib/packlets/crypto-utils/keystore/converters.d.ts +29 -0
- package/lib/packlets/crypto-utils/keystore/converters.js +87 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts +9 -0
- package/lib/packlets/crypto-utils/keystore/index.js +71 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +239 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.js +795 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts +245 -0
- package/lib/packlets/crypto-utils/keystore/model.js +68 -0
- package/lib/packlets/crypto-utils/model.d.ts +236 -0
- package/lib/packlets/crypto-utils/model.js +76 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +62 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js +196 -0
- package/lib/packlets/experimental/formatter.d.ts +1 -1
- package/lib/packlets/experimental/formatter.js +1 -1
- package/lib/packlets/mustache/index.d.ts +3 -0
- package/lib/packlets/mustache/index.js +27 -0
- package/lib/packlets/mustache/interfaces.d.ts +97 -0
- package/lib/packlets/mustache/interfaces.js +26 -0
- package/lib/packlets/mustache/mustacheTemplate.d.ts +76 -0
- package/lib/packlets/mustache/mustacheTemplate.js +249 -0
- package/lib/packlets/record-jar/recordJarHelpers.js +1 -1
- package/lib/packlets/yaml/converters.d.ts +9 -0
- package/lib/packlets/yaml/converters.js +82 -0
- package/lib/packlets/yaml/index.d.ts +2 -0
- package/lib/packlets/yaml/index.js +39 -0
- package/lib/packlets/zip-file-tree/index.d.ts +1 -0
- package/lib/packlets/zip-file-tree/index.js +15 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +31 -2
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +42 -1
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts +27 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.js +43 -0
- package/package.json +37 -18
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// AiPrompt
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* A structured AI prompt with system/user split for direct API calls,
|
|
25
|
+
* and a lazily-constructed combined version for copy/paste workflows.
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export class AiPrompt {
|
|
29
|
+
constructor(user, system) {
|
|
30
|
+
this.system = system;
|
|
31
|
+
this.user = user;
|
|
32
|
+
}
|
|
33
|
+
/** Combined single-string version (user + system joined) for copy/paste. */
|
|
34
|
+
get combined() {
|
|
35
|
+
return `${this.user}\n\n${this.system}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* All valid {@link ModelSpecKey} values.
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export const allModelSpecKeys = ['base', 'tools', 'image'];
|
|
43
|
+
/**
|
|
44
|
+
* Default context key used as fallback when resolving a {@link ModelSpec}.
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export const MODEL_SPEC_BASE_KEY = 'base';
|
|
48
|
+
/**
|
|
49
|
+
* Resolves a {@link ModelSpec} to a concrete model string given an optional context key.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* Resolution rules:
|
|
53
|
+
* 1. If the spec is a string, return it directly (context is irrelevant).
|
|
54
|
+
* 2. If the spec is an object and the context key exists, recurse into that branch.
|
|
55
|
+
* 3. Otherwise, fall back to the {@link MODEL_SPEC_BASE_KEY | 'base'} key.
|
|
56
|
+
* 4. If neither context nor `'base'` exists, use the first available value.
|
|
57
|
+
*
|
|
58
|
+
* @param spec - The model specification to resolve
|
|
59
|
+
* @param context - Optional context key (e.g. `'tools'`)
|
|
60
|
+
* @returns The resolved model string
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export function resolveModel(spec, context) {
|
|
64
|
+
if (typeof spec === 'string') {
|
|
65
|
+
return spec;
|
|
66
|
+
}
|
|
67
|
+
// Try the requested context key first
|
|
68
|
+
if (context !== undefined && context in spec) {
|
|
69
|
+
return resolveModel(spec[context]);
|
|
70
|
+
}
|
|
71
|
+
// Fall back to 'base'
|
|
72
|
+
if (MODEL_SPEC_BASE_KEY in spec) {
|
|
73
|
+
return resolveModel(spec[MODEL_SPEC_BASE_KEY]);
|
|
74
|
+
}
|
|
75
|
+
// Last resort: first value in the record
|
|
76
|
+
const first = Object.values(spec)[0];
|
|
77
|
+
/* c8 ignore next 3 - defensive: only reachable with empty object (prevented by converter) */
|
|
78
|
+
if (first === undefined) {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
return resolveModel(first);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Default AI assist settings (copy-paste only).
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export const DEFAULT_AI_ASSIST = {
|
|
88
|
+
providers: [{ provider: 'copy-paste' }]
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
/**
|
|
21
|
+
* Centralized provider registry — single source of truth for all AI provider metadata.
|
|
22
|
+
* @packageDocumentation
|
|
23
|
+
*/
|
|
24
|
+
import { fail, succeed } from '@fgv/ts-utils';
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Built-in providers
|
|
27
|
+
// ============================================================================
|
|
28
|
+
/**
|
|
29
|
+
* All known AI provider descriptors. Copy-paste first, then alphabetical.
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
const BUILTIN_PROVIDERS = [
|
|
33
|
+
{
|
|
34
|
+
id: 'copy-paste',
|
|
35
|
+
label: 'Copy / Paste',
|
|
36
|
+
buttonLabel: 'AI Assist | Copy',
|
|
37
|
+
needsSecret: false,
|
|
38
|
+
apiFormat: 'openai',
|
|
39
|
+
baseUrl: '',
|
|
40
|
+
defaultModel: '',
|
|
41
|
+
supportedTools: [],
|
|
42
|
+
corsRestricted: false
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'anthropic',
|
|
46
|
+
label: 'Anthropic Claude',
|
|
47
|
+
buttonLabel: 'AI Assist | Claude',
|
|
48
|
+
needsSecret: true,
|
|
49
|
+
apiFormat: 'anthropic',
|
|
50
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
51
|
+
defaultModel: 'claude-sonnet-4-5-20250929',
|
|
52
|
+
supportedTools: ['web_search'],
|
|
53
|
+
corsRestricted: false
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'google-gemini',
|
|
57
|
+
label: 'Google Gemini',
|
|
58
|
+
buttonLabel: 'AI Assist | Gemini',
|
|
59
|
+
needsSecret: true,
|
|
60
|
+
apiFormat: 'gemini',
|
|
61
|
+
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
62
|
+
defaultModel: 'gemini-2.5-flash',
|
|
63
|
+
supportedTools: ['web_search'],
|
|
64
|
+
corsRestricted: false
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'groq',
|
|
68
|
+
label: 'Groq',
|
|
69
|
+
buttonLabel: 'AI Assist | Groq',
|
|
70
|
+
needsSecret: true,
|
|
71
|
+
apiFormat: 'openai',
|
|
72
|
+
baseUrl: 'https://api.groq.com/openai/v1',
|
|
73
|
+
defaultModel: 'llama-3.3-70b-versatile',
|
|
74
|
+
supportedTools: [],
|
|
75
|
+
corsRestricted: false
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'mistral',
|
|
79
|
+
label: 'Mistral',
|
|
80
|
+
buttonLabel: 'AI Assist | Mistral',
|
|
81
|
+
needsSecret: true,
|
|
82
|
+
apiFormat: 'openai',
|
|
83
|
+
baseUrl: 'https://api.mistral.ai/v1',
|
|
84
|
+
defaultModel: 'mistral-large-latest',
|
|
85
|
+
supportedTools: [],
|
|
86
|
+
corsRestricted: false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'openai',
|
|
90
|
+
label: 'OpenAI',
|
|
91
|
+
buttonLabel: 'AI Assist | OpenAI',
|
|
92
|
+
needsSecret: true,
|
|
93
|
+
apiFormat: 'openai',
|
|
94
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
95
|
+
defaultModel: 'gpt-4o',
|
|
96
|
+
supportedTools: ['web_search'],
|
|
97
|
+
corsRestricted: false
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'xai-grok',
|
|
101
|
+
label: 'xAI Grok',
|
|
102
|
+
buttonLabel: 'AI Assist | Grok',
|
|
103
|
+
needsSecret: true,
|
|
104
|
+
apiFormat: 'openai',
|
|
105
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
106
|
+
defaultModel: { base: 'grok-4-1-fast', tools: 'grok-4-1-fast-reasoning' },
|
|
107
|
+
supportedTools: ['web_search'],
|
|
108
|
+
corsRestricted: true
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* Index for O(1) lookup by id.
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
const PROVIDER_BY_ID = new Map(BUILTIN_PROVIDERS.map((d) => [d.id, d]));
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// Public API
|
|
118
|
+
// ============================================================================
|
|
119
|
+
/**
|
|
120
|
+
* All valid provider ID values, in the same order as the registry.
|
|
121
|
+
* @public
|
|
122
|
+
*/
|
|
123
|
+
export const allProviderIds = BUILTIN_PROVIDERS.map((d) => d.id);
|
|
124
|
+
/**
|
|
125
|
+
* Get all known provider descriptors. Copy-paste first, then alphabetical.
|
|
126
|
+
* @returns All built-in provider descriptors
|
|
127
|
+
* @public
|
|
128
|
+
*/
|
|
129
|
+
export function getProviderDescriptors() {
|
|
130
|
+
return BUILTIN_PROVIDERS;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get a provider descriptor by id.
|
|
134
|
+
* @param id - The provider identifier
|
|
135
|
+
* @returns The descriptor, or a failure if the provider is unknown
|
|
136
|
+
* @public
|
|
137
|
+
*/
|
|
138
|
+
export function getProviderDescriptor(id) {
|
|
139
|
+
const descriptor = PROVIDER_BY_ID.get(id);
|
|
140
|
+
if (!descriptor) {
|
|
141
|
+
return fail(`unknown AI provider: ${id}`);
|
|
142
|
+
}
|
|
143
|
+
return succeed(descriptor);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Tool resolution
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the effective tools for a completion call.
|
|
25
|
+
*
|
|
26
|
+
* - If per-call tools are provided, they override settings-level tools entirely.
|
|
27
|
+
* - Otherwise, settings-level enabled tools are used.
|
|
28
|
+
* - Only tools supported by the provider are included.
|
|
29
|
+
* - Returns an empty array if no tools are enabled (= no tools sent).
|
|
30
|
+
*
|
|
31
|
+
* @param descriptor - The provider descriptor (used to filter by supported tools)
|
|
32
|
+
* @param settingsTools - Tool enablement from provider settings (optional)
|
|
33
|
+
* @param perCallTools - Per-call tool override (optional)
|
|
34
|
+
* @returns The resolved list of tool configs to include in the request
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export function resolveEffectiveTools(descriptor, settingsTools, perCallTools) {
|
|
38
|
+
const supported = new Set(descriptor.supportedTools);
|
|
39
|
+
if (perCallTools !== undefined) {
|
|
40
|
+
return perCallTools.filter((t) => supported.has(t.type));
|
|
41
|
+
}
|
|
42
|
+
if (settingsTools === undefined) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
return settingsTools
|
|
46
|
+
.filter((e) => e.enabled && supported.has(e.type))
|
|
47
|
+
.map((e) => { var _a; return (_a = e.config) !== null && _a !== void 0 ? _a : { type: e.type }; });
|
|
48
|
+
}
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// OpenAI / xAI Responses API format
|
|
51
|
+
// ============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Formats a web search tool config for the xAI/OpenAI Responses API.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
function webSearchToResponsesApi(config) {
|
|
57
|
+
const tool = { type: 'web_search' };
|
|
58
|
+
if (config.allowedDomains || config.blockedDomains) {
|
|
59
|
+
const filters = {};
|
|
60
|
+
if (config.allowedDomains) {
|
|
61
|
+
filters.allowed_domains = [...config.allowedDomains];
|
|
62
|
+
}
|
|
63
|
+
if (config.blockedDomains) {
|
|
64
|
+
filters.excluded_domains = [...config.blockedDomains];
|
|
65
|
+
}
|
|
66
|
+
tool.filters = filters;
|
|
67
|
+
}
|
|
68
|
+
if (config.enableImageUnderstanding) {
|
|
69
|
+
tool.enable_image_understanding = true;
|
|
70
|
+
}
|
|
71
|
+
return tool;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Formats tool configs for the xAI/OpenAI Responses API.
|
|
75
|
+
* @param tools - The resolved tool configs
|
|
76
|
+
* @returns Provider-native tool objects for the `tools` request field
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export function toResponsesApiTools(tools) {
|
|
80
|
+
return tools.map((t) => {
|
|
81
|
+
switch (t.type) {
|
|
82
|
+
case 'web_search':
|
|
83
|
+
return webSearchToResponsesApi(t);
|
|
84
|
+
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
85
|
+
default: {
|
|
86
|
+
const _exhaustive = t.type;
|
|
87
|
+
return { type: String(_exhaustive) };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Anthropic Messages API format
|
|
94
|
+
// ============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Formats a web search tool config for the Anthropic Messages API.
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
function webSearchToAnthropic(config) {
|
|
100
|
+
const tool = {
|
|
101
|
+
type: 'web_search_20250305',
|
|
102
|
+
name: 'web_search'
|
|
103
|
+
};
|
|
104
|
+
if (config.maxUses !== undefined) {
|
|
105
|
+
tool.max_uses = config.maxUses;
|
|
106
|
+
}
|
|
107
|
+
if (config.allowedDomains) {
|
|
108
|
+
tool.allowed_domains = [...config.allowedDomains];
|
|
109
|
+
}
|
|
110
|
+
if (config.blockedDomains) {
|
|
111
|
+
tool.blocked_domains = [...config.blockedDomains];
|
|
112
|
+
}
|
|
113
|
+
return tool;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Formats tool configs for the Anthropic Messages API.
|
|
117
|
+
* @param tools - The resolved tool configs
|
|
118
|
+
* @returns Provider-native tool objects for the `tools` request field
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
export function toAnthropicTools(tools) {
|
|
122
|
+
return tools.map((t) => {
|
|
123
|
+
switch (t.type) {
|
|
124
|
+
case 'web_search':
|
|
125
|
+
return webSearchToAnthropic(t);
|
|
126
|
+
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
127
|
+
default: {
|
|
128
|
+
const _exhaustive = t.type;
|
|
129
|
+
return { type: String(_exhaustive) };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Gemini generateContent API format
|
|
136
|
+
// ============================================================================
|
|
137
|
+
/**
|
|
138
|
+
* Formats tool configs for the Gemini generateContent API.
|
|
139
|
+
* Gemini uses `google_search` for search grounding — no per-tool config options.
|
|
140
|
+
* @param tools - The resolved tool configs
|
|
141
|
+
* @returns Provider-native tool objects for the `tools` request field
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
export function toGeminiTools(tools) {
|
|
145
|
+
const result = [];
|
|
146
|
+
for (const t of tools) {
|
|
147
|
+
switch (t.type) {
|
|
148
|
+
case 'web_search':
|
|
149
|
+
result.push({ google_search: {} });
|
|
150
|
+
break;
|
|
151
|
+
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
152
|
+
default: {
|
|
153
|
+
const _exhaustive = t.type;
|
|
154
|
+
result.push({ type: String(_exhaustive) });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=toolFormats.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright (c) 2024 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Constants
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* Current format version for encrypted files.
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export const ENCRYPTED_FILE_FORMAT = 'encrypted-collection-v1';
|
|
28
|
+
/**
|
|
29
|
+
* Default encryption algorithm.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_ALGORITHM = 'AES-256-GCM';
|
|
33
|
+
/**
|
|
34
|
+
* Key size in bytes for AES-256.
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export const AES_256_KEY_SIZE = 32;
|
|
38
|
+
/**
|
|
39
|
+
* IV size in bytes for GCM mode.
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export const GCM_IV_SIZE = 12;
|
|
43
|
+
/**
|
|
44
|
+
* Auth tag size in bytes for GCM mode.
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export const GCM_AUTH_TAG_SIZE = 16;
|
|
48
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Copyright (c) 2024 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
import { Converters as JsonConverters } from '@fgv/ts-json-base';
|
|
21
|
+
import { Converters, fail, succeed } from '@fgv/ts-utils';
|
|
22
|
+
import * as Constants from './constants';
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Base Converters
|
|
25
|
+
// ============================================================================
|
|
26
|
+
/**
|
|
27
|
+
* Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export const encryptionAlgorithm = Converters.enumeratedValue([Constants.DEFAULT_ALGORITHM]);
|
|
31
|
+
/**
|
|
32
|
+
* Converter for {@link CryptoUtils.EncryptedFileFormat | encrypted file format} version.
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export const encryptedFileFormat = Converters.enumeratedValue([
|
|
36
|
+
Constants.ENCRYPTED_FILE_FORMAT
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Converter for {@link CryptoUtils.EncryptedFileErrorMode | encrypted file error mode}.
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export const encryptedFileErrorMode = Converters.enumeratedValue(['fail', 'skip', 'warn']);
|
|
43
|
+
/**
|
|
44
|
+
* Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export const keyDerivationFunction = Converters.enumeratedValue(['pbkdf2']);
|
|
48
|
+
/**
|
|
49
|
+
* Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export const keyDerivationParams = Converters.object({
|
|
53
|
+
kdf: keyDerivationFunction,
|
|
54
|
+
salt: Converters.string,
|
|
55
|
+
iterations: Converters.number
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Converter for base64 strings (validates format).
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
export const base64String = Converters.string.withConstraint((value) => {
|
|
62
|
+
// Basic base64 validation - check for valid characters and padding
|
|
63
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
64
|
+
if (!base64Regex.test(value)) {
|
|
65
|
+
return fail('Invalid base64 encoding');
|
|
66
|
+
}
|
|
67
|
+
return succeed(value);
|
|
68
|
+
});
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Uint8Array Converter
|
|
71
|
+
// ============================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Converter which converts a base64 string to a Uint8Array.
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
export const uint8ArrayFromBase64 = Converters.string.map((base64) => {
|
|
77
|
+
try {
|
|
78
|
+
// Use Buffer in Node.js environment, atob in browser
|
|
79
|
+
if (typeof Buffer !== 'undefined') {
|
|
80
|
+
return succeed(Uint8Array.from(Buffer.from(base64, 'base64')));
|
|
81
|
+
}
|
|
82
|
+
/* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
|
|
83
|
+
const binaryString = atob(base64);
|
|
84
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
85
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
86
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
87
|
+
}
|
|
88
|
+
return succeed(bytes);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
// This catch is for browser's atob() which throws on invalid base64
|
|
92
|
+
// Node's Buffer.from() doesn't throw, it ignores invalid characters
|
|
93
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
94
|
+
return fail(`Invalid base64: ${message}`);
|
|
95
|
+
}
|
|
96
|
+
/* c8 ignore stop */
|
|
97
|
+
});
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Named Secret Converter
|
|
100
|
+
// ============================================================================
|
|
101
|
+
/**
|
|
102
|
+
* Converter for {@link CryptoUtils.INamedSecret | named secret} from JSON representation.
|
|
103
|
+
* Expects key as base64 string in JSON, converts to Uint8Array.
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
export const namedSecret = Converters.object({
|
|
107
|
+
name: Converters.string,
|
|
108
|
+
key: uint8ArrayFromBase64
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
111
|
+
* Base converter for encrypted file structure (without typed metadata).
|
|
112
|
+
*/
|
|
113
|
+
const baseEncryptedFileConverter = Converters.object({
|
|
114
|
+
format: encryptedFileFormat,
|
|
115
|
+
secretName: Converters.string,
|
|
116
|
+
algorithm: encryptionAlgorithm,
|
|
117
|
+
iv: base64String,
|
|
118
|
+
authTag: base64String,
|
|
119
|
+
encryptedData: base64String,
|
|
120
|
+
keyDerivation: keyDerivationParams,
|
|
121
|
+
metadata: JsonConverters.jsonValue
|
|
122
|
+
}, { optionalFields: ['keyDerivation', 'metadata'] });
|
|
123
|
+
/**
|
|
124
|
+
* Creates a converter for {@link CryptoUtils.IEncryptedFile | encrypted files} with optional typed metadata.
|
|
125
|
+
* @typeParam TMetadata - Type of optional unencrypted metadata
|
|
126
|
+
* @param metadataConverter - Optional converter for validating metadata field
|
|
127
|
+
* @returns A converter that validates and converts encrypted file structures
|
|
128
|
+
* @public
|
|
129
|
+
*/
|
|
130
|
+
export function createEncryptedFileConverter(metadataConverter) {
|
|
131
|
+
return Converters.generic((from) => {
|
|
132
|
+
// First validate base structure
|
|
133
|
+
const baseResult = baseEncryptedFileConverter.convert(from);
|
|
134
|
+
if (baseResult.isFailure()) {
|
|
135
|
+
return fail(baseResult.message);
|
|
136
|
+
}
|
|
137
|
+
const base = baseResult.value;
|
|
138
|
+
// Validate metadata with specific converter if provided and metadata exists
|
|
139
|
+
if (metadataConverter !== undefined && base.metadata !== undefined) {
|
|
140
|
+
const metaResult = metadataConverter.convert(base.metadata);
|
|
141
|
+
if (metaResult.isFailure()) {
|
|
142
|
+
return fail(`Invalid metadata: ${metaResult.message}`);
|
|
143
|
+
}
|
|
144
|
+
return succeed(Object.assign(Object.assign({}, base), { metadata: metaResult.value }));
|
|
145
|
+
}
|
|
146
|
+
// Return as-is (metadata is either undefined or untyped JsonValue)
|
|
147
|
+
return succeed(base);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Default converter for encrypted files without typed metadata.
|
|
152
|
+
* @public
|
|
153
|
+
*/
|
|
154
|
+
export const encryptedFile = createEncryptedFileConverter();
|
|
155
|
+
//# sourceMappingURL=converters.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
import { fail, succeed } from '@fgv/ts-utils';
|
|
21
|
+
import { createEncryptedFile } from './encryptedFile';
|
|
22
|
+
/**
|
|
23
|
+
* An {@link IEncryptionProvider} that uses a pre-supplied key and crypto provider.
|
|
24
|
+
*
|
|
25
|
+
* This is useful when you have the raw encryption key from an external source
|
|
26
|
+
* (e.g. a `SecretProvider` callback, password derivation, or a one-shot
|
|
27
|
+
* operation) and don't want to open a full KeyStore.
|
|
28
|
+
*
|
|
29
|
+
* Optionally bound to a specific secret name for safety: if a `boundSecretName`
|
|
30
|
+
* is provided, calls to `encryptByName` with a different name will fail.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const provider = DirectEncryptionProvider.create({
|
|
35
|
+
* cryptoProvider: nodeCryptoProvider,
|
|
36
|
+
* key: myKey,
|
|
37
|
+
* boundSecretName: 'my-collection'
|
|
38
|
+
* }).orThrow();
|
|
39
|
+
*
|
|
40
|
+
* const encrypted = await provider.encryptByName('my-collection', jsonContent);
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export class DirectEncryptionProvider {
|
|
46
|
+
constructor(params) {
|
|
47
|
+
this._cryptoProvider = params.cryptoProvider;
|
|
48
|
+
this._key = params.key;
|
|
49
|
+
this._boundSecretName = params.boundSecretName;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new DirectEncryptionProvider.
|
|
53
|
+
* @param params - Provider configuration
|
|
54
|
+
* @returns Success with provider, or Failure if parameters are invalid
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
static create(params) {
|
|
58
|
+
if (params.key.length === 0) {
|
|
59
|
+
return fail('Encryption key cannot be empty');
|
|
60
|
+
}
|
|
61
|
+
return succeed(new DirectEncryptionProvider(params));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The secret name this provider is bound to, if any.
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
get boundSecretName() {
|
|
68
|
+
return this._boundSecretName;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* {@inheritDoc IEncryptionProvider.encryptByName}
|
|
72
|
+
*/
|
|
73
|
+
async encryptByName(secretName, content, metadata) {
|
|
74
|
+
if (this._boundSecretName !== undefined && secretName !== this._boundSecretName) {
|
|
75
|
+
return fail(`Secret name mismatch: requested '${secretName}' but provider is bound to '${this._boundSecretName}'`);
|
|
76
|
+
}
|
|
77
|
+
return createEncryptedFile({
|
|
78
|
+
content,
|
|
79
|
+
secretName,
|
|
80
|
+
key: this._key,
|
|
81
|
+
cryptoProvider: this._cryptoProvider,
|
|
82
|
+
metadata
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=directEncryptionProvider.js.map
|