posterly-mcp-server 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/generated/platform-manifest.d.ts +3 -3
- package/dist/generated/platform-manifest.js +3 -2
- package/dist/index.js +50 -49
- package/dist/lib/api-client.d.ts +2 -0
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/format.d.ts +21 -0
- package/dist/lib/format.js +125 -0
- package/dist/tools/audit-google-business-profile.js +15 -12
- package/dist/tools/create-oauth-client.js +7 -1
- package/dist/tools/create-post.js +20 -27
- package/dist/tools/create-posts-batch.js +23 -15
- package/dist/tools/create-signed-upload.js +11 -8
- package/dist/tools/create-webhook.js +7 -2
- package/dist/tools/delete-google-business-review-reply.d.ts +1 -1
- package/dist/tools/delete-google-business-review-reply.js +4 -1
- package/dist/tools/delete-oauth-client.js +4 -1
- package/dist/tools/delete-post-group.js +5 -1
- package/dist/tools/delete-post.js +4 -1
- package/dist/tools/delete-webhook.js +4 -1
- package/dist/tools/disconnect-account.js +7 -5
- package/dist/tools/find-slot.js +10 -3
- package/dist/tools/generate-image.js +17 -19
- package/dist/tools/generate-video.js +11 -9
- package/dist/tools/get-account-analytics.js +68 -53
- package/dist/tools/get-brand-profile.js +34 -40
- package/dist/tools/get-brand.js +14 -16
- package/dist/tools/get-connect-link.js +26 -7
- package/dist/tools/get-google-business-review-link.js +6 -6
- package/dist/tools/get-platform-schema.js +23 -16
- package/dist/tools/get-post-analytics.js +54 -51
- package/dist/tools/get-post-missing.js +5 -1
- package/dist/tools/get-post.js +16 -14
- package/dist/tools/get-video-job.d.ts +2 -2
- package/dist/tools/get-video-job.js +28 -11
- package/dist/tools/get-video-options.js +21 -15
- package/dist/tools/get-x-posting-quota.js +12 -9
- package/dist/tools/list-accounts.js +12 -4
- package/dist/tools/list-activity.js +13 -18
- package/dist/tools/list-brand-accounts.js +11 -3
- package/dist/tools/list-brands.js +12 -11
- package/dist/tools/list-google-business-reviews.js +13 -11
- package/dist/tools/list-oauth-clients.js +10 -4
- package/dist/tools/list-platforms.js +12 -5
- package/dist/tools/list-posts.js +12 -7
- package/dist/tools/list-webhooks.js +13 -11
- package/dist/tools/reply-google-business-review.d.ts +1 -1
- package/dist/tools/reply-google-business-review.js +4 -1
- package/dist/tools/run-video-function.js +4 -4
- package/dist/tools/suggest-google-business-review-reply.js +5 -4
- package/dist/tools/test-webhook.js +7 -1
- package/dist/tools/trigger-platform-helper.js +5 -1
- package/dist/tools/update-oauth-client.js +8 -1
- package/dist/tools/update-post-release-id.js +5 -1
- package/dist/tools/update-post-status.d.ts +2 -2
- package/dist/tools/update-post-status.js +7 -1
- package/dist/tools/update-post.js +10 -4
- package/dist/tools/update-webhook.js +7 -1
- package/dist/tools/upload-media-from-url.js +7 -9
- package/dist/tools/upload-media.js +6 -2
- package/dist/tools/whoami.js +18 -17
- package/package.json +1 -1
- package/src/generated/platform-manifest.ts +3 -2
- package/src/index.ts +50 -49
- package/src/lib/api-client.ts +3 -1
- package/src/lib/format.ts +132 -0
- package/src/tools/audit-google-business-profile.ts +18 -12
- package/src/tools/create-oauth-client.ts +8 -2
- package/src/tools/create-post.ts +20 -28
- package/src/tools/create-posts-batch.ts +23 -18
- package/src/tools/create-signed-upload.ts +11 -8
- package/src/tools/create-webhook.ts +7 -3
- package/src/tools/delete-google-business-review-reply.ts +4 -1
- package/src/tools/delete-oauth-client.ts +4 -1
- package/src/tools/delete-post-group.ts +5 -1
- package/src/tools/delete-post.ts +4 -1
- package/src/tools/delete-webhook.ts +4 -2
- package/src/tools/disconnect-account.ts +7 -5
- package/src/tools/find-slot.ts +13 -5
- package/src/tools/generate-image.ts +20 -20
- package/src/tools/generate-video.ts +11 -9
- package/src/tools/get-account-analytics.ts +77 -55
- package/src/tools/get-brand-profile.ts +34 -38
- package/src/tools/get-brand.ts +14 -14
- package/src/tools/get-connect-link.ts +29 -7
- package/src/tools/get-google-business-review-link.ts +6 -6
- package/src/tools/get-platform-schema.ts +29 -16
- package/src/tools/get-post-analytics.ts +59 -52
- package/src/tools/get-post-missing.ts +5 -2
- package/src/tools/get-post.ts +16 -13
- package/src/tools/get-video-job.ts +31 -11
- package/src/tools/get-video-options.ts +27 -15
- package/src/tools/get-x-posting-quota.ts +12 -9
- package/src/tools/list-accounts.ts +15 -6
- package/src/tools/list-activity.ts +16 -20
- package/src/tools/list-brand-accounts.ts +14 -6
- package/src/tools/list-brands.ts +16 -13
- package/src/tools/list-google-business-reviews.ts +16 -12
- package/src/tools/list-oauth-clients.ts +13 -4
- package/src/tools/list-platforms.ts +15 -5
- package/src/tools/list-posts.ts +15 -9
- package/src/tools/list-webhooks.ts +16 -12
- package/src/tools/reply-google-business-review.ts +4 -1
- package/src/tools/run-video-function.ts +4 -4
- package/src/tools/suggest-google-business-review-reply.ts +5 -4
- package/src/tools/test-webhook.ts +7 -1
- package/src/tools/trigger-platform-helper.ts +5 -1
- package/src/tools/update-oauth-client.ts +9 -2
- package/src/tools/update-post-release-id.ts +5 -2
- package/src/tools/update-post-status.ts +7 -1
- package/src/tools/update-post.ts +10 -5
- package/src/tools/update-webhook.ts +7 -2
- package/src/tools/upload-media-from-url.ts +7 -9
- package/src/tools/upload-media.ts +6 -2
- package/src/tools/whoami.ts +21 -23
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export function mdTitle(title, subtitle) {
|
|
2
|
+
return [`## ${title}`, subtitle ? `_${subtitle}_` : ''].filter(Boolean).join('\n');
|
|
3
|
+
}
|
|
4
|
+
export function mdTable(headers, rows) {
|
|
5
|
+
const visibleRows = rows.filter((row) => row.some((cell) => cell !== undefined && cell !== null && cell !== ''));
|
|
6
|
+
if (visibleRows.length === 0)
|
|
7
|
+
return '';
|
|
8
|
+
const header = `| ${headers.map(escapeCell).join(' | ')} |`;
|
|
9
|
+
const divider = `| ${headers.map(() => '---').join(' | ')} |`;
|
|
10
|
+
const body = visibleRows.map((row) => `| ${row.map((cell) => escapeCell(formatCell(cell))).join(' | ')} |`);
|
|
11
|
+
return [header, divider, ...body].join('\n');
|
|
12
|
+
}
|
|
13
|
+
export function mdKeyValue(rows) {
|
|
14
|
+
return mdTable(['Field', 'Value'], rows);
|
|
15
|
+
}
|
|
16
|
+
export function mdSection(title, body) {
|
|
17
|
+
if (!body)
|
|
18
|
+
return '';
|
|
19
|
+
return [`### ${title}`, body].join('\n');
|
|
20
|
+
}
|
|
21
|
+
export function mdBullets(items) {
|
|
22
|
+
return items.filter(Boolean).map((item) => `- ${item}`).join('\n');
|
|
23
|
+
}
|
|
24
|
+
export function mdJson(title, value) {
|
|
25
|
+
return [`### ${title}`, '```json', JSON.stringify(value, null, 2), '```'].join('\n');
|
|
26
|
+
}
|
|
27
|
+
export function mdQuote(text) {
|
|
28
|
+
return text.split('\n').map((line) => `> ${line}`).join('\n');
|
|
29
|
+
}
|
|
30
|
+
export function mdEmpty(title, detail, nextStep) {
|
|
31
|
+
return [
|
|
32
|
+
mdTitle(`No ${title}`),
|
|
33
|
+
detail,
|
|
34
|
+
nextStep ? `\n**Next step:** ${nextStep}` : '',
|
|
35
|
+
].filter(Boolean).join('\n');
|
|
36
|
+
}
|
|
37
|
+
export function mdSuccess(title, rows, nextStep) {
|
|
38
|
+
return [
|
|
39
|
+
mdTitle(`✅ ${title}`),
|
|
40
|
+
mdKeyValue(rows),
|
|
41
|
+
nextStep ? `\n**Next step:** ${nextStep}` : '',
|
|
42
|
+
].filter(Boolean).join('\n');
|
|
43
|
+
}
|
|
44
|
+
export function mdError(message) {
|
|
45
|
+
return [
|
|
46
|
+
mdTitle('⚠️ Posterly tool error'),
|
|
47
|
+
`**Message:** ${message}`,
|
|
48
|
+
].join('\n');
|
|
49
|
+
}
|
|
50
|
+
export function formatNumber(value) {
|
|
51
|
+
return value == null ? 'n/a' : value.toLocaleString();
|
|
52
|
+
}
|
|
53
|
+
export function formatPercent(value) {
|
|
54
|
+
return value == null ? 'n/a' : `${value.toLocaleString()}%`;
|
|
55
|
+
}
|
|
56
|
+
export function formatDelta(value) {
|
|
57
|
+
if (value == null)
|
|
58
|
+
return 'n/a';
|
|
59
|
+
return value >= 0 ? `+${value.toLocaleString()}` : value.toLocaleString();
|
|
60
|
+
}
|
|
61
|
+
export function formatDateTime(value) {
|
|
62
|
+
if (!value)
|
|
63
|
+
return 'n/a';
|
|
64
|
+
const date = new Date(value);
|
|
65
|
+
if (Number.isNaN(date.getTime()))
|
|
66
|
+
return value;
|
|
67
|
+
return date.toLocaleString();
|
|
68
|
+
}
|
|
69
|
+
export function formatDate(value) {
|
|
70
|
+
if (!value)
|
|
71
|
+
return 'n/a';
|
|
72
|
+
const date = new Date(value);
|
|
73
|
+
if (Number.isNaN(date.getTime()))
|
|
74
|
+
return value;
|
|
75
|
+
return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' });
|
|
76
|
+
}
|
|
77
|
+
export function formatBytes(value) {
|
|
78
|
+
if (value == null)
|
|
79
|
+
return 'n/a';
|
|
80
|
+
if (value < 1024)
|
|
81
|
+
return `${value} B`;
|
|
82
|
+
const units = ['KB', 'MB', 'GB', 'TB'];
|
|
83
|
+
let size = value / 1024;
|
|
84
|
+
let unitIndex = 0;
|
|
85
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
86
|
+
size /= 1024;
|
|
87
|
+
unitIndex += 1;
|
|
88
|
+
}
|
|
89
|
+
return `${size.toFixed(size >= 10 ? 0 : 1)} ${units[unitIndex]}`;
|
|
90
|
+
}
|
|
91
|
+
export function compactText(value, maxLength = 90) {
|
|
92
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
93
|
+
if (!text)
|
|
94
|
+
return '';
|
|
95
|
+
return text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 1))}…` : text;
|
|
96
|
+
}
|
|
97
|
+
export function statusLabel(status) {
|
|
98
|
+
const value = String(status || 'unknown').toLowerCase();
|
|
99
|
+
if (['created', 'success', 'succeeded', 'completed', 'published', 'active'].includes(value))
|
|
100
|
+
return `✅ ${status}`;
|
|
101
|
+
if (['scheduled', 'queued', 'pending', 'processing'].includes(value))
|
|
102
|
+
return `🕒 ${status}`;
|
|
103
|
+
if (['failed', 'error', 'inactive', 'deleted'].includes(value))
|
|
104
|
+
return `⚠️ ${status}`;
|
|
105
|
+
if (['draft', 'paused'].includes(value))
|
|
106
|
+
return `⏸️ ${status}`;
|
|
107
|
+
return status || 'unknown';
|
|
108
|
+
}
|
|
109
|
+
export function code(value) {
|
|
110
|
+
return `\`${formatCell(value).replace(/`/g, '\\`')}\``;
|
|
111
|
+
}
|
|
112
|
+
function formatCell(value) {
|
|
113
|
+
if (value === null || value === undefined || value === '')
|
|
114
|
+
return 'n/a';
|
|
115
|
+
if (typeof value === 'boolean')
|
|
116
|
+
return value ? 'yes' : 'no';
|
|
117
|
+
if (typeof value === 'number')
|
|
118
|
+
return value.toLocaleString();
|
|
119
|
+
return String(value);
|
|
120
|
+
}
|
|
121
|
+
function escapeCell(value) {
|
|
122
|
+
return formatCell(value)
|
|
123
|
+
.replace(/\|/g, '\\|')
|
|
124
|
+
.replace(/\r?\n/g, '<br>');
|
|
125
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { formatDateTime, mdKeyValue, mdSection, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const auditGoogleBusinessProfileTool = {
|
|
3
4
|
name: 'audit_google_business_profile',
|
|
4
5
|
description: 'Run a live local-profile audit for a connected Google Business Profile location, including completeness, categories, media, attributes, reviews, and recommendations.',
|
|
@@ -11,17 +12,19 @@ export const auditGoogleBusinessProfileTool = {
|
|
|
11
12
|
const result = await client.auditGoogleBusinessProfile(input);
|
|
12
13
|
const audit = result.audit || {};
|
|
13
14
|
const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
|
|
14
|
-
|
|
15
|
-
`Google Business audit: ${audit.businessName || 'Unknown business'}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
return [
|
|
16
|
+
mdTitle(`Google Business audit: ${audit.businessName || 'Unknown business'}`),
|
|
17
|
+
mdKeyValue([
|
|
18
|
+
['Score', audit.overallScore ?? 'n/a'],
|
|
19
|
+
['Grade', audit.overallGrade || 'n/a'],
|
|
20
|
+
['Fetched', formatDateTime(result.fetchedAt)],
|
|
21
|
+
]),
|
|
22
|
+
recommendations.length > 0
|
|
23
|
+
? mdSection('Top recommendations', mdTable(['Priority', 'Recommendation'], recommendations.slice(0, 5).map((rec) => [
|
|
24
|
+
rec.priority || 'medium',
|
|
25
|
+
rec.description || rec.title || 'Recommendation',
|
|
26
|
+
])))
|
|
27
|
+
: '',
|
|
28
|
+
].filter(Boolean).join('\n\n');
|
|
26
29
|
},
|
|
27
30
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
const scopeSchema = z.enum(['accounts:read', 'accounts:write', 'posts:read', 'posts:write', 'media:write', 'analytics:read']);
|
|
3
4
|
export const createOAuthClientTool = {
|
|
4
5
|
name: 'create_oauth_client',
|
|
@@ -15,6 +16,11 @@ export const createOAuthClientTool = {
|
|
|
15
16
|
async execute(client, input) {
|
|
16
17
|
const { confirm: _confirm, ...payload } = input;
|
|
17
18
|
const result = await client.createOAuthClient(payload);
|
|
18
|
-
return
|
|
19
|
+
return mdSuccess('OAuth client created', [
|
|
20
|
+
['Client', result.client.client_name],
|
|
21
|
+
['Client ID', code(result.client.client_id)],
|
|
22
|
+
['Redirect URIs', result.client.allowed_redirect_uris.join(', ')],
|
|
23
|
+
['Default scopes', result.client.default_scopes.join(', ')],
|
|
24
|
+
]);
|
|
19
25
|
},
|
|
20
26
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/platform-manifest.js';
|
|
3
|
+
import { code, formatDateTime, mdKeyValue, mdSuccess, statusLabel } from '../lib/format.js';
|
|
3
4
|
export const instagramSettingsSchema = z.object({
|
|
4
5
|
__type: z.enum(['instagram', 'instagram-standalone']).optional(),
|
|
5
6
|
post_type: z.enum(['post', 'feed', 'story', 'reel', 'carousel']).optional(),
|
|
@@ -159,32 +160,24 @@ export const createPostTool = {
|
|
|
159
160
|
const result = await client.createPost(payload);
|
|
160
161
|
const p = result.post;
|
|
161
162
|
const ws = result.workspace;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
if (platform_settings) {
|
|
183
|
-
lines.push('• Platform settings: yes');
|
|
184
|
-
}
|
|
185
|
-
if (ws) {
|
|
186
|
-
lines.push(`• Workspace: ${ws.name} (${ws.id}) — resolved from ${ws.resolved_from}`);
|
|
187
|
-
}
|
|
188
|
-
return lines.join('\n');
|
|
163
|
+
return [
|
|
164
|
+
mdSuccess('Post created', [
|
|
165
|
+
['Post ID', code(p.id)],
|
|
166
|
+
['Platform', p.platform || input.platform || 'unknown'],
|
|
167
|
+
['Type', p.post_type],
|
|
168
|
+
['Status', statusLabel(p.status)],
|
|
169
|
+
['Scheduled', p.scheduled_at ? formatDateTime(p.scheduled_at) : 'now'],
|
|
170
|
+
['Thread', thread_posts ? `${thread_posts.length} posts` : undefined],
|
|
171
|
+
['Instagram collaborators', instagram_settings?.collaborators?.length],
|
|
172
|
+
['Instagram first comment', Boolean(instagram_settings?.first_comment)],
|
|
173
|
+
['Platform settings', Boolean(platform_settings)],
|
|
174
|
+
], 'Use `get_post` if you want to review the saved post details.'),
|
|
175
|
+
ws
|
|
176
|
+
? mdKeyValue([
|
|
177
|
+
['Workspace', `${ws.name} (${code(ws.id)})`],
|
|
178
|
+
['Workspace resolved from', ws.resolved_from],
|
|
179
|
+
])
|
|
180
|
+
: '',
|
|
181
|
+
].filter(Boolean).join('\n\n');
|
|
189
182
|
},
|
|
190
183
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { buildCreatePostPayload, createPostInputSchema } from './create-post.js';
|
|
3
|
+
import { code, formatDateTime, mdSection, mdTable, mdTitle, statusLabel } from '../lib/format.js';
|
|
3
4
|
export const createPostsBatchTool = {
|
|
4
5
|
name: 'create_posts_batch',
|
|
5
6
|
description: 'Create 1-25 scheduled or immediate social posts in one API request. This is a DESTRUCTIVE WRITE that creates content on the user\'s real social accounts — once scheduled_at passes, posts may go public and cannot be un-posted.\n\n' +
|
|
@@ -18,21 +19,28 @@ export const createPostsBatchTool = {
|
|
|
18
19
|
async execute(client, input) {
|
|
19
20
|
const payloads = input.posts.map((post) => buildCreatePostPayload(post));
|
|
20
21
|
const result = await client.createPostsBatch({ posts: payloads });
|
|
21
|
-
const
|
|
22
|
-
'Batch create completed.',
|
|
23
|
-
`• Total: ${result.total}`,
|
|
24
|
-
`• Created: ${result.created}`,
|
|
25
|
-
`• Replayed: ${result.replayed}`,
|
|
26
|
-
`• Failed: ${result.failed}`,
|
|
27
|
-
];
|
|
28
|
-
for (const item of result.posts.slice(0, 25)) {
|
|
22
|
+
const createdRows = result.posts.slice(0, 25).map((item) => {
|
|
29
23
|
const post = item.post;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
return [
|
|
25
|
+
item.index,
|
|
26
|
+
code(post.id),
|
|
27
|
+
post.platform || 'unknown',
|
|
28
|
+
post.post_type || 'post',
|
|
29
|
+
statusLabel(post.status || 'created'),
|
|
30
|
+
post.scheduled_at ? formatDateTime(post.scheduled_at) : 'now',
|
|
31
|
+
item.replayed ? 'yes' : 'no',
|
|
32
|
+
];
|
|
33
|
+
});
|
|
34
|
+
const errorRows = result.errors.slice(0, 25).map((error) => [
|
|
35
|
+
error.index,
|
|
36
|
+
error.status,
|
|
37
|
+
error.error,
|
|
38
|
+
]);
|
|
39
|
+
return [
|
|
40
|
+
mdTitle(result.failed ? '⚠️ Batch create completed with issues' : '✅ Batch create completed'),
|
|
41
|
+
mdTable(['Total', 'Created', 'Replayed', 'Failed'], [[result.total, result.created, result.replayed, result.failed]]),
|
|
42
|
+
mdSection('Created posts', mdTable(['Index', 'Post ID', 'Platform', 'Type', 'Status', 'Scheduled', 'Replayed'], createdRows)),
|
|
43
|
+
mdSection('Errors', mdTable(['Index', 'Status', 'Error'], errorRows)),
|
|
44
|
+
].filter(Boolean).join('\n\n');
|
|
37
45
|
},
|
|
38
46
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, formatBytes, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const createSignedUploadTool = {
|
|
3
4
|
name: 'create_signed_upload',
|
|
4
|
-
description: 'Create a signed
|
|
5
|
+
description: 'Create a signed upload URL for a larger image or video. Upload the binary to upload_url; the API validates actual bytes before storage, then public_url can be used with create_post.',
|
|
5
6
|
inputSchema: z.object({
|
|
6
7
|
filename: z.string().describe('Filename with extension, e.g. launch-video.mp4'),
|
|
7
8
|
content_type: z.string().describe('MIME type, e.g. video/mp4'),
|
|
@@ -9,12 +10,14 @@ export const createSignedUploadTool = {
|
|
|
9
10
|
}),
|
|
10
11
|
async execute(client, input) {
|
|
11
12
|
const signed = await client.getSignedUploadUrl(input.filename, input.content_type, input.size);
|
|
12
|
-
return [
|
|
13
|
-
'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
return mdSuccess('Signed upload created', [
|
|
14
|
+
['Filename', input.filename],
|
|
15
|
+
['Content type', input.content_type],
|
|
16
|
+
['Size', formatBytes(input.size)],
|
|
17
|
+
['Upload URL', signed.upload_url],
|
|
18
|
+
['Token', code(signed.token)],
|
|
19
|
+
['Storage path', signed.path],
|
|
20
|
+
['Public URL after upload', signed.public_url],
|
|
21
|
+
]);
|
|
19
22
|
},
|
|
20
23
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { webhookEventSchema } from './webhook-events.js';
|
|
3
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
3
4
|
export const createWebhookTool = {
|
|
4
5
|
name: 'create_webhook',
|
|
5
6
|
description: 'Create a webhook subscription for post/account/analytics events. WRITE WITH OUTBOUND SIDE EFFECTS: show the user the target URL, workspace, events, and active state, then get explicit confirmation before calling. The response includes the signing secret once.',
|
|
@@ -14,7 +15,11 @@ export const createWebhookTool = {
|
|
|
14
15
|
async execute(client, input) {
|
|
15
16
|
const { confirm: _confirm, ...payload } = input;
|
|
16
17
|
const result = await client.createWebhook(payload);
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
return mdSuccess('Webhook created', [
|
|
19
|
+
['Webhook ID', code(result.webhook.id)],
|
|
20
|
+
['URL', result.webhook.url],
|
|
21
|
+
['Events', result.webhook.events.join(', ')],
|
|
22
|
+
['Signing secret', result.secret ? code(result.secret) : undefined],
|
|
23
|
+
], 'Store the signing secret now; Posterly only returns it once.');
|
|
19
24
|
},
|
|
20
25
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const deleteGoogleBusinessReviewReplyTool = {
|
|
3
4
|
name: 'delete_google_business_review_reply',
|
|
4
5
|
description: 'Delete the owner reply from a Google Business Profile review. DESTRUCTIVE: confirm the review and location with the user, then pass confirm=true only after explicit confirmation.',
|
|
@@ -12,6 +13,8 @@ export const deleteGoogleBusinessReviewReplyTool = {
|
|
|
12
13
|
async execute(client, input) {
|
|
13
14
|
const { confirm: _confirm, ...payload } = input;
|
|
14
15
|
const result = await client.deleteGoogleBusinessReviewReply(payload);
|
|
15
|
-
return
|
|
16
|
+
return mdSuccess('Google Business review reply deleted', [
|
|
17
|
+
['Message', result.message || 'Google Business review reply deleted successfully.'],
|
|
18
|
+
]);
|
|
16
19
|
},
|
|
17
20
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const deleteOAuthClientTool = {
|
|
3
4
|
name: 'delete_oauth_client',
|
|
4
5
|
description: 'Delete a self-serve OAuth developer client. DESTRUCTIVE: prevents new authorizations for that client_id; existing access tokens remain revocable as API keys.',
|
|
@@ -8,6 +9,8 @@ export const deleteOAuthClientTool = {
|
|
|
8
9
|
}),
|
|
9
10
|
async execute(client, input) {
|
|
10
11
|
const result = await client.deleteOAuthClient(input.client_id);
|
|
11
|
-
return
|
|
12
|
+
return mdSuccess('OAuth client deleted', [
|
|
13
|
+
['Client ID', code(result.client_id)],
|
|
14
|
+
]);
|
|
12
15
|
},
|
|
13
16
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { mdJson, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const deletePostGroupTool = {
|
|
3
4
|
name: 'delete_post_group',
|
|
4
5
|
description: 'Delete every draft/scheduled/failed/paused post matching a caller-defined group_id, post_group_id, api_group_id, or release_id. DESTRUCTIVE: inspect the group first, list the affected posts, and pass confirm=true only after explicit confirmation.',
|
|
@@ -8,6 +9,9 @@ export const deletePostGroupTool = {
|
|
|
8
9
|
}),
|
|
9
10
|
async execute(client, input) {
|
|
10
11
|
const result = await client.deletePostGroup(input.group_id);
|
|
11
|
-
return
|
|
12
|
+
return [
|
|
13
|
+
mdTitle('✅ Post group delete completed', `Group: ${input.group_id}`),
|
|
14
|
+
mdJson('Result', result),
|
|
15
|
+
].join('\n\n');
|
|
12
16
|
},
|
|
13
17
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const deletePostTool = {
|
|
3
4
|
name: 'delete_post',
|
|
4
5
|
description: 'Delete a scheduled or draft post. DESTRUCTIVE and IRREVERSIBLE — the post and its caption cannot be recovered. Cannot delete published or currently publishing posts.\n\n' +
|
|
@@ -8,6 +9,8 @@ export const deletePostTool = {
|
|
|
8
9
|
}),
|
|
9
10
|
async execute(client, input) {
|
|
10
11
|
const result = await client.deletePost(input.post_id);
|
|
11
|
-
return
|
|
12
|
+
return mdSuccess('Post deleted', [
|
|
13
|
+
['Post ID', code(result.id)],
|
|
14
|
+
]);
|
|
12
15
|
},
|
|
13
16
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const deleteWebhookTool = {
|
|
3
4
|
name: 'delete_webhook',
|
|
4
5
|
description: 'Delete a webhook subscription. DESTRUCTIVE: list the webhook first, show the user its URL/events/workspace, and get explicit confirmation before calling.',
|
|
@@ -8,6 +9,8 @@ export const deleteWebhookTool = {
|
|
|
8
9
|
}),
|
|
9
10
|
async execute(client, input) {
|
|
10
11
|
const result = await client.deleteWebhook(input.webhook_id);
|
|
11
|
-
return
|
|
12
|
+
return mdSuccess('Webhook deleted', [
|
|
13
|
+
['Webhook ID', code(result.id)],
|
|
14
|
+
]);
|
|
12
15
|
},
|
|
13
16
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const disconnectAccountTool = {
|
|
3
4
|
name: 'disconnect_account',
|
|
4
5
|
description: 'Disconnect a connected social account from posterly. DESTRUCTIVE and IRREVERSIBLE: removes the account connection, emits account.disconnected webhooks, and may transfer Instagram scheduled posts to a replacement account.\n\n' +
|
|
@@ -13,10 +14,11 @@ export const disconnectAccountTool = {
|
|
|
13
14
|
}
|
|
14
15
|
const result = await client.disconnectAccount(input.account_id);
|
|
15
16
|
const account = result.account || {};
|
|
16
|
-
return [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
return mdSuccess('Account disconnected', [
|
|
18
|
+
['Account', `${account.platform || 'account'}${account.username ? ` @${account.username}` : ''}`],
|
|
19
|
+
['Account ID', code(account.id || input.account_id)],
|
|
20
|
+
['Transferred posts', result.transferred_post_count ?? 0],
|
|
21
|
+
['Stamped posts', result.stamped_post_count ?? 0],
|
|
22
|
+
]);
|
|
21
23
|
},
|
|
22
24
|
};
|
package/dist/tools/find-slot.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { formatDate, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const findSlotTool = {
|
|
3
4
|
name: 'find_available_slot',
|
|
4
5
|
description: 'Find available time slots for posting. Respects a 1-hour gap between posts and preferred hours (8am–10pm in the given timezone). Returns up to 10 slots. IMPORTANT: pass a timezone explicitly — default is America/New_York and slots will be off if the user is elsewhere. Pass workspace_id to only avoid collisions with posts in that workspace.',
|
|
@@ -25,9 +26,15 @@ export const findSlotTool = {
|
|
|
25
26
|
async execute(client, input) {
|
|
26
27
|
const slots = await client.findAvailableSlots(input);
|
|
27
28
|
if (slots.length === 0) {
|
|
28
|
-
return 'No available slots found in the next 14 days.';
|
|
29
|
+
return mdEmpty('available slots', 'No available posting slots were found in the next 14 days.');
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
return [
|
|
32
|
+
mdTitle(`Available posting slots (${slots.length})`),
|
|
33
|
+
mdTable(['Option', 'Local time', 'Date'], slots.map((slot, index) => [
|
|
34
|
+
index + 1,
|
|
35
|
+
slot.local_time,
|
|
36
|
+
formatDate(slot.time),
|
|
37
|
+
])),
|
|
38
|
+
].join('\n\n');
|
|
32
39
|
},
|
|
33
40
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { mdBullets, mdKeyValue, mdSection, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const generateImageTool = {
|
|
3
4
|
name: 'generate_image',
|
|
4
5
|
description: 'Generate an AI image via Posterly\'s Nano Banana (Gemini) integration. The image is saved to the user\'s media storage and the returned URL can be passed to `create_post` as `media_url`.\n\n' +
|
|
@@ -47,24 +48,21 @@ export const generateImageTool = {
|
|
|
47
48
|
}),
|
|
48
49
|
async execute(client, input) {
|
|
49
50
|
const result = await client.generateImage(input);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
lines.push('');
|
|
67
|
-
lines.push('Pass any of these URLs to create_post as media_url (or media_urls for a carousel).');
|
|
68
|
-
return lines.join('\n');
|
|
51
|
+
return [
|
|
52
|
+
mdTitle(`✅ Generated ${result.images.length} image${result.images.length === 1 ? '' : 's'}`, `Model: ${result.model}`),
|
|
53
|
+
mdTable(['#', 'Image URL', 'Filename'], result.images.map((img, index) => [
|
|
54
|
+
index + 1,
|
|
55
|
+
img.url,
|
|
56
|
+
img.filename,
|
|
57
|
+
])),
|
|
58
|
+
mdKeyValue([
|
|
59
|
+
['Billed from', result.usage.billed_from],
|
|
60
|
+
['Credits used', result.credits_used],
|
|
61
|
+
['Plan usage', result.usage.limit != null ? `${result.usage.used ?? 0}/${result.usage.limit} this ${result.usage.period || 'period'}` : undefined],
|
|
62
|
+
['Plan tier', result.usage.tier || undefined],
|
|
63
|
+
]),
|
|
64
|
+
result.warnings?.length ? mdSection('Warnings', mdBullets(result.warnings)) : '',
|
|
65
|
+
'**Next step:** Pass any URL above to `create_post` as `media_url`, or use multiple as `media_urls` for a carousel.',
|
|
66
|
+
].filter(Boolean).join('\n\n');
|
|
69
67
|
},
|
|
70
68
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdJson, mdKeyValue, mdTitle, statusLabel } from '../lib/format.js';
|
|
2
3
|
export const generateVideoTool = {
|
|
3
4
|
name: 'generate_video',
|
|
4
5
|
description: 'Queue a Veo AI video generation job. COSTS VEO CREDITS: confirm prompt, model, duration, resolution, aspect ratio, audio choice, and credit cost with the user before calling. Poll get_video_job for status and final video_url.',
|
|
@@ -21,14 +22,15 @@ export const generateVideoTool = {
|
|
|
21
22
|
async execute(client, input) {
|
|
22
23
|
const result = await client.generateVideo(input);
|
|
23
24
|
return [
|
|
24
|
-
'Video generation job queued',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
mdTitle('🎬 Video generation job queued'),
|
|
26
|
+
mdKeyValue([
|
|
27
|
+
['Job ID', code(result.job_id)],
|
|
28
|
+
['Status', statusLabel(result.status)],
|
|
29
|
+
['Credit cost', `${result.credit_cost} (${result.included_credit_cost || 0} included, ${result.purchased_credit_cost || 0} purchased)`],
|
|
30
|
+
['Message', result.message],
|
|
31
|
+
]),
|
|
32
|
+
'**Next step:** Poll `get_video_job` with the job ID until the video URL is ready.',
|
|
33
|
+
mdJson('Raw response', result),
|
|
34
|
+
].filter(Boolean).join('\n\n');
|
|
33
35
|
},
|
|
34
36
|
};
|