chirpie 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +114 -43
- package/package.json +17 -3
package/dist/index.js
CHANGED
|
@@ -149,7 +149,7 @@ function getClient() {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// src/commands/post.ts
|
|
152
|
-
var postCommand = new Command3("post").description("Create a post").argument("<text>", "Post text").option("-a, --account <id>", "Account ID").option("-m, --media <urls...>", "Media URLs
|
|
152
|
+
var postCommand = new Command3("post").description("Create a post").argument("<text>", "Post text").option("-a, --account <id>", "Account ID").option("-m, --media <urls...>", "Media URLs. Per-platform caps: X/Bluesky/Mastodon 4, LinkedIn 9, Threads/Pinterest/YouTube/GBP/Snapchat/Reddit 1, Instagram/Facebook/Telegram 10, TikTok up to 35 photos.").option("-s, --schedule <datetime>", "Schedule for later (ISO 8601)").option("--json", "Output raw JSON").action(async (text, opts) => {
|
|
153
153
|
const client = getClient();
|
|
154
154
|
let accountId = opts.account;
|
|
155
155
|
if (!accountId) {
|
|
@@ -188,7 +188,21 @@ var postCommand = new Command3("post").description("Create a post").argument("<t
|
|
|
188
188
|
|
|
189
189
|
// src/commands/thread.ts
|
|
190
190
|
import { Command as Command4 } from "commander";
|
|
191
|
-
|
|
191
|
+
import { readFileSync } from "fs";
|
|
192
|
+
function parseMediaSpec(spec) {
|
|
193
|
+
const m = spec.match(/^(\d+):(.+)$/);
|
|
194
|
+
if (!m) return null;
|
|
195
|
+
const index = parseInt(m[1], 10);
|
|
196
|
+
const urls = m[2].split(",").map((u) => u.trim()).filter(Boolean);
|
|
197
|
+
return { index, urls };
|
|
198
|
+
}
|
|
199
|
+
var threadCommand = new Command4("thread").description("Create a thread").argument("[posts...]", "Thread posts (each argument is one post). Omit when using --file.").option("-a, --account <id>", "Account ID").option(
|
|
200
|
+
"-m, --media <specs...>",
|
|
201
|
+
'Per-post media as "INDEX:url1,url2" (repeatable). Example: -m 0:https://a.png 2:https://b.png,https://c.png'
|
|
202
|
+
).option(
|
|
203
|
+
"-f, --file <path>",
|
|
204
|
+
"JSON file with a full thread payload: { posts: [{ text, media_urls? }, ...], schedule_at?: string }"
|
|
205
|
+
).option("-s, --schedule <datetime>", "Schedule for later (ISO 8601)").option("--json", "Output raw JSON").action(async (posts, opts) => {
|
|
192
206
|
const client = getClient();
|
|
193
207
|
let accountId = opts.account;
|
|
194
208
|
if (!accountId) {
|
|
@@ -205,15 +219,44 @@ var threadCommand = new Command4("thread").description("Create a thread").argume
|
|
|
205
219
|
}
|
|
206
220
|
accountId = active[0].id;
|
|
207
221
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
let postPayload;
|
|
223
|
+
let scheduleAt = opts.schedule;
|
|
224
|
+
if (opts.file) {
|
|
225
|
+
try {
|
|
226
|
+
const raw = JSON.parse(readFileSync(opts.file, "utf8"));
|
|
227
|
+
if (!Array.isArray(raw.posts)) throw new Error("File must contain { posts: [...] }");
|
|
228
|
+
postPayload = raw.posts;
|
|
229
|
+
scheduleAt = scheduleAt ?? raw.schedule_at;
|
|
230
|
+
} catch (err) {
|
|
231
|
+
error(err instanceof Error ? err.message : "Failed to read thread file");
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
if (posts.length < 2) {
|
|
236
|
+
error("Thread must have at least 2 posts (or use --file)");
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
postPayload = posts.map((text) => ({ text }));
|
|
240
|
+
if (opts.media) {
|
|
241
|
+
for (const spec of opts.media) {
|
|
242
|
+
const parsed = parseMediaSpec(spec);
|
|
243
|
+
if (!parsed) {
|
|
244
|
+
error(`Invalid --media spec: "${spec}". Expected "INDEX:url1,url2".`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
if (parsed.index < 0 || parsed.index >= postPayload.length) {
|
|
248
|
+
error(`--media index ${parsed.index} out of range (thread has ${postPayload.length} posts)`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
postPayload[parsed.index].media_urls = parsed.urls;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
211
254
|
}
|
|
212
255
|
try {
|
|
213
256
|
const thread = await client.createThread({
|
|
214
257
|
account_id: accountId,
|
|
215
|
-
posts:
|
|
216
|
-
schedule_at:
|
|
258
|
+
posts: postPayload,
|
|
259
|
+
schedule_at: scheduleAt
|
|
217
260
|
});
|
|
218
261
|
if (opts.json) {
|
|
219
262
|
json(thread);
|
|
@@ -286,6 +329,54 @@ postsCommand.command("delete <id>").description("Delete a post").action(async (i
|
|
|
286
329
|
|
|
287
330
|
// src/commands/accounts.ts
|
|
288
331
|
import { Command as Command6 } from "commander";
|
|
332
|
+
async function readSecretFromStdin(prompt) {
|
|
333
|
+
const stdin = process.stdin;
|
|
334
|
+
process.stderr.write(prompt);
|
|
335
|
+
if (!stdin.isTTY) {
|
|
336
|
+
const readline = await import("readline");
|
|
337
|
+
const rl = readline.createInterface({ input: stdin });
|
|
338
|
+
return new Promise((resolve) => {
|
|
339
|
+
rl.on("line", (line) => {
|
|
340
|
+
rl.close();
|
|
341
|
+
resolve(line.trim());
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return new Promise((resolve) => {
|
|
346
|
+
const chars = [];
|
|
347
|
+
stdin.setRawMode(true);
|
|
348
|
+
stdin.resume();
|
|
349
|
+
stdin.setEncoding("utf8");
|
|
350
|
+
const onData = (ch) => {
|
|
351
|
+
switch (ch) {
|
|
352
|
+
case "\n":
|
|
353
|
+
case "\r":
|
|
354
|
+
case "":
|
|
355
|
+
stdin.setRawMode(false);
|
|
356
|
+
stdin.pause();
|
|
357
|
+
stdin.off("data", onData);
|
|
358
|
+
process.stderr.write("\n");
|
|
359
|
+
resolve(chars.join("").trim());
|
|
360
|
+
break;
|
|
361
|
+
case "":
|
|
362
|
+
stdin.setRawMode(false);
|
|
363
|
+
stdin.pause();
|
|
364
|
+
process.stderr.write("\n");
|
|
365
|
+
process.exit(130);
|
|
366
|
+
break;
|
|
367
|
+
case "\x7F":
|
|
368
|
+
// backspace
|
|
369
|
+
case "\b":
|
|
370
|
+
if (chars.length > 0) chars.pop();
|
|
371
|
+
break;
|
|
372
|
+
default:
|
|
373
|
+
chars.push(ch);
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
stdin.on("data", onData);
|
|
378
|
+
});
|
|
379
|
+
}
|
|
289
380
|
var accountsCommand = new Command6("accounts").description("List connected social accounts (X, Bluesky, LinkedIn, Threads, Mastodon, Instagram, Facebook, Telegram, Reddit, Pinterest, TikTok, YouTube, Google Business Profile, and Snapchat)").option("--json", "Output raw JSON").action(async (opts) => {
|
|
290
381
|
const client = getClient();
|
|
291
382
|
try {
|
|
@@ -326,15 +417,7 @@ accountsCommand.command("connect-bluesky").description("Connect a Bluesky accoun
|
|
|
326
417
|
try {
|
|
327
418
|
let appPassword = opts.appPassword;
|
|
328
419
|
if (!appPassword) {
|
|
329
|
-
|
|
330
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
331
|
-
appPassword = await new Promise((resolve) => {
|
|
332
|
-
process.stderr.write("App Password: ");
|
|
333
|
-
rl.question("", (answer) => {
|
|
334
|
-
rl.close();
|
|
335
|
-
resolve(answer.trim());
|
|
336
|
-
});
|
|
337
|
-
});
|
|
420
|
+
appPassword = await readSecretFromStdin("App Password: ");
|
|
338
421
|
if (!appPassword) {
|
|
339
422
|
error("App password is required");
|
|
340
423
|
process.exit(1);
|
|
@@ -425,15 +508,7 @@ accountsCommand.command("connect-telegram").description("Connect a Telegram bot
|
|
|
425
508
|
let botToken = opts.botToken;
|
|
426
509
|
let chatId = opts.chatId;
|
|
427
510
|
if (!botToken) {
|
|
428
|
-
|
|
429
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
430
|
-
botToken = await new Promise((resolve) => {
|
|
431
|
-
process.stderr.write("Bot Token: ");
|
|
432
|
-
rl.question("", (answer) => {
|
|
433
|
-
rl.close();
|
|
434
|
-
resolve(answer.trim());
|
|
435
|
-
});
|
|
436
|
-
});
|
|
511
|
+
botToken = await readSecretFromStdin("Bot Token: ");
|
|
437
512
|
if (!botToken) {
|
|
438
513
|
error("Bot token is required");
|
|
439
514
|
process.exit(1);
|
|
@@ -465,24 +540,20 @@ accountsCommand.command("connect-telegram").description("Connect a Telegram bot
|
|
|
465
540
|
process.exit(1);
|
|
466
541
|
}
|
|
467
542
|
});
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
})
|
|
477
|
-
accountsCommand.command(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
});
|
|
483
|
-
accountsCommand.command("connect-snapchat").description("Connect a Snapchat account via OAuth").action(async () => {
|
|
484
|
-
info("Snapchat integration is coming soon. Check chirpie.ai for updates.");
|
|
485
|
-
});
|
|
543
|
+
var comingSoonCli = [
|
|
544
|
+
{ cmd: "connect-reddit", label: "Reddit" },
|
|
545
|
+
{ cmd: "connect-pinterest", label: "Pinterest" },
|
|
546
|
+
{ cmd: "connect-tiktok", label: "TikTok" },
|
|
547
|
+
{ cmd: "connect-youtube", label: "YouTube" },
|
|
548
|
+
{ cmd: "connect-google-business", label: "Google Business Profile" },
|
|
549
|
+
{ cmd: "connect-snapchat", label: "Snapchat" }
|
|
550
|
+
];
|
|
551
|
+
for (const { cmd, label } of comingSoonCli) {
|
|
552
|
+
accountsCommand.command(cmd).description(`Connect a ${label} account (coming soon)`).action(() => {
|
|
553
|
+
error(`${label} integration is coming soon. Follow https://chirpie.ai/docs for updates.`);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
486
557
|
|
|
487
558
|
// src/commands/analytics.ts
|
|
488
559
|
import { Command as Command7 } from "commander";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chirpie",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Chirpie CLI — post to social media from the command line",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,12 +26,26 @@
|
|
|
26
26
|
"chirpie",
|
|
27
27
|
"cli",
|
|
28
28
|
"social-media",
|
|
29
|
+
"x",
|
|
29
30
|
"twitter",
|
|
30
|
-
"
|
|
31
|
+
"bluesky",
|
|
32
|
+
"linkedin",
|
|
33
|
+
"threads",
|
|
34
|
+
"mastodon",
|
|
35
|
+
"instagram",
|
|
36
|
+
"facebook",
|
|
37
|
+
"telegram",
|
|
38
|
+
"reddit",
|
|
39
|
+
"pinterest",
|
|
40
|
+
"tiktok",
|
|
41
|
+
"youtube",
|
|
42
|
+
"google-business",
|
|
43
|
+
"snapchat",
|
|
44
|
+
"scheduling"
|
|
31
45
|
],
|
|
32
46
|
"license": "MIT",
|
|
33
47
|
"dependencies": {
|
|
34
|
-
"@chirpie/sdk": "^1.0.
|
|
48
|
+
"@chirpie/sdk": "^1.0.10",
|
|
35
49
|
"commander": "^13"
|
|
36
50
|
},
|
|
37
51
|
"devDependencies": {
|