postfast-mcp 0.8.2 → 0.9.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/dist/core/app.d.ts +42 -0
- package/dist/core/app.js +107 -0
- package/dist/core/app.js.map +1 -0
- package/dist/core/backend-port.d.ts +25 -1
- package/dist/core/build-tools.d.ts +20 -5
- package/dist/core/build-tools.js +50 -15
- package/dist/core/build-tools.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/instructions.d.ts +6 -1
- package/dist/core/instructions.js +47 -58
- package/dist/core/instructions.js.map +1 -1
- package/dist/core/tool-def.d.ts +29 -13
- package/dist/core/tools/accounts.js +12 -4
- package/dist/core/tools/accounts.js.map +1 -1
- package/dist/core/tools/app.d.ts +7 -0
- package/dist/core/tools/app.js +259 -0
- package/dist/core/tools/app.js.map +1 -0
- package/dist/core/tools/inbox-confirm.d.ts +2 -3
- package/dist/core/tools/inbox-confirm.js +5 -6
- package/dist/core/tools/inbox-confirm.js.map +1 -1
- package/dist/core/tools/inbox.js +12 -12
- package/dist/core/tools/inbox.js.map +1 -1
- package/dist/core/tools/index.js +2 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/posts.js +14 -12
- package/dist/core/tools/posts.js.map +1 -1
- package/dist/core/tools/uploads.js +6 -12
- package/dist/core/tools/uploads.js.map +1 -1
- package/dist/core/types.d.ts +117 -0
- package/dist/stdio/index.js +1 -1
- package/dist/view/index.html +125 -0
- package/package.json +8 -3
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{
|
|
12
12
|
"name": "postfast",
|
|
13
13
|
"description": "Schedule, manage, and analyze social media posts via PostFast. Supports Facebook, Instagram, X, TikTok, LinkedIn, YouTube, BlueSky, Threads, Pinterest, Telegram, and Google Business Profile.",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.9.0",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "PostFast",
|
|
17
17
|
"email": "me@peturgeorgievv.com"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postfast",
|
|
3
3
|
"description": "Schedule, manage, and analyze social media posts via PostFast. Supports Facebook, Instagram, X, TikTok, LinkedIn, YouTube, BlueSky, Threads, Pinterest, Telegram, and Google Business Profile. Needs POSTFAST_API_KEY set before Claude Code starts; in PostFast, open API in the sidebar and click Generate API Key.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "PostFast",
|
|
7
7
|
"email": "me@peturgeorgievv.com"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"mcpServers": {
|
|
14
14
|
"postfast": {
|
|
15
15
|
"command": "npx",
|
|
16
|
-
"args": ["-y", "postfast-mcp@0.
|
|
16
|
+
"args": ["-y", "postfast-mcp@0.9.0"],
|
|
17
17
|
"env": {
|
|
18
18
|
"POSTFAST_API_KEY": "${POSTFAST_API_KEY}"
|
|
19
19
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { ToolIcon } from './tool-def.js';
|
|
3
|
+
import type { AppEntrypoint } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a host passes to turn the calendar app on (BuildToolsOptions.app).
|
|
6
|
+
* Each value is an origin (scheme and host, no path) that differs per
|
|
7
|
+
* deployment.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppOptions {
|
|
10
|
+
/** Serves the signed media URLs: the only origin the view loads images and video from. */
|
|
11
|
+
mediaOrigin: string;
|
|
12
|
+
/** The view's dedicated origin (`_meta.ui.domain`). */
|
|
13
|
+
domain: string;
|
|
14
|
+
/** The PostFast web app, target of the "Open in PostFast" links. */
|
|
15
|
+
webAppUrl: string;
|
|
16
|
+
}
|
|
17
|
+
/** Versioned: a change an open view can't handle ships under a new URI. */
|
|
18
|
+
export declare const APP_RESOURCE_URI = "ui://postfast/app-v1";
|
|
19
|
+
export declare const APP_RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
20
|
+
/** Throws unless every option is a bare origin: https, or http on a loopback host. */
|
|
21
|
+
export declare function assertAppOptions(app: AppOptions): void;
|
|
22
|
+
/**
|
|
23
|
+
* Tool `_meta` for the app. With an entrypoint, the tool opens the view and is
|
|
24
|
+
* listed in the host's sidebar (global) or as a conversation tab (thread);
|
|
25
|
+
* without one, only the view can call it. Every app tool is hidden from the
|
|
26
|
+
* model.
|
|
27
|
+
*/
|
|
28
|
+
export declare function appToolMeta(entrypoint?: AppEntrypoint): Record<string, unknown>;
|
|
29
|
+
/** The entrypoint icon in tools/list. */
|
|
30
|
+
export declare const APP_ICON: ToolIcon;
|
|
31
|
+
/** The built view, one self-contained HTML file (dist/view/index.html), read once. */
|
|
32
|
+
export declare function appViewHtml(): string;
|
|
33
|
+
/** Registers the view as the app's UI resource. */
|
|
34
|
+
export declare function registerAppResource(server: McpServer, app: AppOptions): void;
|
|
35
|
+
/**
|
|
36
|
+
* Lists icons on the named tools in tools/list. McpServer has no field for
|
|
37
|
+
* tool icons, so this wraps its tools/list handler and adds `icons` to those
|
|
38
|
+
* tools only; every other field stays the SDK's own output. If the SDK keeps
|
|
39
|
+
* its handlers elsewhere, the icons are skipped with a log and the host falls
|
|
40
|
+
* back to the app's own logo.
|
|
41
|
+
*/
|
|
42
|
+
export declare function listToolIcons(server: McpServer, icons: Map<string, ToolIcon[]>): void;
|
package/dist/core/app.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
/** Versioned: a change an open view can't handle ships under a new URI. */
|
|
3
|
+
export const APP_RESOURCE_URI = 'ui://postfast/app-v1';
|
|
4
|
+
export const APP_RESOURCE_MIME_TYPE = 'text/html;profile=mcp-app';
|
|
5
|
+
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
6
|
+
/** Throws unless every option is a bare origin: https, or http on a loopback host. */
|
|
7
|
+
export function assertAppOptions(app) {
|
|
8
|
+
for (const key of ['mediaOrigin', 'domain', 'webAppUrl']) {
|
|
9
|
+
const value = app?.[key];
|
|
10
|
+
let url;
|
|
11
|
+
try {
|
|
12
|
+
url = new URL(value);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
url = undefined;
|
|
16
|
+
}
|
|
17
|
+
const secure = url?.protocol === 'https:' ||
|
|
18
|
+
(url?.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname));
|
|
19
|
+
if (!url || !secure || url.origin !== value) {
|
|
20
|
+
throw new Error(`app.${key} must be an https origin with no path, such as https://example.com; got ${JSON.stringify(value)}.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Tool `_meta` for the app. With an entrypoint, the tool opens the view and is
|
|
26
|
+
* listed in the host's sidebar (global) or as a conversation tab (thread);
|
|
27
|
+
* without one, only the view can call it. Every app tool is hidden from the
|
|
28
|
+
* model.
|
|
29
|
+
*/
|
|
30
|
+
export function appToolMeta(entrypoint) {
|
|
31
|
+
if (!entrypoint) {
|
|
32
|
+
return { ui: { visibility: ['app'] } };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
ui: { resourceUri: APP_RESOURCE_URI, visibility: ['app'] },
|
|
36
|
+
// The flat key older hosts read; ext-apps' registerAppTool writes both.
|
|
37
|
+
'ui/resourceUri': APP_RESOURCE_URI,
|
|
38
|
+
'openai/ui': { entrypoints: [{ type: entrypoint }] },
|
|
39
|
+
'openai/iconStyle': 'monochrome',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Resource `_meta`: images and video only from the media origin, no network access, fullscreen. */
|
|
43
|
+
function appResourceMeta(app) {
|
|
44
|
+
return {
|
|
45
|
+
ui: {
|
|
46
|
+
csp: { resourceDomains: [app.mediaOrigin], connectDomains: [] },
|
|
47
|
+
domain: app.domain,
|
|
48
|
+
prefersBorder: false,
|
|
49
|
+
},
|
|
50
|
+
'openai/ui': { preferredDisplayMode: 'fullscreen' },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// A calendar outline: 20x20, 1.33 px strokes, currentColor, transparent.
|
|
54
|
+
const CALENDAR_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round">' +
|
|
55
|
+
'<rect x="3" y="4.5" width="14" height="12.5" rx="2.5"/>' +
|
|
56
|
+
'<path d="M3 8.5h14M7 3v3M13 3v3M6.5 11.75h1.5M9.25 11.75h1.5M12 11.75h1.5M6.5 14.25h1.5M9.25 14.25h1.5"/>' +
|
|
57
|
+
'</svg>';
|
|
58
|
+
/** The entrypoint icon in tools/list. */
|
|
59
|
+
export const APP_ICON = {
|
|
60
|
+
src: `data:image/svg+xml;base64,${Buffer.from(CALENDAR_SVG).toString('base64')}`,
|
|
61
|
+
mimeType: 'image/svg+xml',
|
|
62
|
+
sizes: ['any'],
|
|
63
|
+
};
|
|
64
|
+
let viewHtml;
|
|
65
|
+
/** The built view, one self-contained HTML file (dist/view/index.html), read once. */
|
|
66
|
+
export function appViewHtml() {
|
|
67
|
+
viewHtml ??= readFileSync(new URL('../view/index.html', import.meta.url), 'utf8');
|
|
68
|
+
return viewHtml;
|
|
69
|
+
}
|
|
70
|
+
/** Registers the view as the app's UI resource. */
|
|
71
|
+
export function registerAppResource(server, app) {
|
|
72
|
+
const text = appViewHtml();
|
|
73
|
+
const _meta = appResourceMeta(app);
|
|
74
|
+
server.registerResource('postfast-calendar', APP_RESOURCE_URI, {
|
|
75
|
+
title: 'PostFast Calendar',
|
|
76
|
+
description: 'The PostFast content calendar view.',
|
|
77
|
+
mimeType: APP_RESOURCE_MIME_TYPE,
|
|
78
|
+
_meta,
|
|
79
|
+
}, async () => ({
|
|
80
|
+
contents: [{ uri: APP_RESOURCE_URI, mimeType: APP_RESOURCE_MIME_TYPE, text, _meta }],
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Lists icons on the named tools in tools/list. McpServer has no field for
|
|
85
|
+
* tool icons, so this wraps its tools/list handler and adds `icons` to those
|
|
86
|
+
* tools only; every other field stays the SDK's own output. If the SDK keeps
|
|
87
|
+
* its handlers elsewhere, the icons are skipped with a log and the host falls
|
|
88
|
+
* back to the app's own logo.
|
|
89
|
+
*/
|
|
90
|
+
export function listToolIcons(server, icons) {
|
|
91
|
+
const handlers = server.server._requestHandlers;
|
|
92
|
+
const list = handlers instanceof Map ? handlers.get('tools/list') : undefined;
|
|
93
|
+
if (!list) {
|
|
94
|
+
console.error('[postfast-mcp/core] tool icons not listed: no tools/list handler to wrap');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
handlers.set('tools/list', async (request, extra) => {
|
|
98
|
+
const result = (await list(request, extra));
|
|
99
|
+
for (const tool of result.tools ?? []) {
|
|
100
|
+
const toolIcons = icons.get(tool.name);
|
|
101
|
+
if (toolIcons)
|
|
102
|
+
tool.icons = toolIcons;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnalyticsResponse, FollowerHistory, GbpLocation, PaginatedPosts, PinterestBoard, Place, Platform, SignedUploadUrl, SocialAccount, YouTubePlaylist } from './types.js';
|
|
1
|
+
import type { AnalyticsResponse, AppAction, AppCalendar, AppEntrypoint, AppPostDetail, FollowerHistory, GbpLocation, PaginatedPosts, PinterestBoard, Place, Platform, SignedUploadUrl, SocialAccount, YouTubePlaylist } from './types.js';
|
|
2
2
|
export interface ListPostsArgs {
|
|
3
3
|
page: number;
|
|
4
4
|
limit: number;
|
|
@@ -93,6 +93,27 @@ export interface UploadMediaArgs {
|
|
|
93
93
|
data?: string;
|
|
94
94
|
contentType?: string;
|
|
95
95
|
}
|
|
96
|
+
/** One calendar window for the app. */
|
|
97
|
+
export interface AppCalendarArgs {
|
|
98
|
+
/** ISO 8601; posts whose scheduledAt is at or after this. */
|
|
99
|
+
from: string;
|
|
100
|
+
/** ISO 8601; posts whose scheduledAt is at or before this. */
|
|
101
|
+
to: string;
|
|
102
|
+
/** Most dated posts to return. */
|
|
103
|
+
limit: number;
|
|
104
|
+
/** Most undated drafts to return. */
|
|
105
|
+
draftsLimit: number;
|
|
106
|
+
entrypoint: AppEntrypoint;
|
|
107
|
+
/** True when the open view reloads; false when the app opens. */
|
|
108
|
+
refresh: boolean;
|
|
109
|
+
}
|
|
110
|
+
export interface AppPostArgs {
|
|
111
|
+
postId: string;
|
|
112
|
+
}
|
|
113
|
+
export interface AppActionArgs {
|
|
114
|
+
action: AppAction;
|
|
115
|
+
entrypoint?: AppEntrypoint;
|
|
116
|
+
}
|
|
96
117
|
export interface LocalUploadResult {
|
|
97
118
|
key: string;
|
|
98
119
|
type: 'IMAGE' | 'VIDEO';
|
|
@@ -145,4 +166,7 @@ export interface BackendPort {
|
|
|
145
166
|
setInboxConversationStatus?(args: SetInboxConversationStatusArgs, workspaceId?: string): Promise<unknown>;
|
|
146
167
|
assignInboxConversation?(args: AssignInboxConversationArgs, workspaceId?: string): Promise<unknown>;
|
|
147
168
|
listTikTokSounds?(args: ListTikTokSoundsArgs, workspaceId?: string): Promise<unknown>;
|
|
169
|
+
getAppCalendar?(args: AppCalendarArgs, workspaceId?: string): Promise<AppCalendar>;
|
|
170
|
+
getAppPost?(args: AppPostArgs, workspaceId?: string): Promise<AppPostDetail>;
|
|
171
|
+
recordAppAction?(args: AppActionArgs, workspaceId?: string): Promise<unknown>;
|
|
148
172
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { type AppOptions } from './app.js';
|
|
3
4
|
import type { BackendPort } from './backend-port.js';
|
|
4
5
|
import type { Binding, ResolvedTool } from './tool-def.js';
|
|
5
6
|
export interface ConfirmGateOptions {
|
|
@@ -21,11 +22,20 @@ export interface BuildToolsOptions {
|
|
|
21
22
|
*/
|
|
22
23
|
port?: BackendPort;
|
|
23
24
|
/**
|
|
24
|
-
* Remote only.
|
|
25
|
-
* replies, private replies and deletions
|
|
26
|
-
*
|
|
25
|
+
* Remote only. The key that signs prepare_inbox_action / confirm_inbox_action
|
|
26
|
+
* tokens for comment replies, private replies and deletions. The remote
|
|
27
|
+
* catalogue holds every post and reply for the user's yes either way;
|
|
28
|
+
* without a key those two tools are left out (logged once), so comment
|
|
29
|
+
* replies and deletions are unavailable.
|
|
27
30
|
*/
|
|
28
31
|
confirmGate?: ConfirmGateOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Remote only. Adds the calendar app: two view-opening entrypoint tools and
|
|
34
|
+
* the view's analytics call, all hidden from the model, plus the view as a
|
|
35
|
+
* UI resource (registerCatalogTools). When absent, the output is identical
|
|
36
|
+
* to a catalog without the app.
|
|
37
|
+
*/
|
|
38
|
+
app?: AppOptions;
|
|
29
39
|
}
|
|
30
40
|
/** Resolve the catalog for one binding: filter, flatten binding-variant fields. */
|
|
31
41
|
export declare function buildTools(options: BuildToolsOptions): ResolvedTool[];
|
|
@@ -44,9 +54,14 @@ export declare function toolResult(data: unknown): CallToolResult;
|
|
|
44
54
|
/** A business/tool error the model should see and react to (not a protocol error). */
|
|
45
55
|
export declare function toolError(message: string): CallToolResult;
|
|
46
56
|
/** Run a handler, converting thrown errors into `isError` tool results. */
|
|
47
|
-
export declare function runTool(fn: () => Promise<unknown
|
|
57
|
+
export declare function runTool(fn: () => Promise<unknown>, toResult?: (data: unknown) => CallToolResult): Promise<CallToolResult>;
|
|
48
58
|
export interface RegisterToolsOptions extends BuildToolsOptions {
|
|
49
59
|
port: BackendPort;
|
|
50
60
|
}
|
|
51
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Register the resolved catalog for a binding on an MCP server. With `app`,
|
|
63
|
+
* also registers the view as a UI resource and lists the app tools' icons,
|
|
64
|
+
* but only when a registered tool opens the view (a port without the app
|
|
65
|
+
* methods registers neither).
|
|
66
|
+
*/
|
|
52
67
|
export declare function registerCatalogTools(server: McpServer, options: RegisterToolsOptions): void;
|
package/dist/core/build-tools.js
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
|
+
import { APP_RESOURCE_URI, assertAppOptions, listToolIcons, registerAppResource, } from './app.js';
|
|
1
2
|
import { workspaceIdField } from './shared.js';
|
|
2
3
|
import { ALL_TOOLS } from './tools/index.js';
|
|
4
|
+
let warnedNoConfirmSecret = false;
|
|
3
5
|
/** Resolve the catalog for one binding: filter, flatten binding-variant fields. */
|
|
4
6
|
export function buildTools(options) {
|
|
5
|
-
const { binding, withWorkspaceField = false, port, confirmGate } = options;
|
|
6
|
-
const
|
|
7
|
-
if (
|
|
7
|
+
const { binding, withWorkspaceField = false, port, confirmGate, app } = options;
|
|
8
|
+
const remote = binding === 'remote';
|
|
9
|
+
if (remote && confirmGate && !(confirmGate.secret?.length >= 32)) {
|
|
8
10
|
throw new Error('confirmGate.secret must be at least 32 characters.');
|
|
9
11
|
}
|
|
12
|
+
const secret = remote ? confirmGate?.secret : undefined;
|
|
13
|
+
if (remote && !secret && !warnedNoConfirmSecret) {
|
|
14
|
+
warnedNoConfirmSecret = true;
|
|
15
|
+
console.error('[postfast-mcp/core] no confirmGate secret: prepare_inbox_action and confirm_inbox_action are left out, so comment replies and deletions are unavailable');
|
|
16
|
+
}
|
|
17
|
+
const appOn = remote && !!app;
|
|
18
|
+
if (appOn)
|
|
19
|
+
assertAppOptions(app);
|
|
20
|
+
// Only app tools get the app options; the confirm secret goes to every tool as before.
|
|
21
|
+
const contextFor = (def) => ({
|
|
22
|
+
...(secret ? { confirmSecret: secret } : {}),
|
|
23
|
+
...(appOn && def.appOnly ? { app } : {}),
|
|
24
|
+
});
|
|
10
25
|
return ALL_TOOLS.filter((def) => {
|
|
11
26
|
if (def.binding !== 'both' && def.binding !== binding)
|
|
12
27
|
return false;
|
|
13
|
-
if (
|
|
28
|
+
if (def.needsConfirmSecret && !secret)
|
|
29
|
+
return false;
|
|
30
|
+
if (def.appOnly && !appOn)
|
|
14
31
|
return false;
|
|
15
32
|
if (port &&
|
|
16
33
|
def.portMethod &&
|
|
@@ -20,22 +37,25 @@ export function buildTools(options) {
|
|
|
20
37
|
}
|
|
21
38
|
return true;
|
|
22
39
|
}).map((def) => {
|
|
23
|
-
const inputSchema = typeof def.inputSchema === 'function' ? def.inputSchema(binding
|
|
24
|
-
const annotations =
|
|
40
|
+
const inputSchema = typeof def.inputSchema === 'function' ? def.inputSchema(binding) : def.inputSchema;
|
|
41
|
+
const annotations = remote && def.remoteAnnotations ? def.remoteAnnotations : def.annotations;
|
|
42
|
+
const ctx = contextFor(def);
|
|
25
43
|
return {
|
|
26
44
|
name: def.name,
|
|
27
45
|
title: def.title,
|
|
28
|
-
description: typeof def.description === 'function' ? def.description(binding
|
|
46
|
+
description: typeof def.description === 'function' ? def.description(binding) : def.description,
|
|
29
47
|
inputSchema: withWorkspaceField && def.workspaceScoped !== false
|
|
30
48
|
? { ...inputSchema, workspaceId: workspaceIdField }
|
|
31
49
|
: inputSchema,
|
|
32
50
|
annotations: { title: def.title, ...annotations },
|
|
33
51
|
_meta: def._meta,
|
|
52
|
+
...(def.icons ? { icons: def.icons } : {}),
|
|
34
53
|
portMethod: def.portMethod,
|
|
35
|
-
//
|
|
36
|
-
run:
|
|
37
|
-
? (p, args, workspaceId) => def.run(p, args, workspaceId,
|
|
54
|
+
// Hand the confirm secret and app options to run(), also for hosts that call tool.run() themselves.
|
|
55
|
+
run: Object.keys(ctx).length
|
|
56
|
+
? (p, args, workspaceId) => def.run(p, args, workspaceId, ctx)
|
|
38
57
|
: def.run,
|
|
58
|
+
...(def.toResult ? { toResult: def.toResult } : {}),
|
|
39
59
|
};
|
|
40
60
|
});
|
|
41
61
|
}
|
|
@@ -69,18 +89,24 @@ export function toolError(message) {
|
|
|
69
89
|
};
|
|
70
90
|
}
|
|
71
91
|
/** Run a handler, converting thrown errors into `isError` tool results. */
|
|
72
|
-
export async function runTool(fn) {
|
|
92
|
+
export async function runTool(fn, toResult = toolResult) {
|
|
73
93
|
try {
|
|
74
|
-
return
|
|
94
|
+
return toResult(await fn());
|
|
75
95
|
}
|
|
76
96
|
catch (err) {
|
|
77
97
|
return toolError(err.message || 'Tool execution failed.');
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Register the resolved catalog for a binding on an MCP server. With `app`,
|
|
102
|
+
* also registers the view as a UI resource and lists the app tools' icons,
|
|
103
|
+
* but only when a registered tool opens the view (a port without the app
|
|
104
|
+
* methods registers neither).
|
|
105
|
+
*/
|
|
81
106
|
export function registerCatalogTools(server, options) {
|
|
82
107
|
const { port } = options;
|
|
83
|
-
|
|
108
|
+
const tools = buildTools(options);
|
|
109
|
+
for (const tool of tools) {
|
|
84
110
|
server.registerTool(tool.name, {
|
|
85
111
|
title: tool.title,
|
|
86
112
|
description: tool.description,
|
|
@@ -90,7 +116,16 @@ export function registerCatalogTools(server, options) {
|
|
|
90
116
|
}, (args) => runTool(() => {
|
|
91
117
|
const { workspaceId, ...rest } = args ?? {};
|
|
92
118
|
return tool.run(port, rest, workspaceId);
|
|
93
|
-
}));
|
|
119
|
+
}, tool.toResult));
|
|
120
|
+
}
|
|
121
|
+
const opensView = (tool) => tool._meta?.ui?.resourceUri === APP_RESOURCE_URI;
|
|
122
|
+
if (options.app && tools.some(opensView)) {
|
|
123
|
+
registerAppResource(server, options.app);
|
|
124
|
+
const icons = new Map();
|
|
125
|
+
for (const tool of tools)
|
|
126
|
+
if (tool.icons)
|
|
127
|
+
icons.set(tool.name, tool.icons);
|
|
128
|
+
listToolIcons(server, icons);
|
|
94
129
|
}
|
|
95
130
|
}
|
|
96
131
|
//# sourceMappingURL=build-tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-tools.js","sourceRoot":"","sources":["../../src/core/build-tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-tools.js","sourceRoot":"","sources":["../../src/core/build-tools.ts"],"names":[],"mappings":"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"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* remote host (social-schedule-mcp). Import as "postfast-mcp/core".
|
|
5
5
|
*/
|
|
6
6
|
export * from './backend-port.js';
|
|
7
|
+
export { APP_RESOURCE_MIME_TYPE, APP_RESOURCE_URI, appViewHtml, assertAppOptions, type AppOptions, } from './app.js';
|
|
7
8
|
export { buildTools, registerCatalogTools, runTool, toolError, toolResult, type BuildToolsOptions, type ConfirmGateOptions, type RegisterToolsOptions, } from './build-tools.js';
|
|
8
9
|
export { SERVER_INSTRUCTIONS, instructionsFor } from './instructions.js';
|
|
9
10
|
export { CREATE_APPROVAL_STATUSES, CREATE_STATUSES, IMAGE_MIME_TYPES, PLATFORMS, POST_STATUSES, SET_APPROVAL_STATUSES, VIDEO_MIME_TYPES, jsonParse, workspaceIdField, } from './shared.js';
|
|
10
|
-
export type { Binding, ResolvedTool, ResolvedToolAnnotations, ToolAnnotations, ToolDef, } from './tool-def.js';
|
|
11
|
+
export type { Binding, ResolvedTool, ResolvedToolAnnotations, ToolAnnotations, ToolDef, ToolIcon, } from './tool-def.js';
|
|
11
12
|
export { ALL_TOOLS } from './tools/index.js';
|
|
12
13
|
export * from './types.js';
|
package/dist/core/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* remote host (social-schedule-mcp). Import as "postfast-mcp/core".
|
|
5
5
|
*/
|
|
6
6
|
export * from './backend-port.js';
|
|
7
|
+
export { APP_RESOURCE_MIME_TYPE, APP_RESOURCE_URI, appViewHtml, assertAppOptions, } from './app.js';
|
|
7
8
|
export { buildTools, registerCatalogTools, runTool, toolError, toolResult, } from './build-tools.js';
|
|
8
9
|
export { SERVER_INSTRUCTIONS, instructionsFor } from './instructions.js';
|
|
9
10
|
export { CREATE_APPROVAL_STATUSES, CREATE_STATUSES, IMAGE_MIME_TYPES, PLATFORMS, POST_STATUSES, SET_APPROVAL_STATUSES, VIDEO_MIME_TYPES, jsonParse, workspaceIdField, } from './shared.js';
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GAEjB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC;AASrB,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,cAAc,YAAY,CAAC"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { Binding } from './tool-def.js';
|
|
2
2
|
export declare const SERVER_INSTRUCTIONS: Record<Binding, string>;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The server instructions for a binding. The remote text is always the one
|
|
5
|
+
* that holds posts and replies for the user's yes; `opts` is accepted for
|
|
6
|
+
* hosts written against 0.8 and earlier, and ignored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function instructionsFor(binding: Binding, _opts?: {
|
|
4
9
|
gated?: boolean;
|
|
5
10
|
}): string;
|
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
* backend's actual create-posts validation, so it matches what the API
|
|
5
5
|
* enforces (prevents the model discovering rules by failing).
|
|
6
6
|
*
|
|
7
|
-
* The two bindings differ
|
|
8
|
-
* switching and conversation-media/URL uploads
|
|
9
|
-
*
|
|
7
|
+
* The two bindings share most paragraphs. They differ where their tool
|
|
8
|
+
* surfaces differ (workspace switching and conversation-media/URL uploads on
|
|
9
|
+
* the remote host, local files on stdio) and in how posts and comment replies
|
|
10
|
+
* go out: on the remote binding every post is held until approve_posts, and
|
|
11
|
+
* replies and comment deletions go through prepare_inbox_action and
|
|
12
|
+
* confirm_inbox_action, each only after the user says yes.
|
|
10
13
|
*/
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Media: use the key returned by an upload tool, and set mediaItems[].type to match the file (IMAGE for image/* keys, VIDEO for video/* keys — a mismatch is rejected). Videos are capped at 250MB (Telegram 50MB, Bluesky 100MB). Media is REQUIRED on TikTok, YouTube (video only), Instagram, and Pinterest; X, LinkedIn, Facebook, Threads, Bluesky, Telegram, and Google Business Profile allow text-only posts (GBP accepts 1 image and no video). This requirement applies even to DRAFTS — a draft to a media-required platform without media is rejected. So if the user asks to post to a media-required platform and provides no media, ask them for an image/video (or generate and upload one) BEFORE calling create_posts; don't attempt the post without media.
|
|
16
|
-
|
|
17
|
-
Per-platform limits (media counts / characters):
|
|
14
|
+
const INTRO = `PostFast schedules and publishes social posts across X, Instagram, Facebook, TikTok, LinkedIn, YouTube, Threads, Pinterest, Bluesky, Telegram, and Google Business Profile.`;
|
|
15
|
+
const CONNECTED = `Before scheduling, confirm the target account's connectionStatus is CONNECTED. A DISABLED account will not publish (disconnected accounts are the #1 cause of failed posts) — saving a DRAFT is still allowed.`;
|
|
16
|
+
const MEDIA = `Media: use the key returned by an upload tool, and set mediaItems[].type to match the file (IMAGE for image/* keys, VIDEO for video/* keys — a mismatch is rejected). Videos are capped at 250MB (Telegram 50MB, Bluesky 100MB). Media is REQUIRED on TikTok, YouTube (video only), Instagram, and Pinterest; X, LinkedIn, Facebook, Threads, Bluesky, Telegram, and Google Business Profile allow text-only posts (GBP accepts 1 image and no video). This requirement applies even to DRAFTS — a draft to a media-required platform without media is rejected. So if the user asks to post to a media-required platform and provides no media, ask them for an image/video (or generate and upload one) BEFORE calling create_posts; don't attempt the post without media.`;
|
|
17
|
+
const LIMITS = `Per-platform limits (media counts / characters):
|
|
18
18
|
- X: up to 4 images or 1 video (no mixing); 280 chars (4,000 with X Premium).
|
|
19
19
|
- TikTok: 1 video OR up to 10 images; 4,000 chars.
|
|
20
20
|
- Instagram: up to 10 media items, images and videos mixed; 2,200 chars, max 30 hashtags.
|
|
@@ -23,11 +23,10 @@ Per-platform limits (media counts / characters):
|
|
|
23
23
|
- Threads: up to 10 media items, images and videos mixed; 500 chars.
|
|
24
24
|
- Pinterest: up to 5 images or 1 video; title max 100, description max 800.
|
|
25
25
|
- Google Business Profile: text only, or 1 image and no video; 1,500 chars.
|
|
26
|
-
- Bluesky: up to
|
|
26
|
+
- Bluesky: up to 10 images or 1 video. Telegram: up to 10 media items, at most 3 of them videos.
|
|
27
27
|
No post carries more than 10 media items.
|
|
28
|
-
If content exceeds the target platform's character limit, don't schedule it as-is — tell the user and offer to shorten or split it (X is 280 unless the account has X Premium = 4,000)
|
|
29
|
-
|
|
30
|
-
Platform-specific options go in the controls object, e.g.:
|
|
28
|
+
If content exceeds the target platform's character limit, don't schedule it as-is — tell the user and offer to shorten or split it (X is 280 unless the account has X Premium = 4,000).`;
|
|
29
|
+
const CONTROLS = `Platform-specific options go in the controls object, e.g.:
|
|
31
30
|
- Pinterest: controls.pinterestBoardId is REQUIRED — it is a board's boardId from list_pinterest_boards, NOT the Pinterest account's socialMediaId.
|
|
32
31
|
- Google Business Profile: controls.gbpLocationId is required — the locationId from list_gbp_locations.
|
|
33
32
|
- YouTube: controls.youtubePlaylistId is the playlistId from list_youtube_playlists; youtubeIsShort defaults true; title falls back to the first 100 chars of content.
|
|
@@ -37,56 +36,46 @@ Platform-specific options go in the controls object, e.g.:
|
|
|
37
36
|
- Geotag a place (Facebook/Instagram): call search_places, then pass a returned id as controls.facebookPlaceId (Facebook feed posts only — not Reels/Stories/video) and/or controls.instagramLocationId (Instagram single media only — not carousels). One id works for both.
|
|
38
37
|
- LinkedIn: attach a document via controls.linkedinAttachmentKey.
|
|
39
38
|
- X: controls.xRetweetUrl reposts an existing tweet (content/media are ignored).
|
|
40
|
-
Note: list_pinterest_boards / list_youtube_playlists / list_gbp_locations take the account's socialMediaId; each returned item has BOTH an internal id and the platform id (boardId / playlistId / locationId) — pass the PLATFORM id to controls, not the internal id or the account id
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
39
|
+
Note: list_pinterest_boards / list_youtube_playlists / list_gbp_locations take the account's socialMediaId; each returned item has BOTH an internal id and the platform id (boardId / playlistId / locationId) — pass the PLATFORM id to controls, not the internal id or the account id.`;
|
|
40
|
+
const FAILED = `Failed posts carry lastError — usually a disconnected account (reconnect, then retry) or platform-rejected media.`;
|
|
41
|
+
const DELETE_POST = `delete_post removes a post from PostFast only — it never deletes an already-published post from the social platform. Say so when a user asks to delete something that has already gone out.`;
|
|
42
|
+
const REMOTE_DELETE_POST = `${DELETE_POST} Deleting can't be undone, so show the user which post (account, time, first line) and call delete_post only after they say yes in the conversation.`;
|
|
43
|
+
const STDIO_INSTRUCTIONS = [
|
|
44
|
+
INTRO,
|
|
45
|
+
`Flow: list_accounts to see what's connected → create_posts (one socialMediaId per post; batch up to 15) → attach media via upload_media (a local file, by absolute path) or get_upload_urls (signed PUT upload for raw bytes) → a post publishes when status=SCHEDULED and approvalStatus=APPROVED.`,
|
|
46
|
+
`The POSTFAST_API_KEY is workspace-scoped — every tool acts in that workspace.`,
|
|
47
|
+
CONNECTED,
|
|
48
|
+
`Status & timing: status=SCHEDULED requires scheduledAt on every post (ISO-8601, in the future, within 1 year); status=DRAFT must omit scheduledAt. approvalStatus=APPROVED publishes; PENDING_APPROVAL holds it for review. There is no instant "publish now" — for an immediate post, set scheduledAt a few minutes ahead of now (a time at or before the current moment is rejected).`,
|
|
49
|
+
MEDIA,
|
|
50
|
+
LIMITS,
|
|
51
|
+
CONTROLS,
|
|
52
|
+
FAILED,
|
|
53
|
+
DELETE_POST,
|
|
54
|
+
`Social inbox (comments on your posts — TikTok, Instagram, Facebook Pages, Threads; an account's inboxCapable flag from list_accounts says what is live today): read with list_inbox_conversations → list_inbox_items (get_inbox_unread_count for the total), reply with reply_to_inbox_item (public, under a specific comment item) or send_inbox_private_reply (Instagram only: one per comment, within 7 days). Whether and how long a reply can be comes ONLY from the conversation's server-computed canReply / maxReplyLength / windowState / disabledReason — never assume platform rules. Moderate with set_inbox_item_state (HIDE / UNHIDE / DELETE — DELETE removes the comment on the platform and cannot be undone), triage with set_inbox_conversation_status and assign_inbox_conversation, and call mark_inbox_conversation_read after presenting a thread. It is a comments inbox — never present it as DMs or messages.`,
|
|
55
|
+
].join('\n\n');
|
|
56
|
+
const REMOTE_INSTRUCTIONS = [
|
|
57
|
+
INTRO,
|
|
58
|
+
`Flow: list_accounts (+ list_workspaces) to see what's connected → create_posts (one socialMediaId per post; batch up to 15) → attach media via upload_from_url (a public https URL) or upload_media (base64 bytes in data + contentType). Posts are held for approval and nothing publishes until approve_posts sets them to APPROVED: show the user what will be posted, where and when, and call approve_posts only after they say yes, even if they asked you to post it.`,
|
|
59
|
+
`Workspaces: omit workspaceId to use the connection's default; pass a workspaceId (from list_workspaces) to act in another workspace.`,
|
|
60
|
+
CONNECTED,
|
|
61
|
+
`Status & timing: status=SCHEDULED requires scheduledAt on every post (ISO-8601, in the future, within 1 year); status=DRAFT must omit scheduledAt. Every post is created as PENDING_APPROVAL and publishes at its scheduledAt only once approve_posts sets it to APPROVED.`,
|
|
62
|
+
MEDIA,
|
|
63
|
+
LIMITS,
|
|
64
|
+
CONTROLS,
|
|
65
|
+
FAILED,
|
|
66
|
+
REMOTE_DELETE_POST,
|
|
67
|
+
`Social inbox (comments on your posts: TikTok, Instagram, Facebook Pages, Threads; an account's inboxCapable flag from list_accounts says what is live today): read with list_inbox_conversations → list_inbox_items (get_inbox_unread_count for the total). To reply publicly under a comment (REPLY), send an Instagram private reply (PRIVATE_REPLY: one per comment, within 7 days) or delete a comment permanently (DELETE), call prepare_inbox_action, show the user the preview, and carry it out with confirm_inbox_action only after they say yes, even if they gave you the text. Whether and how long a reply can be comes ONLY from the conversation's server-computed canReply / maxReplyLength / windowState / disabledReason; never assume platform rules. Hide or restore a comment with set_inbox_item_state (HIDE / UNHIDE), triage with set_inbox_conversation_status and assign_inbox_conversation, and call mark_inbox_conversation_read after presenting a thread. It is a comments inbox; never present it as DMs or messages.`,
|
|
68
|
+
].join('\n\n');
|
|
61
69
|
export const SERVER_INSTRUCTIONS = {
|
|
62
70
|
stdio: STDIO_INSTRUCTIONS,
|
|
63
71
|
remote: REMOTE_INSTRUCTIONS,
|
|
64
72
|
};
|
|
65
73
|
/**
|
|
66
|
-
* The
|
|
67
|
-
*
|
|
68
|
-
*
|
|
74
|
+
* The server instructions for a binding. The remote text is always the one
|
|
75
|
+
* that holds posts and replies for the user's yes; `opts` is accepted for
|
|
76
|
+
* hosts written against 0.8 and earlier, and ignored.
|
|
69
77
|
*/
|
|
70
|
-
|
|
71
|
-
[
|
|
72
|
-
'Flow: ',
|
|
73
|
-
"Flow: list_accounts (+ list_workspaces) to see what's connected → create_posts (one socialMediaId per post; batch up to 15) → attach media via upload_from_url (a public https URL) or upload_media (base64 bytes in data + contentType). Posts are held for approval and nothing publishes until approve_posts sets them to APPROVED: show the user what will be posted, where and when, and call approve_posts only after they say yes, even if they asked you to post it.",
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
'Status & timing: ',
|
|
77
|
-
'Status & timing: status=SCHEDULED requires scheduledAt on every post (ISO-8601, in the future, within 1 year); status=DRAFT must omit scheduledAt. Every post is created as PENDING_APPROVAL and publishes at its scheduledAt only once approve_posts sets it to APPROVED.',
|
|
78
|
-
],
|
|
79
|
-
[
|
|
80
|
-
'Social inbox ',
|
|
81
|
-
"Social inbox (comments on your posts: TikTok, Instagram, Facebook Pages, Threads; an account's inboxCapable flag from list_accounts says what is live today): read with list_inbox_conversations → list_inbox_items (get_inbox_unread_count for the total). To reply publicly under a comment (REPLY), send an Instagram private reply (PRIVATE_REPLY: one per comment, within 7 days) or delete a comment permanently (DELETE), call prepare_inbox_action, show the user the preview, and carry it out with confirm_inbox_action only after they say yes, even if they gave you the text. Whether and how long a reply can be comes ONLY from the conversation's server-computed canReply / maxReplyLength / windowState / disabledReason; never assume platform rules. Hide or restore a comment with set_inbox_item_state (HIDE / UNHIDE), triage with set_inbox_conversation_status and assign_inbox_conversation, and call mark_inbox_conversation_read after presenting a thread. It is a comments inbox; never present it as DMs or messages.",
|
|
82
|
-
],
|
|
83
|
-
];
|
|
84
|
-
const GATED_REMOTE_INSTRUCTIONS = REMOTE_INSTRUCTIONS.split('\n\n')
|
|
85
|
-
.map((paragraph) => GATED_PARAGRAPHS.find(([prefix]) => paragraph.startsWith(prefix))?.[1] ?? paragraph)
|
|
86
|
-
.join('\n\n');
|
|
87
|
-
export function instructionsFor(binding, opts) {
|
|
88
|
-
if (binding === 'remote' && opts?.gated)
|
|
89
|
-
return GATED_REMOTE_INSTRUCTIONS;
|
|
78
|
+
export function instructionsFor(binding, _opts) {
|
|
90
79
|
return SERVER_INSTRUCTIONS[binding];
|
|
91
80
|
}
|
|
92
81
|
//# sourceMappingURL=instructions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/core/instructions.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/core/instructions.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,MAAM,KAAK,GAAG,6KAA6K,CAAC;AAE5L,MAAM,SAAS,GAAG,gNAAgN,CAAC;AAEnO,MAAM,KAAK,GAAG,8uBAA8uB,CAAC;AAE7vB,MAAM,MAAM,GAAG;;;;;;;;;;;wLAWyK,CAAC;AAEzL,MAAM,QAAQ,GAAG;;;;;;;;;;0RAUyQ,CAAC;AAE3R,MAAM,MAAM,GAAG,mHAAmH,CAAC;AAEnI,MAAM,WAAW,GAAG,6LAA6L,CAAC;AAElN,MAAM,kBAAkB,GAAG,GAAG,WAAW,sJAAsJ,CAAC;AAEhM,MAAM,kBAAkB,GAAG;IACzB,KAAK;IACL,qSAAqS;IACrS,+EAA+E;IAC/E,SAAS;IACT,yXAAyX;IACzX,KAAK;IACL,MAAM;IACN,QAAQ;IACR,MAAM;IACN,WAAW;IACX,y4BAAy4B;CAC14B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAEf,MAAM,mBAAmB,GAAG;IAC1B,KAAK;IACL,8cAA8c;IAC9c,sIAAsI;IACtI,SAAS;IACT,4QAA4Q;IAC5Q,KAAK;IACL,MAAM;IACN,QAAQ;IACR,MAAM;IACN,kBAAkB;IAClB,s/BAAs/B;CACv/B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAEf,MAAM,CAAC,MAAM,mBAAmB,GAA4B;IAC1D,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,mBAAmB;CAC5B,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB,EAAE,KAA2B;IAC3E,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC"}
|