n8n-nodes-postwire 0.1.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/LICENSE.md +9 -0
- package/README.md +71 -0
- package/credentials/PostWireApi.credentials.js +44 -0
- package/credentials/postwire.svg +1 -0
- package/index.js +1 -0
- package/nodes/PostWire/PostWire.node.js +296 -0
- package/nodes/PostWire/postwire.svg +1 -0
- package/package.json +32 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PostWire
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# n8n-nodes-postwire
|
|
2
|
+
|
|
3
|
+
Publish one idea to every social network from n8n — with **a different post written for each one**, not the same text pasted everywhere.
|
|
4
|
+
|
|
5
|
+
[PostWire](https://postwire.io) handles the part that usually stops an n8n workflow: TikTok, Instagram and YouTube all require an approved developer app, and TikTok's App Review asks for a demo video of the integration you cannot build until you are approved. PostWire already holds those approvals, so connecting an account is a single OAuth click.
|
|
6
|
+
|
|
7
|
+
## What the node does
|
|
8
|
+
|
|
9
|
+
| Operation | What it does |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **Write a Post per Network** | One idea in, a native draft out per network — correct length, hashtag conventions, an SEO title and tags for YouTube, a hook-first caption for TikTok, link-in-comment for LinkedIn. Does not publish. |
|
|
12
|
+
| **Publish** | Sends to every selected network in one call. |
|
|
13
|
+
| **Write and Publish** | Both, in one node. |
|
|
14
|
+
| **Schedule** | Queues a post for later, and validates it **now** — so a missing video surfaces while you are watching, not at 7am tomorrow. |
|
|
15
|
+
| **Get Account** | Plan, posts used this month, connected accounts. |
|
|
16
|
+
|
|
17
|
+
Networks: TikTok · Instagram · YouTube · LinkedIn · X · Facebook · Reddit · Bluesky · Mastodon · Telegram · Discord.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
**n8n Cloud** — Settings → Community nodes → Install → `n8n-nodes-postwire`
|
|
22
|
+
|
|
23
|
+
**Self-hosted** — Settings → Community nodes → Install, or:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install n8n-nodes-postwire
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Credentials
|
|
30
|
+
|
|
31
|
+
1. Get a free API key at [postwire.io/dashboard.html](https://postwire.io/dashboard.html) and connect the accounts you want to post to.
|
|
32
|
+
2. In n8n, create a **PostWire API** credential and paste the key.
|
|
33
|
+
3. Press **Test** — it calls `/api/me` and returns your plan and connected accounts, so a wrong key fails now rather than at publish time.
|
|
34
|
+
|
|
35
|
+
Free plan: one brand with every network it connects, 30 posts a month, no card.
|
|
36
|
+
|
|
37
|
+
## Nothing half-publishes
|
|
38
|
+
|
|
39
|
+
The most common way a cross-posting workflow goes wrong is a partial send: you ask for four networks and two go out. This node checks before anything is published — that each network is connected, that the caption is inside that platform's character limit, and that video networks actually have a video. If any of them would fail, the whole call is refused and the error tells you which one and what to change.
|
|
40
|
+
|
|
41
|
+
The node also validates media in the editor, so `youtube will not accept a post without a video` appears before you run the workflow rather than after.
|
|
42
|
+
|
|
43
|
+
## Output
|
|
44
|
+
|
|
45
|
+
**Publish** returns one n8n item per network, so you can wire a Filter or IF node straight onto the failures:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{ "ok": true, "platform": "x", "id": "1934…", "url": "https://x.com/…" }
|
|
49
|
+
{ "ok": false, "platform": "youtube", "error": "youtube needs a video…", "code": "media_required" }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Write a Post per Network** returns `{ drafts: { "<platform>": { text, title?, tags? } } }`.
|
|
53
|
+
|
|
54
|
+
## Pricing
|
|
55
|
+
|
|
56
|
+
Priced per **brand** — one business with all of its networks — not per channel or per social account. Adding a fifth network to a brand never changes the bill.
|
|
57
|
+
|
|
58
|
+
| | Brands | Posts/month | |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| Free | 1 | 30 | no card |
|
|
61
|
+
| Starter | 3 | 300 | $9/mo |
|
|
62
|
+
| Pro | 10 | 2,000 | $29/mo |
|
|
63
|
+
| Agency | 50 | 15,000 | $99/mo |
|
|
64
|
+
|
|
65
|
+
## Also available
|
|
66
|
+
|
|
67
|
+
A REST API and a hosted MCP server (`https://postwire.io/api/mcp`) for AI agents in Claude, Cursor or VS Code. See [postwire.io/mcp](https://postwire.io/mcp/).
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostWireApi = void 0;
|
|
4
|
+
|
|
5
|
+
// PostWire authenticates with a single bearer key. The `test` block below is what makes n8n's
|
|
6
|
+
// "Test connection" button meaningful: /api/me returns the plan, the month's usage and the
|
|
7
|
+
// connected accounts, so a wrong key fails immediately instead of at publish time.
|
|
8
|
+
class PostWireApi {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.name = 'postWireApi';
|
|
11
|
+
this.displayName = 'PostWire API';
|
|
12
|
+
this.documentationUrl = 'https://postwire.io/mcp/';
|
|
13
|
+
this.properties = [
|
|
14
|
+
{
|
|
15
|
+
displayName: 'API Key',
|
|
16
|
+
name: 'apiKey',
|
|
17
|
+
type: 'string',
|
|
18
|
+
typeOptions: { password: true },
|
|
19
|
+
default: '',
|
|
20
|
+
required: true,
|
|
21
|
+
placeholder: 'pw_live_...',
|
|
22
|
+
description:
|
|
23
|
+
'Your PostWire API key. Get one free at https://postwire.io/dashboard.html — the free plan covers one brand, every network it connects, and 30 posts a month, with no card.',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
this.authenticate = {
|
|
27
|
+
type: 'generic',
|
|
28
|
+
properties: {
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
31
|
+
'X-PostWire-Source': 'n8n',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
this.test = {
|
|
36
|
+
request: {
|
|
37
|
+
baseURL: 'https://postwire.io',
|
|
38
|
+
url: '/api/me',
|
|
39
|
+
method: 'GET',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.PostWireApi = PostWireApi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><rect width="24" height="24" rx="5" fill="#0b0e14"/><path d="M6 17V7h4.6a3.2 3.2 0 0 1 0 6.4H8.4" stroke="#3ea6ff" stroke-width="2.1" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="16.6" cy="15.4" r="1.7" fill="#3ea6ff"/></svg>
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostWire = void 0;
|
|
4
|
+
|
|
5
|
+
const { NodeOperationError } = require("n8n-workflow");
|
|
6
|
+
|
|
7
|
+
const BASE = "https://postwire.io";
|
|
8
|
+
|
|
9
|
+
// Every network PostWire can publish to. `mediaNeed` mirrors the server's own rule so the node can
|
|
10
|
+
// warn in the editor rather than letting the call fail — TikTok and YouTube refuse a post with no
|
|
11
|
+
// video, and Instagram refuses one with no media at all.
|
|
12
|
+
const PLATFORMS = [
|
|
13
|
+
{ name: "TikTok", value: "tiktok", mediaNeed: "video" },
|
|
14
|
+
{ name: "Instagram", value: "instagram", mediaNeed: "any" },
|
|
15
|
+
{ name: "YouTube", value: "youtube", mediaNeed: "video" },
|
|
16
|
+
{ name: "LinkedIn", value: "linkedin", mediaNeed: null },
|
|
17
|
+
{ name: "X (Twitter)", value: "x", mediaNeed: null },
|
|
18
|
+
{ name: "Facebook", value: "facebook", mediaNeed: null },
|
|
19
|
+
{ name: "Reddit", value: "reddit", mediaNeed: null },
|
|
20
|
+
{ name: "Bluesky", value: "bluesky", mediaNeed: null },
|
|
21
|
+
{ name: "Mastodon", value: "mastodon", mediaNeed: null },
|
|
22
|
+
{ name: "Telegram", value: "telegram", mediaNeed: null },
|
|
23
|
+
{ name: "Discord", value: "discord", mediaNeed: null },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
class PostWire {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.description = {
|
|
29
|
+
displayName: "PostWire",
|
|
30
|
+
name: "postWire",
|
|
31
|
+
icon: "file:postwire.svg",
|
|
32
|
+
group: ["output"],
|
|
33
|
+
version: 1,
|
|
34
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
35
|
+
description: "Publish one idea natively to every social network",
|
|
36
|
+
defaults: { name: "PostWire" },
|
|
37
|
+
inputs: ["main"],
|
|
38
|
+
outputs: ["main"],
|
|
39
|
+
credentials: [{ name: "postWireApi", required: true }],
|
|
40
|
+
requestDefaults: {
|
|
41
|
+
baseURL: BASE,
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
},
|
|
44
|
+
properties: [
|
|
45
|
+
{
|
|
46
|
+
displayName: "Operation",
|
|
47
|
+
name: "operation",
|
|
48
|
+
type: "options",
|
|
49
|
+
noDataExpression: true,
|
|
50
|
+
options: [
|
|
51
|
+
{
|
|
52
|
+
name: "Write a Post per Network",
|
|
53
|
+
value: "generate",
|
|
54
|
+
description:
|
|
55
|
+
"One prompt in, a native draft out for each network — right length, hashtags and format per platform. Does not publish.",
|
|
56
|
+
action: "Write a native post for each network",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Publish",
|
|
60
|
+
value: "publish",
|
|
61
|
+
description: "Publish to one or more networks in a single call",
|
|
62
|
+
action: "Publish to social networks",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "Write and Publish",
|
|
66
|
+
value: "generateAndPublish",
|
|
67
|
+
description: "Write a native draft per network, then publish them",
|
|
68
|
+
action: "Write and publish in one step",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "Schedule",
|
|
72
|
+
value: "schedule",
|
|
73
|
+
description: "Queue a post for a later time",
|
|
74
|
+
action: "Schedule a post",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "Get Account",
|
|
78
|
+
value: "me",
|
|
79
|
+
description: "Plan, usage this month and connected accounts",
|
|
80
|
+
action: "Get the PostWire account",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
default: "generateAndPublish",
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
displayName: "Networks",
|
|
88
|
+
name: "platforms",
|
|
89
|
+
type: "multiOptions",
|
|
90
|
+
options: PLATFORMS.map((p) => ({ name: p.name, value: p.value })),
|
|
91
|
+
default: ["x", "linkedin"],
|
|
92
|
+
required: true,
|
|
93
|
+
description:
|
|
94
|
+
"Connect these in the PostWire dashboard first. TikTok, Instagram and YouTube are one OAuth click — PostWire carries the platform approvals, so there is no app review on your side.",
|
|
95
|
+
displayOptions: { show: { operation: ["generate", "publish", "generateAndPublish", "schedule"] } },
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
displayName: "Idea",
|
|
100
|
+
name: "prompt",
|
|
101
|
+
type: "string",
|
|
102
|
+
typeOptions: { rows: 3 },
|
|
103
|
+
default: "",
|
|
104
|
+
required: true,
|
|
105
|
+
placeholder: "We shipped scheduling today — one idea, a native post per network",
|
|
106
|
+
description: "What you want to say. More detail gives a better draft.",
|
|
107
|
+
displayOptions: { show: { operation: ["generate", "generateAndPublish"] } },
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
{
|
|
111
|
+
displayName: "Text",
|
|
112
|
+
name: "text",
|
|
113
|
+
type: "string",
|
|
114
|
+
typeOptions: { rows: 3 },
|
|
115
|
+
default: "",
|
|
116
|
+
description: "Posted exactly as written to every selected network",
|
|
117
|
+
displayOptions: { show: { operation: ["publish", "schedule"] } },
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
displayName: "Media URL",
|
|
122
|
+
name: "mediaUrl",
|
|
123
|
+
type: "string",
|
|
124
|
+
default: "",
|
|
125
|
+
placeholder: "https://example.com/clip.mp4",
|
|
126
|
+
description:
|
|
127
|
+
"A direct link to an .mp4 or an image. TikTok and YouTube will not accept a post without a video, and Instagram needs a photo or a video. A Google Drive share link does not work — it returns a web page, not a file.",
|
|
128
|
+
displayOptions: { show: { operation: ["generate", "publish", "generateAndPublish", "schedule"] } },
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
{
|
|
132
|
+
displayName: "Publish At",
|
|
133
|
+
name: "runAt",
|
|
134
|
+
type: "dateTime",
|
|
135
|
+
default: "",
|
|
136
|
+
required: true,
|
|
137
|
+
description: "When to publish. PostWire validates the post now, so a missing video is caught here rather than at 7am tomorrow.",
|
|
138
|
+
displayOptions: { show: { operation: ["schedule"] } },
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
{
|
|
142
|
+
displayName: "Options",
|
|
143
|
+
name: "options",
|
|
144
|
+
type: "collection",
|
|
145
|
+
placeholder: "Add option",
|
|
146
|
+
default: {},
|
|
147
|
+
displayOptions: { show: { operation: ["generate", "publish", "generateAndPublish", "schedule"] } },
|
|
148
|
+
options: [
|
|
149
|
+
{
|
|
150
|
+
displayName: "Title (YouTube)",
|
|
151
|
+
name: "title",
|
|
152
|
+
type: "string",
|
|
153
|
+
default: "",
|
|
154
|
+
description: "Overrides the generated YouTube title",
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
displayName: "Brand",
|
|
158
|
+
name: "brandId",
|
|
159
|
+
type: "string",
|
|
160
|
+
default: "",
|
|
161
|
+
description: "Publish as a specific brand. Leave empty for your only brand.",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
displayName: "Visibility",
|
|
165
|
+
name: "privacy",
|
|
166
|
+
type: "options",
|
|
167
|
+
options: [
|
|
168
|
+
{ name: "Public", value: "public" },
|
|
169
|
+
{ name: "Private", value: "private" },
|
|
170
|
+
{ name: "Unlisted", value: "unlisted" },
|
|
171
|
+
],
|
|
172
|
+
default: "public",
|
|
173
|
+
description: "Used by TikTok and YouTube",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
displayName: "Brand Voice",
|
|
177
|
+
name: "brandVoice",
|
|
178
|
+
type: "string",
|
|
179
|
+
default: "",
|
|
180
|
+
description: "A sentence describing how you want to sound, applied to every generated draft",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async execute() {
|
|
189
|
+
const items = this.getInputData();
|
|
190
|
+
const out = [];
|
|
191
|
+
|
|
192
|
+
for (let i = 0; i < items.length; i++) {
|
|
193
|
+
try {
|
|
194
|
+
const operation = this.getNodeParameter("operation", i);
|
|
195
|
+
|
|
196
|
+
if (operation === "me") {
|
|
197
|
+
const me = await this.helpers.httpRequestWithAuthentication.call(this, "postWireApi", {
|
|
198
|
+
method: "GET",
|
|
199
|
+
url: `${BASE}/api/me`,
|
|
200
|
+
json: true,
|
|
201
|
+
});
|
|
202
|
+
out.push({ json: me, pairedItem: { item: i } });
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const platforms = this.getNodeParameter("platforms", i);
|
|
207
|
+
const opts = this.getNodeParameter("options", i, {});
|
|
208
|
+
const mediaUrl = (this.getNodeParameter("mediaUrl", i, "") || "").trim();
|
|
209
|
+
|
|
210
|
+
if (!platforms || !platforms.length) {
|
|
211
|
+
throw new NodeOperationError(this.getNode(), "Pick at least one network.", { itemIndex: i });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Media rules, checked before the request so the message names the fix rather than the symptom.
|
|
215
|
+
const isVideo = /\.(mp4|mov|webm|m3u8|avi|mkv|m4v)(\?|$)/i.test(mediaUrl);
|
|
216
|
+
const blocked = platforms.filter((p) => {
|
|
217
|
+
const need = (PLATFORMS.find((x) => x.value === p) || {}).mediaNeed;
|
|
218
|
+
if (!need) return false;
|
|
219
|
+
if (need === "video") return !isVideo;
|
|
220
|
+
return !mediaUrl;
|
|
221
|
+
});
|
|
222
|
+
if (blocked.length) {
|
|
223
|
+
throw new NodeOperationError(
|
|
224
|
+
this.getNode(),
|
|
225
|
+
`${blocked.join(" and ")} will not accept a post without ${blocked.some((p) => (PLATFORMS.find((x) => x.value === p) || {}).mediaNeed === "video") ? "a video" : "an image or video"}. Set Media URL to a direct file link, or remove ${blocked.length > 1 ? "those networks" : "that network"} from the list.`,
|
|
226
|
+
{ itemIndex: i },
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const photo_url = mediaUrl && !isVideo ? mediaUrl : undefined;
|
|
231
|
+
const video_url = isVideo ? mediaUrl : undefined;
|
|
232
|
+
|
|
233
|
+
let per_platform;
|
|
234
|
+
if (operation === "generate" || operation === "generateAndPublish") {
|
|
235
|
+
const prompt = this.getNodeParameter("prompt", i);
|
|
236
|
+
const gen = await this.helpers.httpRequestWithAuthentication.call(this, "postWireApi", {
|
|
237
|
+
method: "POST",
|
|
238
|
+
url: `${BASE}/api/generate`,
|
|
239
|
+
body: { prompt, platforms, media_url: mediaUrl || undefined, brand_voice: opts.brandVoice || undefined },
|
|
240
|
+
json: true,
|
|
241
|
+
});
|
|
242
|
+
per_platform = gen.drafts;
|
|
243
|
+
if (operation === "generate") {
|
|
244
|
+
out.push({ json: gen, pairedItem: { item: i } });
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const body = {
|
|
250
|
+
platforms,
|
|
251
|
+
per_platform,
|
|
252
|
+
text: per_platform ? undefined : this.getNodeParameter("text", i, ""),
|
|
253
|
+
photo_url,
|
|
254
|
+
video_url,
|
|
255
|
+
title: opts.title || undefined,
|
|
256
|
+
privacy: opts.privacy || undefined,
|
|
257
|
+
brand_id: opts.brandId || undefined,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (operation === "schedule") {
|
|
261
|
+
const runAt = this.getNodeParameter("runAt", i);
|
|
262
|
+
const r = await this.helpers.httpRequestWithAuthentication.call(this, "postWireApi", {
|
|
263
|
+
method: "POST",
|
|
264
|
+
url: `${BASE}/api/schedule`,
|
|
265
|
+
body: { ...body, run_at: runAt },
|
|
266
|
+
json: true,
|
|
267
|
+
});
|
|
268
|
+
out.push({ json: r, pairedItem: { item: i } });
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const r = await this.helpers.httpRequestWithAuthentication.call(this, "postWireApi", {
|
|
273
|
+
method: "POST",
|
|
274
|
+
url: `${BASE}/api/post`,
|
|
275
|
+
body,
|
|
276
|
+
json: true,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// One n8n item per network, so a Filter or IF node downstream can act on the failures alone
|
|
280
|
+
// instead of unpacking an array by hand.
|
|
281
|
+
for (const res of r.results || [r]) {
|
|
282
|
+
out.push({ json: res, pairedItem: { item: i } });
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (this.continueOnFail()) {
|
|
286
|
+
out.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return [out];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.PostWire = PostWire;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><rect width="24" height="24" rx="5" fill="#0b0e14"/><path d="M6 17V7h4.6a3.2 3.2 0 0 1 0 6.4H8.4" stroke="#3ea6ff" stroke-width="2.1" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="16.6" cy="15.4" r="1.7" fill="#3ea6ff"/></svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-postwire",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Publish one idea natively to TikTok, Instagram, YouTube, LinkedIn, X, Bluesky, Mastodon, Telegram, Discord and Reddit from n8n — a different post written for each network, via PostWire.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"postwire",
|
|
8
|
+
"social media",
|
|
9
|
+
"tiktok",
|
|
10
|
+
"instagram",
|
|
11
|
+
"youtube",
|
|
12
|
+
"linkedin",
|
|
13
|
+
"cross-posting",
|
|
14
|
+
"scheduling"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"homepage": "https://postwire.io",
|
|
18
|
+
"author": { "name": "PostWire", "email": "privacy@postwire.io" },
|
|
19
|
+
"repository": { "type": "git", "url": "git+https://github.com/Perufitlife/n8n-nodes-postwire.git" },
|
|
20
|
+
"main": "index.js",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"prepublishOnly": "node -e \"require('./nodes/PostWire/PostWire.node.js');require('./credentials/PostWireApi.credentials.js');console.log('load check ok')\""
|
|
23
|
+
},
|
|
24
|
+
"files": ["index.js", "nodes", "credentials", "LICENSE.md", "README.md"],
|
|
25
|
+
"n8n": {
|
|
26
|
+
"n8nNodesApiVersion": 1,
|
|
27
|
+
"credentials": ["credentials/PostWireApi.credentials.js"],
|
|
28
|
+
"nodes": ["nodes/PostWire/PostWire.node.js"]
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": { "n8n-workflow": "*" },
|
|
31
|
+
"engines": { "node": ">=18.10" }
|
|
32
|
+
}
|