@autoposting.ai/cli 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.cjs +575 -261
- package/dist/cli.cjs.map +1 -1
- package/package.json +1 -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, 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.2";
|
|
50
50
|
AutopostingError = class extends Error {
|
|
51
51
|
status;
|
|
52
52
|
code;
|
|
@@ -93,6 +93,7 @@ var init_dist = __esm({
|
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
95
|
AgentsResource = class extends Resource {
|
|
96
|
+
/** GET /agents — backend returns `{ items, total, limit, offset }`. */
|
|
96
97
|
list() {
|
|
97
98
|
return this.get("/agents");
|
|
98
99
|
}
|
|
@@ -108,12 +109,14 @@ var init_dist = __esm({
|
|
|
108
109
|
remove(id) {
|
|
109
110
|
return this.delete(`/agents/${id}`);
|
|
110
111
|
}
|
|
112
|
+
/** POST /agents/:id/run — queues a run; returns `{ runId, status }`, not a full AgentRun. */
|
|
111
113
|
run(id) {
|
|
112
114
|
return this.post(`/agents/${id}/run`);
|
|
113
115
|
}
|
|
114
116
|
toggle(id) {
|
|
115
117
|
return this.post(`/agents/${id}/toggle`);
|
|
116
118
|
}
|
|
119
|
+
/** GET /agents/:id/runs — backend returns `{ items, total, limit, offset }`. */
|
|
117
120
|
runs(id) {
|
|
118
121
|
return this.get(`/agents/${id}/runs`);
|
|
119
122
|
}
|
|
@@ -147,6 +150,7 @@ var init_dist = __esm({
|
|
|
147
150
|
}
|
|
148
151
|
};
|
|
149
152
|
CarouselsResource = class extends Resource {
|
|
153
|
+
/** GET /carousels — backend paginates: `{ items, total, limit, offset }`. */
|
|
150
154
|
list() {
|
|
151
155
|
return this.get("/carousels");
|
|
152
156
|
}
|
|
@@ -159,37 +163,38 @@ var init_dist = __esm({
|
|
|
159
163
|
generate(params) {
|
|
160
164
|
return this.post("/carousels/generate", params);
|
|
161
165
|
}
|
|
162
|
-
/** POST /carousels/:id/draft — converts carousel to a post draft */
|
|
166
|
+
/** POST /carousels/:id/create-draft — converts carousel to a post draft */
|
|
163
167
|
draft(id) {
|
|
164
|
-
return this.post(`/carousels/${id}/draft`);
|
|
168
|
+
return this.post(`/carousels/${id}/create-draft`);
|
|
165
169
|
}
|
|
166
170
|
remove(id) {
|
|
167
171
|
return this.client.request("DELETE", `/carousels/${id}`);
|
|
168
172
|
}
|
|
169
173
|
};
|
|
170
174
|
ClipsResource = class extends Resource {
|
|
175
|
+
/** GET /clips — backend returns `{ clips, pagination }`, not a bare array. */
|
|
171
176
|
list() {
|
|
172
177
|
return this.client.request("GET", "/clips");
|
|
173
178
|
}
|
|
174
179
|
retrieve(id) {
|
|
175
180
|
return this.client.request("GET", `/clips/${id}`);
|
|
176
181
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
options?.onProgress?.(100);
|
|
188
|
-
return clip;
|
|
182
|
+
// ponytail: the backend has no single-request /clips/upload; uploading a file means the
|
|
183
|
+
// multipart init/presign/complete flow (upload-initiate → upload-presign → upload-complete).
|
|
184
|
+
// That's too much surface for v0.3.1 — fail honestly and point at importUrl. Upgrade path:
|
|
185
|
+
// implement the init/presign/complete dance here when direct upload is actually needed.
|
|
186
|
+
async upload(_filePath, _options) {
|
|
187
|
+
throw new AutopostingError(
|
|
188
|
+
"Direct clip file upload is not supported by the API yet \u2014 use `importUrl` with a public video URL instead.",
|
|
189
|
+
0,
|
|
190
|
+
"NOT_IMPLEMENTED"
|
|
191
|
+
);
|
|
189
192
|
}
|
|
193
|
+
/** POST /clips/import-url — backend returns `{ clipId }`, not a full Clip. */
|
|
190
194
|
importUrl(params) {
|
|
191
|
-
return this.client.request("POST", "/clips/import", params);
|
|
195
|
+
return this.client.request("POST", "/clips/import-url", params);
|
|
192
196
|
}
|
|
197
|
+
/** POST /clips/:id/render — backend returns render job ids, not a Clip. */
|
|
193
198
|
render(id) {
|
|
194
199
|
return this.client.request("POST", `/clips/${id}/render`);
|
|
195
200
|
}
|
|
@@ -198,45 +203,45 @@ var init_dist = __esm({
|
|
|
198
203
|
}
|
|
199
204
|
};
|
|
200
205
|
PostsResource = class extends Resource {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
/** GET /posts — backend returns a bare array (no pagination metadata). */
|
|
207
|
+
list(params = {}) {
|
|
208
|
+
const { brandSlug, status, limit, page } = params;
|
|
209
|
+
const query = {};
|
|
210
|
+
if (brandSlug) query.brandSlug = brandSlug;
|
|
211
|
+
if (status) query.status = status;
|
|
212
|
+
if (limit !== void 0) query.limit = limit;
|
|
213
|
+
if (page !== void 0 && page > 1) query.offset = (page - 1) * (limit ?? 20);
|
|
214
|
+
return this.client.request("GET", "/posts", void 0, query);
|
|
215
|
+
}
|
|
216
|
+
/** GET /posts/:id */
|
|
210
217
|
getById(id) {
|
|
211
|
-
return this.client.request("GET", `/
|
|
218
|
+
return this.client.request("GET", `/posts/${id}`);
|
|
212
219
|
}
|
|
213
220
|
create(params) {
|
|
214
|
-
return this.client.request("POST", "/
|
|
221
|
+
return this.client.request("POST", "/posts", params);
|
|
215
222
|
}
|
|
216
223
|
update(id, params) {
|
|
217
|
-
return this.client.request("PUT", `/
|
|
224
|
+
return this.client.request("PUT", `/posts/${id}`, params);
|
|
218
225
|
}
|
|
219
|
-
/** DELETE /
|
|
226
|
+
/** DELETE /posts/:id */
|
|
220
227
|
remove(id) {
|
|
221
|
-
return this.client.request("DELETE", `/
|
|
228
|
+
return this.client.request("DELETE", `/posts/${id}`);
|
|
222
229
|
}
|
|
223
230
|
publish(id) {
|
|
224
|
-
return this.client.request("POST", `/
|
|
231
|
+
return this.client.request("POST", `/posts/${id}/publish`);
|
|
225
232
|
}
|
|
226
233
|
schedule(id, scheduledAt) {
|
|
227
|
-
return this.client.request("PUT", `/
|
|
234
|
+
return this.client.request("PUT", `/posts/${id}/schedule`, { scheduledAt });
|
|
228
235
|
}
|
|
229
236
|
retry(id) {
|
|
230
|
-
return this.client.request("POST", `/
|
|
237
|
+
return this.client.request("POST", `/posts/${id}/retry`);
|
|
231
238
|
}
|
|
232
239
|
rewrite(id) {
|
|
233
|
-
return this.client.request("POST", `/
|
|
240
|
+
return this.client.request("POST", `/posts/${id}/rewrite`);
|
|
234
241
|
}
|
|
242
|
+
/** POST /posts/:id/score — backend returns `{ score }` only (no feedback). */
|
|
235
243
|
score(id) {
|
|
236
|
-
return this.client.request(
|
|
237
|
-
"POST",
|
|
238
|
-
`/v1/posts/${id}/score`
|
|
239
|
-
);
|
|
244
|
+
return this.client.request("POST", `/posts/${id}/score`);
|
|
240
245
|
}
|
|
241
246
|
};
|
|
242
247
|
KbResource = class extends Resource {
|
|
@@ -253,7 +258,10 @@ var init_dist = __esm({
|
|
|
253
258
|
return this.delete(`/kbs/${id}`);
|
|
254
259
|
}
|
|
255
260
|
search(id, query) {
|
|
256
|
-
return this.post(
|
|
261
|
+
return this.post(
|
|
262
|
+
`/kbs/${id}/search`,
|
|
263
|
+
{ query }
|
|
264
|
+
);
|
|
257
265
|
}
|
|
258
266
|
ingestUrl(id, url) {
|
|
259
267
|
return this.post(`/kbs/${id}/docs/ingest-url`, { url });
|
|
@@ -263,14 +271,21 @@ var init_dist = __esm({
|
|
|
263
271
|
}
|
|
264
272
|
};
|
|
265
273
|
IdeasResource = class extends Resource {
|
|
274
|
+
/** POST /ideas/generate-topic — returns an object whose `ideas` holds the generated ideas. */
|
|
266
275
|
generate(params = {}) {
|
|
267
|
-
return this.post("/ideas/generate", params);
|
|
276
|
+
return this.post("/ideas/generate-topic", params);
|
|
268
277
|
}
|
|
278
|
+
/** GET /ideas — backend paginates: `{ items, total, limit, offset }`. */
|
|
269
279
|
list() {
|
|
270
280
|
return this.get("/ideas");
|
|
271
281
|
}
|
|
272
|
-
|
|
273
|
-
|
|
282
|
+
/**
|
|
283
|
+
* POST /ideas/enrich — enrich an idea (title/hook/angle) across 1..5 platforms.
|
|
284
|
+
* Async: returns a job id (202). NOTE: the backend enriches the idea *object*, not an
|
|
285
|
+
* id — there is no enrich-by-id route. Poll the result separately (not yet wrapped).
|
|
286
|
+
*/
|
|
287
|
+
enrich(params) {
|
|
288
|
+
return this.post("/ideas/enrich", params);
|
|
274
289
|
}
|
|
275
290
|
remove(id) {
|
|
276
291
|
return this.delete(`/ideas/${id}`);
|
|
@@ -285,6 +300,7 @@ var init_dist = __esm({
|
|
|
285
300
|
list() {
|
|
286
301
|
return this.get("/webhooks");
|
|
287
302
|
}
|
|
303
|
+
// NOTE: the backend has no GET /webhooks/:id route — this 404s until front-back adds it.
|
|
288
304
|
retrieve(id) {
|
|
289
305
|
return this.get(`/webhooks/${id}`);
|
|
290
306
|
}
|
|
@@ -297,6 +313,7 @@ var init_dist = __esm({
|
|
|
297
313
|
remove(id) {
|
|
298
314
|
return this.client.request("DELETE", `/webhooks/${id}`);
|
|
299
315
|
}
|
|
316
|
+
// POST /webhooks/:id/test — backend returns `{ delivered, httpStatus }`.
|
|
300
317
|
test(id) {
|
|
301
318
|
return this.post(`/webhooks/${id}/test`);
|
|
302
319
|
}
|
|
@@ -305,7 +322,19 @@ var init_dist = __esm({
|
|
|
305
322
|
constructor(client) {
|
|
306
323
|
super(client);
|
|
307
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* GET /orgs — backend returns `{ organizations, activeOrgId }`, not a bare array.
|
|
327
|
+
* This route is session-only (better-auth session); an API key receives a bare 401,
|
|
328
|
+
* so guard up front with accurate guidance instead of an opaque "Unauthorized".
|
|
329
|
+
*/
|
|
308
330
|
list() {
|
|
331
|
+
if (this.client.authSource === "api-key") {
|
|
332
|
+
return Promise.reject(
|
|
333
|
+
new Error(
|
|
334
|
+
"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."
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
309
338
|
return this.get("/orgs");
|
|
310
339
|
}
|
|
311
340
|
switchWorkspace(id) {
|
|
@@ -316,9 +345,12 @@ var init_dist = __esm({
|
|
|
316
345
|
)
|
|
317
346
|
);
|
|
318
347
|
}
|
|
319
|
-
return this.
|
|
348
|
+
return this.client.request("PUT", "/orgs/active", { organizationId: id });
|
|
320
349
|
}
|
|
321
350
|
};
|
|
351
|
+
IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "DELETE"]);
|
|
352
|
+
RETRYABLE_STATUS = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
353
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
322
354
|
Autoposting = class {
|
|
323
355
|
/** @internal Not part of the public API — use SDK methods to make requests. */
|
|
324
356
|
_apiKey;
|
|
@@ -327,6 +359,8 @@ var init_dist = __esm({
|
|
|
327
359
|
extraHeaders;
|
|
328
360
|
/** Indicates how the client was authenticated; used by WorkspacesResource. */
|
|
329
361
|
authSource;
|
|
362
|
+
maxRetries;
|
|
363
|
+
retryBaseMs;
|
|
330
364
|
agents;
|
|
331
365
|
billing;
|
|
332
366
|
brands;
|
|
@@ -346,10 +380,12 @@ var init_dist = __esm({
|
|
|
346
380
|
);
|
|
347
381
|
}
|
|
348
382
|
this._apiKey = key;
|
|
349
|
-
this.baseUrl = (config.baseUrl
|
|
383
|
+
this.baseUrl = (config.baseUrl || process.env.AUTOPOSTING_BASE_URL || "https://app.autoposting.ai/api-proxy").replace(/\/$/, "");
|
|
350
384
|
this.timeout = config.timeout ?? 3e4;
|
|
351
385
|
this.extraHeaders = config.headers ?? {};
|
|
352
386
|
this.authSource = config.authSource ?? "api-key";
|
|
387
|
+
this.maxRetries = Math.max(0, config.maxRetries ?? 2);
|
|
388
|
+
this.retryBaseMs = Math.max(0, config.retryBaseMs ?? 300);
|
|
353
389
|
this.agents = new AgentsResource(this);
|
|
354
390
|
this.billing = new BillingResource(this);
|
|
355
391
|
this.brands = new BrandsResource(this);
|
|
@@ -362,7 +398,42 @@ var init_dist = __esm({
|
|
|
362
398
|
this.webhooks = new WebhooksResource(this);
|
|
363
399
|
this.workspaces = new WorkspacesResource(this);
|
|
364
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* GET /auth/profile — resolve the server-side identity behind the current credential.
|
|
403
|
+
* Validates the key (401 on reject) and returns who/what it's scoped to.
|
|
404
|
+
*/
|
|
405
|
+
getProfile() {
|
|
406
|
+
return this.request("GET", "/auth/profile");
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Public entry point. Retries idempotent requests (GET/PUT/DELETE) on transient
|
|
410
|
+
* failures — network errors, timeouts, and 5xx — with exponential backoff + jitter.
|
|
411
|
+
* Non-idempotent methods (POST/PATCH) are never retried, to avoid duplicate side effects.
|
|
412
|
+
*/
|
|
365
413
|
async request(method, path2, body, query) {
|
|
414
|
+
let lastErr;
|
|
415
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
416
|
+
if (attempt > 0) {
|
|
417
|
+
const backoff = this.retryBaseMs * 2 ** (attempt - 1);
|
|
418
|
+
await sleep(backoff + Math.floor(Math.random() * this.retryBaseMs));
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
return await this._send(method, path2, body, query);
|
|
422
|
+
} catch (err) {
|
|
423
|
+
lastErr = err;
|
|
424
|
+
if (attempt < this.maxRetries && this.isRetryable(method, err)) continue;
|
|
425
|
+
throw err;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
throw lastErr;
|
|
429
|
+
}
|
|
430
|
+
isRetryable(method, err) {
|
|
431
|
+
if (!IDEMPOTENT_METHODS.has(method)) return false;
|
|
432
|
+
if (!(err instanceof AutopostingError)) return false;
|
|
433
|
+
if (err.code === "NETWORK_ERROR" || err.code === "TIMEOUT") return true;
|
|
434
|
+
return RETRYABLE_STATUS.has(err.status);
|
|
435
|
+
}
|
|
436
|
+
async _send(method, path2, body, query) {
|
|
366
437
|
let url = `${this.baseUrl}${path2}`;
|
|
367
438
|
if (query && Object.keys(query).length > 0) {
|
|
368
439
|
const params = new URLSearchParams();
|
|
@@ -375,46 +446,101 @@ var init_dist = __esm({
|
|
|
375
446
|
}
|
|
376
447
|
const isFormData = body instanceof FormData;
|
|
377
448
|
const headers = {
|
|
449
|
+
...this.extraHeaders,
|
|
378
450
|
authorization: `Bearer ${this._apiKey}`,
|
|
379
451
|
"user-agent": `autoposting-sdk/${VERSION}`,
|
|
380
|
-
"x-source": "sdk"
|
|
381
|
-
...this.extraHeaders
|
|
452
|
+
"x-source": "sdk"
|
|
382
453
|
};
|
|
383
454
|
if (!isFormData) {
|
|
384
455
|
headers["content-type"] = "application/json";
|
|
385
456
|
}
|
|
386
457
|
const controller = new AbortController();
|
|
387
458
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
388
|
-
let response;
|
|
389
459
|
try {
|
|
390
|
-
response
|
|
391
|
-
method,
|
|
392
|
-
headers,
|
|
393
|
-
body: isFormData ? body : body !== void 0 ? JSON.stringify(body) : void 0,
|
|
394
|
-
signal: controller.signal
|
|
395
|
-
});
|
|
396
|
-
} finally {
|
|
397
|
-
clearTimeout(timer);
|
|
398
|
-
}
|
|
399
|
-
if (!response.ok) {
|
|
400
|
-
let errorBody = {};
|
|
460
|
+
let response;
|
|
401
461
|
try {
|
|
402
|
-
|
|
403
|
-
|
|
462
|
+
response = await fetch(url, {
|
|
463
|
+
method,
|
|
464
|
+
headers,
|
|
465
|
+
body: isFormData ? body : body !== void 0 ? JSON.stringify(body) : void 0,
|
|
466
|
+
signal: controller.signal
|
|
467
|
+
});
|
|
468
|
+
} catch (err) {
|
|
469
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
470
|
+
throw new AutopostingError(
|
|
471
|
+
`Request to ${method} ${path2} timed out after ${this.timeout}ms.`,
|
|
472
|
+
0,
|
|
473
|
+
"TIMEOUT"
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
throw new AutopostingError(
|
|
477
|
+
`Network request to ${method} ${path2} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
478
|
+
0,
|
|
479
|
+
"NETWORK_ERROR"
|
|
480
|
+
);
|
|
404
481
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
const raw = await response.text();
|
|
484
|
+
let errorBody = {};
|
|
485
|
+
if (raw) {
|
|
486
|
+
try {
|
|
487
|
+
errorBody = JSON.parse(raw);
|
|
488
|
+
} catch {
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (!errorBody.error) {
|
|
492
|
+
const snippet = raw.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
493
|
+
errorBody.error = snippet ? `HTTP ${response.status} ${response.statusText}: ${snippet}` : `HTTP ${response.status} ${response.statusText}`;
|
|
410
494
|
}
|
|
495
|
+
const err = createError(response.status, errorBody);
|
|
496
|
+
if (err instanceof RateLimitError) {
|
|
497
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
498
|
+
if (retryAfter) {
|
|
499
|
+
const seconds = Number(retryAfter);
|
|
500
|
+
if (Number.isFinite(seconds)) {
|
|
501
|
+
err.retryAfter = Math.max(0, Math.round(seconds));
|
|
502
|
+
} else {
|
|
503
|
+
const dateMs = Date.parse(retryAfter);
|
|
504
|
+
if (!Number.isNaN(dateMs)) {
|
|
505
|
+
err.retryAfter = Math.max(0, Math.round((dateMs - Date.now()) / 1e3));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
throw err;
|
|
511
|
+
}
|
|
512
|
+
if (response.status === 204) {
|
|
513
|
+
return {};
|
|
514
|
+
}
|
|
515
|
+
const text = await response.text();
|
|
516
|
+
if (!text) return {};
|
|
517
|
+
let parsed;
|
|
518
|
+
try {
|
|
519
|
+
parsed = JSON.parse(text);
|
|
520
|
+
} catch {
|
|
521
|
+
throw new AutopostingError(
|
|
522
|
+
`Expected a JSON response from ${method} ${path2} but received non-JSON (HTTP ${response.status}). Verify the API base URL (AUTOPOSTING_BASE_URL / --base-url) points at the API, not the web app.`,
|
|
523
|
+
response.status,
|
|
524
|
+
"INVALID_RESPONSE"
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
if (typeof parsed === "object" && parsed !== null && parsed.success === true) {
|
|
528
|
+
const env = parsed;
|
|
529
|
+
return env.data !== void 0 ? env.data : {};
|
|
530
|
+
}
|
|
531
|
+
return parsed;
|
|
532
|
+
} catch (err) {
|
|
533
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
534
|
+
throw new AutopostingError(
|
|
535
|
+
`Request to ${method} ${path2} timed out after ${this.timeout}ms.`,
|
|
536
|
+
0,
|
|
537
|
+
"TIMEOUT"
|
|
538
|
+
);
|
|
411
539
|
}
|
|
412
540
|
throw err;
|
|
541
|
+
} finally {
|
|
542
|
+
clearTimeout(timer);
|
|
413
543
|
}
|
|
414
|
-
if (response.status === 204) {
|
|
415
|
-
return {};
|
|
416
|
-
}
|
|
417
|
-
return response.json();
|
|
418
544
|
}
|
|
419
545
|
};
|
|
420
546
|
}
|
|
@@ -862,13 +988,26 @@ var init_tools = __esm({
|
|
|
862
988
|
},
|
|
863
989
|
{
|
|
864
990
|
name: "enrich-idea",
|
|
865
|
-
description: "Enrich an idea
|
|
991
|
+
description: "Enrich an idea (title/hook/angle) into platform-ready drafts across 1..5 platforms. Async \u2014 returns a job ID.",
|
|
866
992
|
inputSchema: {
|
|
867
993
|
type: "object",
|
|
868
994
|
properties: {
|
|
869
|
-
|
|
995
|
+
title: { type: "string", description: "Idea title" },
|
|
996
|
+
hook: { type: "string", description: "Idea hook" },
|
|
997
|
+
angle: { type: "string", description: "Idea angle" },
|
|
998
|
+
platforms: {
|
|
999
|
+
type: "array",
|
|
1000
|
+
description: "Target platforms (1..5)",
|
|
1001
|
+
items: {
|
|
1002
|
+
type: "string",
|
|
1003
|
+
enum: ["twitter", "linkedin", "instagram", "youtube", "threads"]
|
|
1004
|
+
},
|
|
1005
|
+
minItems: 1,
|
|
1006
|
+
maxItems: 5
|
|
1007
|
+
},
|
|
1008
|
+
kbId: { type: "string", description: "Optional knowledge base ID for context" }
|
|
870
1009
|
},
|
|
871
|
-
required: ["
|
|
1010
|
+
required: ["title", "hook", "angle", "platforms"],
|
|
872
1011
|
additionalProperties: false
|
|
873
1012
|
}
|
|
874
1013
|
},
|
|
@@ -1345,7 +1484,17 @@ async function dispatchToolCall(name, args, client) {
|
|
|
1345
1484
|
return ok(result);
|
|
1346
1485
|
}
|
|
1347
1486
|
case "enrich-idea": {
|
|
1348
|
-
const result = await client.ideas.enrich(
|
|
1487
|
+
const result = await client.ideas.enrich({
|
|
1488
|
+
idea: {
|
|
1489
|
+
title: args.title,
|
|
1490
|
+
hook: args.hook,
|
|
1491
|
+
angle: args.angle
|
|
1492
|
+
},
|
|
1493
|
+
platforms: args.platforms.map((platform) => ({
|
|
1494
|
+
platform
|
|
1495
|
+
})),
|
|
1496
|
+
...args.kbId ? { kbId: args.kbId } : {}
|
|
1497
|
+
});
|
|
1349
1498
|
return ok(result);
|
|
1350
1499
|
}
|
|
1351
1500
|
case "delete-idea": {
|
|
@@ -1523,7 +1672,8 @@ var import_node_fs = __toESM(require("fs"));
|
|
|
1523
1672
|
var import_node_os = __toESM(require("os"));
|
|
1524
1673
|
var import_node_path = __toESM(require("path"));
|
|
1525
1674
|
function getCredentialsPath() {
|
|
1526
|
-
const
|
|
1675
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
1676
|
+
const configBase = xdg && import_node_path.default.isAbsolute(xdg) ? xdg : import_node_path.default.join(import_node_os.default.homedir(), ".config");
|
|
1527
1677
|
return import_node_path.default.join(configBase, "autoposting", "credentials.json");
|
|
1528
1678
|
}
|
|
1529
1679
|
function readCredentials() {
|
|
@@ -1539,11 +1689,27 @@ function writeCredentials(creds) {
|
|
|
1539
1689
|
const filePath = getCredentialsPath();
|
|
1540
1690
|
const dir = import_node_path.default.dirname(filePath);
|
|
1541
1691
|
import_node_fs.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1542
|
-
|
|
1692
|
+
const tmpPath = `${filePath}.${process.pid}.tmp`;
|
|
1693
|
+
try {
|
|
1694
|
+
import_node_fs.default.writeFileSync(tmpPath, JSON.stringify(creds, null, 2), { encoding: "utf8", mode: 384 });
|
|
1695
|
+
import_node_fs.default.renameSync(tmpPath, filePath);
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
try {
|
|
1698
|
+
import_node_fs.default.unlinkSync(tmpPath);
|
|
1699
|
+
} catch {
|
|
1700
|
+
}
|
|
1701
|
+
throw err;
|
|
1702
|
+
}
|
|
1543
1703
|
import_node_fs.default.chmodSync(filePath, 384);
|
|
1544
1704
|
}
|
|
1545
1705
|
function saveProfile(name, profile) {
|
|
1546
1706
|
const existing = readCredentials();
|
|
1707
|
+
if (!existing && import_node_fs.default.existsSync(getCredentialsPath())) {
|
|
1708
|
+
try {
|
|
1709
|
+
import_node_fs.default.renameSync(getCredentialsPath(), `${getCredentialsPath()}.corrupt`);
|
|
1710
|
+
} catch {
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1547
1713
|
const creds = existing ?? { activeProfile: name, profiles: {} };
|
|
1548
1714
|
creds.profiles[name] = profile;
|
|
1549
1715
|
creds.activeProfile = name;
|
|
@@ -1608,7 +1774,9 @@ function red(text) {
|
|
|
1608
1774
|
}
|
|
1609
1775
|
function detectOutputMode(options) {
|
|
1610
1776
|
if (options.quiet) return "quiet";
|
|
1611
|
-
if (options.json ||
|
|
1777
|
+
if (options.format === "json" || options.json) return "json";
|
|
1778
|
+
if (options.format === "table") return "tty";
|
|
1779
|
+
if (!process.stdout.isTTY) return "json";
|
|
1612
1780
|
return "tty";
|
|
1613
1781
|
}
|
|
1614
1782
|
function formatOutput(data, mode) {
|
|
@@ -1674,7 +1842,7 @@ var OraSpinner = class {
|
|
|
1674
1842
|
if (this.instance) {
|
|
1675
1843
|
this.instance.fail(message);
|
|
1676
1844
|
this.instance = null;
|
|
1677
|
-
} else {
|
|
1845
|
+
} else if (message) {
|
|
1678
1846
|
process.stderr.write(`${message}
|
|
1679
1847
|
`);
|
|
1680
1848
|
}
|
|
@@ -1691,6 +1859,10 @@ var Printer = class {
|
|
|
1691
1859
|
this.mode = mode;
|
|
1692
1860
|
}
|
|
1693
1861
|
mode;
|
|
1862
|
+
/** True only for human-readable output, where extra hint lines are safe to print. */
|
|
1863
|
+
isTty() {
|
|
1864
|
+
return this.mode === "tty";
|
|
1865
|
+
}
|
|
1694
1866
|
log(data) {
|
|
1695
1867
|
const out = formatOutput(data, this.mode);
|
|
1696
1868
|
if (out !== "") console.log(out);
|
|
@@ -1741,12 +1913,18 @@ function matchesClass(error, className) {
|
|
|
1741
1913
|
return false;
|
|
1742
1914
|
}
|
|
1743
1915
|
function exitCodeFromError(error) {
|
|
1916
|
+
const attached = error?.exitCode;
|
|
1917
|
+
if (typeof attached === "number" && Number.isInteger(attached) && attached >= 0) {
|
|
1918
|
+
return attached;
|
|
1919
|
+
}
|
|
1744
1920
|
if (matchesClass(error, "AuthenticationError")) return EXIT_CODES.AUTH_ERROR;
|
|
1745
1921
|
if (matchesClass(error, "ScopeError")) return EXIT_CODES.SCOPE_ERROR;
|
|
1746
1922
|
if (matchesClass(error, "NotFoundError")) return EXIT_CODES.NOT_FOUND;
|
|
1747
1923
|
if (matchesClass(error, "RateLimitError")) return EXIT_CODES.RATE_LIMITED;
|
|
1748
1924
|
if (matchesClass(error, "ValidationError")) return EXIT_CODES.VALIDATION_ERROR;
|
|
1749
1925
|
if (matchesClass(error, "NetworkError")) return EXIT_CODES.NETWORK_ERROR;
|
|
1926
|
+
const code = error?.code;
|
|
1927
|
+
if (code === "NETWORK_ERROR" || code === "TIMEOUT") return EXIT_CODES.NETWORK_ERROR;
|
|
1750
1928
|
return EXIT_CODES.GENERAL_ERROR;
|
|
1751
1929
|
}
|
|
1752
1930
|
|
|
@@ -1767,7 +1945,7 @@ function createAgentsCommand() {
|
|
|
1767
1945
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1768
1946
|
const list = await client.agents.list();
|
|
1769
1947
|
spinner.stop();
|
|
1770
|
-
const rows = list.map((a) => ({
|
|
1948
|
+
const rows = list.items.map((a) => ({
|
|
1771
1949
|
id: a.id,
|
|
1772
1950
|
name: a.name,
|
|
1773
1951
|
type: a.type,
|
|
@@ -1777,7 +1955,7 @@ function createAgentsCommand() {
|
|
|
1777
1955
|
}));
|
|
1778
1956
|
printer.table(rows, ["id", "name", "type", "brand", "frequency", "enabled"]);
|
|
1779
1957
|
} catch (err) {
|
|
1780
|
-
spinner.
|
|
1958
|
+
spinner.fail();
|
|
1781
1959
|
printer.error(err);
|
|
1782
1960
|
process.exit(resolveExitCode(err));
|
|
1783
1961
|
}
|
|
@@ -1793,7 +1971,7 @@ function createAgentsCommand() {
|
|
|
1793
1971
|
spinner.stop();
|
|
1794
1972
|
printer.log(agent);
|
|
1795
1973
|
} catch (err) {
|
|
1796
|
-
spinner.
|
|
1974
|
+
spinner.fail();
|
|
1797
1975
|
printer.error(err);
|
|
1798
1976
|
process.exit(resolveExitCode(err));
|
|
1799
1977
|
}
|
|
@@ -1819,7 +1997,7 @@ function createAgentsCommand() {
|
|
|
1819
1997
|
spinner.stop();
|
|
1820
1998
|
printer.log(agent);
|
|
1821
1999
|
} catch (err) {
|
|
1822
|
-
spinner.
|
|
2000
|
+
spinner.fail();
|
|
1823
2001
|
printer.error(err);
|
|
1824
2002
|
process.exit(resolveExitCode(err));
|
|
1825
2003
|
}
|
|
@@ -1843,7 +2021,7 @@ function createAgentsCommand() {
|
|
|
1843
2021
|
spinner.stop();
|
|
1844
2022
|
printer.log(agent);
|
|
1845
2023
|
} catch (err) {
|
|
1846
|
-
spinner.
|
|
2024
|
+
spinner.fail();
|
|
1847
2025
|
printer.error(err);
|
|
1848
2026
|
process.exit(resolveExitCode(err));
|
|
1849
2027
|
}
|
|
@@ -1864,7 +2042,7 @@ function createAgentsCommand() {
|
|
|
1864
2042
|
spinner.stop();
|
|
1865
2043
|
printer.log(`Agent "${id}" deleted.`);
|
|
1866
2044
|
} catch (err) {
|
|
1867
|
-
spinner.
|
|
2045
|
+
spinner.fail();
|
|
1868
2046
|
printer.error(err);
|
|
1869
2047
|
process.exit(resolveExitCode(err));
|
|
1870
2048
|
}
|
|
@@ -1876,11 +2054,11 @@ function createAgentsCommand() {
|
|
|
1876
2054
|
try {
|
|
1877
2055
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
1878
2056
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1879
|
-
const
|
|
2057
|
+
const queued = await client.agents.run(id);
|
|
1880
2058
|
spinner.stop();
|
|
1881
|
-
printer.log(
|
|
2059
|
+
printer.log({ runId: queued.runId, status: queued.status });
|
|
1882
2060
|
} catch (err) {
|
|
1883
|
-
spinner.
|
|
2061
|
+
spinner.fail();
|
|
1884
2062
|
printer.error(err);
|
|
1885
2063
|
process.exit(resolveExitCode(err));
|
|
1886
2064
|
}
|
|
@@ -1896,7 +2074,7 @@ function createAgentsCommand() {
|
|
|
1896
2074
|
spinner.stop();
|
|
1897
2075
|
printer.log(agent);
|
|
1898
2076
|
} catch (err) {
|
|
1899
|
-
spinner.
|
|
2077
|
+
spinner.fail();
|
|
1900
2078
|
printer.error(err);
|
|
1901
2079
|
process.exit(resolveExitCode(err));
|
|
1902
2080
|
}
|
|
@@ -1910,7 +2088,7 @@ function createAgentsCommand() {
|
|
|
1910
2088
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1911
2089
|
const list = await client.agents.runs(id);
|
|
1912
2090
|
spinner.stop();
|
|
1913
|
-
const rows = list.map((r) => ({
|
|
2091
|
+
const rows = list.items.map((r) => ({
|
|
1914
2092
|
id: r.id,
|
|
1915
2093
|
status: r.status,
|
|
1916
2094
|
output: r.output ? r.output.length > 60 ? `${r.output.slice(0, 57)}\u2026` : r.output : "\u2014",
|
|
@@ -1918,7 +2096,7 @@ function createAgentsCommand() {
|
|
|
1918
2096
|
}));
|
|
1919
2097
|
printer.table(rows, ["id", "status", "output", "createdAt"]);
|
|
1920
2098
|
} catch (err) {
|
|
1921
|
-
spinner.
|
|
2099
|
+
spinner.fail();
|
|
1922
2100
|
printer.error(err);
|
|
1923
2101
|
process.exit(resolveExitCode(err));
|
|
1924
2102
|
}
|
|
@@ -1931,8 +2109,32 @@ var import_commander2 = require("commander");
|
|
|
1931
2109
|
var import_node_child_process = require("child_process");
|
|
1932
2110
|
|
|
1933
2111
|
// src/auth/device-code-client.ts
|
|
2112
|
+
async function fetchWithTimeout(url, init = {}, timeoutMs = 3e4) {
|
|
2113
|
+
const controller = new AbortController();
|
|
2114
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2115
|
+
try {
|
|
2116
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2117
|
+
} catch (err) {
|
|
2118
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2119
|
+
throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
2120
|
+
}
|
|
2121
|
+
throw err;
|
|
2122
|
+
} finally {
|
|
2123
|
+
clearTimeout(timer);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
async function parseJson(res) {
|
|
2127
|
+
const text = await res.text();
|
|
2128
|
+
try {
|
|
2129
|
+
return JSON.parse(text);
|
|
2130
|
+
} catch {
|
|
2131
|
+
throw new Error(
|
|
2132
|
+
`Expected a JSON response (HTTP ${res.status}) but received non-JSON. Verify the API base URL points at the API, not the web app.`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
1934
2136
|
async function requestDeviceCode(baseUrl) {
|
|
1935
|
-
const res = await
|
|
2137
|
+
const res = await fetchWithTimeout(`${baseUrl}/auth/cli/device-code`, {
|
|
1936
2138
|
method: "POST",
|
|
1937
2139
|
headers: { "Content-Type": "application/json" }
|
|
1938
2140
|
});
|
|
@@ -1940,22 +2142,20 @@ async function requestDeviceCode(baseUrl) {
|
|
|
1940
2142
|
const text = await res.text().catch(() => res.statusText);
|
|
1941
2143
|
throw new Error(`Device code request failed (${res.status}): ${text}`);
|
|
1942
2144
|
}
|
|
1943
|
-
|
|
1944
|
-
return data;
|
|
2145
|
+
return parseJson(res);
|
|
1945
2146
|
}
|
|
1946
2147
|
async function pollDeviceCode(baseUrl, deviceCode) {
|
|
1947
2148
|
const url = `${baseUrl}/auth/cli/poll?device_code=${encodeURIComponent(deviceCode)}`;
|
|
1948
|
-
const res = await
|
|
2149
|
+
const res = await fetchWithTimeout(url);
|
|
1949
2150
|
if (!res.ok) {
|
|
1950
2151
|
const text = await res.text().catch(() => res.statusText);
|
|
1951
2152
|
throw new Error(`Poll request failed (${res.status}): ${text}`);
|
|
1952
2153
|
}
|
|
1953
|
-
|
|
1954
|
-
return data;
|
|
2154
|
+
return parseJson(res);
|
|
1955
2155
|
}
|
|
1956
2156
|
|
|
1957
2157
|
// src/commands/auth.ts
|
|
1958
|
-
var DEFAULT_BASE_URL = "https://app.autoposting.ai";
|
|
2158
|
+
var DEFAULT_BASE_URL = "https://app.autoposting.ai/api-proxy";
|
|
1959
2159
|
function resolveBaseUrl(globals) {
|
|
1960
2160
|
return globals.baseUrl ?? process.env.AUTOPOSTING_BASE_URL ?? DEFAULT_BASE_URL;
|
|
1961
2161
|
}
|
|
@@ -2129,17 +2329,19 @@ function createBillingCommand() {
|
|
|
2129
2329
|
printer.table(
|
|
2130
2330
|
[
|
|
2131
2331
|
{
|
|
2132
|
-
plan: status.
|
|
2133
|
-
status: status.status,
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2332
|
+
plan: status.planName,
|
|
2333
|
+
status: status.status ?? "\u2014",
|
|
2334
|
+
cycle: status.billingCycle ?? "\u2014",
|
|
2335
|
+
accounts: `${status.accountsUsed}/${status.accountLimit === -1 ? "\u221E" : status.accountLimit}`,
|
|
2336
|
+
credits: status.creditBalance,
|
|
2337
|
+
renews: status.currentPeriodEnd ?? "\u2014",
|
|
2338
|
+
"trial ends": status.trialEnd ?? "\u2014"
|
|
2137
2339
|
}
|
|
2138
2340
|
],
|
|
2139
|
-
["plan", "status", "
|
|
2341
|
+
["plan", "status", "cycle", "accounts", "credits", "renews", "trial ends"]
|
|
2140
2342
|
);
|
|
2141
2343
|
} catch (err) {
|
|
2142
|
-
spinner.
|
|
2344
|
+
spinner.fail();
|
|
2143
2345
|
printer.error(err);
|
|
2144
2346
|
process.exit(resolveExitCode2(err));
|
|
2145
2347
|
}
|
|
@@ -2156,22 +2358,23 @@ function createBillingCommand() {
|
|
|
2156
2358
|
printer.table(
|
|
2157
2359
|
[
|
|
2158
2360
|
{
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
remaining: credits.remaining
|
|
2361
|
+
balance: credits.balanceFormatted,
|
|
2362
|
+
"spent (30d)": credits.totalSpent30d
|
|
2162
2363
|
}
|
|
2163
2364
|
],
|
|
2164
|
-
["
|
|
2365
|
+
["balance", "spent (30d)"]
|
|
2165
2366
|
);
|
|
2166
|
-
if (credits.
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2367
|
+
if (credits.recentUsage.length > 0) {
|
|
2368
|
+
const usageRows = credits.recentUsage.map((e) => ({
|
|
2369
|
+
date: e.date,
|
|
2370
|
+
description: e.description,
|
|
2371
|
+
amount: e.amount,
|
|
2372
|
+
type: e.type
|
|
2170
2373
|
}));
|
|
2171
|
-
printer.table(
|
|
2374
|
+
printer.table(usageRows, ["date", "description", "amount", "type"]);
|
|
2172
2375
|
}
|
|
2173
2376
|
} catch (err) {
|
|
2174
|
-
spinner.
|
|
2377
|
+
spinner.fail();
|
|
2175
2378
|
printer.error(err);
|
|
2176
2379
|
process.exit(resolveExitCode2(err));
|
|
2177
2380
|
}
|
|
@@ -2182,6 +2385,24 @@ function createBillingCommand() {
|
|
|
2182
2385
|
// src/commands/brands.ts
|
|
2183
2386
|
var import_commander4 = require("commander");
|
|
2184
2387
|
init_dist();
|
|
2388
|
+
function tokenStatus(c, now = Date.now()) {
|
|
2389
|
+
if (!c.connected) return "\u2014";
|
|
2390
|
+
if (c.refreshError) return "expired";
|
|
2391
|
+
if (c.expiresAt) {
|
|
2392
|
+
const expMs = Date.parse(c.expiresAt);
|
|
2393
|
+
if (Number.isNaN(expMs) || expMs <= now) return "expired";
|
|
2394
|
+
}
|
|
2395
|
+
return "ok";
|
|
2396
|
+
}
|
|
2397
|
+
function buildAuthStatusRow(c, now = Date.now()) {
|
|
2398
|
+
return {
|
|
2399
|
+
platform: c.platform,
|
|
2400
|
+
connected: c.connected ? "yes" : "no",
|
|
2401
|
+
username: c.platformUsername ?? "\u2014",
|
|
2402
|
+
"token status": tokenStatus(c, now),
|
|
2403
|
+
expires: c.expiresAt ?? "\u2014"
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2185
2406
|
function createBrandsCommand() {
|
|
2186
2407
|
const brands = new import_commander4.Command("brands").description("Manage brands");
|
|
2187
2408
|
brands.command("list").description("List all brands").action(async (_opts, cmd) => {
|
|
@@ -2201,7 +2422,7 @@ function createBrandsCommand() {
|
|
|
2201
2422
|
}));
|
|
2202
2423
|
printer.table(rows, ["slug", "name", "platforms", "timezone"]);
|
|
2203
2424
|
} catch (err) {
|
|
2204
|
-
spinner.
|
|
2425
|
+
spinner.fail();
|
|
2205
2426
|
printer.error(err);
|
|
2206
2427
|
process.exit(exitCodeFromError(err));
|
|
2207
2428
|
}
|
|
@@ -2217,7 +2438,7 @@ function createBrandsCommand() {
|
|
|
2217
2438
|
spinner.stop();
|
|
2218
2439
|
printer.log(brand);
|
|
2219
2440
|
} catch (err) {
|
|
2220
|
-
spinner.
|
|
2441
|
+
spinner.fail();
|
|
2221
2442
|
printer.error(err);
|
|
2222
2443
|
process.exit(exitCodeFromError(err));
|
|
2223
2444
|
}
|
|
@@ -2236,7 +2457,7 @@ function createBrandsCommand() {
|
|
|
2236
2457
|
spinner.stop();
|
|
2237
2458
|
printer.log(brand);
|
|
2238
2459
|
} catch (err) {
|
|
2239
|
-
spinner.
|
|
2460
|
+
spinner.fail();
|
|
2240
2461
|
printer.error(err);
|
|
2241
2462
|
process.exit(exitCodeFromError(err));
|
|
2242
2463
|
}
|
|
@@ -2255,7 +2476,7 @@ function createBrandsCommand() {
|
|
|
2255
2476
|
spinner.stop();
|
|
2256
2477
|
printer.log(brand);
|
|
2257
2478
|
} catch (err) {
|
|
2258
|
-
spinner.
|
|
2479
|
+
spinner.fail();
|
|
2259
2480
|
printer.error(err);
|
|
2260
2481
|
process.exit(exitCodeFromError(err));
|
|
2261
2482
|
}
|
|
@@ -2275,7 +2496,7 @@ function createBrandsCommand() {
|
|
|
2275
2496
|
spinner.stop();
|
|
2276
2497
|
printer.log(`Brand "${slug}" deleted.`);
|
|
2277
2498
|
} catch (err) {
|
|
2278
|
-
spinner.
|
|
2499
|
+
spinner.fail();
|
|
2279
2500
|
printer.error(err);
|
|
2280
2501
|
process.exit(exitCodeFromError(err));
|
|
2281
2502
|
}
|
|
@@ -2289,15 +2510,10 @@ function createBrandsCommand() {
|
|
|
2289
2510
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2290
2511
|
const connections = await client.brands.authStatus(slug);
|
|
2291
2512
|
spinner.stop();
|
|
2292
|
-
const rows = connections.map((c) => (
|
|
2293
|
-
|
|
2294
|
-
connected: c.connected ? "yes" : "no",
|
|
2295
|
-
username: c.username ?? "\u2014",
|
|
2296
|
-
"token status": c.tokenExpired === true ? "expired" : c.connected ? "ok" : "\u2014"
|
|
2297
|
-
}));
|
|
2298
|
-
printer.table(rows, ["platform", "connected", "username", "token status"]);
|
|
2513
|
+
const rows = connections.map((c) => buildAuthStatusRow(c));
|
|
2514
|
+
printer.table(rows, ["platform", "connected", "username", "token status", "expires"]);
|
|
2299
2515
|
} catch (err) {
|
|
2300
|
-
spinner.
|
|
2516
|
+
spinner.fail();
|
|
2301
2517
|
printer.error(err);
|
|
2302
2518
|
process.exit(exitCodeFromError(err));
|
|
2303
2519
|
}
|
|
@@ -2324,7 +2540,7 @@ function createCarouselsCommand() {
|
|
|
2324
2540
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2325
2541
|
const list = await client.carousels.list();
|
|
2326
2542
|
spinner.stop();
|
|
2327
|
-
const rows = list.map((c) => ({
|
|
2543
|
+
const rows = list.items.map((c) => ({
|
|
2328
2544
|
id: c.id,
|
|
2329
2545
|
title: c.title ?? "\u2014",
|
|
2330
2546
|
slides: c.slides.length,
|
|
@@ -2333,7 +2549,7 @@ function createCarouselsCommand() {
|
|
|
2333
2549
|
}));
|
|
2334
2550
|
printer.table(rows, ["id", "title", "slides", "status", "createdAt"]);
|
|
2335
2551
|
} catch (err) {
|
|
2336
|
-
spinner.
|
|
2552
|
+
spinner.fail();
|
|
2337
2553
|
printer.error(err);
|
|
2338
2554
|
process.exit(resolveExitCode3(err));
|
|
2339
2555
|
}
|
|
@@ -2349,7 +2565,7 @@ function createCarouselsCommand() {
|
|
|
2349
2565
|
spinner.stop();
|
|
2350
2566
|
printer.log(carousel);
|
|
2351
2567
|
} catch (err) {
|
|
2352
|
-
spinner.
|
|
2568
|
+
spinner.fail();
|
|
2353
2569
|
printer.error(err);
|
|
2354
2570
|
process.exit(resolveExitCode3(err));
|
|
2355
2571
|
}
|
|
@@ -2367,7 +2583,7 @@ function createCarouselsCommand() {
|
|
|
2367
2583
|
spinner.stop();
|
|
2368
2584
|
printer.log(carousel);
|
|
2369
2585
|
} catch (err) {
|
|
2370
|
-
spinner.
|
|
2586
|
+
spinner.fail();
|
|
2371
2587
|
printer.error(err);
|
|
2372
2588
|
process.exit(resolveExitCode3(err));
|
|
2373
2589
|
}
|
|
@@ -2387,7 +2603,7 @@ function createCarouselsCommand() {
|
|
|
2387
2603
|
spinner.stop();
|
|
2388
2604
|
printer.log(carousel);
|
|
2389
2605
|
} catch (err) {
|
|
2390
|
-
spinner.
|
|
2606
|
+
spinner.fail();
|
|
2391
2607
|
printer.error(err);
|
|
2392
2608
|
process.exit(resolveExitCode3(err));
|
|
2393
2609
|
}
|
|
@@ -2403,7 +2619,7 @@ function createCarouselsCommand() {
|
|
|
2403
2619
|
spinner.stop();
|
|
2404
2620
|
printer.log(result);
|
|
2405
2621
|
} catch (err) {
|
|
2406
|
-
spinner.
|
|
2622
|
+
spinner.fail();
|
|
2407
2623
|
printer.error(err);
|
|
2408
2624
|
process.exit(resolveExitCode3(err));
|
|
2409
2625
|
}
|
|
@@ -2423,7 +2639,7 @@ function createCarouselsCommand() {
|
|
|
2423
2639
|
spinner.stop();
|
|
2424
2640
|
printer.log(`Carousel "${id}" deleted.`);
|
|
2425
2641
|
} catch (err) {
|
|
2426
|
-
spinner.
|
|
2642
|
+
spinner.fail();
|
|
2427
2643
|
printer.error(err);
|
|
2428
2644
|
process.exit(resolveExitCode3(err));
|
|
2429
2645
|
}
|
|
@@ -2450,7 +2666,7 @@ function createClipsCommand() {
|
|
|
2450
2666
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2451
2667
|
const list = await client.clips.list();
|
|
2452
2668
|
spinner.stop();
|
|
2453
|
-
const rows = list.map((c) => ({
|
|
2669
|
+
const rows = list.clips.map((c) => ({
|
|
2454
2670
|
id: c.id,
|
|
2455
2671
|
name: c.name,
|
|
2456
2672
|
status: c.status,
|
|
@@ -2458,7 +2674,7 @@ function createClipsCommand() {
|
|
|
2458
2674
|
}));
|
|
2459
2675
|
printer.table(rows, ["id", "name", "status", "duration"]);
|
|
2460
2676
|
} catch (err) {
|
|
2461
|
-
spinner.
|
|
2677
|
+
spinner.fail();
|
|
2462
2678
|
printer.error(err);
|
|
2463
2679
|
process.exit(resolveExitCode4(err));
|
|
2464
2680
|
}
|
|
@@ -2474,7 +2690,7 @@ function createClipsCommand() {
|
|
|
2474
2690
|
spinner.stop();
|
|
2475
2691
|
printer.log(clip);
|
|
2476
2692
|
} catch (err) {
|
|
2477
|
-
spinner.
|
|
2693
|
+
spinner.fail();
|
|
2478
2694
|
printer.error(err);
|
|
2479
2695
|
process.exit(resolveExitCode4(err));
|
|
2480
2696
|
}
|
|
@@ -2495,7 +2711,7 @@ function createClipsCommand() {
|
|
|
2495
2711
|
spinner.stop();
|
|
2496
2712
|
printer.log(clip);
|
|
2497
2713
|
} catch (err) {
|
|
2498
|
-
spinner.
|
|
2714
|
+
spinner.fail();
|
|
2499
2715
|
printer.error(err);
|
|
2500
2716
|
process.exit(resolveExitCode4(err));
|
|
2501
2717
|
}
|
|
@@ -2507,14 +2723,14 @@ function createClipsCommand() {
|
|
|
2507
2723
|
try {
|
|
2508
2724
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2509
2725
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2510
|
-
const
|
|
2726
|
+
const { clipId } = await client.clips.importUrl({
|
|
2511
2727
|
url: opts.url,
|
|
2512
2728
|
...opts.name ? { name: opts.name } : {}
|
|
2513
2729
|
});
|
|
2514
2730
|
spinner.stop();
|
|
2515
|
-
printer.log(
|
|
2731
|
+
printer.log({ clipId });
|
|
2516
2732
|
} catch (err) {
|
|
2517
|
-
spinner.
|
|
2733
|
+
spinner.fail();
|
|
2518
2734
|
printer.error(err);
|
|
2519
2735
|
process.exit(resolveExitCode4(err));
|
|
2520
2736
|
}
|
|
@@ -2526,11 +2742,11 @@ function createClipsCommand() {
|
|
|
2526
2742
|
try {
|
|
2527
2743
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2528
2744
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2529
|
-
const
|
|
2745
|
+
const { jobIds, activeJobIds, reusedJobIds } = await client.clips.render(id);
|
|
2530
2746
|
spinner.stop();
|
|
2531
|
-
printer.log(
|
|
2747
|
+
printer.log({ jobIds, activeJobIds, reusedJobIds });
|
|
2532
2748
|
} catch (err) {
|
|
2533
|
-
spinner.
|
|
2749
|
+
spinner.fail();
|
|
2534
2750
|
printer.error(err);
|
|
2535
2751
|
process.exit(resolveExitCode4(err));
|
|
2536
2752
|
}
|
|
@@ -2550,7 +2766,7 @@ function createClipsCommand() {
|
|
|
2550
2766
|
spinner.stop();
|
|
2551
2767
|
printer.log(`Clip "${id}" deleted.`);
|
|
2552
2768
|
} catch (err) {
|
|
2553
|
-
spinner.
|
|
2769
|
+
spinner.fail();
|
|
2554
2770
|
printer.error(err);
|
|
2555
2771
|
process.exit(resolveExitCode4(err));
|
|
2556
2772
|
}
|
|
@@ -2561,8 +2777,40 @@ function createClipsCommand() {
|
|
|
2561
2777
|
// src/commands/posts.ts
|
|
2562
2778
|
var import_commander7 = require("commander");
|
|
2563
2779
|
init_dist();
|
|
2780
|
+
var VALID_PLATFORMS = ["x", "linkedin", "instagram", "threads", "youtube"];
|
|
2564
2781
|
function parsePlatforms(raw) {
|
|
2565
|
-
|
|
2782
|
+
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
2783
|
+
if (parts.length === 0) {
|
|
2784
|
+
throw new Error("No platforms provided. Pass a comma-separated list, e.g. --platforms x,linkedin");
|
|
2785
|
+
}
|
|
2786
|
+
const invalid = parts.filter((p) => !VALID_PLATFORMS.includes(p));
|
|
2787
|
+
if (invalid.length > 0) {
|
|
2788
|
+
throw new Error(
|
|
2789
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS.join(", ")}`
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
return parts;
|
|
2793
|
+
}
|
|
2794
|
+
function parsePositiveInt(value, flag) {
|
|
2795
|
+
const n = Number(value);
|
|
2796
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
2797
|
+
throw new Error(`${flag} must be a positive integer (received "${value}").`);
|
|
2798
|
+
}
|
|
2799
|
+
return n;
|
|
2800
|
+
}
|
|
2801
|
+
function validateScheduledAt(value) {
|
|
2802
|
+
const ms = Date.parse(value);
|
|
2803
|
+
if (Number.isNaN(ms) || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
|
|
2804
|
+
throw new Error(
|
|
2805
|
+
`--at must be a valid ISO 8601 datetime, e.g. 2026-06-30T14:00:00Z (received "${value}").`
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
if (ms <= Date.now()) {
|
|
2809
|
+
throw new Error(
|
|
2810
|
+
`--at must be in the future (received "${value}", which is in the past). A past schedule time publishes immediately.`
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
return value;
|
|
2566
2814
|
}
|
|
2567
2815
|
function resolveExitCode5(err) {
|
|
2568
2816
|
const attached = err.exitCode;
|
|
@@ -2581,11 +2829,11 @@ function createPostsCommand() {
|
|
|
2581
2829
|
const result = await client.posts.list({
|
|
2582
2830
|
brandSlug: opts.brand,
|
|
2583
2831
|
status: opts.status,
|
|
2584
|
-
limit:
|
|
2585
|
-
page:
|
|
2832
|
+
limit: parsePositiveInt(opts.limit, "--limit"),
|
|
2833
|
+
page: parsePositiveInt(opts.page, "--page")
|
|
2586
2834
|
});
|
|
2587
2835
|
spinner.stop();
|
|
2588
|
-
const rows = result.
|
|
2836
|
+
const rows = result.map((p) => ({
|
|
2589
2837
|
id: p.id,
|
|
2590
2838
|
brand: p.brandSlug,
|
|
2591
2839
|
status: p.status,
|
|
@@ -2595,7 +2843,7 @@ function createPostsCommand() {
|
|
|
2595
2843
|
}));
|
|
2596
2844
|
printer.table(rows, ["id", "brand", "status", "platforms", "text", "scheduledAt"]);
|
|
2597
2845
|
} catch (err) {
|
|
2598
|
-
spinner.
|
|
2846
|
+
spinner.fail();
|
|
2599
2847
|
printer.error(err);
|
|
2600
2848
|
process.exit(resolveExitCode5(err));
|
|
2601
2849
|
}
|
|
@@ -2611,12 +2859,15 @@ function createPostsCommand() {
|
|
|
2611
2859
|
spinner.stop();
|
|
2612
2860
|
printer.log(post);
|
|
2613
2861
|
} catch (err) {
|
|
2614
|
-
spinner.
|
|
2862
|
+
spinner.fail();
|
|
2615
2863
|
printer.error(err);
|
|
2616
2864
|
process.exit(resolveExitCode5(err));
|
|
2617
2865
|
}
|
|
2618
2866
|
});
|
|
2619
|
-
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)").
|
|
2867
|
+
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
|
+
"--thread <text...>",
|
|
2869
|
+
"Additional posts appended after --text to form a thread (X/Threads only, max 25)"
|
|
2870
|
+
).action(
|
|
2620
2871
|
async (opts, cmd) => {
|
|
2621
2872
|
const globals = cmd.optsWithGlobals();
|
|
2622
2873
|
const printer = createPrinter(globals);
|
|
@@ -2628,12 +2879,13 @@ function createPostsCommand() {
|
|
|
2628
2879
|
brandSlug: opts.brand,
|
|
2629
2880
|
text: opts.text,
|
|
2630
2881
|
platforms: parsePlatforms(opts.platforms),
|
|
2631
|
-
...opts.at ? { scheduledAt: opts.at } : {}
|
|
2882
|
+
...opts.at ? { scheduledAt: validateScheduledAt(opts.at) } : {},
|
|
2883
|
+
...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {}
|
|
2632
2884
|
});
|
|
2633
2885
|
spinner.stop();
|
|
2634
2886
|
printer.log(post);
|
|
2635
2887
|
} catch (err) {
|
|
2636
|
-
spinner.
|
|
2888
|
+
spinner.fail();
|
|
2637
2889
|
printer.error(err);
|
|
2638
2890
|
process.exit(resolveExitCode5(err));
|
|
2639
2891
|
}
|
|
@@ -2650,12 +2902,12 @@ function createPostsCommand() {
|
|
|
2650
2902
|
const post = await client.posts.update(id, {
|
|
2651
2903
|
...opts.text ? { text: opts.text } : {},
|
|
2652
2904
|
...opts.platforms ? { platforms: parsePlatforms(opts.platforms) } : {},
|
|
2653
|
-
...opts.at ? { scheduledAt: opts.at } : {}
|
|
2905
|
+
...opts.at ? { scheduledAt: validateScheduledAt(opts.at) } : {}
|
|
2654
2906
|
});
|
|
2655
2907
|
spinner.stop();
|
|
2656
2908
|
printer.log(post);
|
|
2657
2909
|
} catch (err) {
|
|
2658
|
-
spinner.
|
|
2910
|
+
spinner.fail();
|
|
2659
2911
|
printer.error(err);
|
|
2660
2912
|
process.exit(resolveExitCode5(err));
|
|
2661
2913
|
}
|
|
@@ -2676,8 +2928,11 @@ function createPostsCommand() {
|
|
|
2676
2928
|
spinner.stop();
|
|
2677
2929
|
printer.log(`Post "${id}" deleted.`);
|
|
2678
2930
|
} catch (err) {
|
|
2679
|
-
spinner.
|
|
2931
|
+
spinner.fail();
|
|
2680
2932
|
printer.error(err);
|
|
2933
|
+
if (!(err instanceof NotFoundError) && printer.isTty()) {
|
|
2934
|
+
printer.error(`The post may not have been deleted \u2014 verify with: ap posts get "${id}"`);
|
|
2935
|
+
}
|
|
2681
2936
|
process.exit(resolveExitCode5(err));
|
|
2682
2937
|
}
|
|
2683
2938
|
});
|
|
@@ -2692,7 +2947,7 @@ function createPostsCommand() {
|
|
|
2692
2947
|
spinner.stop();
|
|
2693
2948
|
printer.log(post);
|
|
2694
2949
|
} catch (err) {
|
|
2695
|
-
spinner.
|
|
2950
|
+
spinner.fail();
|
|
2696
2951
|
printer.error(err);
|
|
2697
2952
|
process.exit(resolveExitCode5(err));
|
|
2698
2953
|
}
|
|
@@ -2704,11 +2959,11 @@ function createPostsCommand() {
|
|
|
2704
2959
|
try {
|
|
2705
2960
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2706
2961
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2707
|
-
const post = await client.posts.schedule(id, opts.at);
|
|
2962
|
+
const post = await client.posts.schedule(id, validateScheduledAt(opts.at));
|
|
2708
2963
|
spinner.stop();
|
|
2709
2964
|
printer.log(post);
|
|
2710
2965
|
} catch (err) {
|
|
2711
|
-
spinner.
|
|
2966
|
+
spinner.fail();
|
|
2712
2967
|
printer.error(err);
|
|
2713
2968
|
process.exit(resolveExitCode5(err));
|
|
2714
2969
|
}
|
|
@@ -2724,7 +2979,7 @@ function createPostsCommand() {
|
|
|
2724
2979
|
spinner.stop();
|
|
2725
2980
|
printer.log(post);
|
|
2726
2981
|
} catch (err) {
|
|
2727
|
-
spinner.
|
|
2982
|
+
spinner.fail();
|
|
2728
2983
|
printer.error(err);
|
|
2729
2984
|
process.exit(resolveExitCode5(err));
|
|
2730
2985
|
}
|
|
@@ -2740,7 +2995,7 @@ function createPostsCommand() {
|
|
|
2740
2995
|
spinner.stop();
|
|
2741
2996
|
printer.log(post);
|
|
2742
2997
|
} catch (err) {
|
|
2743
|
-
spinner.
|
|
2998
|
+
spinner.fail();
|
|
2744
2999
|
printer.error(err);
|
|
2745
3000
|
process.exit(resolveExitCode5(err));
|
|
2746
3001
|
}
|
|
@@ -2756,7 +3011,7 @@ function createPostsCommand() {
|
|
|
2756
3011
|
spinner.stop();
|
|
2757
3012
|
printer.log(result);
|
|
2758
3013
|
} catch (err) {
|
|
2759
|
-
spinner.
|
|
3014
|
+
spinner.fail();
|
|
2760
3015
|
printer.error(err);
|
|
2761
3016
|
process.exit(resolveExitCode5(err));
|
|
2762
3017
|
}
|
|
@@ -2791,7 +3046,7 @@ function createKbCommand() {
|
|
|
2791
3046
|
}));
|
|
2792
3047
|
printer.table(rows, ["id", "name", "docs", "createdAt"]);
|
|
2793
3048
|
} catch (err) {
|
|
2794
|
-
spinner.
|
|
3049
|
+
spinner.fail();
|
|
2795
3050
|
printer.error(err);
|
|
2796
3051
|
process.exit(resolveExitCode6(err));
|
|
2797
3052
|
}
|
|
@@ -2807,7 +3062,7 @@ function createKbCommand() {
|
|
|
2807
3062
|
spinner.stop();
|
|
2808
3063
|
printer.log(result);
|
|
2809
3064
|
} catch (err) {
|
|
2810
|
-
spinner.
|
|
3065
|
+
spinner.fail();
|
|
2811
3066
|
printer.error(err);
|
|
2812
3067
|
process.exit(resolveExitCode6(err));
|
|
2813
3068
|
}
|
|
@@ -2823,7 +3078,7 @@ function createKbCommand() {
|
|
|
2823
3078
|
spinner.stop();
|
|
2824
3079
|
printer.log(result);
|
|
2825
3080
|
} catch (err) {
|
|
2826
|
-
spinner.
|
|
3081
|
+
spinner.fail();
|
|
2827
3082
|
printer.error(err);
|
|
2828
3083
|
process.exit(resolveExitCode6(err));
|
|
2829
3084
|
}
|
|
@@ -2843,7 +3098,7 @@ function createKbCommand() {
|
|
|
2843
3098
|
spinner.stop();
|
|
2844
3099
|
printer.log(`Knowledge base "${id}" deleted.`);
|
|
2845
3100
|
} catch (err) {
|
|
2846
|
-
spinner.
|
|
3101
|
+
spinner.fail();
|
|
2847
3102
|
printer.error(err);
|
|
2848
3103
|
process.exit(resolveExitCode6(err));
|
|
2849
3104
|
}
|
|
@@ -2855,16 +3110,17 @@ function createKbCommand() {
|
|
|
2855
3110
|
try {
|
|
2856
3111
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2857
3112
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2858
|
-
const
|
|
3113
|
+
const res = await client.kb.search(id, opts.query);
|
|
2859
3114
|
spinner.stop();
|
|
2860
|
-
const rows = results.map((r) => ({
|
|
2861
|
-
|
|
2862
|
-
score: r.score.toFixed(3),
|
|
3115
|
+
const rows = res.results.map((r) => ({
|
|
3116
|
+
kind: r.kind,
|
|
3117
|
+
score: r.score != null ? r.score.toFixed(3) : "\u2014",
|
|
3118
|
+
uri: r.uri ?? "\u2014",
|
|
2863
3119
|
content: r.content.length > 80 ? `${r.content.slice(0, 77)}\u2026` : r.content
|
|
2864
3120
|
}));
|
|
2865
|
-
printer.table(rows, ["
|
|
3121
|
+
printer.table(rows, ["kind", "score", "uri", "content"]);
|
|
2866
3122
|
} catch (err) {
|
|
2867
|
-
spinner.
|
|
3123
|
+
spinner.fail();
|
|
2868
3124
|
printer.error(err);
|
|
2869
3125
|
process.exit(resolveExitCode6(err));
|
|
2870
3126
|
}
|
|
@@ -2880,7 +3136,7 @@ function createKbCommand() {
|
|
|
2880
3136
|
spinner.stop();
|
|
2881
3137
|
printer.log(doc);
|
|
2882
3138
|
} catch (err) {
|
|
2883
|
-
spinner.
|
|
3139
|
+
spinner.fail();
|
|
2884
3140
|
printer.error(err);
|
|
2885
3141
|
process.exit(resolveExitCode6(err));
|
|
2886
3142
|
}
|
|
@@ -2903,7 +3159,7 @@ function createKbCommand() {
|
|
|
2903
3159
|
}));
|
|
2904
3160
|
printer.table(rows, ["id", "name", "type", "status", "createdAt"]);
|
|
2905
3161
|
} catch (err) {
|
|
2906
|
-
spinner.
|
|
3162
|
+
spinner.fail();
|
|
2907
3163
|
printer.error(err);
|
|
2908
3164
|
process.exit(resolveExitCode6(err));
|
|
2909
3165
|
}
|
|
@@ -2919,6 +3175,23 @@ function resolveExitCode7(err) {
|
|
|
2919
3175
|
if (typeof attached === "number") return attached;
|
|
2920
3176
|
return exitCodeFromError(err);
|
|
2921
3177
|
}
|
|
3178
|
+
var VALID_IDEA_PLATFORMS = ["twitter", "linkedin", "instagram", "youtube", "threads"];
|
|
3179
|
+
function parseIdeaPlatforms(raw) {
|
|
3180
|
+
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3181
|
+
if (parts.length === 0) {
|
|
3182
|
+
throw new Error("No platforms provided. e.g. --platforms twitter,linkedin");
|
|
3183
|
+
}
|
|
3184
|
+
if (parts.length > 5) {
|
|
3185
|
+
throw new Error("At most 5 platforms allowed per enrichment request.");
|
|
3186
|
+
}
|
|
3187
|
+
const invalid = parts.filter((p) => !VALID_IDEA_PLATFORMS.includes(p));
|
|
3188
|
+
if (invalid.length > 0) {
|
|
3189
|
+
throw new Error(
|
|
3190
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid: ${VALID_IDEA_PLATFORMS.join(", ")}`
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
return parts.map((platform) => ({ platform }));
|
|
3194
|
+
}
|
|
2922
3195
|
function createIdeasCommand() {
|
|
2923
3196
|
const ideas = new import_commander9.Command("ideas").description("Manage content ideas");
|
|
2924
3197
|
ideas.command("generate").description("Generate content ideas").option("--kb <id>", "Knowledge base ID to draw context from").option("--topic <text>", "Topic or theme for ideas").option("--count <n>", "Number of ideas to generate", "5").action(async (opts, cmd) => {
|
|
@@ -2934,15 +3207,15 @@ function createIdeasCommand() {
|
|
|
2934
3207
|
count: parseInt(opts.count, 10)
|
|
2935
3208
|
});
|
|
2936
3209
|
spinner.stop();
|
|
2937
|
-
const rows = result.map((i) => ({
|
|
2938
|
-
id: i.id,
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3210
|
+
const rows = (result.ideas ?? []).map((i) => ({
|
|
3211
|
+
id: i.id ?? "\u2014",
|
|
3212
|
+
platform: i.targetPlatform,
|
|
3213
|
+
score: i.viralityScore,
|
|
3214
|
+
title: i.title.length > 80 ? `${i.title.slice(0, 77)}\u2026` : i.title
|
|
2942
3215
|
}));
|
|
2943
|
-
printer.table(rows, ["id", "
|
|
3216
|
+
printer.table(rows, ["id", "platform", "score", "title"]);
|
|
2944
3217
|
} catch (err) {
|
|
2945
|
-
spinner.
|
|
3218
|
+
spinner.fail();
|
|
2946
3219
|
printer.error(err);
|
|
2947
3220
|
process.exit(resolveExitCode7(err));
|
|
2948
3221
|
}
|
|
@@ -2956,36 +3229,43 @@ function createIdeasCommand() {
|
|
|
2956
3229
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2957
3230
|
const list = await client.ideas.list();
|
|
2958
3231
|
spinner.stop();
|
|
2959
|
-
const rows = list.map((i) => ({
|
|
3232
|
+
const rows = list.items.map((i) => ({
|
|
2960
3233
|
id: i.id,
|
|
2961
|
-
topic: i.topic
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3234
|
+
topic: i.topic || "\u2014",
|
|
3235
|
+
platform: i.targetPlatform,
|
|
3236
|
+
score: i.viralityScore,
|
|
3237
|
+
status: i.status,
|
|
3238
|
+
title: i.title.length > 60 ? `${i.title.slice(0, 57)}\u2026` : i.title
|
|
2965
3239
|
}));
|
|
2966
|
-
printer.table(rows, ["id", "topic", "
|
|
3240
|
+
printer.table(rows, ["id", "topic", "platform", "score", "status", "title"]);
|
|
2967
3241
|
} catch (err) {
|
|
2968
|
-
spinner.
|
|
3242
|
+
spinner.fail();
|
|
2969
3243
|
printer.error(err);
|
|
2970
3244
|
process.exit(resolveExitCode7(err));
|
|
2971
3245
|
}
|
|
2972
3246
|
});
|
|
2973
|
-
ideas.command("enrich
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3247
|
+
ideas.command("enrich").description("Enrich an idea into platform-ready drafts (async; returns a job ID)").requiredOption("--title <text>", "Idea title").requiredOption("--hook <text>", "Idea hook").requiredOption("--angle <text>", "Idea angle").requiredOption("--platforms <list>", `Comma-separated platforms (${VALID_IDEA_PLATFORMS.join(",")})`).option("--kb <id>", "Knowledge base ID for additional context").action(
|
|
3248
|
+
async (opts, cmd) => {
|
|
3249
|
+
const globals = cmd.optsWithGlobals();
|
|
3250
|
+
const printer = createPrinter(globals);
|
|
3251
|
+
const spinner = printer.spinner("Queuing enrichment\u2026");
|
|
3252
|
+
try {
|
|
3253
|
+
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3254
|
+
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3255
|
+
const { jobId } = await client.ideas.enrich({
|
|
3256
|
+
idea: { title: opts.title, hook: opts.hook, angle: opts.angle },
|
|
3257
|
+
platforms: parseIdeaPlatforms(opts.platforms),
|
|
3258
|
+
...opts.kb ? { kbId: opts.kb } : {}
|
|
3259
|
+
});
|
|
3260
|
+
spinner.stop();
|
|
3261
|
+
printer.log(`Enrichment queued. Job ID: ${jobId}`);
|
|
3262
|
+
} catch (err) {
|
|
3263
|
+
spinner.fail();
|
|
3264
|
+
printer.error(err);
|
|
3265
|
+
process.exit(resolveExitCode7(err));
|
|
3266
|
+
}
|
|
2987
3267
|
}
|
|
2988
|
-
|
|
3268
|
+
);
|
|
2989
3269
|
ideas.command("delete <id>").description("Delete an idea (requires --force)").option("--force", "Confirm deletion without interactive prompt").action(async (id, opts, cmd) => {
|
|
2990
3270
|
const globals = cmd.optsWithGlobals();
|
|
2991
3271
|
const printer = createPrinter(globals);
|
|
@@ -3001,7 +3281,7 @@ function createIdeasCommand() {
|
|
|
3001
3281
|
spinner.stop();
|
|
3002
3282
|
printer.log(`Idea "${id}" deleted.`);
|
|
3003
3283
|
} catch (err) {
|
|
3004
|
-
spinner.
|
|
3284
|
+
spinner.fail();
|
|
3005
3285
|
printer.error(err);
|
|
3006
3286
|
process.exit(resolveExitCode7(err));
|
|
3007
3287
|
}
|
|
@@ -3028,20 +3308,27 @@ function createUsageCommand() {
|
|
|
3028
3308
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3029
3309
|
const summary = await client.usage.summary();
|
|
3030
3310
|
spinner.stop();
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3311
|
+
printer.log(`Period: ${summary.range.from.slice(0, 10)} \u2192 ${summary.range.to.slice(0, 10)}`);
|
|
3312
|
+
printer.table(
|
|
3313
|
+
[
|
|
3314
|
+
{
|
|
3315
|
+
posts: summary.posts.total,
|
|
3316
|
+
published: summary.posts.published,
|
|
3317
|
+
agents: summary.agents.total,
|
|
3318
|
+
"active agents": summary.agents.active,
|
|
3319
|
+
"ai cost (usd)": summary.ai.totalCostUsd,
|
|
3320
|
+
"ai requests": summary.ai.requests
|
|
3321
|
+
}
|
|
3322
|
+
],
|
|
3323
|
+
["posts", "published", "agents", "active agents", "ai cost (usd)", "ai requests"]
|
|
3324
|
+
);
|
|
3325
|
+
const sourceRows = Object.entries(summary.posts.bySource).map(([source, count]) => ({
|
|
3326
|
+
source,
|
|
3327
|
+
posts: count
|
|
3036
3328
|
}));
|
|
3037
|
-
|
|
3038
|
-
printer.log(`No usage data for period: ${summary.period}`);
|
|
3039
|
-
} else {
|
|
3040
|
-
printer.log(`Period: ${summary.period}`);
|
|
3041
|
-
printer.table(rows, ["platform", "posts", "published", "failed"]);
|
|
3042
|
-
}
|
|
3329
|
+
printer.table(sourceRows, ["source", "posts"]);
|
|
3043
3330
|
} catch (err) {
|
|
3044
|
-
spinner.
|
|
3331
|
+
spinner.fail();
|
|
3045
3332
|
printer.error(err);
|
|
3046
3333
|
process.exit(resolveExitCode8(err));
|
|
3047
3334
|
}
|
|
@@ -3077,7 +3364,7 @@ function createWebhooksCommand() {
|
|
|
3077
3364
|
}));
|
|
3078
3365
|
printer.table(rows, ["id", "url", "events", "active", "createdAt"]);
|
|
3079
3366
|
} catch (err) {
|
|
3080
|
-
spinner.
|
|
3367
|
+
spinner.fail();
|
|
3081
3368
|
printer.error(err);
|
|
3082
3369
|
process.exit(resolveExitCode9(err));
|
|
3083
3370
|
}
|
|
@@ -3093,7 +3380,7 @@ function createWebhooksCommand() {
|
|
|
3093
3380
|
spinner.stop();
|
|
3094
3381
|
printer.log(webhook);
|
|
3095
3382
|
} catch (err) {
|
|
3096
|
-
spinner.
|
|
3383
|
+
spinner.fail();
|
|
3097
3384
|
printer.error(err);
|
|
3098
3385
|
process.exit(resolveExitCode9(err));
|
|
3099
3386
|
}
|
|
@@ -3114,7 +3401,7 @@ function createWebhooksCommand() {
|
|
|
3114
3401
|
spinner.stop();
|
|
3115
3402
|
printer.log(webhook);
|
|
3116
3403
|
} catch (err) {
|
|
3117
|
-
spinner.
|
|
3404
|
+
spinner.fail();
|
|
3118
3405
|
printer.error(err);
|
|
3119
3406
|
process.exit(resolveExitCode9(err));
|
|
3120
3407
|
}
|
|
@@ -3136,7 +3423,7 @@ function createWebhooksCommand() {
|
|
|
3136
3423
|
spinner.stop();
|
|
3137
3424
|
printer.log(webhook);
|
|
3138
3425
|
} catch (err) {
|
|
3139
|
-
spinner.
|
|
3426
|
+
spinner.fail();
|
|
3140
3427
|
printer.error(err);
|
|
3141
3428
|
process.exit(resolveExitCode9(err));
|
|
3142
3429
|
}
|
|
@@ -3157,7 +3444,7 @@ function createWebhooksCommand() {
|
|
|
3157
3444
|
spinner.stop();
|
|
3158
3445
|
printer.log(`Webhook "${id}" deleted.`);
|
|
3159
3446
|
} catch (err) {
|
|
3160
|
-
spinner.
|
|
3447
|
+
spinner.fail();
|
|
3161
3448
|
printer.error(err);
|
|
3162
3449
|
process.exit(resolveExitCode9(err));
|
|
3163
3450
|
}
|
|
@@ -3169,11 +3456,13 @@ function createWebhooksCommand() {
|
|
|
3169
3456
|
try {
|
|
3170
3457
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3171
3458
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3172
|
-
await client.webhooks.test(id);
|
|
3459
|
+
const result = await client.webhooks.test(id);
|
|
3173
3460
|
spinner.stop();
|
|
3174
|
-
printer.log(
|
|
3461
|
+
printer.log(
|
|
3462
|
+
`Test event sent to webhook "${id}" \u2014 delivered: ${result.delivered ? "yes" : "no"} (HTTP ${result.httpStatus}).`
|
|
3463
|
+
);
|
|
3175
3464
|
} catch (err) {
|
|
3176
|
-
spinner.
|
|
3465
|
+
spinner.fail();
|
|
3177
3466
|
printer.error(err);
|
|
3178
3467
|
process.exit(resolveExitCode9(err));
|
|
3179
3468
|
}
|
|
@@ -3198,18 +3487,18 @@ function createWorkspacesCommand() {
|
|
|
3198
3487
|
try {
|
|
3199
3488
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3200
3489
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3201
|
-
const
|
|
3490
|
+
const res = await client.workspaces.list();
|
|
3202
3491
|
spinner.stop();
|
|
3203
|
-
const rows =
|
|
3492
|
+
const rows = res.organizations.map((w) => ({
|
|
3204
3493
|
id: w.id,
|
|
3205
3494
|
name: w.name,
|
|
3206
3495
|
slug: w.slug,
|
|
3207
|
-
active: w.
|
|
3496
|
+
active: w.id === res.activeOrgId ? "yes" : "no",
|
|
3208
3497
|
createdAt: w.createdAt
|
|
3209
3498
|
}));
|
|
3210
3499
|
printer.table(rows, ["id", "name", "slug", "active", "createdAt"]);
|
|
3211
3500
|
} catch (err) {
|
|
3212
|
-
spinner.
|
|
3501
|
+
spinner.fail();
|
|
3213
3502
|
printer.error(err);
|
|
3214
3503
|
process.exit(resolveExitCode10(err));
|
|
3215
3504
|
}
|
|
@@ -3220,13 +3509,12 @@ function createWorkspacesCommand() {
|
|
|
3220
3509
|
const spinner = printer.spinner(`Switching to workspace "${id}"\u2026`);
|
|
3221
3510
|
try {
|
|
3222
3511
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3223
|
-
const
|
|
3224
|
-
const client = new Autoposting({ apiKey: cred.apiKey, authSource });
|
|
3512
|
+
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3225
3513
|
await client.workspaces.switchWorkspace(id);
|
|
3226
3514
|
spinner.stop();
|
|
3227
3515
|
printer.log(`Switched to workspace "${id}".`);
|
|
3228
3516
|
} catch (err) {
|
|
3229
|
-
spinner.
|
|
3517
|
+
spinner.fail();
|
|
3230
3518
|
printer.error(err);
|
|
3231
3519
|
process.exit(resolveExitCode10(err));
|
|
3232
3520
|
}
|
|
@@ -3238,7 +3526,7 @@ function createWorkspacesCommand() {
|
|
|
3238
3526
|
var import_commander13 = require("commander");
|
|
3239
3527
|
init_dist();
|
|
3240
3528
|
var import_node_fs2 = __toESM(require("fs"));
|
|
3241
|
-
var API_HEALTH_URL = "https://app.autoposting.ai/api/health";
|
|
3529
|
+
var API_HEALTH_URL = "https://app.autoposting.ai/api-proxy/health";
|
|
3242
3530
|
async function runChecks() {
|
|
3243
3531
|
const results = [];
|
|
3244
3532
|
results.push({ name: "CLI Version", status: "pass", value: VERSION });
|
|
@@ -3251,7 +3539,7 @@ async function runChecks() {
|
|
|
3251
3539
|
});
|
|
3252
3540
|
try {
|
|
3253
3541
|
resolveAuth();
|
|
3254
|
-
results.push({ name: "Auth", status: "pass", value: "
|
|
3542
|
+
results.push({ name: "Auth", status: "pass", value: "credentials configured" });
|
|
3255
3543
|
} catch {
|
|
3256
3544
|
results.push({ name: "Auth", status: "fail", value: "not configured" });
|
|
3257
3545
|
}
|
|
@@ -3309,39 +3597,65 @@ function createDoctorCommand() {
|
|
|
3309
3597
|
|
|
3310
3598
|
// src/commands/whoami.ts
|
|
3311
3599
|
var import_commander14 = require("commander");
|
|
3600
|
+
init_dist();
|
|
3312
3601
|
function createWhoamiCommand() {
|
|
3313
|
-
return new import_commander14.Command("whoami").description("Show current
|
|
3602
|
+
return new import_commander14.Command("whoami").description("Show the current identity and validate the key against the server").action(async (_opts, cmd) => {
|
|
3603
|
+
const globals = cmd.optsWithGlobals();
|
|
3604
|
+
let cred;
|
|
3314
3605
|
try {
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3606
|
+
cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3607
|
+
} catch (err) {
|
|
3608
|
+
console.error(`Error: ${err.message}`);
|
|
3609
|
+
process.exitCode = exitCodeFromError(err);
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
const masked = `${cred.apiKey.slice(0, 8)}${"*".repeat(Math.max(0, cred.apiKey.length - 8))}`;
|
|
3613
|
+
let validity = "unverified";
|
|
3614
|
+
let identity;
|
|
3615
|
+
try {
|
|
3616
|
+
const client = new Autoposting({ apiKey: cred.apiKey, maxRetries: 0 });
|
|
3617
|
+
identity = await client.getProfile();
|
|
3618
|
+
validity = "valid";
|
|
3619
|
+
} catch (err) {
|
|
3620
|
+
const code = exitCodeFromError(err);
|
|
3621
|
+
validity = code === EXIT_CODES.AUTH_ERROR || code === EXIT_CODES.SCOPE_ERROR ? "rejected" : "unverified";
|
|
3622
|
+
}
|
|
3623
|
+
const exitCode = validity === "rejected" ? EXIT_CODES.AUTH_ERROR : 0;
|
|
3624
|
+
const creds = cred.source === "stored" ? readCredentials() : void 0;
|
|
3625
|
+
const profileName = creds?.activeProfile ?? "unknown";
|
|
3626
|
+
const email = creds?.profiles[profileName]?.email;
|
|
3627
|
+
if (globals.json) {
|
|
3628
|
+
const out = {
|
|
3629
|
+
source: cred.source,
|
|
3630
|
+
apiKey: masked,
|
|
3631
|
+
valid: validity === "valid",
|
|
3632
|
+
validity
|
|
3633
|
+
};
|
|
3634
|
+
if (identity?.orgId) {
|
|
3635
|
+
out.orgId = identity.orgId;
|
|
3636
|
+
out.authType = identity.authType;
|
|
3637
|
+
if (identity.email) out.email = identity.email;
|
|
3330
3638
|
}
|
|
3331
|
-
console.log(`Source: ${cred.source}`);
|
|
3332
3639
|
if (cred.source === "stored") {
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
const profile = creds?.profiles[profileName];
|
|
3336
|
-
console.log(`Profile: ${profileName}`);
|
|
3337
|
-
if (profile?.workspace) console.log(`Workspace: ${profile.workspace}`);
|
|
3338
|
-
if (profile?.email) console.log(`Email: ${profile.email}`);
|
|
3640
|
+
out.profile = profileName;
|
|
3641
|
+
if (email) out.email = email;
|
|
3339
3642
|
}
|
|
3340
|
-
console.log(
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3643
|
+
console.log(JSON.stringify(out, null, 2));
|
|
3644
|
+
process.exitCode = exitCode;
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
console.log(`Source: ${cred.source}`);
|
|
3648
|
+
if (cred.source === "stored") {
|
|
3649
|
+
console.log(`Profile: ${profileName}`);
|
|
3650
|
+
if (email) console.log(`Email: ${email}`);
|
|
3651
|
+
}
|
|
3652
|
+
if (identity?.orgId) {
|
|
3653
|
+
console.log(`Org: ${identity.orgId}`);
|
|
3654
|
+
console.log(`Auth: ${identity.authType}`);
|
|
3344
3655
|
}
|
|
3656
|
+
const tag = validity === "valid" ? "\u2713 valid" : validity === "rejected" ? "\u2717 rejected by server" : "\u26A0 unverified (could not reach server)";
|
|
3657
|
+
console.log(`API key: ${masked} ${tag}`);
|
|
3658
|
+
process.exitCode = exitCode;
|
|
3345
3659
|
});
|
|
3346
3660
|
}
|
|
3347
3661
|
|