@autoposting.ai/cli 0.3.2 → 0.3.3
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 -0
- package/dist/cli.cjs +332 -15
- package/dist/cli.cjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -69,7 +69,10 @@ ap doctor
|
|
|
69
69
|
# Posts — create, schedule, publish, AI-rewrite, AI-score
|
|
70
70
|
ap posts create --brand my-brand --text "Launch day!" --platforms x,linkedin,threads
|
|
71
71
|
ap posts create --brand my-brand --text "Tweet 1" --thread "Tweet 2" "Tweet 3" --platforms x # multi-tweet thread (X/Threads, max 25)
|
|
72
|
+
ap posts create --brand my-brand --text "Ship day 🚀" --platforms x --media chart.png demo.mp4 --alt-text "Q2 chart" # attach media (alt text aligns by index)
|
|
73
|
+
ap posts create --brand my-brand --text "Update" --platforms linkedin --account linkedin=@acme-corp # pick one of several accounts on a platform
|
|
72
74
|
ap posts schedule <id> --at "2025-06-01T09:00:00Z"
|
|
75
|
+
ap posts schedule <id> --cancel # unschedule, back to draft
|
|
73
76
|
ap posts publish <id>
|
|
74
77
|
ap posts rewrite <id> # AI-rewrite for better engagement
|
|
75
78
|
ap posts score <id> # AI scoring with feedback
|
package/dist/cli.cjs
CHANGED
|
@@ -42,11 +42,11 @@ function createError(status, body) {
|
|
|
42
42
|
if (status >= 500) return new ServerError(message, status, code);
|
|
43
43
|
return new AutopostingError(message, status, code);
|
|
44
44
|
}
|
|
45
|
-
var VERSION, AutopostingError, AuthenticationError, ScopeError, NotFoundError, ValidationError, RateLimitError, ServerError, Resource, AgentsResource, BillingResource, BrandsResource, CarouselsResource, ClipsResource, PostsResource, KbResource, IdeasResource, UsageResource, WebhooksResource, WorkspacesResource, IDEMPOTENT_METHODS, RETRYABLE_STATUS, sleep, Autoposting;
|
|
45
|
+
var VERSION, AutopostingError, AuthenticationError, ScopeError, NotFoundError, ValidationError, RateLimitError, ServerError, Resource, MediaResource, AgentsResource, BillingResource, BrandsResource, CarouselsResource, ClipsResource, PostsResource, KbResource, IdeasResource, UsageResource, WebhooksResource, WorkspacesResource, IDEMPOTENT_METHODS, RETRYABLE_STATUS, sleep, Autoposting;
|
|
46
46
|
var init_dist = __esm({
|
|
47
47
|
"../sdk/dist/index.mjs"() {
|
|
48
48
|
"use strict";
|
|
49
|
-
VERSION = "0.3.
|
|
49
|
+
VERSION = "0.3.3";
|
|
50
50
|
AutopostingError = class extends Error {
|
|
51
51
|
status;
|
|
52
52
|
code;
|
|
@@ -92,6 +92,15 @@ var init_dist = __esm({
|
|
|
92
92
|
return this.client.request("DELETE", path2);
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
|
+
MediaResource = class extends Resource {
|
|
96
|
+
/** POST /media/upload — multipart/form-data, ≤20MB, image/* | video/*. */
|
|
97
|
+
upload(file) {
|
|
98
|
+
const form = new FormData();
|
|
99
|
+
const blob = file.data instanceof Blob ? file.data : new Blob([file.data], { type: file.contentType });
|
|
100
|
+
form.append("file", blob, file.filename);
|
|
101
|
+
return this.client.request("POST", "/media/upload", form);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
95
104
|
AgentsResource = class extends Resource {
|
|
96
105
|
/** GET /agents — backend returns `{ items, total, limit, offset }`. */
|
|
97
106
|
list() {
|
|
@@ -233,6 +242,10 @@ var init_dist = __esm({
|
|
|
233
242
|
schedule(id, scheduledAt) {
|
|
234
243
|
return this.client.request("PUT", `/posts/${id}/schedule`, { scheduledAt });
|
|
235
244
|
}
|
|
245
|
+
/** PUT /posts/:id/schedule with { cancel: true } — returns the post to draft. */
|
|
246
|
+
unschedule(id) {
|
|
247
|
+
return this.client.request("PUT", `/posts/${id}/schedule`, { cancel: true });
|
|
248
|
+
}
|
|
236
249
|
retry(id) {
|
|
237
250
|
return this.client.request("POST", `/posts/${id}/retry`);
|
|
238
251
|
}
|
|
@@ -361,6 +374,7 @@ var init_dist = __esm({
|
|
|
361
374
|
authSource;
|
|
362
375
|
maxRetries;
|
|
363
376
|
retryBaseMs;
|
|
377
|
+
media;
|
|
364
378
|
agents;
|
|
365
379
|
billing;
|
|
366
380
|
brands;
|
|
@@ -386,6 +400,7 @@ var init_dist = __esm({
|
|
|
386
400
|
this.authSource = config.authSource ?? "api-key";
|
|
387
401
|
this.maxRetries = Math.max(0, config.maxRetries ?? 2);
|
|
388
402
|
this.retryBaseMs = Math.max(0, config.retryBaseMs ?? 300);
|
|
403
|
+
this.media = new MediaResource(this);
|
|
389
404
|
this.agents = new AgentsResource(this);
|
|
390
405
|
this.billing = new BillingResource(this);
|
|
391
406
|
this.brands = new BrandsResource(this);
|
|
@@ -2777,16 +2792,226 @@ function createClipsCommand() {
|
|
|
2777
2792
|
// src/commands/posts.ts
|
|
2778
2793
|
var import_commander7 = require("commander");
|
|
2779
2794
|
init_dist();
|
|
2795
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
2796
|
+
var import_node_path2 = __toESM(require("path"));
|
|
2797
|
+
|
|
2798
|
+
// src/lib/media-flags.ts
|
|
2799
|
+
var import_node_fs2 = require("fs");
|
|
2780
2800
|
var VALID_PLATFORMS = ["x", "linkedin", "instagram", "threads", "youtube"];
|
|
2801
|
+
var EXT_TO_MIME = {
|
|
2802
|
+
jpg: "image/jpeg",
|
|
2803
|
+
jpeg: "image/jpeg",
|
|
2804
|
+
png: "image/png",
|
|
2805
|
+
gif: "image/gif",
|
|
2806
|
+
webp: "image/webp",
|
|
2807
|
+
mp4: "video/mp4",
|
|
2808
|
+
mov: "video/quicktime",
|
|
2809
|
+
webm: "video/webm"
|
|
2810
|
+
};
|
|
2811
|
+
function extToMime(filename) {
|
|
2812
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
2813
|
+
const mime = EXT_TO_MIME[ext];
|
|
2814
|
+
if (!mime) {
|
|
2815
|
+
throw new Error(
|
|
2816
|
+
`Unsupported file extension ".${ext || "(none)"}" in "${filename}". Supported: ${Object.keys(EXT_TO_MIME).join(", ")}`
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2819
|
+
return mime;
|
|
2820
|
+
}
|
|
2821
|
+
function parsePairs(flag, vals) {
|
|
2822
|
+
const result = {};
|
|
2823
|
+
for (const val of vals) {
|
|
2824
|
+
const eqIdx = val.indexOf("=");
|
|
2825
|
+
if (eqIdx < 1) {
|
|
2826
|
+
throw new Error(`${flag}: expected "platform=value" format, got "${val}"`);
|
|
2827
|
+
}
|
|
2828
|
+
const p = val.slice(0, eqIdx).trim();
|
|
2829
|
+
const v = val.slice(eqIdx + 1);
|
|
2830
|
+
if (!VALID_PLATFORMS.includes(p)) {
|
|
2831
|
+
throw new Error(`${flag}: unknown platform "${p}". Valid: ${VALID_PLATFORMS.join(", ")}`);
|
|
2832
|
+
}
|
|
2833
|
+
result[p] = v;
|
|
2834
|
+
}
|
|
2835
|
+
return result;
|
|
2836
|
+
}
|
|
2837
|
+
function parsePlatformMediaPairs(flag, vals) {
|
|
2838
|
+
const result = {};
|
|
2839
|
+
for (const val of vals) {
|
|
2840
|
+
const eqIdx = val.indexOf("=");
|
|
2841
|
+
if (eqIdx < 1) {
|
|
2842
|
+
throw new Error(`${flag}: expected "platform=path[,path...]" format, got "${val}"`);
|
|
2843
|
+
}
|
|
2844
|
+
const p = val.slice(0, eqIdx).trim();
|
|
2845
|
+
const paths = val.slice(eqIdx + 1).split(",").map((s) => s.trim()).filter(Boolean);
|
|
2846
|
+
if (!VALID_PLATFORMS.includes(p)) {
|
|
2847
|
+
throw new Error(`${flag}: unknown platform "${p}". Valid: ${VALID_PLATFORMS.join(", ")}`);
|
|
2848
|
+
}
|
|
2849
|
+
result[p] = paths;
|
|
2850
|
+
}
|
|
2851
|
+
return result;
|
|
2852
|
+
}
|
|
2853
|
+
function validateMediaExtensions(paths) {
|
|
2854
|
+
for (const p of paths) extToMime(p);
|
|
2855
|
+
}
|
|
2856
|
+
function validateMediaCount(paths) {
|
|
2857
|
+
if (paths.length > 10) {
|
|
2858
|
+
throw new Error(`--media accepts at most 10 files (received ${paths.length}).`);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
function validateMediaPaths(paths) {
|
|
2862
|
+
for (const p of paths) {
|
|
2863
|
+
if (!(0, import_node_fs2.existsSync)(p)) {
|
|
2864
|
+
throw new Error(`Media file not found: "${p}"`);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
function alignAltText(mediaPaths, altTexts) {
|
|
2869
|
+
if (altTexts.length > mediaPaths.length) {
|
|
2870
|
+
throw new Error(
|
|
2871
|
+
`--alt-text has more values (${altTexts.length}) than --media (${mediaPaths.length}). Alt text aligns to media by index.`
|
|
2872
|
+
);
|
|
2873
|
+
}
|
|
2874
|
+
return mediaPaths.map((_, i) => altTexts[i]);
|
|
2875
|
+
}
|
|
2876
|
+
function buildYoutubeOptions(opts) {
|
|
2877
|
+
const { ytTitle, ytDescription, ytTags, ytPrivacy, ytCategory, ytMadeForKids } = opts;
|
|
2878
|
+
const o = {};
|
|
2879
|
+
if (ytTitle) o.title = ytTitle;
|
|
2880
|
+
if (ytDescription) o.description = ytDescription;
|
|
2881
|
+
if (ytTags) o.tags = ytTags.split(",").map((t) => t.trim()).filter(Boolean);
|
|
2882
|
+
if (ytPrivacy) o.privacyStatus = ytPrivacy;
|
|
2883
|
+
if (ytCategory) o.categoryId = ytCategory;
|
|
2884
|
+
if (ytMadeForKids !== void 0) o.madeForKids = ytMadeForKids;
|
|
2885
|
+
return Object.keys(o).length > 0 ? o : void 0;
|
|
2886
|
+
}
|
|
2887
|
+
function buildInstagramOptions(opts) {
|
|
2888
|
+
const { igReel, igShareToFeed, igCoverUrl, igThumbOffsetMs, igCollaborators } = opts;
|
|
2889
|
+
const hasAnyReelFlag = igReel !== void 0 || igShareToFeed !== void 0 || igCoverUrl !== void 0 || igThumbOffsetMs !== void 0 || igCollaborators !== void 0;
|
|
2890
|
+
if (!hasAnyReelFlag) return void 0;
|
|
2891
|
+
const reel = {};
|
|
2892
|
+
if (igShareToFeed !== void 0) reel.shareToFeed = igShareToFeed;
|
|
2893
|
+
if (igCoverUrl) reel.coverUrl = igCoverUrl;
|
|
2894
|
+
if (igThumbOffsetMs !== void 0) {
|
|
2895
|
+
const n = Number(igThumbOffsetMs);
|
|
2896
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
2897
|
+
throw new Error(
|
|
2898
|
+
`--ig-thumb-offset-ms must be a non-negative number (received "${igThumbOffsetMs}")`
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
reel.thumbOffsetMs = n;
|
|
2902
|
+
}
|
|
2903
|
+
if (igCollaborators) {
|
|
2904
|
+
reel.collaborators = igCollaborators.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2905
|
+
}
|
|
2906
|
+
return { reel };
|
|
2907
|
+
}
|
|
2908
|
+
function buildThreadsOptions(opts) {
|
|
2909
|
+
const { threadsReplyTo, threadsReplyControl } = opts;
|
|
2910
|
+
if (!threadsReplyTo && !threadsReplyControl) return void 0;
|
|
2911
|
+
const o = {};
|
|
2912
|
+
if (threadsReplyTo) o.replyToId = threadsReplyTo;
|
|
2913
|
+
if (threadsReplyControl) o.replyControl = threadsReplyControl;
|
|
2914
|
+
return o;
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
// src/lib/account-select.ts
|
|
2918
|
+
var VALID_PLATFORMS2 = ["x", "linkedin", "instagram", "threads", "youtube"];
|
|
2919
|
+
async function resolveTargetAccounts({
|
|
2920
|
+
brandSlug,
|
|
2921
|
+
platforms,
|
|
2922
|
+
accountFlags,
|
|
2923
|
+
client,
|
|
2924
|
+
isTty
|
|
2925
|
+
}) {
|
|
2926
|
+
const accountMap = {};
|
|
2927
|
+
for (const flag of accountFlags) {
|
|
2928
|
+
const eqIdx = flag.indexOf("=");
|
|
2929
|
+
if (eqIdx < 1) {
|
|
2930
|
+
throw Object.assign(
|
|
2931
|
+
new Error(`--account: expected "platform=handle|id" format, got "${flag}"`),
|
|
2932
|
+
{ exitCode: 1 }
|
|
2933
|
+
);
|
|
2934
|
+
}
|
|
2935
|
+
const p = flag.slice(0, eqIdx).trim();
|
|
2936
|
+
if (!VALID_PLATFORMS2.includes(p)) {
|
|
2937
|
+
throw Object.assign(
|
|
2938
|
+
new Error(`--account: unknown platform "${p}". Valid: ${VALID_PLATFORMS2.join(", ")}`),
|
|
2939
|
+
{ exitCode: 1 }
|
|
2940
|
+
);
|
|
2941
|
+
}
|
|
2942
|
+
accountMap[p] = flag.slice(eqIdx + 1).trim();
|
|
2943
|
+
}
|
|
2944
|
+
const connections = await client.brands.authStatus(brandSlug);
|
|
2945
|
+
const byPlatform = {};
|
|
2946
|
+
for (const conn of connections) {
|
|
2947
|
+
if (!conn.connected) continue;
|
|
2948
|
+
const p = conn.platform;
|
|
2949
|
+
if (!byPlatform[p]) byPlatform[p] = [];
|
|
2950
|
+
byPlatform[p].push(conn);
|
|
2951
|
+
}
|
|
2952
|
+
const result = {};
|
|
2953
|
+
for (const platform of platforms) {
|
|
2954
|
+
const accounts = byPlatform[platform] ?? [];
|
|
2955
|
+
const specifiedValue = accountMap[platform];
|
|
2956
|
+
if (specifiedValue !== void 0) {
|
|
2957
|
+
const normalized = specifiedValue.startsWith("@") ? specifiedValue.slice(1) : specifiedValue;
|
|
2958
|
+
const match = accounts.find(
|
|
2959
|
+
(a) => a.platformUsername?.toLowerCase() === normalized.toLowerCase() || a.platformUserId === specifiedValue
|
|
2960
|
+
);
|
|
2961
|
+
if (!match?.platformUserId) {
|
|
2962
|
+
const valid = accounts.map((a) => ` @${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`).join("\n");
|
|
2963
|
+
throw Object.assign(
|
|
2964
|
+
new Error(
|
|
2965
|
+
`--account: unknown ${platform} account "${specifiedValue}".
|
|
2966
|
+
Connected accounts:
|
|
2967
|
+
${valid || " (none)"}`
|
|
2968
|
+
),
|
|
2969
|
+
{ exitCode: 1 }
|
|
2970
|
+
);
|
|
2971
|
+
}
|
|
2972
|
+
if (!result[platform]) result[platform] = [];
|
|
2973
|
+
result[platform].push(match.platformUserId);
|
|
2974
|
+
} else if (accounts.length >= 2) {
|
|
2975
|
+
if (isTty) {
|
|
2976
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
2977
|
+
const choices = accounts.map((a) => ({
|
|
2978
|
+
name: `@${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`,
|
|
2979
|
+
value: a.platformUserId ?? ""
|
|
2980
|
+
}));
|
|
2981
|
+
const selected = await checkbox({
|
|
2982
|
+
message: `Multiple ${platform} accounts connected \u2014 select which to post to:`,
|
|
2983
|
+
choices
|
|
2984
|
+
});
|
|
2985
|
+
const valid = selected.filter(Boolean);
|
|
2986
|
+
if (valid.length > 0) result[platform] = valid;
|
|
2987
|
+
} else {
|
|
2988
|
+
const list = accounts.map((a) => ` @${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`).join("\n");
|
|
2989
|
+
throw Object.assign(
|
|
2990
|
+
new Error(
|
|
2991
|
+
`Multiple ${platform} accounts connected but no --account specified (non-interactive mode).
|
|
2992
|
+
Connected accounts:
|
|
2993
|
+
${list}
|
|
2994
|
+
Pass --account ${platform}=<handle|id> to select.`
|
|
2995
|
+
),
|
|
2996
|
+
{ exitCode: 1 }
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
return result;
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/commands/posts.ts
|
|
3005
|
+
var VALID_PLATFORMS3 = ["x", "linkedin", "instagram", "threads", "youtube"];
|
|
2781
3006
|
function parsePlatforms(raw) {
|
|
2782
3007
|
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
2783
3008
|
if (parts.length === 0) {
|
|
2784
3009
|
throw new Error("No platforms provided. Pass a comma-separated list, e.g. --platforms x,linkedin");
|
|
2785
3010
|
}
|
|
2786
|
-
const invalid = parts.filter((p) => !
|
|
3011
|
+
const invalid = parts.filter((p) => !VALID_PLATFORMS3.includes(p));
|
|
2787
3012
|
if (invalid.length > 0) {
|
|
2788
3013
|
throw new Error(
|
|
2789
|
-
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${
|
|
3014
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS3.join(", ")}`
|
|
2790
3015
|
);
|
|
2791
3016
|
}
|
|
2792
3017
|
return parts;
|
|
@@ -2867,25 +3092,109 @@ function createPostsCommand() {
|
|
|
2867
3092
|
posts.command("create").description("Create a new post").requiredOption("--brand <slug>", "Brand slug").requiredOption("--text <text>", "Post text content").requiredOption("--platforms <list>", "Comma-separated platforms (e.g. x,linkedin)").option("--at <iso>", "Schedule date/time (ISO 8601)").option(
|
|
2868
3093
|
"--thread <text...>",
|
|
2869
3094
|
"Additional posts appended after --text to form a thread (X/Threads only, max 25)"
|
|
2870
|
-
).action(
|
|
3095
|
+
).option("--media <path...>", "Media file paths to attach (max 10)").option("--alt-text <text...>", "Alt text for --media files, aligned by index").option("--platform-text <pairs...>", "Per-platform text, e.g. x=Hello linkedin=World").option("--platform-media <pairs...>", "Per-platform media, e.g. instagram=photo.jpg x=banner.png").option("--yt-title <t>", "YouTube video title").option("--yt-description <d>", "YouTube video description").option("--yt-tags <list>", "YouTube tags (comma-separated)").option("--yt-privacy <v>", "YouTube privacy status (public|unlisted|private)").option("--yt-category <id>", "YouTube category ID").option("--yt-made-for-kids", "Mark YouTube video as made for kids").option("--ig-reel", "Post as an Instagram Reel").option("--ig-share-to-feed", "Share Reel to feed").option("--ig-cover-url <url>", "Reel cover image URL").option("--ig-thumb-offset-ms <n>", "Reel thumbnail offset in milliseconds").option("--ig-collaborators <list>", "Reel collaborator handles (comma-separated)").option("--threads-reply-to <id>", "Threads post ID to reply to").option("--threads-reply-control <v>", "Threads reply control (everyone|accounts_you_follow|mentioned_only)").option("--account <p=handle|id...>", "Target specific account per platform, e.g. x=@handle").action(
|
|
2871
3096
|
async (opts, cmd) => {
|
|
2872
3097
|
const globals = cmd.optsWithGlobals();
|
|
2873
3098
|
const printer = createPrinter(globals);
|
|
2874
|
-
|
|
3099
|
+
let spinner;
|
|
2875
3100
|
try {
|
|
2876
3101
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2877
3102
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3103
|
+
const platforms = parsePlatforms(opts.platforms);
|
|
3104
|
+
const scheduledAt = opts.at ? validateScheduledAt(opts.at) : void 0;
|
|
3105
|
+
if (opts.media && opts.media.length > 0) {
|
|
3106
|
+
validateMediaCount(opts.media);
|
|
3107
|
+
validateMediaPaths(opts.media);
|
|
3108
|
+
validateMediaExtensions(opts.media);
|
|
3109
|
+
}
|
|
3110
|
+
const platformTexts = opts.platformText && opts.platformText.length > 0 ? parsePairs("--platform-text", opts.platformText) : void 0;
|
|
3111
|
+
const platformMediaPaths = opts.platformMedia && opts.platformMedia.length > 0 ? parsePlatformMediaPairs("--platform-media", opts.platformMedia) : {};
|
|
3112
|
+
for (const paths of Object.values(platformMediaPaths)) {
|
|
3113
|
+
validateMediaPaths(paths);
|
|
3114
|
+
validateMediaExtensions(paths);
|
|
3115
|
+
}
|
|
3116
|
+
const altTexts = alignAltText(opts.media ?? [], opts.altText ?? []);
|
|
3117
|
+
const youtubeOptions = buildYoutubeOptions({
|
|
3118
|
+
ytTitle: opts.ytTitle,
|
|
3119
|
+
ytDescription: opts.ytDescription,
|
|
3120
|
+
ytTags: opts.ytTags,
|
|
3121
|
+
ytPrivacy: opts.ytPrivacy,
|
|
3122
|
+
ytCategory: opts.ytCategory,
|
|
3123
|
+
ytMadeForKids: opts.ytMadeForKids
|
|
3124
|
+
});
|
|
3125
|
+
const instagramOptions = buildInstagramOptions({
|
|
3126
|
+
igReel: opts.igReel,
|
|
3127
|
+
igShareToFeed: opts.igShareToFeed,
|
|
3128
|
+
igCoverUrl: opts.igCoverUrl,
|
|
3129
|
+
igThumbOffsetMs: opts.igThumbOffsetMs,
|
|
3130
|
+
igCollaborators: opts.igCollaborators
|
|
3131
|
+
});
|
|
3132
|
+
const threadsOptions = buildThreadsOptions({
|
|
3133
|
+
threadsReplyTo: opts.threadsReplyTo,
|
|
3134
|
+
threadsReplyControl: opts.threadsReplyControl
|
|
3135
|
+
});
|
|
3136
|
+
const targetAccountIds = await resolveTargetAccounts({
|
|
3137
|
+
brandSlug: opts.brand,
|
|
3138
|
+
platforms,
|
|
3139
|
+
accountFlags: opts.account ?? [],
|
|
3140
|
+
client,
|
|
3141
|
+
// Picker reads stdin — gate on a real interactive terminal, not the
|
|
3142
|
+
// output mode (printer.isTty() is true even when stdout/stdin is piped).
|
|
3143
|
+
isTty: Boolean(process.stdin.isTTY)
|
|
3144
|
+
});
|
|
3145
|
+
spinner = printer.spinner("Creating post\u2026");
|
|
3146
|
+
const mediaInputs = [];
|
|
3147
|
+
for (let i = 0; i < (opts.media ?? []).length; i++) {
|
|
3148
|
+
const filePath = opts.media[i];
|
|
3149
|
+
const data = await import_promises.default.readFile(filePath);
|
|
3150
|
+
const filename = import_node_path2.default.basename(filePath);
|
|
3151
|
+
const contentType = extToMime(filename);
|
|
3152
|
+
const uploaded = await client.media.upload({
|
|
3153
|
+
data: new Uint8Array(data),
|
|
3154
|
+
filename,
|
|
3155
|
+
contentType
|
|
3156
|
+
});
|
|
3157
|
+
mediaInputs.push({
|
|
3158
|
+
url: uploaded.url,
|
|
3159
|
+
type: uploaded.type,
|
|
3160
|
+
...altTexts[i] ? { altText: altTexts[i] } : {}
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
const platformMediaResult = {};
|
|
3164
|
+
for (const [p, paths] of Object.entries(platformMediaPaths)) {
|
|
3165
|
+
const uploads = [];
|
|
3166
|
+
for (const filePath of paths) {
|
|
3167
|
+
const data = await import_promises.default.readFile(filePath);
|
|
3168
|
+
const filename = import_node_path2.default.basename(filePath);
|
|
3169
|
+
const contentType = extToMime(filename);
|
|
3170
|
+
const uploaded = await client.media.upload({
|
|
3171
|
+
data: new Uint8Array(data),
|
|
3172
|
+
filename,
|
|
3173
|
+
contentType
|
|
3174
|
+
});
|
|
3175
|
+
uploads.push({ url: uploaded.url, type: uploaded.type });
|
|
3176
|
+
}
|
|
3177
|
+
platformMediaResult[p] = uploads;
|
|
3178
|
+
}
|
|
2878
3179
|
const post = await client.posts.create({
|
|
2879
3180
|
brandSlug: opts.brand,
|
|
2880
3181
|
text: opts.text,
|
|
2881
|
-
platforms
|
|
2882
|
-
...
|
|
2883
|
-
...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {}
|
|
3182
|
+
platforms,
|
|
3183
|
+
...scheduledAt ? { scheduledAt } : {},
|
|
3184
|
+
...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {},
|
|
3185
|
+
...mediaInputs.length > 0 ? { media: mediaInputs } : {},
|
|
3186
|
+
...Object.keys(platformMediaResult).length > 0 ? { platformMedia: platformMediaResult } : {},
|
|
3187
|
+
...platformTexts && Object.keys(platformTexts).length > 0 ? { platformTexts } : {},
|
|
3188
|
+
...Object.keys(targetAccountIds).length > 0 ? { targetAccountIds } : {},
|
|
3189
|
+
...instagramOptions ? { instagramOptions } : {},
|
|
3190
|
+
...threadsOptions ? { threadsOptions } : {},
|
|
3191
|
+
...youtubeOptions ? { youtubeOptions } : {},
|
|
3192
|
+
source: "cli"
|
|
2884
3193
|
});
|
|
2885
3194
|
spinner.stop();
|
|
2886
3195
|
printer.log(post);
|
|
2887
3196
|
} catch (err) {
|
|
2888
|
-
spinner
|
|
3197
|
+
spinner?.fail();
|
|
2889
3198
|
printer.error(err);
|
|
2890
3199
|
process.exit(resolveExitCode5(err));
|
|
2891
3200
|
}
|
|
@@ -2952,14 +3261,22 @@ function createPostsCommand() {
|
|
|
2952
3261
|
process.exit(resolveExitCode5(err));
|
|
2953
3262
|
}
|
|
2954
3263
|
});
|
|
2955
|
-
posts.command("schedule <id>").description("Schedule a post for a specific time").
|
|
3264
|
+
posts.command("schedule <id>").description("Schedule a post for a specific time, or --cancel to unschedule it").option("--at <iso>", "ISO 8601 datetime to schedule the post").option("--cancel", "Unschedule the post (return it to draft)").action(async (id, opts, cmd) => {
|
|
2956
3265
|
const globals = cmd.optsWithGlobals();
|
|
2957
3266
|
const printer = createPrinter(globals);
|
|
2958
|
-
const spinner = printer.spinner(
|
|
3267
|
+
const spinner = printer.spinner(
|
|
3268
|
+
opts.cancel ? `Unscheduling post "${id}"\u2026` : `Scheduling post "${id}"\u2026`
|
|
3269
|
+
);
|
|
2959
3270
|
try {
|
|
3271
|
+
if (opts.cancel && opts.at) {
|
|
3272
|
+
throw new Error("Cannot use --at together with --cancel \u2014 choose one.");
|
|
3273
|
+
}
|
|
3274
|
+
if (!opts.cancel && !opts.at) {
|
|
3275
|
+
throw new Error("Provide either --at <iso> to schedule, or --cancel to unschedule.");
|
|
3276
|
+
}
|
|
2960
3277
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2961
3278
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2962
|
-
const post = await client.posts.schedule(id, validateScheduledAt(opts.at));
|
|
3279
|
+
const post = opts.cancel ? await client.posts.unschedule(id) : await client.posts.schedule(id, validateScheduledAt(opts.at));
|
|
2963
3280
|
spinner.stop();
|
|
2964
3281
|
printer.log(post);
|
|
2965
3282
|
} catch (err) {
|
|
@@ -3525,7 +3842,7 @@ function createWorkspacesCommand() {
|
|
|
3525
3842
|
// src/commands/doctor.ts
|
|
3526
3843
|
var import_commander13 = require("commander");
|
|
3527
3844
|
init_dist();
|
|
3528
|
-
var
|
|
3845
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
3529
3846
|
var API_HEALTH_URL = "https://app.autoposting.ai/api-proxy/health";
|
|
3530
3847
|
async function runChecks() {
|
|
3531
3848
|
const results = [];
|
|
@@ -3545,7 +3862,7 @@ async function runChecks() {
|
|
|
3545
3862
|
}
|
|
3546
3863
|
const credPath = getCredentialsPath();
|
|
3547
3864
|
try {
|
|
3548
|
-
const stat =
|
|
3865
|
+
const stat = import_node_fs3.default.statSync(credPath);
|
|
3549
3866
|
const mode = stat.mode & 511;
|
|
3550
3867
|
const isSecure = mode === 384;
|
|
3551
3868
|
results.push({
|