@unchainedshop/api 4.8.18 → 4.8.19
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/lib/adminUiPlugins.d.ts +70 -0
- package/lib/adminUiPlugins.js +111 -0
- package/lib/chat/generateImageHandler.d.ts +273 -7
- package/lib/express/createMCPMiddleware.js +6 -3
- package/lib/fastify/mcpHandler.js +5 -2
- package/lib/mcp/tools/quotation/index.js +1 -1
- package/lib/resolvers/mutations/bookmarks/createBookmark.d.ts +1 -1
- package/lib/resolvers/mutations/bookmarks/createBookmark.js +4 -3
- package/lib/resolvers/type/index.d.ts +4 -0
- package/lib/resolvers/type/quotation-types.d.ts +4 -0
- package/lib/resolvers/type/quotation-types.js +1 -0
- package/lib/roles/loggedIn.js +1 -0
- package/lib/schema/types/quotation.js +5 -0
- package/package.json +8 -8
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface AdminUIPluginEntityConfig {
|
|
2
|
+
path: string;
|
|
3
|
+
label: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
requiredRole?: string;
|
|
6
|
+
components: {
|
|
7
|
+
list: string;
|
|
8
|
+
detail: string;
|
|
9
|
+
create?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface AdminUIPluginPageConfig {
|
|
13
|
+
path: string;
|
|
14
|
+
label: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
requiredRole?: string;
|
|
17
|
+
component: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AdminUIPluginTabConfig {
|
|
20
|
+
label: string;
|
|
21
|
+
component: string;
|
|
22
|
+
requiredRole?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AdminUIPluginWidgetConfig {
|
|
25
|
+
component: string;
|
|
26
|
+
width?: 'full' | 'half' | 'third';
|
|
27
|
+
}
|
|
28
|
+
export interface AdminUIPluginSlotConfig {
|
|
29
|
+
component: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AdminUIPluginConfig {
|
|
32
|
+
name: string;
|
|
33
|
+
version?: string;
|
|
34
|
+
bundlePath: string;
|
|
35
|
+
navigation?: {
|
|
36
|
+
label: string;
|
|
37
|
+
icon?: string;
|
|
38
|
+
requiredRole?: string;
|
|
39
|
+
};
|
|
40
|
+
slots: {
|
|
41
|
+
entities?: AdminUIPluginEntityConfig[];
|
|
42
|
+
pages?: AdminUIPluginPageConfig[];
|
|
43
|
+
'dashboard:widgets'?: AdminUIPluginWidgetConfig[];
|
|
44
|
+
'product:tabs'?: AdminUIPluginTabConfig[];
|
|
45
|
+
'assortment:tabs'?: AdminUIPluginTabConfig[];
|
|
46
|
+
'filter:tabs'?: AdminUIPluginTabConfig[];
|
|
47
|
+
'user:tabs'?: AdminUIPluginTabConfig[];
|
|
48
|
+
'order:tabs'?: AdminUIPluginTabConfig[];
|
|
49
|
+
[key: string]: AdminUIPluginTabConfig[] | AdminUIPluginSlotConfig[] | AdminUIPluginEntityConfig[] | AdminUIPluginPageConfig[] | AdminUIPluginWidgetConfig[] | undefined;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
interface StaticAsset {
|
|
53
|
+
content: string | (() => string);
|
|
54
|
+
contentType: string;
|
|
55
|
+
cacheControl: string;
|
|
56
|
+
etag?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface PreparedPluginAssets {
|
|
59
|
+
routes: Map<string, StaticAsset>;
|
|
60
|
+
validPlugins: AdminUIPluginConfig[];
|
|
61
|
+
importMapTag: string | null;
|
|
62
|
+
}
|
|
63
|
+
export declare const resolveAdminUIPath: () => string | null;
|
|
64
|
+
export declare function preparePluginAssets(plugins: AdminUIPluginConfig[], log: {
|
|
65
|
+
info: (...args: any[]) => void;
|
|
66
|
+
warn: (...args: any[]) => void;
|
|
67
|
+
}, options?: {
|
|
68
|
+
devMode?: boolean;
|
|
69
|
+
}): PreparedPluginAssets;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
const PLUGIN_NAME_RE = /^[a-z0-9]([a-z0-9_-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9_-]*[a-z0-9])?)*$/i;
|
|
5
|
+
const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
|
|
6
|
+
const SDK_FILES = ['ui.mjs', 'form.mjs', 'hooks.mjs', 'providers.mjs', 'modal.mjs'];
|
|
7
|
+
const IMPORT_MAP = {
|
|
8
|
+
imports: {
|
|
9
|
+
'@unchainedshop/admin-ui/ui': '/admin-ui-sdk/ui.mjs',
|
|
10
|
+
'@unchainedshop/admin-ui/form': '/admin-ui-sdk/form.mjs',
|
|
11
|
+
'@unchainedshop/admin-ui/hooks': '/admin-ui-sdk/hooks.mjs',
|
|
12
|
+
'@unchainedshop/admin-ui/modal': '/admin-ui-sdk/modal.mjs',
|
|
13
|
+
'@unchainedshop/admin-ui/providers': '/admin-ui-sdk/providers.mjs',
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export const resolveAdminUIPath = () => {
|
|
17
|
+
try {
|
|
18
|
+
const staticURL = import.meta.resolve('@unchainedshop/admin-ui');
|
|
19
|
+
return new URL(staticURL).pathname.split('/').slice(0, -1).join('/');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export function preparePluginAssets(plugins, log, options = {}) {
|
|
26
|
+
const { devMode = false } = options;
|
|
27
|
+
const routes = new Map();
|
|
28
|
+
const validPlugins = plugins.filter((p) => {
|
|
29
|
+
if (!PLUGIN_NAME_RE.test(p.name)) {
|
|
30
|
+
log.warn(`Skipping admin-ui plugin with invalid name: "${p.name}"`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
});
|
|
35
|
+
if (validPlugins.length > 0) {
|
|
36
|
+
const pluginList = validPlugins
|
|
37
|
+
.map((p) => `${p.name}${p.version ? `@${p.version}` : ''}`)
|
|
38
|
+
.join(', ');
|
|
39
|
+
log.info(`Loading ${validPlugins.length} admin-ui plugin(s): ${pluginList}`);
|
|
40
|
+
}
|
|
41
|
+
const contentHash = (content) => createHash('sha256').update(content).digest('hex').slice(0, 8);
|
|
42
|
+
const pluginBundles = new Map();
|
|
43
|
+
for (const plugin of validPlugins) {
|
|
44
|
+
try {
|
|
45
|
+
const content = readFileSync(resolve(plugin.bundlePath), 'utf-8');
|
|
46
|
+
pluginBundles.set(plugin.name, { content, hash: contentHash(content) });
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
log.warn(`Failed to read bundle for plugin "${plugin.name}" at ${plugin.bundlePath}: ${err.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const manifestJSON = JSON.stringify(validPlugins
|
|
53
|
+
.filter((p) => pluginBundles.has(p.name))
|
|
54
|
+
.map((plugin) => {
|
|
55
|
+
const bundle = pluginBundles.get(plugin.name);
|
|
56
|
+
return {
|
|
57
|
+
...Object.fromEntries(Object.entries(plugin).filter(([k]) => k !== 'bundlePath')),
|
|
58
|
+
bundleUrl: `/admin-plugins/${plugin.name}.js?v=${bundle.hash}`,
|
|
59
|
+
};
|
|
60
|
+
}));
|
|
61
|
+
const devCacheControl = 'no-cache, no-store, must-revalidate';
|
|
62
|
+
const manifestHash = contentHash(manifestJSON);
|
|
63
|
+
routes.set('/admin-ui-plugins.json', {
|
|
64
|
+
content: devMode ? () => manifestJSON : manifestJSON,
|
|
65
|
+
contentType: 'application/json',
|
|
66
|
+
cacheControl: devMode ? devCacheControl : `public, max-age=0, must-revalidate`,
|
|
67
|
+
etag: `"${manifestHash}"`,
|
|
68
|
+
});
|
|
69
|
+
for (const plugin of validPlugins.filter((p) => pluginBundles.has(p.name))) {
|
|
70
|
+
const bundlePath = resolve(plugin.bundlePath);
|
|
71
|
+
if (devMode) {
|
|
72
|
+
routes.set(`/admin-plugins/${plugin.name}.js`, {
|
|
73
|
+
content: () => readFileSync(bundlePath, 'utf-8'),
|
|
74
|
+
contentType: 'application/javascript',
|
|
75
|
+
cacheControl: devCacheControl,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const { content } = pluginBundles.get(plugin.name);
|
|
80
|
+
routes.set(`/admin-plugins/${plugin.name}.js`, {
|
|
81
|
+
content,
|
|
82
|
+
contentType: 'application/javascript',
|
|
83
|
+
cacheControl: IMMUTABLE_CACHE,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let importMapTag = null;
|
|
88
|
+
if (pluginBundles.size > 0) {
|
|
89
|
+
const adminUIPath = resolveAdminUIPath();
|
|
90
|
+
if (adminUIPath) {
|
|
91
|
+
for (const file of SDK_FILES) {
|
|
92
|
+
const sdkPath = join(adminUIPath, '..', 'dist', file);
|
|
93
|
+
if (existsSync(sdkPath)) {
|
|
94
|
+
routes.set(`/admin-ui-sdk/${file}`, {
|
|
95
|
+
content: readFileSync(sdkPath, 'utf-8'),
|
|
96
|
+
contentType: 'application/javascript',
|
|
97
|
+
cacheControl: IMMUTABLE_CACHE,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const importMapJSON = JSON.stringify(IMPORT_MAP);
|
|
102
|
+
routes.set('/admin-ui-importmap.json', {
|
|
103
|
+
content: importMapJSON,
|
|
104
|
+
contentType: 'application/json',
|
|
105
|
+
cacheControl: IMMUTABLE_CACHE,
|
|
106
|
+
});
|
|
107
|
+
importMapTag = `<script type="importmap">${importMapJSON}</script>`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { routes, validPlugins, importMapTag };
|
|
111
|
+
}
|
|
@@ -2,11 +2,277 @@ import type * as aiTypes from 'ai';
|
|
|
2
2
|
declare const generateImageHandler: (req: any) => ({ model, uploadUrl, }: {
|
|
3
3
|
model: aiTypes.ImageModel;
|
|
4
4
|
uploadUrl?: string;
|
|
5
|
-
}) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
}) => ({
|
|
6
|
+
title?: string;
|
|
7
|
+
providerOptions?: import("@ai-sdk/provider-utils").ProviderOptions;
|
|
8
|
+
metadata?: import("@ai-sdk/provider").JSONObject;
|
|
9
|
+
inputSchema: aiTypes.FlexibleSchema<{
|
|
10
|
+
prompt: any;
|
|
11
|
+
size?: string | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
contextSchema?: aiTypes.FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
|
|
14
|
+
needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
|
|
15
|
+
prompt: any;
|
|
16
|
+
size?: string | undefined;
|
|
17
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>> | undefined;
|
|
18
|
+
onInputStart?: ((options: aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
19
|
+
onInputDelta?: ((options: {
|
|
20
|
+
inputTextDelta: string;
|
|
21
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
22
|
+
onInputAvailable?: ((options: {
|
|
23
|
+
input: {
|
|
24
|
+
prompt: any;
|
|
25
|
+
size?: string | undefined;
|
|
26
|
+
};
|
|
27
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
28
|
+
toModelOutput?: ((options: {
|
|
29
|
+
toolCallId: string;
|
|
30
|
+
input: {
|
|
31
|
+
prompt: any;
|
|
32
|
+
size?: string | undefined;
|
|
33
|
+
};
|
|
34
|
+
output: NoInfer<string | {
|
|
35
|
+
imageUrl: any;
|
|
36
|
+
prompt: any;
|
|
37
|
+
}>;
|
|
38
|
+
}) => import("@ai-sdk/provider-utils").ToolResultOutput | PromiseLike<import("@ai-sdk/provider-utils").ToolResultOutput>) | undefined;
|
|
39
|
+
} & {
|
|
40
|
+
outputSchema?: aiTypes.FlexibleSchema<string | {
|
|
41
|
+
imageUrl: any;
|
|
42
|
+
prompt: any;
|
|
43
|
+
}> | undefined;
|
|
44
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
45
|
+
prompt: any;
|
|
46
|
+
size?: string | undefined;
|
|
47
|
+
}, string | {
|
|
48
|
+
imageUrl: any;
|
|
49
|
+
prompt: any;
|
|
50
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
51
|
+
} & {
|
|
52
|
+
description?: string | ((options: {
|
|
53
|
+
context: NoInfer<import("@ai-sdk/provider-utils").Context>;
|
|
54
|
+
experimental_sandbox?: aiTypes.Experimental_SandboxSession;
|
|
55
|
+
}) => string) | undefined;
|
|
56
|
+
strict?: boolean;
|
|
57
|
+
inputExamples?: {
|
|
58
|
+
input: NoInfer<{
|
|
59
|
+
prompt: any;
|
|
60
|
+
size?: string | undefined;
|
|
61
|
+
}>;
|
|
62
|
+
}[] | undefined;
|
|
63
|
+
id?: never;
|
|
64
|
+
isProviderExecuted?: never;
|
|
65
|
+
args?: never;
|
|
66
|
+
supportsDeferredResults?: never;
|
|
67
|
+
} & {
|
|
68
|
+
type?: undefined | "function";
|
|
69
|
+
} & {
|
|
70
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
71
|
+
prompt: any;
|
|
72
|
+
size?: string | undefined;
|
|
73
|
+
}, string | {
|
|
74
|
+
imageUrl: any;
|
|
75
|
+
prompt: any;
|
|
76
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
77
|
+
}) | ({
|
|
78
|
+
title?: string;
|
|
79
|
+
providerOptions?: import("@ai-sdk/provider-utils").ProviderOptions;
|
|
80
|
+
metadata?: import("@ai-sdk/provider").JSONObject;
|
|
81
|
+
inputSchema: aiTypes.FlexibleSchema<{
|
|
82
|
+
prompt: any;
|
|
83
|
+
size?: string | undefined;
|
|
84
|
+
}>;
|
|
85
|
+
contextSchema?: aiTypes.FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
|
|
86
|
+
needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
|
|
87
|
+
prompt: any;
|
|
88
|
+
size?: string | undefined;
|
|
89
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>> | undefined;
|
|
90
|
+
onInputStart?: ((options: aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
91
|
+
onInputDelta?: ((options: {
|
|
92
|
+
inputTextDelta: string;
|
|
93
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
94
|
+
onInputAvailable?: ((options: {
|
|
95
|
+
input: {
|
|
96
|
+
prompt: any;
|
|
97
|
+
size?: string | undefined;
|
|
98
|
+
};
|
|
99
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
100
|
+
toModelOutput?: ((options: {
|
|
101
|
+
toolCallId: string;
|
|
102
|
+
input: {
|
|
103
|
+
prompt: any;
|
|
104
|
+
size?: string | undefined;
|
|
105
|
+
};
|
|
106
|
+
output: NoInfer<string | {
|
|
107
|
+
imageUrl: any;
|
|
108
|
+
prompt: any;
|
|
109
|
+
}>;
|
|
110
|
+
}) => import("@ai-sdk/provider-utils").ToolResultOutput | PromiseLike<import("@ai-sdk/provider-utils").ToolResultOutput>) | undefined;
|
|
111
|
+
} & {
|
|
112
|
+
outputSchema?: aiTypes.FlexibleSchema<string | {
|
|
113
|
+
imageUrl: any;
|
|
114
|
+
prompt: any;
|
|
115
|
+
}> | undefined;
|
|
116
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
117
|
+
prompt: any;
|
|
118
|
+
size?: string | undefined;
|
|
119
|
+
}, string | {
|
|
120
|
+
imageUrl: any;
|
|
121
|
+
prompt: any;
|
|
122
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
123
|
+
} & {
|
|
124
|
+
description?: string | ((options: {
|
|
125
|
+
context: NoInfer<import("@ai-sdk/provider-utils").Context>;
|
|
126
|
+
experimental_sandbox?: aiTypes.Experimental_SandboxSession;
|
|
127
|
+
}) => string) | undefined;
|
|
128
|
+
strict?: boolean;
|
|
129
|
+
inputExamples?: {
|
|
130
|
+
input: NoInfer<{
|
|
131
|
+
prompt: any;
|
|
132
|
+
size?: string | undefined;
|
|
133
|
+
}>;
|
|
134
|
+
}[] | undefined;
|
|
135
|
+
id?: never;
|
|
136
|
+
isProviderExecuted?: never;
|
|
137
|
+
args?: never;
|
|
138
|
+
supportsDeferredResults?: never;
|
|
139
|
+
} & {
|
|
140
|
+
type: "dynamic";
|
|
141
|
+
} & {
|
|
142
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
143
|
+
prompt: any;
|
|
144
|
+
size?: string | undefined;
|
|
145
|
+
}, string | {
|
|
146
|
+
imageUrl: any;
|
|
147
|
+
prompt: any;
|
|
148
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
149
|
+
}) | ({
|
|
150
|
+
title?: string;
|
|
151
|
+
providerOptions?: import("@ai-sdk/provider-utils").ProviderOptions;
|
|
152
|
+
metadata?: import("@ai-sdk/provider").JSONObject;
|
|
153
|
+
inputSchema: aiTypes.FlexibleSchema<{
|
|
154
|
+
prompt: any;
|
|
155
|
+
size?: string | undefined;
|
|
156
|
+
}>;
|
|
157
|
+
contextSchema?: aiTypes.FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
|
|
158
|
+
needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
|
|
159
|
+
prompt: any;
|
|
160
|
+
size?: string | undefined;
|
|
161
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>> | undefined;
|
|
162
|
+
onInputStart?: ((options: aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
163
|
+
onInputDelta?: ((options: {
|
|
164
|
+
inputTextDelta: string;
|
|
165
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
166
|
+
onInputAvailable?: ((options: {
|
|
167
|
+
input: {
|
|
168
|
+
prompt: any;
|
|
169
|
+
size?: string | undefined;
|
|
170
|
+
};
|
|
171
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
172
|
+
toModelOutput?: ((options: {
|
|
173
|
+
toolCallId: string;
|
|
174
|
+
input: {
|
|
175
|
+
prompt: any;
|
|
176
|
+
size?: string | undefined;
|
|
177
|
+
};
|
|
178
|
+
output: NoInfer<string | {
|
|
179
|
+
imageUrl: any;
|
|
180
|
+
prompt: any;
|
|
181
|
+
}>;
|
|
182
|
+
}) => import("@ai-sdk/provider-utils").ToolResultOutput | PromiseLike<import("@ai-sdk/provider-utils").ToolResultOutput>) | undefined;
|
|
183
|
+
} & {
|
|
184
|
+
outputSchema?: aiTypes.FlexibleSchema<string | {
|
|
185
|
+
imageUrl: any;
|
|
186
|
+
prompt: any;
|
|
187
|
+
}> | undefined;
|
|
188
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
189
|
+
prompt: any;
|
|
190
|
+
size?: string | undefined;
|
|
191
|
+
}, string | {
|
|
192
|
+
imageUrl: any;
|
|
193
|
+
prompt: any;
|
|
194
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
195
|
+
} & {
|
|
196
|
+
type: "provider";
|
|
197
|
+
id: `${string}.${string}`;
|
|
198
|
+
args: Record<string, unknown>;
|
|
199
|
+
description?: never;
|
|
200
|
+
strict?: never;
|
|
201
|
+
inputExamples?: never;
|
|
202
|
+
} & {
|
|
203
|
+
isProviderExecuted: false;
|
|
204
|
+
supportsDeferredResults?: never;
|
|
205
|
+
} & {
|
|
206
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
207
|
+
prompt: any;
|
|
208
|
+
size?: string | undefined;
|
|
209
|
+
}, string | {
|
|
210
|
+
imageUrl: any;
|
|
211
|
+
prompt: any;
|
|
212
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
213
|
+
}) | ({
|
|
214
|
+
title?: string;
|
|
215
|
+
providerOptions?: import("@ai-sdk/provider-utils").ProviderOptions;
|
|
216
|
+
metadata?: import("@ai-sdk/provider").JSONObject;
|
|
217
|
+
inputSchema: aiTypes.FlexibleSchema<{
|
|
218
|
+
prompt: any;
|
|
219
|
+
size?: string | undefined;
|
|
220
|
+
}>;
|
|
221
|
+
contextSchema?: aiTypes.FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
|
|
222
|
+
needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
|
|
223
|
+
prompt: any;
|
|
224
|
+
size?: string | undefined;
|
|
225
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>> | undefined;
|
|
226
|
+
onInputStart?: ((options: aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
227
|
+
onInputDelta?: ((options: {
|
|
228
|
+
inputTextDelta: string;
|
|
229
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
230
|
+
onInputAvailable?: ((options: {
|
|
231
|
+
input: {
|
|
232
|
+
prompt: any;
|
|
233
|
+
size?: string | undefined;
|
|
234
|
+
};
|
|
235
|
+
} & aiTypes.ToolExecutionOptions<NoInfer<import("@ai-sdk/provider-utils").Context>>) => void | PromiseLike<void>) | undefined;
|
|
236
|
+
toModelOutput?: ((options: {
|
|
237
|
+
toolCallId: string;
|
|
238
|
+
input: {
|
|
239
|
+
prompt: any;
|
|
240
|
+
size?: string | undefined;
|
|
241
|
+
};
|
|
242
|
+
output: NoInfer<string | {
|
|
243
|
+
imageUrl: any;
|
|
244
|
+
prompt: any;
|
|
245
|
+
}>;
|
|
246
|
+
}) => import("@ai-sdk/provider-utils").ToolResultOutput | PromiseLike<import("@ai-sdk/provider-utils").ToolResultOutput>) | undefined;
|
|
247
|
+
} & {
|
|
248
|
+
outputSchema?: aiTypes.FlexibleSchema<string | {
|
|
249
|
+
imageUrl: any;
|
|
250
|
+
prompt: any;
|
|
251
|
+
}> | undefined;
|
|
252
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
253
|
+
prompt: any;
|
|
254
|
+
size?: string | undefined;
|
|
255
|
+
}, string | {
|
|
256
|
+
imageUrl: any;
|
|
257
|
+
prompt: any;
|
|
258
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
259
|
+
} & {
|
|
260
|
+
type: "provider";
|
|
261
|
+
id: `${string}.${string}`;
|
|
262
|
+
args: Record<string, unknown>;
|
|
263
|
+
description?: never;
|
|
264
|
+
strict?: never;
|
|
265
|
+
inputExamples?: never;
|
|
266
|
+
} & {
|
|
267
|
+
isProviderExecuted: true;
|
|
268
|
+
supportsDeferredResults?: boolean;
|
|
269
|
+
} & {
|
|
270
|
+
execute: aiTypes.ToolExecuteFunction<{
|
|
271
|
+
prompt: any;
|
|
272
|
+
size?: string | undefined;
|
|
273
|
+
}, string | {
|
|
274
|
+
imageUrl: any;
|
|
275
|
+
prompt: any;
|
|
276
|
+
}, NoInfer<import("@ai-sdk/provider-utils").Context>>;
|
|
277
|
+
});
|
|
12
278
|
export default generateImageHandler;
|
|
@@ -31,13 +31,15 @@ const handlePostRequest = async (req, res) => {
|
|
|
31
31
|
});
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
+
Object.assign(transports[sessionId].context, req.unchainedContext);
|
|
34
35
|
transport = transports[sessionId].transport;
|
|
35
36
|
}
|
|
36
37
|
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
38
|
+
const contextHolder = req.unchainedContext;
|
|
37
39
|
transport = new StreamableHTTPServerTransport({
|
|
38
40
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
39
41
|
onsessioninitialized: (sessionId) => {
|
|
40
|
-
transports[sessionId] = { transport, userId: currentUserId };
|
|
42
|
+
transports[sessionId] = { transport, userId: currentUserId, context: contextHolder };
|
|
41
43
|
},
|
|
42
44
|
});
|
|
43
45
|
transport.onclose = () => {
|
|
@@ -45,12 +47,12 @@ const handlePostRequest = async (req, res) => {
|
|
|
45
47
|
delete transports[transport.sessionId];
|
|
46
48
|
}
|
|
47
49
|
};
|
|
48
|
-
const roles =
|
|
50
|
+
const roles = contextHolder.user?.roles || [];
|
|
49
51
|
const { default: initMCPServer } = await import("../mcp/index.js");
|
|
50
52
|
const server = initMCPServer(new McpServer({
|
|
51
53
|
name: 'Unchained MCP Server',
|
|
52
54
|
version: '1.0.0',
|
|
53
|
-
}),
|
|
55
|
+
}), contextHolder, roles);
|
|
54
56
|
await server.connect(transport);
|
|
55
57
|
}
|
|
56
58
|
else {
|
|
@@ -74,6 +76,7 @@ const handleSessionRequest = async (req, res) => {
|
|
|
74
76
|
res.status(400).send('Invalid or missing session ID');
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
79
|
+
Object.assign(transports[sessionId].context, req.unchainedContext);
|
|
77
80
|
const transport = transports[sessionId].transport;
|
|
78
81
|
await transport.handleRequest(req, res);
|
|
79
82
|
};
|
|
@@ -45,13 +45,15 @@ const mcpHandler = async (req, res) => {
|
|
|
45
45
|
id: null,
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
|
+
Object.assign(transports[sessionId].context, req.unchainedContext);
|
|
48
49
|
transport = transports[sessionId].transport;
|
|
49
50
|
}
|
|
50
51
|
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
52
|
+
const contextHolder = req.unchainedContext;
|
|
51
53
|
transport = new StreamableHTTPServerTransport({
|
|
52
54
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
53
55
|
onsessioninitialized: (sessionId) => {
|
|
54
|
-
transports[sessionId] = { transport, userId: currentUserId };
|
|
56
|
+
transports[sessionId] = { transport, userId: currentUserId, context: contextHolder };
|
|
55
57
|
},
|
|
56
58
|
});
|
|
57
59
|
transport.onclose = () => {
|
|
@@ -64,7 +66,7 @@ const mcpHandler = async (req, res) => {
|
|
|
64
66
|
const server = initMCPServer(new McpServer({
|
|
65
67
|
name: 'Unchained MCP Server',
|
|
66
68
|
version: '1.0.0',
|
|
67
|
-
}),
|
|
69
|
+
}), contextHolder, roles);
|
|
68
70
|
await server.connect(transport);
|
|
69
71
|
}
|
|
70
72
|
else {
|
|
@@ -85,6 +87,7 @@ const mcpHandler = async (req, res) => {
|
|
|
85
87
|
if (!sessionId || !transports[sessionId] || transports[sessionId].userId !== currentUserId) {
|
|
86
88
|
return res.status(400).send('Invalid or missing session ID');
|
|
87
89
|
}
|
|
90
|
+
Object.assign(transports[sessionId].context, req.unchainedContext);
|
|
88
91
|
const transport = transports[sessionId].transport;
|
|
89
92
|
await transport.handleRequest(req.raw, res.raw);
|
|
90
93
|
return res;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { quotationManagement, QuotationManagementSchema } from "./quotationManagement.js";
|
|
2
2
|
export const registerQuotationTools = (server, context) => {
|
|
3
|
-
server.tool('quotation_management', 'Unified quotation management system. Supports: LIST (get quotations with filters and pagination), GET (single quotation by ID), COUNT (count quotations),
|
|
3
|
+
server.tool('quotation_management', 'Unified quotation management system. Supports: LIST (get quotations with filters and pagination), GET (single quotation by ID), COUNT (count quotations), VERIFY (verify a REQUESTED quotation), MAKE_PROPOSAL (create proposal for PROCESSING quotation), REJECT (reject any quotation except FULFILLED). Quotations go through lifecycle: REQUESTED → PROCESSING → PROPOSED → FULFILLED/REJECTED.', QuotationManagementSchema, async (params) => quotationManagement(context, params));
|
|
4
4
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
2
|
export default function createBookmark(root: never, { productId, userId, meta }: {
|
|
3
3
|
productId: string;
|
|
4
|
-
userId
|
|
4
|
+
userId?: string;
|
|
5
5
|
meta?: any;
|
|
6
6
|
}, { modules, userId: currentUserId }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-bookmarks").Bookmark> | null>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { BookmarkAlreadyExistsError, InvalidIdError, ProductNotFoundError } from "../../../errors.js";
|
|
3
3
|
export default async function createBookmark(root, { productId, userId, meta }, { modules, userId: currentUserId }) {
|
|
4
|
-
|
|
4
|
+
const targetUserId = userId || currentUserId;
|
|
5
|
+
log(`mutation createBookmark for ${targetUserId}`, {
|
|
5
6
|
productId,
|
|
6
7
|
userId: currentUserId,
|
|
7
8
|
});
|
|
@@ -11,13 +12,13 @@ export default async function createBookmark(root, { productId, userId, meta },
|
|
|
11
12
|
throw new ProductNotFoundError({ productId });
|
|
12
13
|
const [bookmark] = await modules.bookmarks.findBookmarks({
|
|
13
14
|
productId,
|
|
14
|
-
userId,
|
|
15
|
+
userId: targetUserId,
|
|
15
16
|
meta,
|
|
16
17
|
});
|
|
17
18
|
if (bookmark)
|
|
18
19
|
throw new BookmarkAlreadyExistsError({ bookmarkId: bookmark._id });
|
|
19
20
|
const bookmarkId = await modules.bookmarks.create({
|
|
20
|
-
userId,
|
|
21
|
+
userId: targetUserId,
|
|
21
22
|
productId,
|
|
22
23
|
meta,
|
|
23
24
|
});
|
|
@@ -623,6 +623,10 @@ declare const types: {
|
|
|
623
623
|
isExpired: (obj: import("@unchainedshop/core-quotations").Quotation, { referenceDate }: {
|
|
624
624
|
referenceDate: Date;
|
|
625
625
|
}, { modules }: import("../../context.ts").Context) => boolean;
|
|
626
|
+
price: (obj: import("@unchainedshop/core-quotations").Quotation) => {
|
|
627
|
+
amount: number;
|
|
628
|
+
currencyCode: string;
|
|
629
|
+
} | null;
|
|
626
630
|
product: (obj: import("@unchainedshop/core-quotations").Quotation, _: never, { loaders }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-products").Product>;
|
|
627
631
|
status: (obj: import("@unchainedshop/core-quotations").Quotation, _: never, { modules }: import("../../context.ts").Context) => import("@unchainedshop/core-quotations").QuotationStatus;
|
|
628
632
|
user: (obj: import("@unchainedshop/core-quotations").Quotation, _: never, { loaders }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-users").User>;
|
|
@@ -6,6 +6,10 @@ export declare const Quotation: {
|
|
|
6
6
|
isExpired: (obj: QuotationType, { referenceDate }: {
|
|
7
7
|
referenceDate: Date;
|
|
8
8
|
}, { modules }: Context) => boolean;
|
|
9
|
+
price: (obj: QuotationType) => {
|
|
10
|
+
amount: number;
|
|
11
|
+
currencyCode: string;
|
|
12
|
+
} | null;
|
|
9
13
|
product: (obj: QuotationType, _: never, { loaders }: Context) => Promise<import("@unchainedshop/core-products").Product>;
|
|
10
14
|
status: (obj: QuotationType, _: never, { modules }: Context) => import("@unchainedshop/core-quotations").QuotationStatus;
|
|
11
15
|
user: (obj: QuotationType, _: never, { loaders }: Context) => Promise<import("@unchainedshop/core-users").User>;
|
|
@@ -2,6 +2,7 @@ export const Quotation = {
|
|
|
2
2
|
country: async (obj, _, { loaders }) => obj.countryCode ? loaders.countryLoader.load({ isoCode: obj.countryCode }) : null,
|
|
3
3
|
currency: async (obj, _, { loaders }) => obj.currencyCode ? loaders.currencyLoader.load({ isoCode: obj.currencyCode }) : null,
|
|
4
4
|
isExpired: (obj, { referenceDate }, { modules }) => modules.quotations.isExpired(obj, { referenceDate }),
|
|
5
|
+
price: (obj) => obj.price != null && obj.currencyCode ? { amount: obj.price, currencyCode: obj.currencyCode } : null,
|
|
5
6
|
product: async (obj, _, { loaders }) => {
|
|
6
7
|
const product = await loaders.productLoader.load({
|
|
7
8
|
productId: obj.productId,
|
package/lib/roles/loggedIn.js
CHANGED
|
@@ -171,6 +171,7 @@ export const loggedIn = (role, actions) => {
|
|
|
171
171
|
role.allow(actions.reviewProduct, () => true);
|
|
172
172
|
role.allow(actions.updateProductReview, isOwnedProductReview);
|
|
173
173
|
role.allow(actions.requestQuotation, () => true);
|
|
174
|
+
role.allow(actions.viewQuotation, isOwnedQuotation);
|
|
174
175
|
role.allow(actions.answerQuotation, isOwnedQuotation);
|
|
175
176
|
role.allow(actions.manageBookmarks, isOwnedBookmark);
|
|
176
177
|
role.allow(actions.createBookmark, isOwnBookmarkUser);
|
|
@@ -57,6 +57,11 @@ export default [
|
|
|
57
57
|
country: Country
|
|
58
58
|
currency: Currency
|
|
59
59
|
configuration: [ProductConfigurationParameter!]
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
Proposed unit price (minor units of the quotation's currency), set when the quotation reaches PROPOSED
|
|
63
|
+
"""
|
|
64
|
+
price: Price
|
|
60
65
|
}
|
|
61
66
|
`,
|
|
62
67
|
];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/api",
|
|
3
3
|
"description": "GraphQL API layer for the Unchained Engine with Express/Fastify adapters and MCP server",
|
|
4
|
-
"version": "4.8.
|
|
4
|
+
"version": "4.8.19",
|
|
5
5
|
"main": "lib/api-index.js",
|
|
6
6
|
"types": "lib/api-index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
},
|
|
61
61
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@ai-sdk/mcp": "^
|
|
63
|
+
"@ai-sdk/mcp": "^2.0.29",
|
|
64
64
|
"@fastify/cookie": ">= 11 < 12",
|
|
65
65
|
"@fastify/multipart": ">= 9 < 11",
|
|
66
66
|
"@fastify/session": ">= 11 < 12",
|
|
67
|
-
"@fastify/static": ">=
|
|
67
|
+
"@fastify/static": ">= 10.1.3 < 11",
|
|
68
68
|
"@modelcontextprotocol/sdk": ">= 1 < 2",
|
|
69
|
-
"ai": ">=
|
|
69
|
+
"ai": ">= 7 < 8",
|
|
70
70
|
"express": ">= 5 < 6",
|
|
71
71
|
"express-session": ">= 1.18 < 2",
|
|
72
72
|
"fastify": ">= 5.2 < 6",
|
|
@@ -129,16 +129,16 @@
|
|
|
129
129
|
"zod": "^3.25.76 || ^4"
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
|
-
"@ai-sdk/mcp": "^
|
|
132
|
+
"@ai-sdk/mcp": "^2.0.29",
|
|
133
133
|
"@fastify/cookie": "^11.0.2",
|
|
134
134
|
"@fastify/multipart": "^10.0.0",
|
|
135
135
|
"@fastify/session": "^11.1.0",
|
|
136
|
-
"@fastify/static": "^
|
|
136
|
+
"@fastify/static": "^10.1.3",
|
|
137
137
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
138
138
|
"@types/express": "^5.0.3",
|
|
139
139
|
"@types/express-session": "^1.18.2",
|
|
140
|
-
"@types/node": "^
|
|
141
|
-
"ai": "^
|
|
140
|
+
"@types/node": "^26.2.0",
|
|
141
|
+
"ai": "^7.0.58",
|
|
142
142
|
"express": "^5.1.0",
|
|
143
143
|
"express-session": "^1.18.1",
|
|
144
144
|
"fastify": "^5.4.0",
|