social-light 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,9 +19,6 @@ An AI-powered social media scheduling tool for Bluesky with CLI and web interfac
19
19
 
20
20
  [Node.js/npm](https://nodejs.org) must be installed on your system
21
21
 
22
- > [!WARNING]
23
- > There's a know issues with the latest version of Node.js (v23) and 'better-sqlite3'. Please use Node.js v22 (LTS) or lower.
24
-
25
22
  ## Accounts
26
23
 
27
24
  You will need an account on [blue sky](https://bsky.app) and an [OpenAI](https://openai.com) account to use the AI features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-light",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "AI-powered social media scheduling tool",
5
5
  "main": "src/index.mjs",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "ai": "^2.2.31",
27
- "better-sqlite3": "^9.3.0",
27
+ "better-sqlite3": "^11.9.1",
28
28
  "chalk": "^5.3.0",
29
29
  "cors": "^2.8.5",
30
30
  "dotenv": "^16.3.1",
@@ -3,7 +3,6 @@ import ora from "ora";
3
3
  import inquirer from "inquirer";
4
4
  import fs from "fs-extra";
5
5
  import path from "path";
6
- import os from "os";
7
6
  import dotenv from "dotenv";
8
7
 
9
8
  import {
@@ -158,6 +157,7 @@ export const initialize = async (argv) => {
158
157
  {
159
158
  type: "input",
160
159
  name: "openaiKey",
160
+ mask: "*",
161
161
  message:
162
162
  "Enter your OpenAI API key for AI features (press Enter to skip):",
163
163
  },
@@ -0,0 +1,114 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import inquirer from "inquirer";
4
+ import fs from "fs-extra";
5
+ import path from "path";
6
+ import dotenv from "dotenv";
7
+ import os from "os";
8
+
9
+ import { configExists } from "../utils/config.mjs";
10
+
11
+ // Load environment variables
12
+ dotenv.config();
13
+
14
+ /**
15
+ * Remove the Social Light application configuration
16
+ * @example
17
+ * await uninitialize();
18
+ */
19
+ export const uninitialize = async () => {
20
+ const spinner = ora("Removing Social Light...").start();
21
+ const deletedFiles = [];
22
+
23
+ try {
24
+ const exists = configExists();
25
+
26
+ // If config already prompt for confirmation
27
+ if (exists) {
28
+ spinner.stop();
29
+
30
+ const { confirm } = await inquirer.prompt([
31
+ {
32
+ type: "confirm",
33
+ name: "confirm",
34
+ message: "Delete Social Light configuration?",
35
+ default: false,
36
+ },
37
+ ]);
38
+
39
+ if (!confirm) {
40
+ console.log(chalk.yellow("Unitialization cancelled."));
41
+ return;
42
+ }
43
+
44
+ spinner.start("Removing Social Light configuration...");
45
+
46
+ // Remove configuration file in current directory
47
+ const configPath = path.join(process.cwd(), "config.json");
48
+ if (fs.existsSync(configPath)) {
49
+ fs.removeSync(configPath);
50
+ deletedFiles.push(configPath);
51
+ spinner.text = "Configuration file removed...";
52
+ }
53
+
54
+ // Remove database files
55
+ const dbDir = path.join(process.cwd(), "data");
56
+ if (fs.existsSync(dbDir)) {
57
+ fs.removeSync(dbDir);
58
+ deletedFiles.push(dbDir);
59
+ spinner.text = "Database files removed...";
60
+ }
61
+
62
+ // Remove configuration in $HOME/.social-light directory
63
+ const homeSocialLightDir = path.join(os.homedir(), ".social-light");
64
+ if (fs.existsSync(homeSocialLightDir)) {
65
+ // Get list of files before deletion for reporting
66
+ const configFiles = fs
67
+ .readdirSync(homeSocialLightDir)
68
+ .map((file) => path.join(homeSocialLightDir, file));
69
+
70
+ // Add them to deleted files list
71
+ deletedFiles.push(...configFiles);
72
+
73
+ // Remove the directory and all contents
74
+ fs.removeSync(homeSocialLightDir);
75
+ spinner.text = "Home social-light directory removed...";
76
+ }
77
+
78
+ // Clean environment variables related to Social Light
79
+ const envPath = path.join(process.cwd(), ".env");
80
+ if (fs.existsSync(envPath)) {
81
+ fs.removeSync(envPath);
82
+ deletedFiles.push(envPath);
83
+ spinner.text = "Environment file removed...";
84
+ }
85
+
86
+ spinner.succeed("Social Light configuration removed successfully!");
87
+
88
+ // Print list of deleted files
89
+ console.log(
90
+ chalk.green("\n✓ All Social Light configurations have been removed.")
91
+ );
92
+
93
+ if (deletedFiles.length > 0) {
94
+ console.log(chalk.cyan("\nDeleted files and directories:"));
95
+ deletedFiles.forEach((file) => {
96
+ console.log(chalk.gray(` • ${file}`));
97
+ });
98
+ }
99
+
100
+ console.log(
101
+ chalk.cyan("\nYou can reinitialize anytime with:"),
102
+ chalk.cyan("social-light init")
103
+ );
104
+ return;
105
+ }
106
+
107
+ spinner.info("Not initialized. No configuration to remove.");
108
+ return;
109
+ } catch (error) {
110
+ spinner.fail(`Unintialization failed: ${error.message}`);
111
+ console.error(chalk.red("Error details:"), error);
112
+ process.exit(1);
113
+ }
114
+ };
package/src/index.mjs CHANGED
@@ -10,6 +10,7 @@ import { hideBin } from "yargs/helpers";
10
10
  import chalk from "chalk";
11
11
 
12
12
  import { initialize } from "./commands/init.mjs";
13
+ import { uninitialize } from "./commands/uninit.mjs";
13
14
  import { createPost } from "./commands/create.mjs";
14
15
 
15
16
  import { list } from "./commands/list.mjs";
@@ -41,6 +42,7 @@ const main = async () => {
41
42
  console.log(NPMPackage.version);
42
43
  })
43
44
  .command("init", "Initialize Social Light configuration", {}, initialize)
45
+ .command("uninit", "Remove Social Light configuration", {}, uninitialize)
44
46
  .command(
45
47
  "create",
46
48
  "Create a new social media post",
package/delete/tiktok.mjs DELETED
@@ -1,315 +0,0 @@
1
- import { SocialPlatform } from "./base.mjs";
2
- import fetch from "node-fetch";
3
-
4
- /**
5
- * TikTok Platform API Implementation
6
- * Uses TikTok's Content Posting API for posting and managing content
7
- */
8
- export class TikTokPlatform extends SocialPlatform {
9
- /**
10
- * Constructor for TikTok platform
11
- * @param {Object} config - Platform-specific configuration
12
- * @param {string} config.accessToken - TikTok API access token
13
- * @param {string} config.clientKey - TikTok client key
14
- * @param {string} config.clientSecret - TikTok client secret
15
- */
16
- constructor(config = {}) {
17
- super(config);
18
- this.name = "tiktok";
19
- this.baseUrl = "https://open.tiktokapis.com/v2";
20
- this.authenticated = false;
21
- }
22
-
23
- /**
24
- * Check if platform is properly configured
25
- * @returns {boolean} True if platform is configured
26
- */
27
- isConfigured() {
28
- return Boolean(
29
- this.config.accessToken &&
30
- this.config.clientKey &&
31
- this.config.clientSecret
32
- );
33
- }
34
-
35
- /**
36
- * Authenticate with the TikTok API
37
- * @returns {Promise<boolean>} True if authentication successful
38
- */
39
- async authenticate() {
40
- if (!this.isConfigured()) {
41
- throw new Error("TikTok API not properly configured");
42
- }
43
-
44
- try {
45
- // Query creator info to verify access token is valid
46
- const response = await fetch(
47
- `${this.baseUrl}/post/publish/creator_info/query/`,
48
- {
49
- method: "POST",
50
- headers: {
51
- Authorization: `Bearer ${this.config.accessToken}`,
52
- "Content-Type": "application/json; charset=UTF-8",
53
- },
54
- body: JSON.stringify({}),
55
- }
56
- );
57
-
58
- if (!response.ok) {
59
- const error = await response.json();
60
- throw new Error(
61
- `TikTok authentication failed: ${JSON.stringify(error)}`
62
- );
63
- }
64
-
65
- const creatorInfo = await response.json();
66
- this.creatorInfo = creatorInfo.data;
67
- this.authenticated = true;
68
- return true;
69
- } catch (error) {
70
- console.error("TikTok authentication error:", error);
71
- this.authenticated = false;
72
- throw error;
73
- }
74
- }
75
-
76
- /**
77
- * Post content to TikTok
78
- * @param {Object} post - Post content and metadata
79
- * @param {string} post.text - Text content/caption of the post
80
- * @param {Array<string>} post.mediaUrls - URLs of videos/images to post (required)
81
- * @param {Object} post.options - TikTok-specific options
82
- * @returns {Promise<Object>} Response including publish ID
83
- */
84
- async post(post) {
85
- if (!this.authenticated && !(await this.authenticate())) {
86
- throw new Error("TikTok authentication required");
87
- }
88
-
89
- if (!post.mediaUrls || post.mediaUrls.length === 0) {
90
- throw new Error("TikTok post requires at least one video or image URL");
91
- }
92
-
93
- // TikTok API has different endpoints for videos and images
94
- const mediaType = this._determineMediaType(post.mediaUrls[0]);
95
-
96
- if (mediaType === "video") {
97
- return this._postVideo(post);
98
- } else if (mediaType === "image") {
99
- return this._postImage(post);
100
- } else {
101
- throw new Error(`Unsupported media type: ${mediaType}`);
102
- }
103
- }
104
-
105
- /**
106
- * Get status of a TikTok post
107
- * @param {string} publishId - Publish ID of the post to check
108
- * @returns {Promise<Object>} Post status information
109
- */
110
- async getPostStatus(publishId) {
111
- if (!this.authenticated && !(await this.authenticate())) {
112
- throw new Error("TikTok authentication required");
113
- }
114
-
115
- try {
116
- const response = await fetch(
117
- `${this.baseUrl}/post/publish/status/fetch/`,
118
- {
119
- method: "POST",
120
- headers: {
121
- Authorization: `Bearer ${this.config.accessToken}`,
122
- "Content-Type": "application/json; charset=UTF-8",
123
- },
124
- body: JSON.stringify({
125
- publish_id: publishId,
126
- }),
127
- }
128
- );
129
-
130
- if (!response.ok) {
131
- const error = await response.json();
132
- throw new Error(`Failed to get post status: ${JSON.stringify(error)}`);
133
- }
134
-
135
- const result = await response.json();
136
- return {
137
- publishId,
138
- status: result.data.status,
139
- statusMessage: result.data.status_msg,
140
- videoId: result.data.video_id,
141
- shareUrl: result.data.share_url,
142
- };
143
- } catch (error) {
144
- console.error("TikTok get status error:", error);
145
- throw error;
146
- }
147
- }
148
-
149
- /**
150
- * Delete a TikTok post
151
- * Note: TikTok Content Posting API doesn't directly support post deletion
152
- * @param {string} postId - ID of the post to delete
153
- * @returns {Promise<boolean>} True if deletion successful
154
- */
155
- async deletePost(postId) {
156
- throw new Error(
157
- "TikTok API does not support post deletion via the Content Posting API"
158
- );
159
- }
160
-
161
- /**
162
- * Post a video to TikTok
163
- * @param {Object} post - Post content and metadata
164
- * @returns {Promise<Object>} Response with publish ID
165
- * @private
166
- */
167
- async _postVideo(post) {
168
- try {
169
- // Prepare post data
170
- const postInfo = {
171
- title: post.text || "",
172
- privacy_level: post.options?.privacyLevel || "PUBLIC_TO_EVERYONE",
173
- disable_duet: post.options?.disableDuet || false,
174
- disable_comment: post.options?.disableComment || false,
175
- disable_stitch: post.options?.disableStitch || false,
176
- };
177
-
178
- if (post.options?.videoCoverTimestamp) {
179
- postInfo.video_cover_timestamp_ms = post.options.videoCoverTimestamp;
180
- }
181
-
182
- // Initialize video upload
183
- const initResponse = await fetch(
184
- `${this.baseUrl}/post/publish/video/init/`,
185
- {
186
- method: "POST",
187
- headers: {
188
- Authorization: `Bearer ${this.config.accessToken}`,
189
- "Content-Type": "application/json; charset=UTF-8",
190
- },
191
- body: JSON.stringify({
192
- post_info: postInfo,
193
- source_info: {
194
- source: "PULL_FROM_URL",
195
- video_url: post.mediaUrls[0],
196
- },
197
- }),
198
- }
199
- );
200
-
201
- if (!initResponse.ok) {
202
- const error = await initResponse.json();
203
- throw new Error(
204
- `TikTok video post initialization failed: ${JSON.stringify(error)}`
205
- );
206
- }
207
-
208
- const initResult = await initResponse.json();
209
- const publishId = initResult.data.publish_id;
210
-
211
- return {
212
- publishId,
213
- status: "PROCESSING",
214
- type: "video",
215
- };
216
- } catch (error) {
217
- console.error("TikTok video post error:", error);
218
- throw error;
219
- }
220
- }
221
-
222
- /**
223
- * Post an image to TikTok
224
- * @param {Object} post - Post content and metadata
225
- * @returns {Promise<Object>} Response with publish ID
226
- * @private
227
- */
228
- async _postImage(post) {
229
- try {
230
- // Prepare post data
231
- const postInfo = {
232
- title: post.text || "",
233
- privacy_level: post.options?.privacyLevel || "PUBLIC_TO_EVERYONE",
234
- disable_comment: post.options?.disableComment || false,
235
- };
236
-
237
- // Initialize image upload
238
- const initResponse = await fetch(
239
- `${this.baseUrl}/post/publish/content/init/`,
240
- {
241
- method: "POST",
242
- headers: {
243
- Authorization: `Bearer ${this.config.accessToken}`,
244
- "Content-Type": "application/json; charset=UTF-8",
245
- },
246
- body: JSON.stringify({
247
- post_info: postInfo,
248
- media_type: "PHOTO",
249
- post_mode: "DIRECT_POST",
250
- source_info: {
251
- source: "PULL_FROM_URL",
252
- photo_urls: post.mediaUrls.slice(0, 9), // TikTok supports up to 9 images
253
- },
254
- }),
255
- }
256
- );
257
-
258
- if (!initResponse.ok) {
259
- const error = await initResponse.json();
260
- throw new Error(
261
- `TikTok image post initialization failed: ${JSON.stringify(error)}`
262
- );
263
- }
264
-
265
- const initResult = await initResponse.json();
266
- const publishId = initResult.data.publish_id;
267
-
268
- return {
269
- publishId,
270
- status: "PROCESSING",
271
- type: "image",
272
- };
273
- } catch (error) {
274
- console.error("TikTok image post error:", error);
275
- throw error;
276
- }
277
- }
278
-
279
- /**
280
- * Determine media type from URL
281
- * @param {string} url - Media URL
282
- * @returns {string} Media type: 'video', 'image', or 'unknown'
283
- * @private
284
- */
285
- _determineMediaType(url) {
286
- const lowercaseUrl = url.toLowerCase();
287
- const videoExtensions = [".mp4", ".mov", ".avi", ".wmv", ".flv", ".webm"];
288
- const imageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
289
-
290
- for (const ext of videoExtensions) {
291
- if (lowercaseUrl.endsWith(ext)) {
292
- return "video";
293
- }
294
- }
295
-
296
- for (const ext of imageExtensions) {
297
- if (lowercaseUrl.endsWith(ext)) {
298
- return "image";
299
- }
300
- }
301
-
302
- // If we can't determine from extension, check for common patterns
303
- if (lowercaseUrl.includes("video") || lowercaseUrl.includes("mov")) {
304
- return "video";
305
- } else if (
306
- lowercaseUrl.includes("image") ||
307
- lowercaseUrl.includes("photo")
308
- ) {
309
- return "image";
310
- }
311
-
312
- // Default to video as it's more common for TikTok
313
- return "video";
314
- }
315
- }
@@ -1,258 +0,0 @@
1
- import { SocialPlatform } from './base.mjs';
2
- import fetch from 'node-fetch';
3
-
4
- /**
5
- * Twitter (X) Platform API Implementation
6
- * Uses Twitter API v2 for posting and managing tweets
7
- */
8
- export class TwitterPlatform extends SocialPlatform {
9
- /**
10
- * Constructor for Twitter platform
11
- * @param {Object} config - Platform-specific configuration
12
- * @param {string} config.apiKey - Twitter API key
13
- * @param {string} config.apiSecret - Twitter API secret
14
- * @param {string} config.accessToken - Twitter access token
15
- * @param {string} config.accessTokenSecret - Twitter access token secret
16
- */
17
- constructor(config = {}) {
18
- super(config);
19
- this.name = 'twitter';
20
- this.baseUrl = 'https://api.twitter.com/2';
21
- this.authenticated = false;
22
- this.bearerToken = null;
23
- }
24
-
25
- /**
26
- * Check if platform is properly configured
27
- * @returns {boolean} True if platform is configured
28
- */
29
- isConfigured() {
30
- return Boolean(
31
- this.config.apiKey &&
32
- this.config.apiSecret &&
33
- this.config.accessToken &&
34
- this.config.accessTokenSecret
35
- );
36
- }
37
-
38
- /**
39
- * Authenticate with the Twitter API
40
- * @returns {Promise<boolean>} True if authentication successful
41
- */
42
- async authenticate() {
43
- if (!this.isConfigured()) {
44
- throw new Error('Twitter API not properly configured');
45
- }
46
-
47
- try {
48
- // For OAuth 1.0a authentication, we need to implement a proper signature method
49
- // This is a simplified version for demonstration purposes
50
- // In a real implementation, you'd use a library like 'oauth-1.0a'
51
- const authHeader = this._generateOAuthHeader();
52
-
53
- const response = await fetch(`${this.baseUrl}/users/me`, {
54
- method: 'GET',
55
- headers: {
56
- Authorization: authHeader
57
- }
58
- });
59
-
60
- if (!response.ok) {
61
- const error = await response.json();
62
- throw new Error(`Twitter authentication failed: ${JSON.stringify(error)}`);
63
- }
64
-
65
- this.authenticated = true;
66
- return true;
67
- } catch (error) {
68
- console.error('Twitter authentication error:', error);
69
- this.authenticated = false;
70
- throw error;
71
- }
72
- }
73
-
74
- /**
75
- * Post content to Twitter
76
- * @param {Object} post - Post content and metadata
77
- * @param {string} post.text - Text content of the tweet (required)
78
- * @param {Array<string>} post.mediaUrls - URLs of media to attach (optional)
79
- * @param {Object} post.options - Twitter-specific options
80
- * @returns {Promise<Object>} Response including tweet ID
81
- */
82
- async post(post) {
83
- if (!this.authenticated && !await this.authenticate()) {
84
- throw new Error('Twitter authentication required');
85
- }
86
-
87
- if (!post.text) {
88
- throw new Error('Tweet text is required');
89
- }
90
-
91
- try {
92
- const tweetData = {
93
- text: post.text
94
- };
95
-
96
- // Handle media attachments if provided
97
- if (post.mediaUrls && post.mediaUrls.length > 0) {
98
- const mediaIds = await this._uploadMedia(post.mediaUrls);
99
- if (mediaIds.length > 0) {
100
- tweetData.media = {
101
- media_ids: mediaIds
102
- };
103
- }
104
- }
105
-
106
- // Add any Twitter-specific options
107
- if (post.options) {
108
- // Handle reply settings if provided
109
- if (post.options.replySettings) {
110
- tweetData.reply_settings = post.options.replySettings;
111
- }
112
-
113
- // Handle quote tweet if provided
114
- if (post.options.quoteTweetId) {
115
- tweetData.quote_tweet_id = post.options.quoteTweetId;
116
- }
117
-
118
- // Handle reply if provided
119
- if (post.options.inReplyToTweetId) {
120
- tweetData.reply = {
121
- in_reply_to_tweet_id: post.options.inReplyToTweetId
122
- };
123
- }
124
- }
125
-
126
- const authHeader = this._generateOAuthHeader('POST', `${this.baseUrl}/tweets`);
127
-
128
- const response = await fetch(`${this.baseUrl}/tweets`, {
129
- method: 'POST',
130
- headers: {
131
- 'Authorization': authHeader,
132
- 'Content-Type': 'application/json'
133
- },
134
- body: JSON.stringify(tweetData)
135
- });
136
-
137
- if (!response.ok) {
138
- const error = await response.json();
139
- throw new Error(`Twitter post failed: ${JSON.stringify(error)}`);
140
- }
141
-
142
- const result = await response.json();
143
- return {
144
- id: result.data.id,
145
- text: result.data.text,
146
- url: `https://twitter.com/user/status/${result.data.id}`
147
- };
148
- } catch (error) {
149
- console.error('Twitter post error:', error);
150
- throw error;
151
- }
152
- }
153
-
154
- /**
155
- * Get status of a tweet
156
- * @param {string} tweetId - ID of the tweet to check
157
- * @returns {Promise<Object>} Tweet status information
158
- */
159
- async getPostStatus(tweetId) {
160
- if (!this.authenticated && !await this.authenticate()) {
161
- throw new Error('Twitter authentication required');
162
- }
163
-
164
- try {
165
- const authHeader = this._generateOAuthHeader('GET', `${this.baseUrl}/tweets/${tweetId}`);
166
-
167
- const response = await fetch(`${this.baseUrl}/tweets/${tweetId}?tweet.fields=public_metrics`, {
168
- method: 'GET',
169
- headers: {
170
- Authorization: authHeader
171
- }
172
- });
173
-
174
- if (!response.ok) {
175
- const error = await response.json();
176
- throw new Error(`Failed to get tweet status: ${JSON.stringify(error)}`);
177
- }
178
-
179
- const result = await response.json();
180
- return {
181
- id: result.data.id,
182
- text: result.data.text,
183
- metrics: result.data.public_metrics,
184
- url: `https://twitter.com/user/status/${result.data.id}`
185
- };
186
- } catch (error) {
187
- console.error('Twitter get status error:', error);
188
- throw error;
189
- }
190
- }
191
-
192
- /**
193
- * Delete a tweet
194
- * @param {string} tweetId - ID of the tweet to delete
195
- * @returns {Promise<boolean>} True if deletion successful
196
- */
197
- async deletePost(tweetId) {
198
- if (!this.authenticated && !await this.authenticate()) {
199
- throw new Error('Twitter authentication required');
200
- }
201
-
202
- try {
203
- const authHeader = this._generateOAuthHeader('DELETE', `${this.baseUrl}/tweets/${tweetId}`);
204
-
205
- const response = await fetch(`${this.baseUrl}/tweets/${tweetId}`, {
206
- method: 'DELETE',
207
- headers: {
208
- Authorization: authHeader
209
- }
210
- });
211
-
212
- if (!response.ok) {
213
- const error = await response.json();
214
- throw new Error(`Failed to delete tweet: ${JSON.stringify(error)}`);
215
- }
216
-
217
- return true;
218
- } catch (error) {
219
- console.error('Twitter delete error:', error);
220
- throw error;
221
- }
222
- }
223
-
224
- /**
225
- * Upload media to Twitter
226
- * @param {Array<string>} mediaUrls - Array of media URLs to upload
227
- * @returns {Promise<Array<string>>} Array of media IDs
228
- * @private
229
- */
230
- async _uploadMedia(mediaUrls) {
231
- // This is a placeholder implementation
232
- // In a real implementation, you would:
233
- // 1. Download each media file or read from local file
234
- // 2. Upload to Twitter's media endpoint (v1.1/media/upload)
235
- // 3. Return the media IDs
236
-
237
- const mediaIds = [];
238
- // Implementation would go here
239
-
240
- return mediaIds;
241
- }
242
-
243
- /**
244
- * Generate OAuth 1.0a header for Twitter API
245
- * @param {string} method - HTTP method
246
- * @param {string} url - Request URL
247
- * @returns {string} OAuth header string
248
- * @private
249
- */
250
- _generateOAuthHeader(method = 'GET', url = '') {
251
- // This is a simplified placeholder implementation
252
- // In a real implementation, you would use a library like 'oauth-1.0a'
253
- // to generate proper OAuth 1.0a signatures
254
-
255
- // Return simplified header for demo purposes
256
- return `OAuth oauth_consumer_key="${this.config.apiKey}", oauth_token="${this.config.accessToken}"`;
257
- }
258
- }