@autoposting.ai/cli 0.3.1 → 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 +488 -86
- package/dist/cli.cjs.map +1 -1
- package/package.json +2 -1
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, 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
|
}
|
|
@@ -324,9 +337,17 @@ var init_dist = __esm({
|
|
|
324
337
|
}
|
|
325
338
|
/**
|
|
326
339
|
* GET /orgs — backend returns `{ organizations, activeOrgId }`, not a bare array.
|
|
327
|
-
*
|
|
340
|
+
* This route is session-only (better-auth session); an API key receives a bare 401,
|
|
341
|
+
* so guard up front with accurate guidance instead of an opaque "Unauthorized".
|
|
328
342
|
*/
|
|
329
343
|
list() {
|
|
344
|
+
if (this.client.authSource === "api-key") {
|
|
345
|
+
return Promise.reject(
|
|
346
|
+
new Error(
|
|
347
|
+
"Listing workspaces requires session auth. An API key is scoped to a single workspace and cannot list organizations \u2014 manage workspaces from the web dashboard."
|
|
348
|
+
)
|
|
349
|
+
);
|
|
350
|
+
}
|
|
330
351
|
return this.get("/orgs");
|
|
331
352
|
}
|
|
332
353
|
switchWorkspace(id) {
|
|
@@ -340,6 +361,9 @@ var init_dist = __esm({
|
|
|
340
361
|
return this.client.request("PUT", "/orgs/active", { organizationId: id });
|
|
341
362
|
}
|
|
342
363
|
};
|
|
364
|
+
IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "DELETE"]);
|
|
365
|
+
RETRYABLE_STATUS = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
366
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
343
367
|
Autoposting = class {
|
|
344
368
|
/** @internal Not part of the public API — use SDK methods to make requests. */
|
|
345
369
|
_apiKey;
|
|
@@ -348,6 +372,9 @@ var init_dist = __esm({
|
|
|
348
372
|
extraHeaders;
|
|
349
373
|
/** Indicates how the client was authenticated; used by WorkspacesResource. */
|
|
350
374
|
authSource;
|
|
375
|
+
maxRetries;
|
|
376
|
+
retryBaseMs;
|
|
377
|
+
media;
|
|
351
378
|
agents;
|
|
352
379
|
billing;
|
|
353
380
|
brands;
|
|
@@ -371,6 +398,9 @@ var init_dist = __esm({
|
|
|
371
398
|
this.timeout = config.timeout ?? 3e4;
|
|
372
399
|
this.extraHeaders = config.headers ?? {};
|
|
373
400
|
this.authSource = config.authSource ?? "api-key";
|
|
401
|
+
this.maxRetries = Math.max(0, config.maxRetries ?? 2);
|
|
402
|
+
this.retryBaseMs = Math.max(0, config.retryBaseMs ?? 300);
|
|
403
|
+
this.media = new MediaResource(this);
|
|
374
404
|
this.agents = new AgentsResource(this);
|
|
375
405
|
this.billing = new BillingResource(this);
|
|
376
406
|
this.brands = new BrandsResource(this);
|
|
@@ -383,7 +413,42 @@ var init_dist = __esm({
|
|
|
383
413
|
this.webhooks = new WebhooksResource(this);
|
|
384
414
|
this.workspaces = new WorkspacesResource(this);
|
|
385
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* GET /auth/profile — resolve the server-side identity behind the current credential.
|
|
418
|
+
* Validates the key (401 on reject) and returns who/what it's scoped to.
|
|
419
|
+
*/
|
|
420
|
+
getProfile() {
|
|
421
|
+
return this.request("GET", "/auth/profile");
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Public entry point. Retries idempotent requests (GET/PUT/DELETE) on transient
|
|
425
|
+
* failures — network errors, timeouts, and 5xx — with exponential backoff + jitter.
|
|
426
|
+
* Non-idempotent methods (POST/PATCH) are never retried, to avoid duplicate side effects.
|
|
427
|
+
*/
|
|
386
428
|
async request(method, path2, body, query) {
|
|
429
|
+
let lastErr;
|
|
430
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
431
|
+
if (attempt > 0) {
|
|
432
|
+
const backoff = this.retryBaseMs * 2 ** (attempt - 1);
|
|
433
|
+
await sleep(backoff + Math.floor(Math.random() * this.retryBaseMs));
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
return await this._send(method, path2, body, query);
|
|
437
|
+
} catch (err) {
|
|
438
|
+
lastErr = err;
|
|
439
|
+
if (attempt < this.maxRetries && this.isRetryable(method, err)) continue;
|
|
440
|
+
throw err;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
throw lastErr;
|
|
444
|
+
}
|
|
445
|
+
isRetryable(method, err) {
|
|
446
|
+
if (!IDEMPOTENT_METHODS.has(method)) return false;
|
|
447
|
+
if (!(err instanceof AutopostingError)) return false;
|
|
448
|
+
if (err.code === "NETWORK_ERROR" || err.code === "TIMEOUT") return true;
|
|
449
|
+
return RETRYABLE_STATUS.has(err.status);
|
|
450
|
+
}
|
|
451
|
+
async _send(method, path2, body, query) {
|
|
387
452
|
let url = `${this.baseUrl}${path2}`;
|
|
388
453
|
if (query && Object.keys(query).length > 0) {
|
|
389
454
|
const params = new URLSearchParams();
|
|
@@ -1792,7 +1857,7 @@ var OraSpinner = class {
|
|
|
1792
1857
|
if (this.instance) {
|
|
1793
1858
|
this.instance.fail(message);
|
|
1794
1859
|
this.instance = null;
|
|
1795
|
-
} else {
|
|
1860
|
+
} else if (message) {
|
|
1796
1861
|
process.stderr.write(`${message}
|
|
1797
1862
|
`);
|
|
1798
1863
|
}
|
|
@@ -1809,6 +1874,10 @@ var Printer = class {
|
|
|
1809
1874
|
this.mode = mode;
|
|
1810
1875
|
}
|
|
1811
1876
|
mode;
|
|
1877
|
+
/** True only for human-readable output, where extra hint lines are safe to print. */
|
|
1878
|
+
isTty() {
|
|
1879
|
+
return this.mode === "tty";
|
|
1880
|
+
}
|
|
1812
1881
|
log(data) {
|
|
1813
1882
|
const out = formatOutput(data, this.mode);
|
|
1814
1883
|
if (out !== "") console.log(out);
|
|
@@ -1901,7 +1970,7 @@ function createAgentsCommand() {
|
|
|
1901
1970
|
}));
|
|
1902
1971
|
printer.table(rows, ["id", "name", "type", "brand", "frequency", "enabled"]);
|
|
1903
1972
|
} catch (err) {
|
|
1904
|
-
spinner.
|
|
1973
|
+
spinner.fail();
|
|
1905
1974
|
printer.error(err);
|
|
1906
1975
|
process.exit(resolveExitCode(err));
|
|
1907
1976
|
}
|
|
@@ -1917,7 +1986,7 @@ function createAgentsCommand() {
|
|
|
1917
1986
|
spinner.stop();
|
|
1918
1987
|
printer.log(agent);
|
|
1919
1988
|
} catch (err) {
|
|
1920
|
-
spinner.
|
|
1989
|
+
spinner.fail();
|
|
1921
1990
|
printer.error(err);
|
|
1922
1991
|
process.exit(resolveExitCode(err));
|
|
1923
1992
|
}
|
|
@@ -1943,7 +2012,7 @@ function createAgentsCommand() {
|
|
|
1943
2012
|
spinner.stop();
|
|
1944
2013
|
printer.log(agent);
|
|
1945
2014
|
} catch (err) {
|
|
1946
|
-
spinner.
|
|
2015
|
+
spinner.fail();
|
|
1947
2016
|
printer.error(err);
|
|
1948
2017
|
process.exit(resolveExitCode(err));
|
|
1949
2018
|
}
|
|
@@ -1967,7 +2036,7 @@ function createAgentsCommand() {
|
|
|
1967
2036
|
spinner.stop();
|
|
1968
2037
|
printer.log(agent);
|
|
1969
2038
|
} catch (err) {
|
|
1970
|
-
spinner.
|
|
2039
|
+
spinner.fail();
|
|
1971
2040
|
printer.error(err);
|
|
1972
2041
|
process.exit(resolveExitCode(err));
|
|
1973
2042
|
}
|
|
@@ -1988,7 +2057,7 @@ function createAgentsCommand() {
|
|
|
1988
2057
|
spinner.stop();
|
|
1989
2058
|
printer.log(`Agent "${id}" deleted.`);
|
|
1990
2059
|
} catch (err) {
|
|
1991
|
-
spinner.
|
|
2060
|
+
spinner.fail();
|
|
1992
2061
|
printer.error(err);
|
|
1993
2062
|
process.exit(resolveExitCode(err));
|
|
1994
2063
|
}
|
|
@@ -2004,7 +2073,7 @@ function createAgentsCommand() {
|
|
|
2004
2073
|
spinner.stop();
|
|
2005
2074
|
printer.log({ runId: queued.runId, status: queued.status });
|
|
2006
2075
|
} catch (err) {
|
|
2007
|
-
spinner.
|
|
2076
|
+
spinner.fail();
|
|
2008
2077
|
printer.error(err);
|
|
2009
2078
|
process.exit(resolveExitCode(err));
|
|
2010
2079
|
}
|
|
@@ -2020,7 +2089,7 @@ function createAgentsCommand() {
|
|
|
2020
2089
|
spinner.stop();
|
|
2021
2090
|
printer.log(agent);
|
|
2022
2091
|
} catch (err) {
|
|
2023
|
-
spinner.
|
|
2092
|
+
spinner.fail();
|
|
2024
2093
|
printer.error(err);
|
|
2025
2094
|
process.exit(resolveExitCode(err));
|
|
2026
2095
|
}
|
|
@@ -2042,7 +2111,7 @@ function createAgentsCommand() {
|
|
|
2042
2111
|
}));
|
|
2043
2112
|
printer.table(rows, ["id", "status", "output", "createdAt"]);
|
|
2044
2113
|
} catch (err) {
|
|
2045
|
-
spinner.
|
|
2114
|
+
spinner.fail();
|
|
2046
2115
|
printer.error(err);
|
|
2047
2116
|
process.exit(resolveExitCode(err));
|
|
2048
2117
|
}
|
|
@@ -2287,7 +2356,7 @@ function createBillingCommand() {
|
|
|
2287
2356
|
["plan", "status", "cycle", "accounts", "credits", "renews", "trial ends"]
|
|
2288
2357
|
);
|
|
2289
2358
|
} catch (err) {
|
|
2290
|
-
spinner.
|
|
2359
|
+
spinner.fail();
|
|
2291
2360
|
printer.error(err);
|
|
2292
2361
|
process.exit(resolveExitCode2(err));
|
|
2293
2362
|
}
|
|
@@ -2320,7 +2389,7 @@ function createBillingCommand() {
|
|
|
2320
2389
|
printer.table(usageRows, ["date", "description", "amount", "type"]);
|
|
2321
2390
|
}
|
|
2322
2391
|
} catch (err) {
|
|
2323
|
-
spinner.
|
|
2392
|
+
spinner.fail();
|
|
2324
2393
|
printer.error(err);
|
|
2325
2394
|
process.exit(resolveExitCode2(err));
|
|
2326
2395
|
}
|
|
@@ -2331,6 +2400,24 @@ function createBillingCommand() {
|
|
|
2331
2400
|
// src/commands/brands.ts
|
|
2332
2401
|
var import_commander4 = require("commander");
|
|
2333
2402
|
init_dist();
|
|
2403
|
+
function tokenStatus(c, now = Date.now()) {
|
|
2404
|
+
if (!c.connected) return "\u2014";
|
|
2405
|
+
if (c.refreshError) return "expired";
|
|
2406
|
+
if (c.expiresAt) {
|
|
2407
|
+
const expMs = Date.parse(c.expiresAt);
|
|
2408
|
+
if (Number.isNaN(expMs) || expMs <= now) return "expired";
|
|
2409
|
+
}
|
|
2410
|
+
return "ok";
|
|
2411
|
+
}
|
|
2412
|
+
function buildAuthStatusRow(c, now = Date.now()) {
|
|
2413
|
+
return {
|
|
2414
|
+
platform: c.platform,
|
|
2415
|
+
connected: c.connected ? "yes" : "no",
|
|
2416
|
+
username: c.platformUsername ?? "\u2014",
|
|
2417
|
+
"token status": tokenStatus(c, now),
|
|
2418
|
+
expires: c.expiresAt ?? "\u2014"
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2334
2421
|
function createBrandsCommand() {
|
|
2335
2422
|
const brands = new import_commander4.Command("brands").description("Manage brands");
|
|
2336
2423
|
brands.command("list").description("List all brands").action(async (_opts, cmd) => {
|
|
@@ -2350,7 +2437,7 @@ function createBrandsCommand() {
|
|
|
2350
2437
|
}));
|
|
2351
2438
|
printer.table(rows, ["slug", "name", "platforms", "timezone"]);
|
|
2352
2439
|
} catch (err) {
|
|
2353
|
-
spinner.
|
|
2440
|
+
spinner.fail();
|
|
2354
2441
|
printer.error(err);
|
|
2355
2442
|
process.exit(exitCodeFromError(err));
|
|
2356
2443
|
}
|
|
@@ -2366,7 +2453,7 @@ function createBrandsCommand() {
|
|
|
2366
2453
|
spinner.stop();
|
|
2367
2454
|
printer.log(brand);
|
|
2368
2455
|
} catch (err) {
|
|
2369
|
-
spinner.
|
|
2456
|
+
spinner.fail();
|
|
2370
2457
|
printer.error(err);
|
|
2371
2458
|
process.exit(exitCodeFromError(err));
|
|
2372
2459
|
}
|
|
@@ -2385,7 +2472,7 @@ function createBrandsCommand() {
|
|
|
2385
2472
|
spinner.stop();
|
|
2386
2473
|
printer.log(brand);
|
|
2387
2474
|
} catch (err) {
|
|
2388
|
-
spinner.
|
|
2475
|
+
spinner.fail();
|
|
2389
2476
|
printer.error(err);
|
|
2390
2477
|
process.exit(exitCodeFromError(err));
|
|
2391
2478
|
}
|
|
@@ -2404,7 +2491,7 @@ function createBrandsCommand() {
|
|
|
2404
2491
|
spinner.stop();
|
|
2405
2492
|
printer.log(brand);
|
|
2406
2493
|
} catch (err) {
|
|
2407
|
-
spinner.
|
|
2494
|
+
spinner.fail();
|
|
2408
2495
|
printer.error(err);
|
|
2409
2496
|
process.exit(exitCodeFromError(err));
|
|
2410
2497
|
}
|
|
@@ -2424,7 +2511,7 @@ function createBrandsCommand() {
|
|
|
2424
2511
|
spinner.stop();
|
|
2425
2512
|
printer.log(`Brand "${slug}" deleted.`);
|
|
2426
2513
|
} catch (err) {
|
|
2427
|
-
spinner.
|
|
2514
|
+
spinner.fail();
|
|
2428
2515
|
printer.error(err);
|
|
2429
2516
|
process.exit(exitCodeFromError(err));
|
|
2430
2517
|
}
|
|
@@ -2438,15 +2525,10 @@ function createBrandsCommand() {
|
|
|
2438
2525
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2439
2526
|
const connections = await client.brands.authStatus(slug);
|
|
2440
2527
|
spinner.stop();
|
|
2441
|
-
const rows = connections.map((c) => (
|
|
2442
|
-
|
|
2443
|
-
connected: c.connected ? "yes" : "no",
|
|
2444
|
-
username: c.username ?? "\u2014",
|
|
2445
|
-
"token status": c.tokenExpired === true ? "expired" : c.connected ? "ok" : "\u2014"
|
|
2446
|
-
}));
|
|
2447
|
-
printer.table(rows, ["platform", "connected", "username", "token status"]);
|
|
2528
|
+
const rows = connections.map((c) => buildAuthStatusRow(c));
|
|
2529
|
+
printer.table(rows, ["platform", "connected", "username", "token status", "expires"]);
|
|
2448
2530
|
} catch (err) {
|
|
2449
|
-
spinner.
|
|
2531
|
+
spinner.fail();
|
|
2450
2532
|
printer.error(err);
|
|
2451
2533
|
process.exit(exitCodeFromError(err));
|
|
2452
2534
|
}
|
|
@@ -2482,7 +2564,7 @@ function createCarouselsCommand() {
|
|
|
2482
2564
|
}));
|
|
2483
2565
|
printer.table(rows, ["id", "title", "slides", "status", "createdAt"]);
|
|
2484
2566
|
} catch (err) {
|
|
2485
|
-
spinner.
|
|
2567
|
+
spinner.fail();
|
|
2486
2568
|
printer.error(err);
|
|
2487
2569
|
process.exit(resolveExitCode3(err));
|
|
2488
2570
|
}
|
|
@@ -2498,7 +2580,7 @@ function createCarouselsCommand() {
|
|
|
2498
2580
|
spinner.stop();
|
|
2499
2581
|
printer.log(carousel);
|
|
2500
2582
|
} catch (err) {
|
|
2501
|
-
spinner.
|
|
2583
|
+
spinner.fail();
|
|
2502
2584
|
printer.error(err);
|
|
2503
2585
|
process.exit(resolveExitCode3(err));
|
|
2504
2586
|
}
|
|
@@ -2516,7 +2598,7 @@ function createCarouselsCommand() {
|
|
|
2516
2598
|
spinner.stop();
|
|
2517
2599
|
printer.log(carousel);
|
|
2518
2600
|
} catch (err) {
|
|
2519
|
-
spinner.
|
|
2601
|
+
spinner.fail();
|
|
2520
2602
|
printer.error(err);
|
|
2521
2603
|
process.exit(resolveExitCode3(err));
|
|
2522
2604
|
}
|
|
@@ -2536,7 +2618,7 @@ function createCarouselsCommand() {
|
|
|
2536
2618
|
spinner.stop();
|
|
2537
2619
|
printer.log(carousel);
|
|
2538
2620
|
} catch (err) {
|
|
2539
|
-
spinner.
|
|
2621
|
+
spinner.fail();
|
|
2540
2622
|
printer.error(err);
|
|
2541
2623
|
process.exit(resolveExitCode3(err));
|
|
2542
2624
|
}
|
|
@@ -2552,7 +2634,7 @@ function createCarouselsCommand() {
|
|
|
2552
2634
|
spinner.stop();
|
|
2553
2635
|
printer.log(result);
|
|
2554
2636
|
} catch (err) {
|
|
2555
|
-
spinner.
|
|
2637
|
+
spinner.fail();
|
|
2556
2638
|
printer.error(err);
|
|
2557
2639
|
process.exit(resolveExitCode3(err));
|
|
2558
2640
|
}
|
|
@@ -2572,7 +2654,7 @@ function createCarouselsCommand() {
|
|
|
2572
2654
|
spinner.stop();
|
|
2573
2655
|
printer.log(`Carousel "${id}" deleted.`);
|
|
2574
2656
|
} catch (err) {
|
|
2575
|
-
spinner.
|
|
2657
|
+
spinner.fail();
|
|
2576
2658
|
printer.error(err);
|
|
2577
2659
|
process.exit(resolveExitCode3(err));
|
|
2578
2660
|
}
|
|
@@ -2607,7 +2689,7 @@ function createClipsCommand() {
|
|
|
2607
2689
|
}));
|
|
2608
2690
|
printer.table(rows, ["id", "name", "status", "duration"]);
|
|
2609
2691
|
} catch (err) {
|
|
2610
|
-
spinner.
|
|
2692
|
+
spinner.fail();
|
|
2611
2693
|
printer.error(err);
|
|
2612
2694
|
process.exit(resolveExitCode4(err));
|
|
2613
2695
|
}
|
|
@@ -2623,7 +2705,7 @@ function createClipsCommand() {
|
|
|
2623
2705
|
spinner.stop();
|
|
2624
2706
|
printer.log(clip);
|
|
2625
2707
|
} catch (err) {
|
|
2626
|
-
spinner.
|
|
2708
|
+
spinner.fail();
|
|
2627
2709
|
printer.error(err);
|
|
2628
2710
|
process.exit(resolveExitCode4(err));
|
|
2629
2711
|
}
|
|
@@ -2644,7 +2726,7 @@ function createClipsCommand() {
|
|
|
2644
2726
|
spinner.stop();
|
|
2645
2727
|
printer.log(clip);
|
|
2646
2728
|
} catch (err) {
|
|
2647
|
-
spinner.
|
|
2729
|
+
spinner.fail();
|
|
2648
2730
|
printer.error(err);
|
|
2649
2731
|
process.exit(resolveExitCode4(err));
|
|
2650
2732
|
}
|
|
@@ -2663,7 +2745,7 @@ function createClipsCommand() {
|
|
|
2663
2745
|
spinner.stop();
|
|
2664
2746
|
printer.log({ clipId });
|
|
2665
2747
|
} catch (err) {
|
|
2666
|
-
spinner.
|
|
2748
|
+
spinner.fail();
|
|
2667
2749
|
printer.error(err);
|
|
2668
2750
|
process.exit(resolveExitCode4(err));
|
|
2669
2751
|
}
|
|
@@ -2679,7 +2761,7 @@ function createClipsCommand() {
|
|
|
2679
2761
|
spinner.stop();
|
|
2680
2762
|
printer.log({ jobIds, activeJobIds, reusedJobIds });
|
|
2681
2763
|
} catch (err) {
|
|
2682
|
-
spinner.
|
|
2764
|
+
spinner.fail();
|
|
2683
2765
|
printer.error(err);
|
|
2684
2766
|
process.exit(resolveExitCode4(err));
|
|
2685
2767
|
}
|
|
@@ -2699,7 +2781,7 @@ function createClipsCommand() {
|
|
|
2699
2781
|
spinner.stop();
|
|
2700
2782
|
printer.log(`Clip "${id}" deleted.`);
|
|
2701
2783
|
} catch (err) {
|
|
2702
|
-
spinner.
|
|
2784
|
+
spinner.fail();
|
|
2703
2785
|
printer.error(err);
|
|
2704
2786
|
process.exit(resolveExitCode4(err));
|
|
2705
2787
|
}
|
|
@@ -2710,16 +2792,226 @@ function createClipsCommand() {
|
|
|
2710
2792
|
// src/commands/posts.ts
|
|
2711
2793
|
var import_commander7 = require("commander");
|
|
2712
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");
|
|
2713
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"];
|
|
2714
3006
|
function parsePlatforms(raw) {
|
|
2715
3007
|
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
2716
3008
|
if (parts.length === 0) {
|
|
2717
3009
|
throw new Error("No platforms provided. Pass a comma-separated list, e.g. --platforms x,linkedin");
|
|
2718
3010
|
}
|
|
2719
|
-
const invalid = parts.filter((p) => !
|
|
3011
|
+
const invalid = parts.filter((p) => !VALID_PLATFORMS3.includes(p));
|
|
2720
3012
|
if (invalid.length > 0) {
|
|
2721
3013
|
throw new Error(
|
|
2722
|
-
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${
|
|
3014
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS3.join(", ")}`
|
|
2723
3015
|
);
|
|
2724
3016
|
}
|
|
2725
3017
|
return parts;
|
|
@@ -2732,11 +3024,17 @@ function parsePositiveInt(value, flag) {
|
|
|
2732
3024
|
return n;
|
|
2733
3025
|
}
|
|
2734
3026
|
function validateScheduledAt(value) {
|
|
2735
|
-
|
|
3027
|
+
const ms = Date.parse(value);
|
|
3028
|
+
if (Number.isNaN(ms) || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
|
|
2736
3029
|
throw new Error(
|
|
2737
3030
|
`--at must be a valid ISO 8601 datetime, e.g. 2026-06-30T14:00:00Z (received "${value}").`
|
|
2738
3031
|
);
|
|
2739
3032
|
}
|
|
3033
|
+
if (ms <= Date.now()) {
|
|
3034
|
+
throw new Error(
|
|
3035
|
+
`--at must be in the future (received "${value}", which is in the past). A past schedule time publishes immediately.`
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
2740
3038
|
return value;
|
|
2741
3039
|
}
|
|
2742
3040
|
function resolveExitCode5(err) {
|
|
@@ -2770,7 +3068,7 @@ function createPostsCommand() {
|
|
|
2770
3068
|
}));
|
|
2771
3069
|
printer.table(rows, ["id", "brand", "status", "platforms", "text", "scheduledAt"]);
|
|
2772
3070
|
} catch (err) {
|
|
2773
|
-
spinner.
|
|
3071
|
+
spinner.fail();
|
|
2774
3072
|
printer.error(err);
|
|
2775
3073
|
process.exit(resolveExitCode5(err));
|
|
2776
3074
|
}
|
|
@@ -2786,7 +3084,7 @@ function createPostsCommand() {
|
|
|
2786
3084
|
spinner.stop();
|
|
2787
3085
|
printer.log(post);
|
|
2788
3086
|
} catch (err) {
|
|
2789
|
-
spinner.
|
|
3087
|
+
spinner.fail();
|
|
2790
3088
|
printer.error(err);
|
|
2791
3089
|
process.exit(resolveExitCode5(err));
|
|
2792
3090
|
}
|
|
@@ -2794,25 +3092,109 @@ function createPostsCommand() {
|
|
|
2794
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(
|
|
2795
3093
|
"--thread <text...>",
|
|
2796
3094
|
"Additional posts appended after --text to form a thread (X/Threads only, max 25)"
|
|
2797
|
-
).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(
|
|
2798
3096
|
async (opts, cmd) => {
|
|
2799
3097
|
const globals = cmd.optsWithGlobals();
|
|
2800
3098
|
const printer = createPrinter(globals);
|
|
2801
|
-
|
|
3099
|
+
let spinner;
|
|
2802
3100
|
try {
|
|
2803
3101
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2804
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
|
+
}
|
|
2805
3179
|
const post = await client.posts.create({
|
|
2806
3180
|
brandSlug: opts.brand,
|
|
2807
3181
|
text: opts.text,
|
|
2808
|
-
platforms
|
|
2809
|
-
...
|
|
2810
|
-
...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"
|
|
2811
3193
|
});
|
|
2812
3194
|
spinner.stop();
|
|
2813
3195
|
printer.log(post);
|
|
2814
3196
|
} catch (err) {
|
|
2815
|
-
spinner
|
|
3197
|
+
spinner?.fail();
|
|
2816
3198
|
printer.error(err);
|
|
2817
3199
|
process.exit(resolveExitCode5(err));
|
|
2818
3200
|
}
|
|
@@ -2834,7 +3216,7 @@ function createPostsCommand() {
|
|
|
2834
3216
|
spinner.stop();
|
|
2835
3217
|
printer.log(post);
|
|
2836
3218
|
} catch (err) {
|
|
2837
|
-
spinner.
|
|
3219
|
+
spinner.fail();
|
|
2838
3220
|
printer.error(err);
|
|
2839
3221
|
process.exit(resolveExitCode5(err));
|
|
2840
3222
|
}
|
|
@@ -2855,8 +3237,11 @@ function createPostsCommand() {
|
|
|
2855
3237
|
spinner.stop();
|
|
2856
3238
|
printer.log(`Post "${id}" deleted.`);
|
|
2857
3239
|
} catch (err) {
|
|
2858
|
-
spinner.
|
|
3240
|
+
spinner.fail();
|
|
2859
3241
|
printer.error(err);
|
|
3242
|
+
if (!(err instanceof NotFoundError) && printer.isTty()) {
|
|
3243
|
+
printer.error(`The post may not have been deleted \u2014 verify with: ap posts get "${id}"`);
|
|
3244
|
+
}
|
|
2860
3245
|
process.exit(resolveExitCode5(err));
|
|
2861
3246
|
}
|
|
2862
3247
|
});
|
|
@@ -2871,23 +3256,31 @@ function createPostsCommand() {
|
|
|
2871
3256
|
spinner.stop();
|
|
2872
3257
|
printer.log(post);
|
|
2873
3258
|
} catch (err) {
|
|
2874
|
-
spinner.
|
|
3259
|
+
spinner.fail();
|
|
2875
3260
|
printer.error(err);
|
|
2876
3261
|
process.exit(resolveExitCode5(err));
|
|
2877
3262
|
}
|
|
2878
3263
|
});
|
|
2879
|
-
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) => {
|
|
2880
3265
|
const globals = cmd.optsWithGlobals();
|
|
2881
3266
|
const printer = createPrinter(globals);
|
|
2882
|
-
const spinner = printer.spinner(
|
|
3267
|
+
const spinner = printer.spinner(
|
|
3268
|
+
opts.cancel ? `Unscheduling post "${id}"\u2026` : `Scheduling post "${id}"\u2026`
|
|
3269
|
+
);
|
|
2883
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
|
+
}
|
|
2884
3277
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2885
3278
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2886
|
-
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));
|
|
2887
3280
|
spinner.stop();
|
|
2888
3281
|
printer.log(post);
|
|
2889
3282
|
} catch (err) {
|
|
2890
|
-
spinner.
|
|
3283
|
+
spinner.fail();
|
|
2891
3284
|
printer.error(err);
|
|
2892
3285
|
process.exit(resolveExitCode5(err));
|
|
2893
3286
|
}
|
|
@@ -2903,7 +3296,7 @@ function createPostsCommand() {
|
|
|
2903
3296
|
spinner.stop();
|
|
2904
3297
|
printer.log(post);
|
|
2905
3298
|
} catch (err) {
|
|
2906
|
-
spinner.
|
|
3299
|
+
spinner.fail();
|
|
2907
3300
|
printer.error(err);
|
|
2908
3301
|
process.exit(resolveExitCode5(err));
|
|
2909
3302
|
}
|
|
@@ -2919,7 +3312,7 @@ function createPostsCommand() {
|
|
|
2919
3312
|
spinner.stop();
|
|
2920
3313
|
printer.log(post);
|
|
2921
3314
|
} catch (err) {
|
|
2922
|
-
spinner.
|
|
3315
|
+
spinner.fail();
|
|
2923
3316
|
printer.error(err);
|
|
2924
3317
|
process.exit(resolveExitCode5(err));
|
|
2925
3318
|
}
|
|
@@ -2935,7 +3328,7 @@ function createPostsCommand() {
|
|
|
2935
3328
|
spinner.stop();
|
|
2936
3329
|
printer.log(result);
|
|
2937
3330
|
} catch (err) {
|
|
2938
|
-
spinner.
|
|
3331
|
+
spinner.fail();
|
|
2939
3332
|
printer.error(err);
|
|
2940
3333
|
process.exit(resolveExitCode5(err));
|
|
2941
3334
|
}
|
|
@@ -2970,7 +3363,7 @@ function createKbCommand() {
|
|
|
2970
3363
|
}));
|
|
2971
3364
|
printer.table(rows, ["id", "name", "docs", "createdAt"]);
|
|
2972
3365
|
} catch (err) {
|
|
2973
|
-
spinner.
|
|
3366
|
+
spinner.fail();
|
|
2974
3367
|
printer.error(err);
|
|
2975
3368
|
process.exit(resolveExitCode6(err));
|
|
2976
3369
|
}
|
|
@@ -2986,7 +3379,7 @@ function createKbCommand() {
|
|
|
2986
3379
|
spinner.stop();
|
|
2987
3380
|
printer.log(result);
|
|
2988
3381
|
} catch (err) {
|
|
2989
|
-
spinner.
|
|
3382
|
+
spinner.fail();
|
|
2990
3383
|
printer.error(err);
|
|
2991
3384
|
process.exit(resolveExitCode6(err));
|
|
2992
3385
|
}
|
|
@@ -3002,7 +3395,7 @@ function createKbCommand() {
|
|
|
3002
3395
|
spinner.stop();
|
|
3003
3396
|
printer.log(result);
|
|
3004
3397
|
} catch (err) {
|
|
3005
|
-
spinner.
|
|
3398
|
+
spinner.fail();
|
|
3006
3399
|
printer.error(err);
|
|
3007
3400
|
process.exit(resolveExitCode6(err));
|
|
3008
3401
|
}
|
|
@@ -3022,7 +3415,7 @@ function createKbCommand() {
|
|
|
3022
3415
|
spinner.stop();
|
|
3023
3416
|
printer.log(`Knowledge base "${id}" deleted.`);
|
|
3024
3417
|
} catch (err) {
|
|
3025
|
-
spinner.
|
|
3418
|
+
spinner.fail();
|
|
3026
3419
|
printer.error(err);
|
|
3027
3420
|
process.exit(resolveExitCode6(err));
|
|
3028
3421
|
}
|
|
@@ -3044,7 +3437,7 @@ function createKbCommand() {
|
|
|
3044
3437
|
}));
|
|
3045
3438
|
printer.table(rows, ["kind", "score", "uri", "content"]);
|
|
3046
3439
|
} catch (err) {
|
|
3047
|
-
spinner.
|
|
3440
|
+
spinner.fail();
|
|
3048
3441
|
printer.error(err);
|
|
3049
3442
|
process.exit(resolveExitCode6(err));
|
|
3050
3443
|
}
|
|
@@ -3060,7 +3453,7 @@ function createKbCommand() {
|
|
|
3060
3453
|
spinner.stop();
|
|
3061
3454
|
printer.log(doc);
|
|
3062
3455
|
} catch (err) {
|
|
3063
|
-
spinner.
|
|
3456
|
+
spinner.fail();
|
|
3064
3457
|
printer.error(err);
|
|
3065
3458
|
process.exit(resolveExitCode6(err));
|
|
3066
3459
|
}
|
|
@@ -3083,7 +3476,7 @@ function createKbCommand() {
|
|
|
3083
3476
|
}));
|
|
3084
3477
|
printer.table(rows, ["id", "name", "type", "status", "createdAt"]);
|
|
3085
3478
|
} catch (err) {
|
|
3086
|
-
spinner.
|
|
3479
|
+
spinner.fail();
|
|
3087
3480
|
printer.error(err);
|
|
3088
3481
|
process.exit(resolveExitCode6(err));
|
|
3089
3482
|
}
|
|
@@ -3139,7 +3532,7 @@ function createIdeasCommand() {
|
|
|
3139
3532
|
}));
|
|
3140
3533
|
printer.table(rows, ["id", "platform", "score", "title"]);
|
|
3141
3534
|
} catch (err) {
|
|
3142
|
-
spinner.
|
|
3535
|
+
spinner.fail();
|
|
3143
3536
|
printer.error(err);
|
|
3144
3537
|
process.exit(resolveExitCode7(err));
|
|
3145
3538
|
}
|
|
@@ -3163,7 +3556,7 @@ function createIdeasCommand() {
|
|
|
3163
3556
|
}));
|
|
3164
3557
|
printer.table(rows, ["id", "topic", "platform", "score", "status", "title"]);
|
|
3165
3558
|
} catch (err) {
|
|
3166
|
-
spinner.
|
|
3559
|
+
spinner.fail();
|
|
3167
3560
|
printer.error(err);
|
|
3168
3561
|
process.exit(resolveExitCode7(err));
|
|
3169
3562
|
}
|
|
@@ -3184,7 +3577,7 @@ function createIdeasCommand() {
|
|
|
3184
3577
|
spinner.stop();
|
|
3185
3578
|
printer.log(`Enrichment queued. Job ID: ${jobId}`);
|
|
3186
3579
|
} catch (err) {
|
|
3187
|
-
spinner.
|
|
3580
|
+
spinner.fail();
|
|
3188
3581
|
printer.error(err);
|
|
3189
3582
|
process.exit(resolveExitCode7(err));
|
|
3190
3583
|
}
|
|
@@ -3205,7 +3598,7 @@ function createIdeasCommand() {
|
|
|
3205
3598
|
spinner.stop();
|
|
3206
3599
|
printer.log(`Idea "${id}" deleted.`);
|
|
3207
3600
|
} catch (err) {
|
|
3208
|
-
spinner.
|
|
3601
|
+
spinner.fail();
|
|
3209
3602
|
printer.error(err);
|
|
3210
3603
|
process.exit(resolveExitCode7(err));
|
|
3211
3604
|
}
|
|
@@ -3252,7 +3645,7 @@ function createUsageCommand() {
|
|
|
3252
3645
|
}));
|
|
3253
3646
|
printer.table(sourceRows, ["source", "posts"]);
|
|
3254
3647
|
} catch (err) {
|
|
3255
|
-
spinner.
|
|
3648
|
+
spinner.fail();
|
|
3256
3649
|
printer.error(err);
|
|
3257
3650
|
process.exit(resolveExitCode8(err));
|
|
3258
3651
|
}
|
|
@@ -3288,7 +3681,7 @@ function createWebhooksCommand() {
|
|
|
3288
3681
|
}));
|
|
3289
3682
|
printer.table(rows, ["id", "url", "events", "active", "createdAt"]);
|
|
3290
3683
|
} catch (err) {
|
|
3291
|
-
spinner.
|
|
3684
|
+
spinner.fail();
|
|
3292
3685
|
printer.error(err);
|
|
3293
3686
|
process.exit(resolveExitCode9(err));
|
|
3294
3687
|
}
|
|
@@ -3304,7 +3697,7 @@ function createWebhooksCommand() {
|
|
|
3304
3697
|
spinner.stop();
|
|
3305
3698
|
printer.log(webhook);
|
|
3306
3699
|
} catch (err) {
|
|
3307
|
-
spinner.
|
|
3700
|
+
spinner.fail();
|
|
3308
3701
|
printer.error(err);
|
|
3309
3702
|
process.exit(resolveExitCode9(err));
|
|
3310
3703
|
}
|
|
@@ -3325,7 +3718,7 @@ function createWebhooksCommand() {
|
|
|
3325
3718
|
spinner.stop();
|
|
3326
3719
|
printer.log(webhook);
|
|
3327
3720
|
} catch (err) {
|
|
3328
|
-
spinner.
|
|
3721
|
+
spinner.fail();
|
|
3329
3722
|
printer.error(err);
|
|
3330
3723
|
process.exit(resolveExitCode9(err));
|
|
3331
3724
|
}
|
|
@@ -3347,7 +3740,7 @@ function createWebhooksCommand() {
|
|
|
3347
3740
|
spinner.stop();
|
|
3348
3741
|
printer.log(webhook);
|
|
3349
3742
|
} catch (err) {
|
|
3350
|
-
spinner.
|
|
3743
|
+
spinner.fail();
|
|
3351
3744
|
printer.error(err);
|
|
3352
3745
|
process.exit(resolveExitCode9(err));
|
|
3353
3746
|
}
|
|
@@ -3368,7 +3761,7 @@ function createWebhooksCommand() {
|
|
|
3368
3761
|
spinner.stop();
|
|
3369
3762
|
printer.log(`Webhook "${id}" deleted.`);
|
|
3370
3763
|
} catch (err) {
|
|
3371
|
-
spinner.
|
|
3764
|
+
spinner.fail();
|
|
3372
3765
|
printer.error(err);
|
|
3373
3766
|
process.exit(resolveExitCode9(err));
|
|
3374
3767
|
}
|
|
@@ -3386,7 +3779,7 @@ function createWebhooksCommand() {
|
|
|
3386
3779
|
`Test event sent to webhook "${id}" \u2014 delivered: ${result.delivered ? "yes" : "no"} (HTTP ${result.httpStatus}).`
|
|
3387
3780
|
);
|
|
3388
3781
|
} catch (err) {
|
|
3389
|
-
spinner.
|
|
3782
|
+
spinner.fail();
|
|
3390
3783
|
printer.error(err);
|
|
3391
3784
|
process.exit(resolveExitCode9(err));
|
|
3392
3785
|
}
|
|
@@ -3422,7 +3815,7 @@ function createWorkspacesCommand() {
|
|
|
3422
3815
|
}));
|
|
3423
3816
|
printer.table(rows, ["id", "name", "slug", "active", "createdAt"]);
|
|
3424
3817
|
} catch (err) {
|
|
3425
|
-
spinner.
|
|
3818
|
+
spinner.fail();
|
|
3426
3819
|
printer.error(err);
|
|
3427
3820
|
process.exit(resolveExitCode10(err));
|
|
3428
3821
|
}
|
|
@@ -3433,13 +3826,12 @@ function createWorkspacesCommand() {
|
|
|
3433
3826
|
const spinner = printer.spinner(`Switching to workspace "${id}"\u2026`);
|
|
3434
3827
|
try {
|
|
3435
3828
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3436
|
-
const
|
|
3437
|
-
const client = new Autoposting({ apiKey: cred.apiKey, authSource });
|
|
3829
|
+
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3438
3830
|
await client.workspaces.switchWorkspace(id);
|
|
3439
3831
|
spinner.stop();
|
|
3440
3832
|
printer.log(`Switched to workspace "${id}".`);
|
|
3441
3833
|
} catch (err) {
|
|
3442
|
-
spinner.
|
|
3834
|
+
spinner.fail();
|
|
3443
3835
|
printer.error(err);
|
|
3444
3836
|
process.exit(resolveExitCode10(err));
|
|
3445
3837
|
}
|
|
@@ -3450,7 +3842,7 @@ function createWorkspacesCommand() {
|
|
|
3450
3842
|
// src/commands/doctor.ts
|
|
3451
3843
|
var import_commander13 = require("commander");
|
|
3452
3844
|
init_dist();
|
|
3453
|
-
var
|
|
3845
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
3454
3846
|
var API_HEALTH_URL = "https://app.autoposting.ai/api-proxy/health";
|
|
3455
3847
|
async function runChecks() {
|
|
3456
3848
|
const results = [];
|
|
@@ -3470,7 +3862,7 @@ async function runChecks() {
|
|
|
3470
3862
|
}
|
|
3471
3863
|
const credPath = getCredentialsPath();
|
|
3472
3864
|
try {
|
|
3473
|
-
const stat =
|
|
3865
|
+
const stat = import_node_fs3.default.statSync(credPath);
|
|
3474
3866
|
const mode = stat.mode & 511;
|
|
3475
3867
|
const isSecure = mode === 384;
|
|
3476
3868
|
results.push({
|
|
@@ -3536,9 +3928,10 @@ function createWhoamiCommand() {
|
|
|
3536
3928
|
}
|
|
3537
3929
|
const masked = `${cred.apiKey.slice(0, 8)}${"*".repeat(Math.max(0, cred.apiKey.length - 8))}`;
|
|
3538
3930
|
let validity = "unverified";
|
|
3931
|
+
let identity;
|
|
3539
3932
|
try {
|
|
3540
|
-
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3541
|
-
await client.
|
|
3933
|
+
const client = new Autoposting({ apiKey: cred.apiKey, maxRetries: 0 });
|
|
3934
|
+
identity = await client.getProfile();
|
|
3542
3935
|
validity = "valid";
|
|
3543
3936
|
} catch (err) {
|
|
3544
3937
|
const code = exitCodeFromError(err);
|
|
@@ -3555,6 +3948,11 @@ function createWhoamiCommand() {
|
|
|
3555
3948
|
valid: validity === "valid",
|
|
3556
3949
|
validity
|
|
3557
3950
|
};
|
|
3951
|
+
if (identity?.orgId) {
|
|
3952
|
+
out.orgId = identity.orgId;
|
|
3953
|
+
out.authType = identity.authType;
|
|
3954
|
+
if (identity.email) out.email = identity.email;
|
|
3955
|
+
}
|
|
3558
3956
|
if (cred.source === "stored") {
|
|
3559
3957
|
out.profile = profileName;
|
|
3560
3958
|
if (email) out.email = email;
|
|
@@ -3568,6 +3966,10 @@ function createWhoamiCommand() {
|
|
|
3568
3966
|
console.log(`Profile: ${profileName}`);
|
|
3569
3967
|
if (email) console.log(`Email: ${email}`);
|
|
3570
3968
|
}
|
|
3969
|
+
if (identity?.orgId) {
|
|
3970
|
+
console.log(`Org: ${identity.orgId}`);
|
|
3971
|
+
console.log(`Auth: ${identity.authType}`);
|
|
3972
|
+
}
|
|
3571
3973
|
const tag = validity === "valid" ? "\u2713 valid" : validity === "rejected" ? "\u2717 rejected by server" : "\u26A0 unverified (could not reach server)";
|
|
3572
3974
|
console.log(`API key: ${masked} ${tag}`);
|
|
3573
3975
|
process.exitCode = exitCode;
|