@aisa-one/cli 0.1.8 → 0.2.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.
@@ -2,12 +2,11 @@ import ora from "ora";
2
2
  import chalk from "chalk";
3
3
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
- import fetch from "node-fetch";
6
5
  import { error, success, hint, truncate } from "../utils/display.js";
7
6
  import { expandHome, ensureDir, writeSkillFiles, removeDir, detectAgents } from "../utils/file.js";
8
7
  import { AGENT_DIRS } from "../constants.js";
9
8
  // --- GitHub-backed skill registry ---
10
- const SKILLS_REPO = "AIsa-team/OpenClaw-Skills";
9
+ const SKILLS_REPO = "AIsa-team/agent-skills";
11
10
  const GH_API = `https://api.github.com/repos/${SKILLS_REPO}`;
12
11
  const GH_RAW = `https://raw.githubusercontent.com/${SKILLS_REPO}/main`;
13
12
  /** Fetch the repo tree and extract top-level skill folder names. */
@@ -48,7 +47,8 @@ function parseSkillFrontmatter(slug, content) {
48
47
  if (metaMatch) {
49
48
  try {
50
49
  const meta = JSON.parse(metaMatch[1]);
51
- info.emoji = meta?.openclaw?.emoji || "";
50
+ // agent-skills repo uses metadata.aisa; legacy skills used metadata.openclaw.
51
+ info.emoji = meta?.aisa?.emoji || meta?.openclaw?.emoji || "";
52
52
  }
53
53
  catch {
54
54
  // ignore malformed metadata
@@ -80,8 +80,8 @@ const TEMPLATES = {
80
80
  default: `---
81
81
  name: my-skill
82
82
  description: "Describe what this skill does."
83
- homepage: https://openclaw.ai
84
- metadata: {"openclaw":{"emoji":"","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
83
+ homepage: https://aisa.one
84
+ metadata: {"aisa":{"emoji":"","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
85
85
  ---
86
86
 
87
87
  # My Skill
@@ -103,8 +103,8 @@ aisa run <slug> <path> -q "param=value"
103
103
  llm: `---
104
104
  name: llm-assistant
105
105
  description: "Use AISA's unified LLM gateway to chat with 70+ AI models."
106
- homepage: https://openclaw.ai
107
- metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
106
+ homepage: https://aisa.one
107
+ metadata: {"aisa":{"emoji":"🤖","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
108
108
  ---
109
109
 
110
110
  # LLM Assistant Skill
@@ -120,7 +120,7 @@ export AISA_API_KEY=sk-your-key
120
120
  ## Chat Completion
121
121
 
122
122
  \`\`\`bash
123
- aisa chat "Your question here" --model gpt-4.1
123
+ aisa chat "Your question here" --model gpt-4.1-mini
124
124
  \`\`\`
125
125
 
126
126
  ## Streaming
@@ -139,8 +139,8 @@ aisa models --provider anthropic
139
139
  search: `---
140
140
  name: web-search
141
141
  description: "Search the web, YouTube, and academic papers via AISA APIs."
142
- homepage: https://openclaw.ai
143
- metadata: {"openclaw":{"emoji":"🔍","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
142
+ homepage: https://aisa.one
143
+ metadata: {"aisa":{"emoji":"🔍","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
144
144
  ---
145
145
 
146
146
  # Web Search Skill
@@ -174,8 +174,8 @@ aisa scholar "transformer architecture"
174
174
  finance: `---
175
175
  name: finance-analyst
176
176
  description: "Access stock prices, earnings, SEC filings, and financial data via AISA."
177
- homepage: https://openclaw.ai
178
- metadata: {"openclaw":{"emoji":"📊","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
177
+ homepage: https://aisa.one
178
+ metadata: {"aisa":{"emoji":"📊","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
179
179
  ---
180
180
 
181
181
  # Finance Analyst Skill
@@ -206,8 +206,8 @@ aisa crypto ETH --period 30d
206
206
  twitter: `---
207
207
  name: twitter-manager
208
208
  description: "Search Twitter, get user profiles and trends via AISA."
209
- homepage: https://openclaw.ai
210
- metadata: {"openclaw":{"emoji":"🐦","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
209
+ homepage: https://aisa.one
210
+ metadata: {"aisa":{"emoji":"🐦","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
211
211
  ---
212
212
 
213
213
  # Twitter Manager Skill
@@ -241,8 +241,8 @@ aisa twitter trends
241
241
  video: `---
242
242
  name: video-generator
243
243
  description: "Generate videos from text prompts using AISA's video synthesis API."
244
- homepage: https://openclaw.ai
245
- metadata: {"openclaw":{"emoji":"🎬","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
244
+ homepage: https://aisa.one
245
+ metadata: {"aisa":{"emoji":"🎬","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
246
246
  ---
247
247
 
248
248
  # Video Generator Skill
@@ -1,14 +1,138 @@
1
- export declare function tweetAction(text: string, options: {
2
- replyTo?: string;
1
+ export declare function twitterUserAction(username: string, options: {
2
+ raw?: boolean;
3
+ }): Promise<void>;
4
+ export declare function twitterUserAboutAction(username: string, options: {
5
+ raw?: boolean;
6
+ }): Promise<void>;
7
+ export declare function twitterBatchUsersAction(ids: string, options: {
8
+ raw?: boolean;
9
+ }): Promise<void>;
10
+ export declare function twitterUserTweetsAction(username: string, options: {
3
11
  raw?: boolean;
12
+ cursor?: string;
13
+ }): Promise<void>;
14
+ export declare function twitterMentionsAction(username: string, options: {
15
+ raw?: boolean;
16
+ cursor?: string;
17
+ }): Promise<void>;
18
+ export declare function twitterFollowersAction(username: string, options: {
19
+ raw?: boolean;
20
+ cursor?: string;
21
+ }): Promise<void>;
22
+ export declare function twitterFollowingAction(username: string, options: {
23
+ raw?: boolean;
24
+ cursor?: string;
25
+ }): Promise<void>;
26
+ export declare function twitterVerifiedFollowersAction(userId: string, options: {
27
+ raw?: boolean;
28
+ cursor?: string;
29
+ }): Promise<void>;
30
+ export declare function twitterCheckFollowAction(source: string, target: string, options: {
31
+ raw?: boolean;
32
+ }): Promise<void>;
33
+ export declare function twitterUserSearchAction(query: string, options: {
34
+ raw?: boolean;
35
+ cursor?: string;
4
36
  }): Promise<void>;
5
37
  export declare function twitterSearchAction(query: string, options: {
6
38
  limit?: string;
39
+ type?: string;
7
40
  raw?: boolean;
41
+ cursor?: string;
8
42
  }): Promise<void>;
9
- export declare function twitterUserAction(username: string, options: {
43
+ export declare function twitterDetailAction(ids: string, options: {
44
+ raw?: boolean;
45
+ }): Promise<void>;
46
+ export declare function twitterRepliesAction(tweetId: string, options: {
47
+ raw?: boolean;
48
+ cursor?: string;
49
+ }): Promise<void>;
50
+ export declare function twitterQuotesAction(tweetId: string, options: {
51
+ raw?: boolean;
52
+ cursor?: string;
53
+ }): Promise<void>;
54
+ export declare function twitterRetweetersAction(tweetId: string, options: {
55
+ raw?: boolean;
56
+ cursor?: string;
57
+ }): Promise<void>;
58
+ export declare function twitterThreadAction(tweetId: string, options: {
59
+ raw?: boolean;
60
+ cursor?: string;
61
+ }): Promise<void>;
62
+ export declare function twitterArticleAction(tweetId: string, options: {
10
63
  raw?: boolean;
11
64
  }): Promise<void>;
12
65
  export declare function twitterTrendsAction(options: {
66
+ woeid?: string;
67
+ count?: string;
68
+ raw?: boolean;
69
+ }): Promise<void>;
70
+ export declare function twitterListMembersAction(listId: string, options: {
71
+ raw?: boolean;
72
+ cursor?: string;
73
+ }): Promise<void>;
74
+ export declare function twitterListFollowersAction(listId: string, options: {
75
+ raw?: boolean;
76
+ cursor?: string;
77
+ }): Promise<void>;
78
+ export declare function twitterCommunityInfoAction(communityId: string, options: {
79
+ raw?: boolean;
80
+ }): Promise<void>;
81
+ export declare function twitterCommunityMembersAction(communityId: string, options: {
82
+ raw?: boolean;
83
+ cursor?: string;
84
+ }): Promise<void>;
85
+ export declare function twitterCommunityModsAction(communityId: string, options: {
86
+ raw?: boolean;
87
+ cursor?: string;
88
+ }): Promise<void>;
89
+ export declare function twitterCommunityTweetsAction(communityId: string, options: {
90
+ raw?: boolean;
91
+ cursor?: string;
92
+ }): Promise<void>;
93
+ export declare function twitterCommunitySearchAction(query: string, options: {
94
+ raw?: boolean;
95
+ cursor?: string;
96
+ }): Promise<void>;
97
+ export declare function twitterSpaceAction(spaceId: string, options: {
98
+ raw?: boolean;
99
+ }): Promise<void>;
100
+ export declare function twitterLoginAction(options: {
101
+ username?: string;
102
+ email?: string;
103
+ password?: string;
104
+ proxy?: string;
105
+ totp?: string;
106
+ cookies?: string;
107
+ raw?: boolean;
108
+ }): Promise<void>;
109
+ export declare function twitterLogoutAction(): void;
110
+ export declare function tweetAction(text: string, options: {
111
+ replyTo?: string;
112
+ mediaIds?: string;
113
+ raw?: boolean;
114
+ }): Promise<void>;
115
+ export declare function twitterLikeAction(tweetId: string, options: {
116
+ raw?: boolean;
117
+ }): Promise<void>;
118
+ export declare function twitterUnlikeAction(tweetId: string, options: {
119
+ raw?: boolean;
120
+ }): Promise<void>;
121
+ export declare function twitterRetweetAction(tweetId: string, options: {
122
+ raw?: boolean;
123
+ }): Promise<void>;
124
+ export declare function twitterDeleteAction(tweetId: string, options: {
125
+ raw?: boolean;
126
+ }): Promise<void>;
127
+ export declare function twitterFollowAction(userId: string, options: {
128
+ raw?: boolean;
129
+ }): Promise<void>;
130
+ export declare function twitterUnfollowAction(userId: string, options: {
131
+ raw?: boolean;
132
+ }): Promise<void>;
133
+ export declare function twitterUploadMediaAction(filePath: string, options: {
134
+ raw?: boolean;
135
+ }): Promise<void>;
136
+ export declare function twitterDmAction(userId: string, text: string, options: {
13
137
  raw?: boolean;
14
138
  }): Promise<void>;
@@ -1,29 +1,33 @@
1
1
  import ora from "ora";
2
2
  import chalk from "chalk";
3
- import { requireApiKey } from "../config.js";
3
+ import { requireApiKey, getConfig, setConfig } from "../config.js";
4
4
  import { apiRequest } from "../api.js";
5
- import { error, formatJson } from "../utils/display.js";
6
- export async function tweetAction(text, options) {
5
+ import { APIS_BASE_URL } from "../constants.js";
6
+ import { error, success, formatJson } from "../utils/display.js";
7
+ function requireCookies() {
8
+ const cookies = getConfig("twitterCookies");
9
+ const proxy = getConfig("twitterProxy");
10
+ if (!cookies) {
11
+ error('No Twitter cookies configured. Run "aisa twitter login" or "aisa config set twitterCookies <value>".');
12
+ process.exit(1);
13
+ }
14
+ if (!proxy) {
15
+ error('No Twitter proxy configured. Run "aisa config set twitterProxy <value>".');
16
+ process.exit(1);
17
+ }
18
+ return { login_cookies: cookies, proxy };
19
+ }
20
+ /** Simple GET against a twitter read endpoint. */
21
+ async function twitterGet(endpoint, query, label, options) {
7
22
  const key = requireApiKey();
8
- const spinner = ora("Posting tweet...").start();
9
- const body = { tweet_text: text };
10
- if (options.replyTo)
11
- body.reply_to_tweet_id = options.replyTo;
12
- // Note: create_tweet_v2 requires login_cookies and proxy
13
- // This will fail without those - provide a helpful message
14
- const res = await apiRequest(key, "twitter/create_tweet_v2", {
15
- method: "POST",
16
- body,
23
+ const spinner = ora(label).start();
24
+ const res = await apiRequest(key, endpoint, {
25
+ query,
17
26
  domain: true,
18
27
  });
19
28
  if (!res.success) {
20
- spinner.fail("Failed to post tweet");
21
- if (res.error?.includes("login_cookies")) {
22
- error("Tweet creation requires Twitter login cookies. See: https://docs.aisa.one");
23
- }
24
- else {
25
- error(res.error || "Unknown error");
26
- }
29
+ spinner.fail(label.replace(/\.{3}$/, " failed"));
30
+ error(res.error || "Unknown error");
27
31
  return;
28
32
  }
29
33
  spinner.stop();
@@ -31,22 +35,21 @@ export async function tweetAction(text, options) {
31
35
  console.log(JSON.stringify(res.data));
32
36
  }
33
37
  else {
34
- console.log(chalk.green("Tweet posted!"));
35
38
  console.log(formatJson(res.data));
36
39
  }
37
40
  }
38
- export async function twitterSearchAction(query, options) {
41
+ /** POST against a twitter write endpoint (auto-injects cookies/proxy). */
42
+ async function twitterWrite(endpoint, body, label, options) {
39
43
  const key = requireApiKey();
40
- const spinner = ora(`Searching tweets: "${query}"...`).start();
41
- const q = { query, queryType: "Latest" };
42
- if (options.limit)
43
- q.cursor = "";
44
- const res = await apiRequest(key, "twitter/tweet/advanced_search", {
45
- query: q,
44
+ const auth = requireCookies();
45
+ const spinner = ora(label).start();
46
+ const res = await apiRequest(key, endpoint, {
47
+ method: "POST",
48
+ body: { ...auth, ...body },
46
49
  domain: true,
47
50
  });
48
51
  if (!res.success) {
49
- spinner.fail("Twitter search failed");
52
+ spinner.fail(label.replace(/\.{3}$/, " failed"));
50
53
  error(res.error || "Unknown error");
51
54
  return;
52
55
  }
@@ -58,6 +61,7 @@ export async function twitterSearchAction(query, options) {
58
61
  console.log(formatJson(res.data));
59
62
  }
60
63
  }
64
+ // ── Read: User Endpoints ─────────────────────────────────────────────────────
61
65
  export async function twitterUserAction(username, options) {
62
66
  const key = requireApiKey();
63
67
  const spinner = ora(`Fetching @${username}...`).start();
@@ -92,24 +96,280 @@ export async function twitterUserAction(username, options) {
92
96
  }
93
97
  }
94
98
  }
99
+ export async function twitterUserAboutAction(username, options) {
100
+ return twitterGet("twitter/user_about", { userName: username }, `Fetching about @${username}...`, options);
101
+ }
102
+ export async function twitterBatchUsersAction(ids, options) {
103
+ return twitterGet("twitter/user/batch_info_by_ids", { userIds: ids }, "Fetching users...", options);
104
+ }
105
+ export async function twitterUserTweetsAction(username, options) {
106
+ const q = { userName: username };
107
+ if (options.cursor)
108
+ q.cursor = options.cursor;
109
+ return twitterGet("twitter/user/last_tweets", q, `Fetching tweets by @${username}...`, options);
110
+ }
111
+ export async function twitterMentionsAction(username, options) {
112
+ const q = { userName: username };
113
+ if (options.cursor)
114
+ q.cursor = options.cursor;
115
+ return twitterGet("twitter/user/mentions", q, `Fetching mentions of @${username}...`, options);
116
+ }
117
+ export async function twitterFollowersAction(username, options) {
118
+ const q = { userName: username };
119
+ if (options.cursor)
120
+ q.cursor = options.cursor;
121
+ return twitterGet("twitter/user/followers", q, `Fetching followers of @${username}...`, options);
122
+ }
123
+ export async function twitterFollowingAction(username, options) {
124
+ const q = { userName: username };
125
+ if (options.cursor)
126
+ q.cursor = options.cursor;
127
+ return twitterGet("twitter/user/followings", q, `Fetching following of @${username}...`, options);
128
+ }
129
+ export async function twitterVerifiedFollowersAction(userId, options) {
130
+ const q = { user_id: userId };
131
+ if (options.cursor)
132
+ q.cursor = options.cursor;
133
+ return twitterGet("twitter/user/verifiedFollowers", q, "Fetching verified followers...", options);
134
+ }
135
+ export async function twitterCheckFollowAction(source, target, options) {
136
+ return twitterGet("twitter/user/check_follow_relationship", { source_user_name: source, target_user_name: target }, `Checking follow relationship @${source} → @${target}...`, options);
137
+ }
138
+ export async function twitterUserSearchAction(query, options) {
139
+ const q = { query };
140
+ if (options.cursor)
141
+ q.cursor = options.cursor;
142
+ return twitterGet("twitter/user/search", q, `Searching users: "${query}"...`, options);
143
+ }
144
+ // ── Read: Tweet Endpoints ────────────────────────────────────────────────────
145
+ export async function twitterSearchAction(query, options) {
146
+ const q = {
147
+ query,
148
+ queryType: options.type === "top" ? "Top" : "Latest",
149
+ };
150
+ if (options.cursor)
151
+ q.cursor = options.cursor;
152
+ if (options.limit)
153
+ q.count = options.limit;
154
+ return twitterGet("twitter/tweet/advanced_search", q, `Searching tweets: "${query}"...`, options);
155
+ }
156
+ export async function twitterDetailAction(ids, options) {
157
+ return twitterGet("twitter/tweets", { tweet_ids: ids }, "Fetching tweet details...", options);
158
+ }
159
+ export async function twitterRepliesAction(tweetId, options) {
160
+ const q = { tweetId };
161
+ if (options.cursor)
162
+ q.cursor = options.cursor;
163
+ return twitterGet("twitter/tweet/replies", q, "Fetching replies...", options);
164
+ }
165
+ export async function twitterQuotesAction(tweetId, options) {
166
+ const q = { tweetId };
167
+ if (options.cursor)
168
+ q.cursor = options.cursor;
169
+ return twitterGet("twitter/tweet/quotes", q, "Fetching quotes...", options);
170
+ }
171
+ export async function twitterRetweetersAction(tweetId, options) {
172
+ const q = { tweetId };
173
+ if (options.cursor)
174
+ q.cursor = options.cursor;
175
+ return twitterGet("twitter/tweet/retweeters", q, "Fetching retweeters...", options);
176
+ }
177
+ export async function twitterThreadAction(tweetId, options) {
178
+ const q = { tweetId };
179
+ if (options.cursor)
180
+ q.cursor = options.cursor;
181
+ return twitterGet("twitter/tweet/thread_context", q, "Fetching thread...", options);
182
+ }
183
+ export async function twitterArticleAction(tweetId, options) {
184
+ return twitterGet("twitter/article", { tweet_id: tweetId }, "Fetching article...", options);
185
+ }
186
+ // ── Read: Trends ─────────────────────────────────────────────────────────────
95
187
  export async function twitterTrendsAction(options) {
188
+ const q = {
189
+ woeid: options.woeid || "1",
190
+ count: options.count || "30",
191
+ };
192
+ return twitterGet("twitter/trends", q, "Fetching trends...", options);
193
+ }
194
+ // ── Read: Lists ──────────────────────────────────────────────────────────────
195
+ export async function twitterListMembersAction(listId, options) {
196
+ const q = { list_id: listId };
197
+ if (options.cursor)
198
+ q.cursor = options.cursor;
199
+ return twitterGet("twitter/list/members", q, "Fetching list members...", options);
200
+ }
201
+ export async function twitterListFollowersAction(listId, options) {
202
+ const q = { list_id: listId };
203
+ if (options.cursor)
204
+ q.cursor = options.cursor;
205
+ return twitterGet("twitter/list/followers", q, "Fetching list followers...", options);
206
+ }
207
+ // ── Read: Communities ────────────────────────────────────────────────────────
208
+ export async function twitterCommunityInfoAction(communityId, options) {
209
+ return twitterGet("twitter/community/info", { community_id: communityId }, "Fetching community info...", options);
210
+ }
211
+ export async function twitterCommunityMembersAction(communityId, options) {
212
+ const q = { community_id: communityId };
213
+ if (options.cursor)
214
+ q.cursor = options.cursor;
215
+ return twitterGet("twitter/community/members", q, "Fetching community members...", options);
216
+ }
217
+ export async function twitterCommunityModsAction(communityId, options) {
218
+ const q = { community_id: communityId };
219
+ if (options.cursor)
220
+ q.cursor = options.cursor;
221
+ return twitterGet("twitter/community/moderators", q, "Fetching community moderators...", options);
222
+ }
223
+ export async function twitterCommunityTweetsAction(communityId, options) {
224
+ const q = { community_id: communityId };
225
+ if (options.cursor)
226
+ q.cursor = options.cursor;
227
+ return twitterGet("twitter/community/tweets", q, "Fetching community tweets...", options);
228
+ }
229
+ export async function twitterCommunitySearchAction(query, options) {
230
+ const q = { query };
231
+ if (options.cursor)
232
+ q.cursor = options.cursor;
233
+ return twitterGet("twitter/community/get_tweets_from_all_community", q, `Searching community tweets: "${query}"...`, options);
234
+ }
235
+ // ── Read: Spaces ─────────────────────────────────────────────────────────────
236
+ export async function twitterSpaceAction(spaceId, options) {
237
+ return twitterGet("twitter/spaces/detail", { space_id: spaceId }, "Fetching space details...", options);
238
+ }
239
+ // ── Write: Login / Logout ────────────────────────────────────────────────────
240
+ export async function twitterLoginAction(options) {
241
+ // Direct cookie import mode
242
+ if (options.cookies) {
243
+ if (!options.proxy) {
244
+ error("--proxy is required when setting cookies directly.");
245
+ process.exit(1);
246
+ }
247
+ setConfig("twitterCookies", options.cookies);
248
+ setConfig("twitterProxy", options.proxy);
249
+ success("Twitter cookies and proxy saved.");
250
+ return;
251
+ }
252
+ // Login via API
253
+ if (!options.username || !options.password || !options.proxy) {
254
+ error("Required: --username, --password, --proxy (and optionally --email, --totp)");
255
+ process.exit(1);
256
+ }
96
257
  const key = requireApiKey();
97
- const spinner = ora("Fetching trends...").start();
98
- // woeid 1 = worldwide
99
- const res = await apiRequest(key, "twitter/trends", {
100
- query: { woeid: "1", count: "30" },
101
- domain: true,
102
- });
258
+ const spinner = ora("Logging in to Twitter...").start();
259
+ const body = {
260
+ user_name: options.username,
261
+ password: options.password,
262
+ proxy: options.proxy,
263
+ };
264
+ if (options.email)
265
+ body.email = options.email;
266
+ if (options.totp)
267
+ body.totp_secret = options.totp;
268
+ const res = await apiRequest(key, "twitter/user_login_v2", { method: "POST", body, domain: true });
103
269
  if (!res.success) {
104
- spinner.fail("Failed to fetch trends");
270
+ spinner.fail("Twitter login failed");
105
271
  error(res.error || "Unknown error");
106
272
  return;
107
273
  }
108
274
  spinner.stop();
275
+ // Extract and store cookies
276
+ const data = res.data;
277
+ const cookies = data?.login_cookies ||
278
+ data?.data?.login_cookies;
279
+ if (cookies) {
280
+ setConfig("twitterCookies", cookies);
281
+ setConfig("twitterProxy", options.proxy);
282
+ success("Logged in! Cookies and proxy saved.");
283
+ }
284
+ else {
285
+ console.log(chalk.yellow("Login response did not contain login_cookies. Raw response:"));
286
+ console.log(formatJson(res.data));
287
+ }
109
288
  if (options.raw) {
110
289
  console.log(JSON.stringify(res.data));
111
290
  }
291
+ }
292
+ export function twitterLogoutAction() {
293
+ setConfig("twitterCookies", "");
294
+ setConfig("twitterProxy", "");
295
+ success("Twitter cookies and proxy cleared.");
296
+ }
297
+ // ── Write: Tweet ─────────────────────────────────────────────────────────────
298
+ export async function tweetAction(text, options) {
299
+ const body = { tweet_text: text };
300
+ if (options.replyTo)
301
+ body.reply_to_tweet_id = options.replyTo;
302
+ if (options.mediaIds)
303
+ body.media_ids = options.mediaIds.split(",");
304
+ return twitterWrite("twitter/create_tweet_v2", body, "Posting tweet...", options);
305
+ }
306
+ // ── Write: Like / Unlike ─────────────────────────────────────────────────────
307
+ export async function twitterLikeAction(tweetId, options) {
308
+ return twitterWrite("twitter/like_tweet_v2", { tweet_id: tweetId }, "Liking tweet...", options);
309
+ }
310
+ export async function twitterUnlikeAction(tweetId, options) {
311
+ return twitterWrite("twitter/unlike_tweet_v2", { tweet_id: tweetId }, "Unliking tweet...", options);
312
+ }
313
+ // ── Write: Retweet ───────────────────────────────────────────────────────────
314
+ export async function twitterRetweetAction(tweetId, options) {
315
+ return twitterWrite("twitter/retweet_tweet_v2", { tweet_id: tweetId }, "Retweeting...", options);
316
+ }
317
+ // ── Write: Delete ────────────────────────────────────────────────────────────
318
+ export async function twitterDeleteAction(tweetId, options) {
319
+ return twitterWrite("twitter/delete_tweet_v2", { tweet_id: tweetId }, "Deleting tweet...", options);
320
+ }
321
+ // ── Write: Follow / Unfollow ─────────────────────────────────────────────────
322
+ export async function twitterFollowAction(userId, options) {
323
+ return twitterWrite("twitter/follow_user_v2", { user_id: userId }, "Following user...", options);
324
+ }
325
+ export async function twitterUnfollowAction(userId, options) {
326
+ return twitterWrite("twitter/unfollow_user_v2", { user_id: userId }, "Unfollowing user...", options);
327
+ }
328
+ // ── Write: Upload Media ──────────────────────────────────────────────────────
329
+ export async function twitterUploadMediaAction(filePath, options) {
330
+ const key = requireApiKey();
331
+ const auth = requireCookies();
332
+ const spinner = ora("Uploading media...").start();
333
+ let formData;
334
+ try {
335
+ const { readFileSync } = await import("node:fs");
336
+ const { basename } = await import("node:path");
337
+ formData = new FormData();
338
+ formData.append("file", new Blob([readFileSync(filePath)]), basename(filePath));
339
+ }
340
+ catch {
341
+ spinner.fail("Failed to read file");
342
+ error(`Could not read file: ${filePath}`);
343
+ return;
344
+ }
345
+ formData.append("login_cookies", auth.login_cookies);
346
+ formData.append("proxy", auth.proxy);
347
+ const url = `${APIS_BASE_URL}/twitter/upload_media_v2`;
348
+ const res = await fetch(url, {
349
+ method: "POST",
350
+ headers: {
351
+ Authorization: `Bearer ${key}`,
352
+ "x-aisa-source": "cli",
353
+ },
354
+ body: formData,
355
+ });
356
+ if (!res.ok) {
357
+ spinner.fail("Upload failed");
358
+ const text = await res.text();
359
+ error(`${res.status}: ${text}`);
360
+ return;
361
+ }
362
+ const data = await res.json();
363
+ spinner.stop();
364
+ if (options.raw) {
365
+ console.log(JSON.stringify(data));
366
+ }
112
367
  else {
113
- console.log(formatJson(res.data));
368
+ success("Media uploaded!");
369
+ console.log(formatJson(data));
114
370
  }
115
371
  }
372
+ // ── Write: Direct Message ────────────────────────────────────────────────────
373
+ export async function twitterDmAction(userId, text, options) {
374
+ return twitterWrite("twitter/send_dm_to_user", { user_id: userId, text }, "Sending DM...", options);
375
+ }
package/dist/config.js CHANGED
@@ -4,9 +4,11 @@ const config = new Conf({
4
4
  projectName: "aisa-cli",
5
5
  schema: {
6
6
  apiKey: { type: "string", default: "" },
7
- defaultModel: { type: "string", default: "gpt-4.1" },
7
+ defaultModel: { type: "string", default: "gpt-4.1-mini" },
8
8
  baseUrl: { type: "string", default: "https://api.aisa.one/v1" },
9
9
  outputFormat: { type: "string", default: "text" },
10
+ twitterCookies: { type: "string", default: "" },
11
+ twitterProxy: { type: "string", default: "" },
10
12
  },
11
13
  });
12
14
  export function getApiKey() {
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.1.8";
1
+ export declare const VERSION = "0.2.0";
2
2
  export declare const BASE_URL = "https://api.aisa.one";
3
3
  export declare const CLI_BASE_URL = "https://api.aisa.one/v1";
4
4
  export declare const APIS_BASE_URL = "https://api.aisa.one/apis/v1";
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.1.8";
1
+ export const VERSION = "0.2.0";
2
2
  export const BASE_URL = "https://api.aisa.one";
3
3
  export const CLI_BASE_URL = `${BASE_URL}/v1`;
4
4
  export const APIS_BASE_URL = `${BASE_URL}/apis/v1`;