@reepl/cli 0.1.0

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.
@@ -0,0 +1,719 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/core/constants.ts
8
+ var REEPL_API_BASE_URL = "https://api.reepl.io/v1";
9
+ var REEPL_PUBLIC_API_BASE_URL = "https://4n7fgax35e.execute-api.eu-north-1.amazonaws.com/v1";
10
+ var COGNITO_DOMAIN = "https://auth.reepl.io";
11
+ var COGNITO_WEB_APP_CLIENT_ID = "7np7fkkg920i4pqvgsmbct7bnn";
12
+ var REEPL_WEB_APP_URL = "https://app.reepl.io";
13
+ var MCP_CONNECT_PATH = "/mcp-connect";
14
+ var TOKEN_REFRESH_BUFFER_MS = 10 * 60 * 1e3;
15
+
16
+ // src/core/errors.ts
17
+ var EXIT_CODES = {
18
+ UNAUTHENTICATED: 4,
19
+ REFRESH_TOKEN_INVALID: 4,
20
+ CONFIG: 4,
21
+ PLAN_REQUIRED: 5,
22
+ PREMIUM_REQUIRED: 5,
23
+ RECONNECT_REQUIRED: 6,
24
+ MISSING_DM_SCOPES: 6,
25
+ GEMINI_NOT_LINKED: 6,
26
+ RATE_LIMITED: 7,
27
+ NOT_FOUND: 8,
28
+ VALIDATION: 9,
29
+ VERSION_CONFLICT: 9,
30
+ API_ERROR: 1,
31
+ NETWORK: 2
32
+ };
33
+ var ReeplError = class extends Error {
34
+ code;
35
+ status;
36
+ details;
37
+ constructor(code, message, opts = {}) {
38
+ super(message, opts.cause !== void 0 ? { cause: opts.cause } : void 0);
39
+ this.name = "ReeplError";
40
+ this.code = code;
41
+ this.status = opts.status;
42
+ this.details = opts.details;
43
+ }
44
+ get exitCode() {
45
+ return EXIT_CODES[this.code] ?? 1;
46
+ }
47
+ toJSON() {
48
+ return { code: this.code, message: this.message, ...this.status ? { status: this.status } : {} };
49
+ }
50
+ };
51
+ function parseApiError(status, raw) {
52
+ let body = raw;
53
+ try {
54
+ body = JSON.parse(raw);
55
+ } catch {
56
+ }
57
+ const isCodeToken = (v) => typeof v === "string" && /^[a-z0-9_]+$/i.test(v.trim());
58
+ const backendCode = typeof body === "object" && body ? body.code || body.errorCode || (isCodeToken(body.error) ? body.error : void 0) : void 0;
59
+ const message = typeof body === "object" && body && (body.message || body.error) || typeof body === "string" && body || `Request failed with status ${status}`;
60
+ const codeStr = String(backendCode ?? "").toUpperCase();
61
+ if (codeStr === "UPGRADE_REQUIRED" || codeStr === "PLAN_REQUIRED") {
62
+ return new ReeplError("PLAN_REQUIRED", message || "This action requires a paid Reepl plan.", { status, details: body });
63
+ }
64
+ if (codeStr.includes("PREMIUM") || codeStr === "REQUIRES_PREMIUM") {
65
+ return new ReeplError("PREMIUM_REQUIRED", message || "This action requires a Reepl Premium plan.", { status, details: body });
66
+ }
67
+ if (codeStr === "MISSING_DM_SCOPES") {
68
+ return new ReeplError("MISSING_DM_SCOPES", message || "Reconnect X with DM permissions to continue.", { status, details: body });
69
+ }
70
+ if (codeStr === "GEMINI_NOT_LINKED") {
71
+ return new ReeplError("GEMINI_NOT_LINKED", message || "Link your Gemini API key in Reepl settings.", { status, details: body });
72
+ }
73
+ if (codeStr.includes("RECONNECT") || codeStr.includes("NOT_CONNECTED")) {
74
+ return new ReeplError("RECONNECT_REQUIRED", message || "Reconnect the integration to continue.", { status, details: body });
75
+ }
76
+ if (codeStr === "VERSION_CONFLICT") {
77
+ return new ReeplError("VERSION_CONFLICT", message || "The record changed since you fetched it. Refetch and retry.", { status, details: body });
78
+ }
79
+ if (status === 401) return new ReeplError("UNAUTHENTICATED", message || "Not authenticated. Run `reepl login`.", { status, details: body });
80
+ if (status === 402) return new ReeplError("PLAN_REQUIRED", message, { status, details: body });
81
+ if (status === 403) {
82
+ if (/premium/i.test(message)) return new ReeplError("PREMIUM_REQUIRED", message, { status, details: body });
83
+ return new ReeplError("PLAN_REQUIRED", message, { status, details: body });
84
+ }
85
+ if (status === 404) return new ReeplError("NOT_FOUND", message, { status, details: body });
86
+ if (status === 409) return new ReeplError("VERSION_CONFLICT", message, { status, details: body });
87
+ if (status === 422 || status === 400) return new ReeplError("VALIDATION", message, { status, details: body });
88
+ if (status === 429) return new ReeplError("RATE_LIMITED", message || "Rate limited. Try again shortly.", { status, details: body });
89
+ return new ReeplError("API_ERROR", message, { status, details: body });
90
+ }
91
+
92
+ // src/core/client.ts
93
+ var METHODS_WITH_BODY = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
94
+ var ReeplClient = class {
95
+ getToken;
96
+ workspaceId;
97
+ apiBaseUrl;
98
+ publicBaseUrl;
99
+ fetchImpl;
100
+ constructor(opts) {
101
+ this.getToken = opts.getToken;
102
+ this.workspaceId = opts.workspaceId ?? null;
103
+ this.apiBaseUrl = opts.apiBaseUrl ?? REEPL_API_BASE_URL;
104
+ this.publicBaseUrl = opts.publicBaseUrl ?? REEPL_PUBLIC_API_BASE_URL;
105
+ this.fetchImpl = opts.fetchImpl ?? fetch;
106
+ }
107
+ async request(opts) {
108
+ const auth = opts.auth ?? true;
109
+ const base = opts.base === "public" ? this.publicBaseUrl : this.apiBaseUrl;
110
+ const workspace = opts.workspace === void 0 ? this.workspaceId : opts.workspace;
111
+ const hasBody = METHODS_WITH_BODY.has(opts.method);
112
+ const query = {
113
+ ...opts.query ?? {}
114
+ };
115
+ let body = opts.body;
116
+ if (workspace) {
117
+ if (hasBody) {
118
+ body = { ...body, workspace_id: workspace };
119
+ } else {
120
+ query.workspace_id = workspace;
121
+ }
122
+ }
123
+ const url = new URL(base + opts.path);
124
+ for (const [k, v] of Object.entries(query)) {
125
+ if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
126
+ }
127
+ const headers = {};
128
+ if (hasBody && body !== void 0) headers["Content-Type"] = "application/json";
129
+ if (auth) headers["Authorization"] = `Bearer ${await this.getToken()}`;
130
+ let response;
131
+ try {
132
+ response = await this.fetchImpl(url.toString(), {
133
+ method: opts.method,
134
+ headers,
135
+ body: hasBody && body !== void 0 ? JSON.stringify(body) : void 0
136
+ });
137
+ } catch (cause) {
138
+ throw new ReeplError("NETWORK", `Could not reach ${url.host}.`, { cause });
139
+ }
140
+ const text = await response.text();
141
+ if (!response.ok) throw parseApiError(response.status, text);
142
+ if (!text) return void 0;
143
+ try {
144
+ return JSON.parse(text);
145
+ } catch {
146
+ return text;
147
+ }
148
+ }
149
+ // Convenience verbs.
150
+ get(path, opts = {}) {
151
+ return this.request({ method: "GET", path, ...opts });
152
+ }
153
+ post(path, opts = {}) {
154
+ return this.request({ method: "POST", path, ...opts });
155
+ }
156
+ put(path, opts = {}) {
157
+ return this.request({ method: "PUT", path, ...opts });
158
+ }
159
+ delete(path, opts = {}) {
160
+ return this.request({ method: "DELETE", path, ...opts });
161
+ }
162
+ };
163
+
164
+ // src/core/auth/cognito.ts
165
+ function decodeJwt(token) {
166
+ const parts = token.split(".");
167
+ if (parts.length < 2) return null;
168
+ try {
169
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
170
+ const json = Buffer.from(payload, "base64").toString("utf8");
171
+ return JSON.parse(json);
172
+ } catch {
173
+ return null;
174
+ }
175
+ }
176
+ function jwtExpiryMs(token) {
177
+ const claims = decodeJwt(token);
178
+ const exp = claims?.exp;
179
+ return typeof exp === "number" ? exp * 1e3 : null;
180
+ }
181
+ async function refreshIdToken(refreshToken, opts = {}) {
182
+ const clientId = opts.clientId ?? COGNITO_WEB_APP_CLIENT_ID;
183
+ const domain = opts.cognitoDomain ?? COGNITO_DOMAIN;
184
+ const doFetch = opts.fetchImpl ?? fetch;
185
+ const body = new URLSearchParams({
186
+ grant_type: "refresh_token",
187
+ client_id: clientId,
188
+ refresh_token: refreshToken
189
+ });
190
+ let response;
191
+ try {
192
+ response = await doFetch(`${domain}/oauth2/token`, {
193
+ method: "POST",
194
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
195
+ body: body.toString()
196
+ });
197
+ } catch (cause) {
198
+ throw new ReeplError("NETWORK", "Could not reach Cognito to refresh the session.", { cause });
199
+ }
200
+ const text = await response.text();
201
+ if (!response.ok) {
202
+ if (/invalid_grant/i.test(text)) {
203
+ throw new ReeplError("REFRESH_TOKEN_INVALID", "Your session expired. Run `reepl login` again.", {
204
+ status: response.status
205
+ });
206
+ }
207
+ throw new ReeplError("UNAUTHENTICATED", `Token refresh failed (${response.status}).`, {
208
+ status: response.status,
209
+ details: text
210
+ });
211
+ }
212
+ let json;
213
+ try {
214
+ json = JSON.parse(text);
215
+ } catch {
216
+ throw new ReeplError("UNAUTHENTICATED", "Malformed token response from Cognito.");
217
+ }
218
+ if (!json.id_token) {
219
+ throw new ReeplError("UNAUTHENTICATED", "Cognito response did not include an id_token.");
220
+ }
221
+ return {
222
+ idToken: json.id_token,
223
+ accessToken: json.access_token,
224
+ expiresIn: typeof json.expires_in === "number" ? json.expires_in : 3600
225
+ };
226
+ }
227
+
228
+ // src/core/auth/tokenManager.ts
229
+ var TokenManager = class {
230
+ store;
231
+ opts;
232
+ inflight = null;
233
+ constructor(store, opts = {}) {
234
+ this.store = store;
235
+ this.opts = opts;
236
+ }
237
+ now() {
238
+ return this.opts.now ? this.opts.now() : Date.now();
239
+ }
240
+ /** Get a valid ID token or throw UNAUTHENTICATED if there are no stored credentials. */
241
+ async getIdToken() {
242
+ if (this.inflight) return this.inflight;
243
+ this.inflight = this.resolveToken().finally(() => {
244
+ this.inflight = null;
245
+ });
246
+ return this.inflight;
247
+ }
248
+ async resolveToken() {
249
+ const tokens = await this.store.load();
250
+ if (!tokens) {
251
+ throw new ReeplError("UNAUTHENTICATED", "Not authenticated. Run `reepl login` first.");
252
+ }
253
+ const buffer = this.opts.bufferMs ?? TOKEN_REFRESH_BUFFER_MS;
254
+ if (tokens.expiresAt - this.now() > buffer) {
255
+ return tokens.idToken;
256
+ }
257
+ const refreshed = await refreshIdToken(tokens.refreshToken, {
258
+ clientId: this.opts.clientId,
259
+ cognitoDomain: this.opts.cognitoDomain,
260
+ fetchImpl: this.opts.fetchImpl
261
+ });
262
+ const expiresAt = jwtExpiryMs(refreshed.idToken) ?? this.now() + refreshed.expiresIn * 1e3;
263
+ const next = {
264
+ idToken: refreshed.idToken,
265
+ // Cognito refresh does not return a new refresh token — keep the existing one.
266
+ refreshToken: tokens.refreshToken,
267
+ accessToken: refreshed.accessToken ?? tokens.accessToken,
268
+ expiresAt
269
+ };
270
+ await this.store.save(next);
271
+ return next.idToken;
272
+ }
273
+ };
274
+
275
+ // src/core/endpoints/profile.ts
276
+ var profile_exports = {};
277
+ __export(profile_exports, {
278
+ getProfile: () => getProfile,
279
+ hasActivePlan: () => hasActivePlan
280
+ });
281
+ function getProfile(client) {
282
+ return client.get("/account/profile");
283
+ }
284
+ function hasActivePlan(profile) {
285
+ return Boolean(profile.subscription_status?.hasActivePurchase);
286
+ }
287
+
288
+ // src/core/endpoints/workspaces.ts
289
+ var workspaces_exports = {};
290
+ __export(workspaces_exports, {
291
+ getDefaultWorkspaceId: () => getDefaultWorkspaceId,
292
+ listWorkspaces: () => listWorkspaces
293
+ });
294
+ async function listWorkspaces(client) {
295
+ const data = await client.get("/organizations");
296
+ const defaultId = data.default_organisationID;
297
+ const orgs = Array.isArray(data.organizations) ? data.organizations : [];
298
+ return orgs.map((o) => {
299
+ const id = String(o.organisationID ?? o.organization_id ?? o.organizationID ?? o.id ?? "");
300
+ return {
301
+ ...o,
302
+ id,
303
+ name: o.name ?? o.organisationName ?? o.workspaceName,
304
+ isDefault: Boolean(defaultId && id === defaultId)
305
+ };
306
+ });
307
+ }
308
+ async function getDefaultWorkspaceId(client) {
309
+ const data = await client.get("/organizations");
310
+ return data.default_organisationID ?? null;
311
+ }
312
+
313
+ // src/core/endpoints/linkedin.ts
314
+ var linkedin_exports = {};
315
+ __export(linkedin_exports, {
316
+ addComment: () => addComment,
317
+ createDraft: () => createDraft,
318
+ deleteDraft: () => deleteDraft,
319
+ deletePost: () => deletePost,
320
+ listDrafts: () => listDrafts,
321
+ listPosts: () => listPosts,
322
+ publishNow: () => publishNow,
323
+ schedulePost: () => schedulePost,
324
+ updateDraft: () => updateDraft,
325
+ updateScheduledPost: () => updateScheduledPost
326
+ });
327
+ function createDraft(client, input) {
328
+ return client.post("/post/drafts", {
329
+ body: { ...input, generatedVia: "CLI" }
330
+ });
331
+ }
332
+ function listDrafts(client, opts = {}) {
333
+ return client.get("/post/drafts", {
334
+ query: { limit: opts.limit, search: opts.search, sortOrder: opts.sortOrder }
335
+ });
336
+ }
337
+ function updateDraft(client, draftId, patch) {
338
+ return client.put("/post/drafts", { body: { draft_id: draftId, ...patch } });
339
+ }
340
+ function deleteDraft(client, draftId) {
341
+ return client.delete("/post/drafts", { query: { draftId } });
342
+ }
343
+ function publishNow(client, input) {
344
+ return client.post("/linkedin/posts", { body: { ...input, generatedVia: "CLI" } });
345
+ }
346
+ function schedulePost(client, input) {
347
+ return client.post("/linkedin/posts/schedule", {
348
+ body: { ...input, generatedVia: "CLI" }
349
+ });
350
+ }
351
+ function listPosts(client, opts = {}) {
352
+ return client.get("/linkedin/posts", {
353
+ query: {
354
+ status: opts.status,
355
+ limit: opts.limit,
356
+ search: opts.search,
357
+ nextToken: opts.nextToken,
358
+ userId: opts.userId
359
+ }
360
+ });
361
+ }
362
+ function updateScheduledPost(client, postId, patch) {
363
+ return client.put(`/linkedin/posts/${encodeURIComponent(postId)}/schedule`, { body: patch });
364
+ }
365
+ function deletePost(client, postId) {
366
+ return client.delete(`/linkedin/posts/${encodeURIComponent(postId)}`);
367
+ }
368
+ function addComment(client, postId, commentText) {
369
+ return client.post(`/linkedin/posts/${encodeURIComponent(postId)}/comments`, {
370
+ body: { commentText }
371
+ });
372
+ }
373
+
374
+ // src/core/endpoints/twitter.ts
375
+ var twitter_exports = {};
376
+ __export(twitter_exports, {
377
+ createDraft: () => createDraft2,
378
+ createPost: () => createPost,
379
+ deleteDraft: () => deleteDraft2,
380
+ deletePost: () => deletePost2,
381
+ listDrafts: () => listDrafts2,
382
+ listPosts: () => listPosts2,
383
+ reply: () => reply,
384
+ updatePost: () => updatePost
385
+ });
386
+ function createPost(client, input) {
387
+ return client.post("/twitter/posts", { body: { ...input, generatedVia: "CLI" } });
388
+ }
389
+ function listPosts2(client, opts = {}) {
390
+ return client.get("/twitter/posts", {
391
+ query: { status: opts.status, limit: opts.limit, search: opts.search, nextToken: opts.nextToken }
392
+ });
393
+ }
394
+ function updatePost(client, postId, patch) {
395
+ return client.put(`/twitter/posts/${encodeURIComponent(postId)}`, { body: patch });
396
+ }
397
+ function deletePost2(client, postId) {
398
+ return client.delete(`/twitter/posts/${encodeURIComponent(postId)}`);
399
+ }
400
+ function createDraft2(client, input) {
401
+ return client.post("/twitter/drafts", { body: { ...input, generatedVia: "CLI" } });
402
+ }
403
+ function listDrafts2(client, opts = {}) {
404
+ return client.get("/twitter/drafts", { query: { limit: opts.limit, search: opts.search } });
405
+ }
406
+ function deleteDraft2(client, draftId) {
407
+ return client.delete("/twitter/drafts", { query: { draftId } });
408
+ }
409
+ function reply(client, tweetId2, text) {
410
+ return client.post("/twitter/engagement/reply", { body: { tweetId: tweetId2, text } });
411
+ }
412
+
413
+ // src/core/endpoints/calendar.ts
414
+ var calendar_exports = {};
415
+ __export(calendar_exports, {
416
+ getSchedule: () => getSchedule
417
+ });
418
+ var TOLERATED_CODES = /* @__PURE__ */ new Set([
419
+ "PLAN_REQUIRED",
420
+ "PREMIUM_REQUIRED",
421
+ "RECONNECT_REQUIRED",
422
+ "MISSING_DM_SCOPES",
423
+ "NOT_FOUND"
424
+ ]);
425
+ function tolerate(err) {
426
+ if (err instanceof ReeplError && TOLERATED_CODES.has(err.code)) return [];
427
+ throw err;
428
+ }
429
+ function coercePosts(raw) {
430
+ if (Array.isArray(raw)) return raw;
431
+ if (raw && typeof raw === "object") {
432
+ const obj = raw;
433
+ for (const key of ["posts", "items", "scheduledPosts", "data"]) {
434
+ if (Array.isArray(obj[key])) return obj[key];
435
+ }
436
+ }
437
+ return [];
438
+ }
439
+ function normalizeLinkedIn(raw) {
440
+ return coercePosts(raw).map((p) => ({
441
+ platform: "linkedin",
442
+ id: String(p.postId ?? p.id ?? p.post_id ?? ""),
443
+ scheduledFor: p.scheduledFor ?? p.scheduled_for,
444
+ status: p.status,
445
+ content: String(p.content ?? p.text ?? "").slice(0, 140)
446
+ }));
447
+ }
448
+ function normalizeTwitter(raw) {
449
+ return coercePosts(raw).map((p) => ({
450
+ platform: "x",
451
+ id: String(p.postId ?? p.id ?? ""),
452
+ scheduledFor: p.scheduledFor ?? p.scheduled_for,
453
+ status: p.status,
454
+ content: String(
455
+ p.content ?? (Array.isArray(p.threadTweets) ? p.threadTweets.map((t) => t.text).join(" | ") : "")
456
+ ).slice(0, 140)
457
+ }));
458
+ }
459
+ async function getSchedule(client, opts = {}) {
460
+ const platform = opts.platform ?? "all";
461
+ const tasks = [];
462
+ if (platform === "all" || platform === "linkedin") {
463
+ tasks.push(
464
+ listPosts(client, { status: "scheduled", limit: opts.limit }).then(normalizeLinkedIn).catch(tolerate)
465
+ );
466
+ }
467
+ if (platform === "all" || platform === "x") {
468
+ tasks.push(
469
+ listPosts2(client, { status: "scheduled", limit: opts.limit }).then(normalizeTwitter).catch(tolerate)
470
+ );
471
+ }
472
+ const results = (await Promise.all(tasks)).flat();
473
+ return results.sort((a, b) => (a.scheduledFor ?? "~").localeCompare(b.scheduledFor ?? "~"));
474
+ }
475
+
476
+ // src/core/endpoints/reddit.ts
477
+ var reddit_exports = {};
478
+ __export(reddit_exports, {
479
+ comment: () => comment,
480
+ search: () => search,
481
+ searchStatus: () => searchStatus
482
+ });
483
+ function search(client, keyword) {
484
+ return client.get("/reddit/discovery/search", { query: { keyword } });
485
+ }
486
+ function searchStatus(client) {
487
+ return client.get("/reddit/discovery/status");
488
+ }
489
+ function comment(client, postId, text) {
490
+ return client.post("/reddit/comment", { body: { postId, text } });
491
+ }
492
+
493
+ // src/core/endpoints/signals.ts
494
+ var signals_exports = {};
495
+ __export(signals_exports, {
496
+ getInbox: () => getInbox
497
+ });
498
+ function getInbox(client) {
499
+ return client.get("/signals");
500
+ }
501
+
502
+ // src/core/endpoints/carousel.ts
503
+ var carousel_exports = {};
504
+ __export(carousel_exports, {
505
+ createDraft: () => createDraft3,
506
+ deleteDraft: () => deleteDraft3,
507
+ generate: () => generate,
508
+ getDraft: () => getDraft,
509
+ listDrafts: () => listDrafts3,
510
+ mapSlides: () => mapSlides,
511
+ updateDraft: () => updateDraft2
512
+ });
513
+ import { randomUUID } from "crypto";
514
+ function mapSlides(slides) {
515
+ return slides.map((s) => ({
516
+ id: randomUUID(),
517
+ title: s.headline ?? s.title ?? "",
518
+ body: s.body ?? s.content ?? "",
519
+ ...(s.layout || s.layoutType) && { layoutType: s.layout ?? s.layoutType },
520
+ bgImage: null,
521
+ backgroundTemplate: null,
522
+ showUserDetails: true
523
+ }));
524
+ }
525
+ function listDrafts3(client, opts = {}) {
526
+ return client.get("/carousel/drafts", { query: { limit: opts.limit, search: opts.search } });
527
+ }
528
+ function getDraft(client, draftId) {
529
+ return client.get("/carousel/drafts", { query: { draftId } });
530
+ }
531
+ function createDraft3(client, input) {
532
+ const carousels = mapSlides(input.slides);
533
+ return client.post("/carousel/drafts", {
534
+ body: {
535
+ title: input.title,
536
+ carousels,
537
+ slide_count: carousels.length,
538
+ ...input.theme !== void 0 ? { theme: input.theme } : {},
539
+ ...input.titleFontSize !== void 0 ? { titleFontSize: input.titleFontSize } : {},
540
+ ...input.bodyFontSize !== void 0 ? { bodyFontSize: input.bodyFontSize } : {},
541
+ generatedVia: "CLI"
542
+ }
543
+ });
544
+ }
545
+ function updateDraft2(client, draftId, patch) {
546
+ const updates = {};
547
+ if (patch.title !== void 0) updates.title = patch.title;
548
+ if (patch.theme !== void 0) updates.theme = patch.theme;
549
+ if (patch.titleFontSize !== void 0) updates.titleFontSize = patch.titleFontSize;
550
+ if (patch.bodyFontSize !== void 0) updates.bodyFontSize = patch.bodyFontSize;
551
+ if (patch.slides !== void 0) {
552
+ const carousels = mapSlides(patch.slides);
553
+ updates.carousels = carousels;
554
+ updates.slide_count = carousels.length;
555
+ }
556
+ return client.put("/carousel/drafts", { body: { draft_id: draftId, ...updates } });
557
+ }
558
+ function deleteDraft3(client, draftId) {
559
+ return client.delete("/carousel/drafts", { query: { draftId } });
560
+ }
561
+ function generate(client, userID, input) {
562
+ if (!input.topic && !input.url) throw new Error("Provide --topic or --url to generate.");
563
+ const contentType = input.url ? /youtube\.com|youtu\.be/.test(input.url) ? "youtube" : "article" : "text";
564
+ return client.post("/carousel", {
565
+ base: "public",
566
+ auth: false,
567
+ workspace: null,
568
+ body: {
569
+ userID,
570
+ contentType,
571
+ content: input.url ?? input.topic,
572
+ numberOfSlides: Math.min(Math.max(1, input.numberOfSlides ?? 5), 10),
573
+ ...input.aiInstructions ? { aiInstructions: input.aiInstructions } : {}
574
+ }
575
+ });
576
+ }
577
+
578
+ // src/core/endpoints/writingStyle.ts
579
+ var writingStyle_exports = {};
580
+ __export(writingStyle_exports, {
581
+ get: () => get,
582
+ override: () => override,
583
+ setVisibility: () => setVisibility,
584
+ testVoiceStyle: () => testVoiceStyle,
585
+ train: () => train
586
+ });
587
+ function get(client) {
588
+ return client.get("/writing-style");
589
+ }
590
+ function override(client, facet, instructions) {
591
+ return client.post("/writing-style/override", { body: { facet, instructions } });
592
+ }
593
+ function setVisibility(client, visibility) {
594
+ return client.post("/writing-style/visibility", { body: { visibility } });
595
+ }
596
+ function train(client, facet) {
597
+ return client.post("/writing-style/train", { body: { facet } });
598
+ }
599
+ function testVoiceStyle(client, facet, input) {
600
+ return client.post("/ai/test-voice-style", { body: { facet, input } });
601
+ }
602
+
603
+ // src/core/endpoints/voice.ts
604
+ var voice_exports = {};
605
+ __export(voice_exports, {
606
+ get: () => get2,
607
+ update: () => update
608
+ });
609
+ function get2(client) {
610
+ return client.get("/profile/voice-profile");
611
+ }
612
+ function update(client, body) {
613
+ return client.put("/profile/voice-profile", { body });
614
+ }
615
+
616
+ // src/core/endpoints/dms.ts
617
+ var dms_exports = {};
618
+ __export(dms_exports, {
619
+ getMessages: () => getMessages,
620
+ listConversations: () => listConversations,
621
+ refresh: () => refresh,
622
+ refreshStatus: () => refreshStatus,
623
+ sendMessage: () => sendMessage
624
+ });
625
+ function refresh(client) {
626
+ return client.post("/inbox/twitter/refresh");
627
+ }
628
+ function refreshStatus(client) {
629
+ return client.get("/inbox/twitter/refresh-status");
630
+ }
631
+ function listConversations(client, opts = {}) {
632
+ return client.get("/inbox/twitter/conversations", { query: { limit: opts.limit, offset: opts.offset } });
633
+ }
634
+ function getMessages(client, conversationId) {
635
+ return client.get(`/inbox/twitter/conversations/${encodeURIComponent(conversationId)}/messages`);
636
+ }
637
+ function sendMessage(client, participantId, text) {
638
+ return client.post(`/inbox/twitter/conversations/${encodeURIComponent(participantId)}/messages`, {
639
+ body: { text }
640
+ });
641
+ }
642
+
643
+ // src/core/postref.ts
644
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
645
+ function redditId(input) {
646
+ const m = input.match(/\/comments\/([a-z0-9]+)/i);
647
+ if (m) return m[1];
648
+ if (/^t3_[a-z0-9]+$/i.test(input) || /^[a-z0-9]{5,8}$/i.test(input)) return input;
649
+ return null;
650
+ }
651
+ function tweetId(input) {
652
+ const m = input.match(/status(?:es)?\/(\d+)/);
653
+ if (m) return m[1];
654
+ if (/^\d{5,}$/.test(input)) return input;
655
+ return null;
656
+ }
657
+ function resolvePostRef(input, hint) {
658
+ const value = input.trim();
659
+ let host = "";
660
+ try {
661
+ host = new URL(value).host.toLowerCase();
662
+ } catch {
663
+ }
664
+ const isReddit = hint === "reddit" || host.includes("reddit.com");
665
+ const isX = hint === "x" || host.includes("x.com") || host.includes("twitter.com");
666
+ const isLinkedIn = hint === "linkedin" || host.includes("linkedin.com");
667
+ if (isReddit) {
668
+ const id = redditId(value);
669
+ if (id) return { platform: "reddit", id };
670
+ throw new Error(`Could not parse a Reddit submission id from "${value}".`);
671
+ }
672
+ if (isX) {
673
+ const id = tweetId(value);
674
+ if (id) return { platform: "x", id };
675
+ throw new Error(`Could not parse a tweet id from "${value}".`);
676
+ }
677
+ if (isLinkedIn) {
678
+ if (UUID_RE.test(value)) return { platform: "linkedin", id: value };
679
+ throw new Error(
680
+ "Commenting on a public LinkedIn URL is not supported via the API \u2014 the endpoint only addresses posts published through Reepl (by their Reepl post id). Use the Chrome extension to comment on arbitrary LinkedIn posts."
681
+ );
682
+ }
683
+ if (/^\d{5,}$/.test(value)) return { platform: "x", id: value };
684
+ if (/^t3_[a-z0-9]+$/i.test(value)) return { platform: "reddit", id: value };
685
+ if (UUID_RE.test(value)) return { platform: "linkedin", id: value };
686
+ throw new Error(
687
+ `Could not determine the platform for "${value}". Pass --platform linkedin|x|reddit.`
688
+ );
689
+ }
690
+
691
+ export {
692
+ REEPL_API_BASE_URL,
693
+ REEPL_PUBLIC_API_BASE_URL,
694
+ COGNITO_DOMAIN,
695
+ COGNITO_WEB_APP_CLIENT_ID,
696
+ REEPL_WEB_APP_URL,
697
+ MCP_CONNECT_PATH,
698
+ TOKEN_REFRESH_BUFFER_MS,
699
+ ReeplError,
700
+ parseApiError,
701
+ ReeplClient,
702
+ decodeJwt,
703
+ jwtExpiryMs,
704
+ refreshIdToken,
705
+ TokenManager,
706
+ profile_exports,
707
+ workspaces_exports,
708
+ linkedin_exports,
709
+ twitter_exports,
710
+ calendar_exports,
711
+ reddit_exports,
712
+ signals_exports,
713
+ carousel_exports,
714
+ writingStyle_exports,
715
+ voice_exports,
716
+ dms_exports,
717
+ resolvePostRef
718
+ };
719
+ //# sourceMappingURL=chunk-QSNFNE5Z.js.map