@autoposting.ai/cli 0.3.0 → 0.3.1
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 +423 -194
- package/dist/cli.cjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -46,7 +46,7 @@ var VERSION, AutopostingError, AuthenticationError, ScopeError, NotFoundError, V
|
|
|
46
46
|
var init_dist = __esm({
|
|
47
47
|
"../sdk/dist/index.mjs"() {
|
|
48
48
|
"use strict";
|
|
49
|
-
VERSION = "0.3.
|
|
49
|
+
VERSION = "0.3.1";
|
|
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,6 +322,10 @@ 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
|
+
* NOTE: this route is session-only (better-auth session); an API key receives 401.
|
|
328
|
+
*/
|
|
308
329
|
list() {
|
|
309
330
|
return this.get("/orgs");
|
|
310
331
|
}
|
|
@@ -316,7 +337,7 @@ var init_dist = __esm({
|
|
|
316
337
|
)
|
|
317
338
|
);
|
|
318
339
|
}
|
|
319
|
-
return this.
|
|
340
|
+
return this.client.request("PUT", "/orgs/active", { organizationId: id });
|
|
320
341
|
}
|
|
321
342
|
};
|
|
322
343
|
Autoposting = class {
|
|
@@ -346,7 +367,7 @@ var init_dist = __esm({
|
|
|
346
367
|
);
|
|
347
368
|
}
|
|
348
369
|
this._apiKey = key;
|
|
349
|
-
this.baseUrl = (config.baseUrl
|
|
370
|
+
this.baseUrl = (config.baseUrl || process.env.AUTOPOSTING_BASE_URL || "https://app.autoposting.ai/api-proxy").replace(/\/$/, "");
|
|
350
371
|
this.timeout = config.timeout ?? 3e4;
|
|
351
372
|
this.extraHeaders = config.headers ?? {};
|
|
352
373
|
this.authSource = config.authSource ?? "api-key";
|
|
@@ -375,46 +396,101 @@ var init_dist = __esm({
|
|
|
375
396
|
}
|
|
376
397
|
const isFormData = body instanceof FormData;
|
|
377
398
|
const headers = {
|
|
399
|
+
...this.extraHeaders,
|
|
378
400
|
authorization: `Bearer ${this._apiKey}`,
|
|
379
401
|
"user-agent": `autoposting-sdk/${VERSION}`,
|
|
380
|
-
"x-source": "sdk"
|
|
381
|
-
...this.extraHeaders
|
|
402
|
+
"x-source": "sdk"
|
|
382
403
|
};
|
|
383
404
|
if (!isFormData) {
|
|
384
405
|
headers["content-type"] = "application/json";
|
|
385
406
|
}
|
|
386
407
|
const controller = new AbortController();
|
|
387
408
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
388
|
-
let response;
|
|
389
409
|
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 = {};
|
|
410
|
+
let response;
|
|
401
411
|
try {
|
|
402
|
-
|
|
403
|
-
|
|
412
|
+
response = await fetch(url, {
|
|
413
|
+
method,
|
|
414
|
+
headers,
|
|
415
|
+
body: isFormData ? body : body !== void 0 ? JSON.stringify(body) : void 0,
|
|
416
|
+
signal: controller.signal
|
|
417
|
+
});
|
|
418
|
+
} catch (err) {
|
|
419
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
420
|
+
throw new AutopostingError(
|
|
421
|
+
`Request to ${method} ${path2} timed out after ${this.timeout}ms.`,
|
|
422
|
+
0,
|
|
423
|
+
"TIMEOUT"
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
throw new AutopostingError(
|
|
427
|
+
`Network request to ${method} ${path2} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
428
|
+
0,
|
|
429
|
+
"NETWORK_ERROR"
|
|
430
|
+
);
|
|
404
431
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
432
|
+
if (!response.ok) {
|
|
433
|
+
const raw = await response.text();
|
|
434
|
+
let errorBody = {};
|
|
435
|
+
if (raw) {
|
|
436
|
+
try {
|
|
437
|
+
errorBody = JSON.parse(raw);
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (!errorBody.error) {
|
|
442
|
+
const snippet = raw.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
443
|
+
errorBody.error = snippet ? `HTTP ${response.status} ${response.statusText}: ${snippet}` : `HTTP ${response.status} ${response.statusText}`;
|
|
444
|
+
}
|
|
445
|
+
const err = createError(response.status, errorBody);
|
|
446
|
+
if (err instanceof RateLimitError) {
|
|
447
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
448
|
+
if (retryAfter) {
|
|
449
|
+
const seconds = Number(retryAfter);
|
|
450
|
+
if (Number.isFinite(seconds)) {
|
|
451
|
+
err.retryAfter = Math.max(0, Math.round(seconds));
|
|
452
|
+
} else {
|
|
453
|
+
const dateMs = Date.parse(retryAfter);
|
|
454
|
+
if (!Number.isNaN(dateMs)) {
|
|
455
|
+
err.retryAfter = Math.max(0, Math.round((dateMs - Date.now()) / 1e3));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
410
459
|
}
|
|
460
|
+
throw err;
|
|
461
|
+
}
|
|
462
|
+
if (response.status === 204) {
|
|
463
|
+
return {};
|
|
464
|
+
}
|
|
465
|
+
const text = await response.text();
|
|
466
|
+
if (!text) return {};
|
|
467
|
+
let parsed;
|
|
468
|
+
try {
|
|
469
|
+
parsed = JSON.parse(text);
|
|
470
|
+
} catch {
|
|
471
|
+
throw new AutopostingError(
|
|
472
|
+
`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.`,
|
|
473
|
+
response.status,
|
|
474
|
+
"INVALID_RESPONSE"
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (typeof parsed === "object" && parsed !== null && parsed.success === true) {
|
|
478
|
+
const env = parsed;
|
|
479
|
+
return env.data !== void 0 ? env.data : {};
|
|
480
|
+
}
|
|
481
|
+
return parsed;
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
484
|
+
throw new AutopostingError(
|
|
485
|
+
`Request to ${method} ${path2} timed out after ${this.timeout}ms.`,
|
|
486
|
+
0,
|
|
487
|
+
"TIMEOUT"
|
|
488
|
+
);
|
|
411
489
|
}
|
|
412
490
|
throw err;
|
|
491
|
+
} finally {
|
|
492
|
+
clearTimeout(timer);
|
|
413
493
|
}
|
|
414
|
-
if (response.status === 204) {
|
|
415
|
-
return {};
|
|
416
|
-
}
|
|
417
|
-
return response.json();
|
|
418
494
|
}
|
|
419
495
|
};
|
|
420
496
|
}
|
|
@@ -862,13 +938,26 @@ var init_tools = __esm({
|
|
|
862
938
|
},
|
|
863
939
|
{
|
|
864
940
|
name: "enrich-idea",
|
|
865
|
-
description: "Enrich an idea
|
|
941
|
+
description: "Enrich an idea (title/hook/angle) into platform-ready drafts across 1..5 platforms. Async \u2014 returns a job ID.",
|
|
866
942
|
inputSchema: {
|
|
867
943
|
type: "object",
|
|
868
944
|
properties: {
|
|
869
|
-
|
|
945
|
+
title: { type: "string", description: "Idea title" },
|
|
946
|
+
hook: { type: "string", description: "Idea hook" },
|
|
947
|
+
angle: { type: "string", description: "Idea angle" },
|
|
948
|
+
platforms: {
|
|
949
|
+
type: "array",
|
|
950
|
+
description: "Target platforms (1..5)",
|
|
951
|
+
items: {
|
|
952
|
+
type: "string",
|
|
953
|
+
enum: ["twitter", "linkedin", "instagram", "youtube", "threads"]
|
|
954
|
+
},
|
|
955
|
+
minItems: 1,
|
|
956
|
+
maxItems: 5
|
|
957
|
+
},
|
|
958
|
+
kbId: { type: "string", description: "Optional knowledge base ID for context" }
|
|
870
959
|
},
|
|
871
|
-
required: ["
|
|
960
|
+
required: ["title", "hook", "angle", "platforms"],
|
|
872
961
|
additionalProperties: false
|
|
873
962
|
}
|
|
874
963
|
},
|
|
@@ -1345,7 +1434,17 @@ async function dispatchToolCall(name, args, client) {
|
|
|
1345
1434
|
return ok(result);
|
|
1346
1435
|
}
|
|
1347
1436
|
case "enrich-idea": {
|
|
1348
|
-
const result = await client.ideas.enrich(
|
|
1437
|
+
const result = await client.ideas.enrich({
|
|
1438
|
+
idea: {
|
|
1439
|
+
title: args.title,
|
|
1440
|
+
hook: args.hook,
|
|
1441
|
+
angle: args.angle
|
|
1442
|
+
},
|
|
1443
|
+
platforms: args.platforms.map((platform) => ({
|
|
1444
|
+
platform
|
|
1445
|
+
})),
|
|
1446
|
+
...args.kbId ? { kbId: args.kbId } : {}
|
|
1447
|
+
});
|
|
1349
1448
|
return ok(result);
|
|
1350
1449
|
}
|
|
1351
1450
|
case "delete-idea": {
|
|
@@ -1523,7 +1622,8 @@ var import_node_fs = __toESM(require("fs"));
|
|
|
1523
1622
|
var import_node_os = __toESM(require("os"));
|
|
1524
1623
|
var import_node_path = __toESM(require("path"));
|
|
1525
1624
|
function getCredentialsPath() {
|
|
1526
|
-
const
|
|
1625
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
1626
|
+
const configBase = xdg && import_node_path.default.isAbsolute(xdg) ? xdg : import_node_path.default.join(import_node_os.default.homedir(), ".config");
|
|
1527
1627
|
return import_node_path.default.join(configBase, "autoposting", "credentials.json");
|
|
1528
1628
|
}
|
|
1529
1629
|
function readCredentials() {
|
|
@@ -1539,11 +1639,27 @@ function writeCredentials(creds) {
|
|
|
1539
1639
|
const filePath = getCredentialsPath();
|
|
1540
1640
|
const dir = import_node_path.default.dirname(filePath);
|
|
1541
1641
|
import_node_fs.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1542
|
-
|
|
1642
|
+
const tmpPath = `${filePath}.${process.pid}.tmp`;
|
|
1643
|
+
try {
|
|
1644
|
+
import_node_fs.default.writeFileSync(tmpPath, JSON.stringify(creds, null, 2), { encoding: "utf8", mode: 384 });
|
|
1645
|
+
import_node_fs.default.renameSync(tmpPath, filePath);
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
try {
|
|
1648
|
+
import_node_fs.default.unlinkSync(tmpPath);
|
|
1649
|
+
} catch {
|
|
1650
|
+
}
|
|
1651
|
+
throw err;
|
|
1652
|
+
}
|
|
1543
1653
|
import_node_fs.default.chmodSync(filePath, 384);
|
|
1544
1654
|
}
|
|
1545
1655
|
function saveProfile(name, profile) {
|
|
1546
1656
|
const existing = readCredentials();
|
|
1657
|
+
if (!existing && import_node_fs.default.existsSync(getCredentialsPath())) {
|
|
1658
|
+
try {
|
|
1659
|
+
import_node_fs.default.renameSync(getCredentialsPath(), `${getCredentialsPath()}.corrupt`);
|
|
1660
|
+
} catch {
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1547
1663
|
const creds = existing ?? { activeProfile: name, profiles: {} };
|
|
1548
1664
|
creds.profiles[name] = profile;
|
|
1549
1665
|
creds.activeProfile = name;
|
|
@@ -1608,7 +1724,9 @@ function red(text) {
|
|
|
1608
1724
|
}
|
|
1609
1725
|
function detectOutputMode(options) {
|
|
1610
1726
|
if (options.quiet) return "quiet";
|
|
1611
|
-
if (options.json ||
|
|
1727
|
+
if (options.format === "json" || options.json) return "json";
|
|
1728
|
+
if (options.format === "table") return "tty";
|
|
1729
|
+
if (!process.stdout.isTTY) return "json";
|
|
1612
1730
|
return "tty";
|
|
1613
1731
|
}
|
|
1614
1732
|
function formatOutput(data, mode) {
|
|
@@ -1741,12 +1859,18 @@ function matchesClass(error, className) {
|
|
|
1741
1859
|
return false;
|
|
1742
1860
|
}
|
|
1743
1861
|
function exitCodeFromError(error) {
|
|
1862
|
+
const attached = error?.exitCode;
|
|
1863
|
+
if (typeof attached === "number" && Number.isInteger(attached) && attached >= 0) {
|
|
1864
|
+
return attached;
|
|
1865
|
+
}
|
|
1744
1866
|
if (matchesClass(error, "AuthenticationError")) return EXIT_CODES.AUTH_ERROR;
|
|
1745
1867
|
if (matchesClass(error, "ScopeError")) return EXIT_CODES.SCOPE_ERROR;
|
|
1746
1868
|
if (matchesClass(error, "NotFoundError")) return EXIT_CODES.NOT_FOUND;
|
|
1747
1869
|
if (matchesClass(error, "RateLimitError")) return EXIT_CODES.RATE_LIMITED;
|
|
1748
1870
|
if (matchesClass(error, "ValidationError")) return EXIT_CODES.VALIDATION_ERROR;
|
|
1749
1871
|
if (matchesClass(error, "NetworkError")) return EXIT_CODES.NETWORK_ERROR;
|
|
1872
|
+
const code = error?.code;
|
|
1873
|
+
if (code === "NETWORK_ERROR" || code === "TIMEOUT") return EXIT_CODES.NETWORK_ERROR;
|
|
1750
1874
|
return EXIT_CODES.GENERAL_ERROR;
|
|
1751
1875
|
}
|
|
1752
1876
|
|
|
@@ -1767,7 +1891,7 @@ function createAgentsCommand() {
|
|
|
1767
1891
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1768
1892
|
const list = await client.agents.list();
|
|
1769
1893
|
spinner.stop();
|
|
1770
|
-
const rows = list.map((a) => ({
|
|
1894
|
+
const rows = list.items.map((a) => ({
|
|
1771
1895
|
id: a.id,
|
|
1772
1896
|
name: a.name,
|
|
1773
1897
|
type: a.type,
|
|
@@ -1876,9 +2000,9 @@ function createAgentsCommand() {
|
|
|
1876
2000
|
try {
|
|
1877
2001
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
1878
2002
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1879
|
-
const
|
|
2003
|
+
const queued = await client.agents.run(id);
|
|
1880
2004
|
spinner.stop();
|
|
1881
|
-
printer.log(
|
|
2005
|
+
printer.log({ runId: queued.runId, status: queued.status });
|
|
1882
2006
|
} catch (err) {
|
|
1883
2007
|
spinner.stop();
|
|
1884
2008
|
printer.error(err);
|
|
@@ -1910,7 +2034,7 @@ function createAgentsCommand() {
|
|
|
1910
2034
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
1911
2035
|
const list = await client.agents.runs(id);
|
|
1912
2036
|
spinner.stop();
|
|
1913
|
-
const rows = list.map((r) => ({
|
|
2037
|
+
const rows = list.items.map((r) => ({
|
|
1914
2038
|
id: r.id,
|
|
1915
2039
|
status: r.status,
|
|
1916
2040
|
output: r.output ? r.output.length > 60 ? `${r.output.slice(0, 57)}\u2026` : r.output : "\u2014",
|
|
@@ -1931,8 +2055,32 @@ var import_commander2 = require("commander");
|
|
|
1931
2055
|
var import_node_child_process = require("child_process");
|
|
1932
2056
|
|
|
1933
2057
|
// src/auth/device-code-client.ts
|
|
2058
|
+
async function fetchWithTimeout(url, init = {}, timeoutMs = 3e4) {
|
|
2059
|
+
const controller = new AbortController();
|
|
2060
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2061
|
+
try {
|
|
2062
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2063
|
+
} catch (err) {
|
|
2064
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2065
|
+
throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
2066
|
+
}
|
|
2067
|
+
throw err;
|
|
2068
|
+
} finally {
|
|
2069
|
+
clearTimeout(timer);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
async function parseJson(res) {
|
|
2073
|
+
const text = await res.text();
|
|
2074
|
+
try {
|
|
2075
|
+
return JSON.parse(text);
|
|
2076
|
+
} catch {
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
`Expected a JSON response (HTTP ${res.status}) but received non-JSON. Verify the API base URL points at the API, not the web app.`
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
1934
2082
|
async function requestDeviceCode(baseUrl) {
|
|
1935
|
-
const res = await
|
|
2083
|
+
const res = await fetchWithTimeout(`${baseUrl}/auth/cli/device-code`, {
|
|
1936
2084
|
method: "POST",
|
|
1937
2085
|
headers: { "Content-Type": "application/json" }
|
|
1938
2086
|
});
|
|
@@ -1940,22 +2088,20 @@ async function requestDeviceCode(baseUrl) {
|
|
|
1940
2088
|
const text = await res.text().catch(() => res.statusText);
|
|
1941
2089
|
throw new Error(`Device code request failed (${res.status}): ${text}`);
|
|
1942
2090
|
}
|
|
1943
|
-
|
|
1944
|
-
return data;
|
|
2091
|
+
return parseJson(res);
|
|
1945
2092
|
}
|
|
1946
2093
|
async function pollDeviceCode(baseUrl, deviceCode) {
|
|
1947
2094
|
const url = `${baseUrl}/auth/cli/poll?device_code=${encodeURIComponent(deviceCode)}`;
|
|
1948
|
-
const res = await
|
|
2095
|
+
const res = await fetchWithTimeout(url);
|
|
1949
2096
|
if (!res.ok) {
|
|
1950
2097
|
const text = await res.text().catch(() => res.statusText);
|
|
1951
2098
|
throw new Error(`Poll request failed (${res.status}): ${text}`);
|
|
1952
2099
|
}
|
|
1953
|
-
|
|
1954
|
-
return data;
|
|
2100
|
+
return parseJson(res);
|
|
1955
2101
|
}
|
|
1956
2102
|
|
|
1957
2103
|
// src/commands/auth.ts
|
|
1958
|
-
var DEFAULT_BASE_URL = "https://app.autoposting.ai";
|
|
2104
|
+
var DEFAULT_BASE_URL = "https://app.autoposting.ai/api-proxy";
|
|
1959
2105
|
function resolveBaseUrl(globals) {
|
|
1960
2106
|
return globals.baseUrl ?? process.env.AUTOPOSTING_BASE_URL ?? DEFAULT_BASE_URL;
|
|
1961
2107
|
}
|
|
@@ -2129,14 +2275,16 @@ function createBillingCommand() {
|
|
|
2129
2275
|
printer.table(
|
|
2130
2276
|
[
|
|
2131
2277
|
{
|
|
2132
|
-
plan: status.
|
|
2133
|
-
status: status.status,
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2278
|
+
plan: status.planName,
|
|
2279
|
+
status: status.status ?? "\u2014",
|
|
2280
|
+
cycle: status.billingCycle ?? "\u2014",
|
|
2281
|
+
accounts: `${status.accountsUsed}/${status.accountLimit === -1 ? "\u221E" : status.accountLimit}`,
|
|
2282
|
+
credits: status.creditBalance,
|
|
2283
|
+
renews: status.currentPeriodEnd ?? "\u2014",
|
|
2284
|
+
"trial ends": status.trialEnd ?? "\u2014"
|
|
2137
2285
|
}
|
|
2138
2286
|
],
|
|
2139
|
-
["plan", "status", "
|
|
2287
|
+
["plan", "status", "cycle", "accounts", "credits", "renews", "trial ends"]
|
|
2140
2288
|
);
|
|
2141
2289
|
} catch (err) {
|
|
2142
2290
|
spinner.stop();
|
|
@@ -2156,19 +2304,20 @@ function createBillingCommand() {
|
|
|
2156
2304
|
printer.table(
|
|
2157
2305
|
[
|
|
2158
2306
|
{
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
remaining: credits.remaining
|
|
2307
|
+
balance: credits.balanceFormatted,
|
|
2308
|
+
"spent (30d)": credits.totalSpent30d
|
|
2162
2309
|
}
|
|
2163
2310
|
],
|
|
2164
|
-
["
|
|
2311
|
+
["balance", "spent (30d)"]
|
|
2165
2312
|
);
|
|
2166
|
-
if (credits.
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2313
|
+
if (credits.recentUsage.length > 0) {
|
|
2314
|
+
const usageRows = credits.recentUsage.map((e) => ({
|
|
2315
|
+
date: e.date,
|
|
2316
|
+
description: e.description,
|
|
2317
|
+
amount: e.amount,
|
|
2318
|
+
type: e.type
|
|
2170
2319
|
}));
|
|
2171
|
-
printer.table(
|
|
2320
|
+
printer.table(usageRows, ["date", "description", "amount", "type"]);
|
|
2172
2321
|
}
|
|
2173
2322
|
} catch (err) {
|
|
2174
2323
|
spinner.stop();
|
|
@@ -2324,7 +2473,7 @@ function createCarouselsCommand() {
|
|
|
2324
2473
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2325
2474
|
const list = await client.carousels.list();
|
|
2326
2475
|
spinner.stop();
|
|
2327
|
-
const rows = list.map((c) => ({
|
|
2476
|
+
const rows = list.items.map((c) => ({
|
|
2328
2477
|
id: c.id,
|
|
2329
2478
|
title: c.title ?? "\u2014",
|
|
2330
2479
|
slides: c.slides.length,
|
|
@@ -2450,7 +2599,7 @@ function createClipsCommand() {
|
|
|
2450
2599
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2451
2600
|
const list = await client.clips.list();
|
|
2452
2601
|
spinner.stop();
|
|
2453
|
-
const rows = list.map((c) => ({
|
|
2602
|
+
const rows = list.clips.map((c) => ({
|
|
2454
2603
|
id: c.id,
|
|
2455
2604
|
name: c.name,
|
|
2456
2605
|
status: c.status,
|
|
@@ -2507,12 +2656,12 @@ function createClipsCommand() {
|
|
|
2507
2656
|
try {
|
|
2508
2657
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2509
2658
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2510
|
-
const
|
|
2659
|
+
const { clipId } = await client.clips.importUrl({
|
|
2511
2660
|
url: opts.url,
|
|
2512
2661
|
...opts.name ? { name: opts.name } : {}
|
|
2513
2662
|
});
|
|
2514
2663
|
spinner.stop();
|
|
2515
|
-
printer.log(
|
|
2664
|
+
printer.log({ clipId });
|
|
2516
2665
|
} catch (err) {
|
|
2517
2666
|
spinner.stop();
|
|
2518
2667
|
printer.error(err);
|
|
@@ -2526,9 +2675,9 @@ function createClipsCommand() {
|
|
|
2526
2675
|
try {
|
|
2527
2676
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2528
2677
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2529
|
-
const
|
|
2678
|
+
const { jobIds, activeJobIds, reusedJobIds } = await client.clips.render(id);
|
|
2530
2679
|
spinner.stop();
|
|
2531
|
-
printer.log(
|
|
2680
|
+
printer.log({ jobIds, activeJobIds, reusedJobIds });
|
|
2532
2681
|
} catch (err) {
|
|
2533
2682
|
spinner.stop();
|
|
2534
2683
|
printer.error(err);
|
|
@@ -2561,8 +2710,34 @@ function createClipsCommand() {
|
|
|
2561
2710
|
// src/commands/posts.ts
|
|
2562
2711
|
var import_commander7 = require("commander");
|
|
2563
2712
|
init_dist();
|
|
2713
|
+
var VALID_PLATFORMS = ["x", "linkedin", "instagram", "threads", "youtube"];
|
|
2564
2714
|
function parsePlatforms(raw) {
|
|
2565
|
-
|
|
2715
|
+
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
2716
|
+
if (parts.length === 0) {
|
|
2717
|
+
throw new Error("No platforms provided. Pass a comma-separated list, e.g. --platforms x,linkedin");
|
|
2718
|
+
}
|
|
2719
|
+
const invalid = parts.filter((p) => !VALID_PLATFORMS.includes(p));
|
|
2720
|
+
if (invalid.length > 0) {
|
|
2721
|
+
throw new Error(
|
|
2722
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS.join(", ")}`
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2725
|
+
return parts;
|
|
2726
|
+
}
|
|
2727
|
+
function parsePositiveInt(value, flag) {
|
|
2728
|
+
const n = Number(value);
|
|
2729
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
2730
|
+
throw new Error(`${flag} must be a positive integer (received "${value}").`);
|
|
2731
|
+
}
|
|
2732
|
+
return n;
|
|
2733
|
+
}
|
|
2734
|
+
function validateScheduledAt(value) {
|
|
2735
|
+
if (Number.isNaN(Date.parse(value)) || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
|
|
2736
|
+
throw new Error(
|
|
2737
|
+
`--at must be a valid ISO 8601 datetime, e.g. 2026-06-30T14:00:00Z (received "${value}").`
|
|
2738
|
+
);
|
|
2739
|
+
}
|
|
2740
|
+
return value;
|
|
2566
2741
|
}
|
|
2567
2742
|
function resolveExitCode5(err) {
|
|
2568
2743
|
const attached = err.exitCode;
|
|
@@ -2581,11 +2756,11 @@ function createPostsCommand() {
|
|
|
2581
2756
|
const result = await client.posts.list({
|
|
2582
2757
|
brandSlug: opts.brand,
|
|
2583
2758
|
status: opts.status,
|
|
2584
|
-
limit:
|
|
2585
|
-
page:
|
|
2759
|
+
limit: parsePositiveInt(opts.limit, "--limit"),
|
|
2760
|
+
page: parsePositiveInt(opts.page, "--page")
|
|
2586
2761
|
});
|
|
2587
2762
|
spinner.stop();
|
|
2588
|
-
const rows = result.
|
|
2763
|
+
const rows = result.map((p) => ({
|
|
2589
2764
|
id: p.id,
|
|
2590
2765
|
brand: p.brandSlug,
|
|
2591
2766
|
status: p.status,
|
|
@@ -2616,7 +2791,10 @@ function createPostsCommand() {
|
|
|
2616
2791
|
process.exit(resolveExitCode5(err));
|
|
2617
2792
|
}
|
|
2618
2793
|
});
|
|
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)").
|
|
2794
|
+
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
|
+
"--thread <text...>",
|
|
2796
|
+
"Additional posts appended after --text to form a thread (X/Threads only, max 25)"
|
|
2797
|
+
).action(
|
|
2620
2798
|
async (opts, cmd) => {
|
|
2621
2799
|
const globals = cmd.optsWithGlobals();
|
|
2622
2800
|
const printer = createPrinter(globals);
|
|
@@ -2628,7 +2806,8 @@ function createPostsCommand() {
|
|
|
2628
2806
|
brandSlug: opts.brand,
|
|
2629
2807
|
text: opts.text,
|
|
2630
2808
|
platforms: parsePlatforms(opts.platforms),
|
|
2631
|
-
...opts.at ? { scheduledAt: opts.at } : {}
|
|
2809
|
+
...opts.at ? { scheduledAt: validateScheduledAt(opts.at) } : {},
|
|
2810
|
+
...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {}
|
|
2632
2811
|
});
|
|
2633
2812
|
spinner.stop();
|
|
2634
2813
|
printer.log(post);
|
|
@@ -2650,7 +2829,7 @@ function createPostsCommand() {
|
|
|
2650
2829
|
const post = await client.posts.update(id, {
|
|
2651
2830
|
...opts.text ? { text: opts.text } : {},
|
|
2652
2831
|
...opts.platforms ? { platforms: parsePlatforms(opts.platforms) } : {},
|
|
2653
|
-
...opts.at ? { scheduledAt: opts.at } : {}
|
|
2832
|
+
...opts.at ? { scheduledAt: validateScheduledAt(opts.at) } : {}
|
|
2654
2833
|
});
|
|
2655
2834
|
spinner.stop();
|
|
2656
2835
|
printer.log(post);
|
|
@@ -2704,7 +2883,7 @@ function createPostsCommand() {
|
|
|
2704
2883
|
try {
|
|
2705
2884
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2706
2885
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2707
|
-
const post = await client.posts.schedule(id, opts.at);
|
|
2886
|
+
const post = await client.posts.schedule(id, validateScheduledAt(opts.at));
|
|
2708
2887
|
spinner.stop();
|
|
2709
2888
|
printer.log(post);
|
|
2710
2889
|
} catch (err) {
|
|
@@ -2855,14 +3034,15 @@ function createKbCommand() {
|
|
|
2855
3034
|
try {
|
|
2856
3035
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
2857
3036
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2858
|
-
const
|
|
3037
|
+
const res = await client.kb.search(id, opts.query);
|
|
2859
3038
|
spinner.stop();
|
|
2860
|
-
const rows = results.map((r) => ({
|
|
2861
|
-
|
|
2862
|
-
score: r.score.toFixed(3),
|
|
3039
|
+
const rows = res.results.map((r) => ({
|
|
3040
|
+
kind: r.kind,
|
|
3041
|
+
score: r.score != null ? r.score.toFixed(3) : "\u2014",
|
|
3042
|
+
uri: r.uri ?? "\u2014",
|
|
2863
3043
|
content: r.content.length > 80 ? `${r.content.slice(0, 77)}\u2026` : r.content
|
|
2864
3044
|
}));
|
|
2865
|
-
printer.table(rows, ["
|
|
3045
|
+
printer.table(rows, ["kind", "score", "uri", "content"]);
|
|
2866
3046
|
} catch (err) {
|
|
2867
3047
|
spinner.stop();
|
|
2868
3048
|
printer.error(err);
|
|
@@ -2919,6 +3099,23 @@ function resolveExitCode7(err) {
|
|
|
2919
3099
|
if (typeof attached === "number") return attached;
|
|
2920
3100
|
return exitCodeFromError(err);
|
|
2921
3101
|
}
|
|
3102
|
+
var VALID_IDEA_PLATFORMS = ["twitter", "linkedin", "instagram", "youtube", "threads"];
|
|
3103
|
+
function parseIdeaPlatforms(raw) {
|
|
3104
|
+
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3105
|
+
if (parts.length === 0) {
|
|
3106
|
+
throw new Error("No platforms provided. e.g. --platforms twitter,linkedin");
|
|
3107
|
+
}
|
|
3108
|
+
if (parts.length > 5) {
|
|
3109
|
+
throw new Error("At most 5 platforms allowed per enrichment request.");
|
|
3110
|
+
}
|
|
3111
|
+
const invalid = parts.filter((p) => !VALID_IDEA_PLATFORMS.includes(p));
|
|
3112
|
+
if (invalid.length > 0) {
|
|
3113
|
+
throw new Error(
|
|
3114
|
+
`Unsupported platform(s): ${invalid.join(", ")}. Valid: ${VALID_IDEA_PLATFORMS.join(", ")}`
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
return parts.map((platform) => ({ platform }));
|
|
3118
|
+
}
|
|
2922
3119
|
function createIdeasCommand() {
|
|
2923
3120
|
const ideas = new import_commander9.Command("ideas").description("Manage content ideas");
|
|
2924
3121
|
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,13 +3131,13 @@ function createIdeasCommand() {
|
|
|
2934
3131
|
count: parseInt(opts.count, 10)
|
|
2935
3132
|
});
|
|
2936
3133
|
spinner.stop();
|
|
2937
|
-
const rows = result.map((i) => ({
|
|
2938
|
-
id: i.id,
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3134
|
+
const rows = (result.ideas ?? []).map((i) => ({
|
|
3135
|
+
id: i.id ?? "\u2014",
|
|
3136
|
+
platform: i.targetPlatform,
|
|
3137
|
+
score: i.viralityScore,
|
|
3138
|
+
title: i.title.length > 80 ? `${i.title.slice(0, 77)}\u2026` : i.title
|
|
2942
3139
|
}));
|
|
2943
|
-
printer.table(rows, ["id", "
|
|
3140
|
+
printer.table(rows, ["id", "platform", "score", "title"]);
|
|
2944
3141
|
} catch (err) {
|
|
2945
3142
|
spinner.stop();
|
|
2946
3143
|
printer.error(err);
|
|
@@ -2956,36 +3153,43 @@ function createIdeasCommand() {
|
|
|
2956
3153
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
2957
3154
|
const list = await client.ideas.list();
|
|
2958
3155
|
spinner.stop();
|
|
2959
|
-
const rows = list.map((i) => ({
|
|
3156
|
+
const rows = list.items.map((i) => ({
|
|
2960
3157
|
id: i.id,
|
|
2961
|
-
topic: i.topic
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3158
|
+
topic: i.topic || "\u2014",
|
|
3159
|
+
platform: i.targetPlatform,
|
|
3160
|
+
score: i.viralityScore,
|
|
3161
|
+
status: i.status,
|
|
3162
|
+
title: i.title.length > 60 ? `${i.title.slice(0, 57)}\u2026` : i.title
|
|
2965
3163
|
}));
|
|
2966
|
-
printer.table(rows, ["id", "topic", "
|
|
3164
|
+
printer.table(rows, ["id", "topic", "platform", "score", "status", "title"]);
|
|
2967
3165
|
} catch (err) {
|
|
2968
3166
|
spinner.stop();
|
|
2969
3167
|
printer.error(err);
|
|
2970
3168
|
process.exit(resolveExitCode7(err));
|
|
2971
3169
|
}
|
|
2972
3170
|
});
|
|
2973
|
-
ideas.command("enrich
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3171
|
+
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(
|
|
3172
|
+
async (opts, cmd) => {
|
|
3173
|
+
const globals = cmd.optsWithGlobals();
|
|
3174
|
+
const printer = createPrinter(globals);
|
|
3175
|
+
const spinner = printer.spinner("Queuing enrichment\u2026");
|
|
3176
|
+
try {
|
|
3177
|
+
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3178
|
+
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3179
|
+
const { jobId } = await client.ideas.enrich({
|
|
3180
|
+
idea: { title: opts.title, hook: opts.hook, angle: opts.angle },
|
|
3181
|
+
platforms: parseIdeaPlatforms(opts.platforms),
|
|
3182
|
+
...opts.kb ? { kbId: opts.kb } : {}
|
|
3183
|
+
});
|
|
3184
|
+
spinner.stop();
|
|
3185
|
+
printer.log(`Enrichment queued. Job ID: ${jobId}`);
|
|
3186
|
+
} catch (err) {
|
|
3187
|
+
spinner.stop();
|
|
3188
|
+
printer.error(err);
|
|
3189
|
+
process.exit(resolveExitCode7(err));
|
|
3190
|
+
}
|
|
2987
3191
|
}
|
|
2988
|
-
|
|
3192
|
+
);
|
|
2989
3193
|
ideas.command("delete <id>").description("Delete an idea (requires --force)").option("--force", "Confirm deletion without interactive prompt").action(async (id, opts, cmd) => {
|
|
2990
3194
|
const globals = cmd.optsWithGlobals();
|
|
2991
3195
|
const printer = createPrinter(globals);
|
|
@@ -3028,18 +3232,25 @@ function createUsageCommand() {
|
|
|
3028
3232
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3029
3233
|
const summary = await client.usage.summary();
|
|
3030
3234
|
spinner.stop();
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3235
|
+
printer.log(`Period: ${summary.range.from.slice(0, 10)} \u2192 ${summary.range.to.slice(0, 10)}`);
|
|
3236
|
+
printer.table(
|
|
3237
|
+
[
|
|
3238
|
+
{
|
|
3239
|
+
posts: summary.posts.total,
|
|
3240
|
+
published: summary.posts.published,
|
|
3241
|
+
agents: summary.agents.total,
|
|
3242
|
+
"active agents": summary.agents.active,
|
|
3243
|
+
"ai cost (usd)": summary.ai.totalCostUsd,
|
|
3244
|
+
"ai requests": summary.ai.requests
|
|
3245
|
+
}
|
|
3246
|
+
],
|
|
3247
|
+
["posts", "published", "agents", "active agents", "ai cost (usd)", "ai requests"]
|
|
3248
|
+
);
|
|
3249
|
+
const sourceRows = Object.entries(summary.posts.bySource).map(([source, count]) => ({
|
|
3250
|
+
source,
|
|
3251
|
+
posts: count
|
|
3036
3252
|
}));
|
|
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
|
-
}
|
|
3253
|
+
printer.table(sourceRows, ["source", "posts"]);
|
|
3043
3254
|
} catch (err) {
|
|
3044
3255
|
spinner.stop();
|
|
3045
3256
|
printer.error(err);
|
|
@@ -3169,9 +3380,11 @@ function createWebhooksCommand() {
|
|
|
3169
3380
|
try {
|
|
3170
3381
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3171
3382
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3172
|
-
await client.webhooks.test(id);
|
|
3383
|
+
const result = await client.webhooks.test(id);
|
|
3173
3384
|
spinner.stop();
|
|
3174
|
-
printer.log(
|
|
3385
|
+
printer.log(
|
|
3386
|
+
`Test event sent to webhook "${id}" \u2014 delivered: ${result.delivered ? "yes" : "no"} (HTTP ${result.httpStatus}).`
|
|
3387
|
+
);
|
|
3175
3388
|
} catch (err) {
|
|
3176
3389
|
spinner.stop();
|
|
3177
3390
|
printer.error(err);
|
|
@@ -3198,13 +3411,13 @@ function createWorkspacesCommand() {
|
|
|
3198
3411
|
try {
|
|
3199
3412
|
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3200
3413
|
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3201
|
-
const
|
|
3414
|
+
const res = await client.workspaces.list();
|
|
3202
3415
|
spinner.stop();
|
|
3203
|
-
const rows =
|
|
3416
|
+
const rows = res.organizations.map((w) => ({
|
|
3204
3417
|
id: w.id,
|
|
3205
3418
|
name: w.name,
|
|
3206
3419
|
slug: w.slug,
|
|
3207
|
-
active: w.
|
|
3420
|
+
active: w.id === res.activeOrgId ? "yes" : "no",
|
|
3208
3421
|
createdAt: w.createdAt
|
|
3209
3422
|
}));
|
|
3210
3423
|
printer.table(rows, ["id", "name", "slug", "active", "createdAt"]);
|
|
@@ -3238,7 +3451,7 @@ function createWorkspacesCommand() {
|
|
|
3238
3451
|
var import_commander13 = require("commander");
|
|
3239
3452
|
init_dist();
|
|
3240
3453
|
var import_node_fs2 = __toESM(require("fs"));
|
|
3241
|
-
var API_HEALTH_URL = "https://app.autoposting.ai/api/health";
|
|
3454
|
+
var API_HEALTH_URL = "https://app.autoposting.ai/api-proxy/health";
|
|
3242
3455
|
async function runChecks() {
|
|
3243
3456
|
const results = [];
|
|
3244
3457
|
results.push({ name: "CLI Version", status: "pass", value: VERSION });
|
|
@@ -3251,7 +3464,7 @@ async function runChecks() {
|
|
|
3251
3464
|
});
|
|
3252
3465
|
try {
|
|
3253
3466
|
resolveAuth();
|
|
3254
|
-
results.push({ name: "Auth", status: "pass", value: "
|
|
3467
|
+
results.push({ name: "Auth", status: "pass", value: "credentials configured" });
|
|
3255
3468
|
} catch {
|
|
3256
3469
|
results.push({ name: "Auth", status: "fail", value: "not configured" });
|
|
3257
3470
|
}
|
|
@@ -3309,39 +3522,55 @@ function createDoctorCommand() {
|
|
|
3309
3522
|
|
|
3310
3523
|
// src/commands/whoami.ts
|
|
3311
3524
|
var import_commander14 = require("commander");
|
|
3525
|
+
init_dist();
|
|
3312
3526
|
function createWhoamiCommand() {
|
|
3313
|
-
return new import_commander14.Command("whoami").description("Show current
|
|
3527
|
+
return new import_commander14.Command("whoami").description("Show the current identity and validate the key against the server").action(async (_opts, cmd) => {
|
|
3528
|
+
const globals = cmd.optsWithGlobals();
|
|
3529
|
+
let cred;
|
|
3314
3530
|
try {
|
|
3315
|
-
|
|
3316
|
-
const cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3317
|
-
const masked = `${cred.apiKey.slice(0, 8)}${"*".repeat(Math.max(0, cred.apiKey.length - 8))}`;
|
|
3318
|
-
if (globals.json) {
|
|
3319
|
-
const out = { source: cred.source, apiKey: masked };
|
|
3320
|
-
if (cred.source === "stored") {
|
|
3321
|
-
const creds = readCredentials();
|
|
3322
|
-
const profileName = creds?.activeProfile ?? "unknown";
|
|
3323
|
-
const profile = creds?.profiles[profileName];
|
|
3324
|
-
out.profile = profileName;
|
|
3325
|
-
if (profile?.workspace) out.workspace = profile.workspace;
|
|
3326
|
-
if (profile?.email) out.email = profile.email;
|
|
3327
|
-
}
|
|
3328
|
-
console.log(JSON.stringify(out, null, 2));
|
|
3329
|
-
return;
|
|
3330
|
-
}
|
|
3331
|
-
console.log(`Source: ${cred.source}`);
|
|
3332
|
-
if (cred.source === "stored") {
|
|
3333
|
-
const creds = readCredentials();
|
|
3334
|
-
const profileName = creds?.activeProfile ?? "unknown";
|
|
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}`);
|
|
3339
|
-
}
|
|
3340
|
-
console.log(`API key: ${masked}`);
|
|
3531
|
+
cred = resolveAuth({ apiKey: globals.apiKey });
|
|
3341
3532
|
} catch (err) {
|
|
3342
3533
|
console.error(`Error: ${err.message}`);
|
|
3343
|
-
process.
|
|
3534
|
+
process.exitCode = exitCodeFromError(err);
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
const masked = `${cred.apiKey.slice(0, 8)}${"*".repeat(Math.max(0, cred.apiKey.length - 8))}`;
|
|
3538
|
+
let validity = "unverified";
|
|
3539
|
+
try {
|
|
3540
|
+
const client = new Autoposting({ apiKey: cred.apiKey });
|
|
3541
|
+
await client.usage.summary();
|
|
3542
|
+
validity = "valid";
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
const code = exitCodeFromError(err);
|
|
3545
|
+
validity = code === EXIT_CODES.AUTH_ERROR || code === EXIT_CODES.SCOPE_ERROR ? "rejected" : "unverified";
|
|
3546
|
+
}
|
|
3547
|
+
const exitCode = validity === "rejected" ? EXIT_CODES.AUTH_ERROR : 0;
|
|
3548
|
+
const creds = cred.source === "stored" ? readCredentials() : void 0;
|
|
3549
|
+
const profileName = creds?.activeProfile ?? "unknown";
|
|
3550
|
+
const email = creds?.profiles[profileName]?.email;
|
|
3551
|
+
if (globals.json) {
|
|
3552
|
+
const out = {
|
|
3553
|
+
source: cred.source,
|
|
3554
|
+
apiKey: masked,
|
|
3555
|
+
valid: validity === "valid",
|
|
3556
|
+
validity
|
|
3557
|
+
};
|
|
3558
|
+
if (cred.source === "stored") {
|
|
3559
|
+
out.profile = profileName;
|
|
3560
|
+
if (email) out.email = email;
|
|
3561
|
+
}
|
|
3562
|
+
console.log(JSON.stringify(out, null, 2));
|
|
3563
|
+
process.exitCode = exitCode;
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
console.log(`Source: ${cred.source}`);
|
|
3567
|
+
if (cred.source === "stored") {
|
|
3568
|
+
console.log(`Profile: ${profileName}`);
|
|
3569
|
+
if (email) console.log(`Email: ${email}`);
|
|
3344
3570
|
}
|
|
3571
|
+
const tag = validity === "valid" ? "\u2713 valid" : validity === "rejected" ? "\u2717 rejected by server" : "\u26A0 unverified (could not reach server)";
|
|
3572
|
+
console.log(`API key: ${masked} ${tag}`);
|
|
3573
|
+
process.exitCode = exitCode;
|
|
3345
3574
|
});
|
|
3346
3575
|
}
|
|
3347
3576
|
|