@aisa-one/cli 0.3.0 → 0.5.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +143 -1
  2. package/LICENSE +21 -0
  3. package/README.md +151 -162
  4. package/dist/api.d.ts +10 -0
  5. package/dist/api.js +14 -3
  6. package/dist/catalog.d.ts +1 -18
  7. package/dist/catalog.js +1 -24
  8. package/dist/cli-error.d.ts +17 -0
  9. package/dist/cli-error.js +25 -0
  10. package/dist/cli-guidance.d.ts +14 -0
  11. package/dist/cli-guidance.js +55 -0
  12. package/dist/commands/account.d.ts +17 -0
  13. package/dist/commands/account.js +45 -1
  14. package/dist/commands/api.d.ts +0 -11
  15. package/dist/commands/api.js +6 -175
  16. package/dist/commands/auth.d.ts +2 -1
  17. package/dist/commands/auth.js +43 -9
  18. package/dist/commands/brand-logos.d.ts +6 -0
  19. package/dist/commands/brand-logos.js +21 -0
  20. package/dist/commands/configCmd.js +8 -3
  21. package/dist/commands/connect-shared.d.ts +136 -0
  22. package/dist/commands/connect-shared.js +143 -0
  23. package/dist/commands/connect-t2.d.ts +18 -0
  24. package/dist/commands/connect-t2.js +1599 -0
  25. package/dist/commands/connect-terminal.d.ts +65 -0
  26. package/dist/commands/connect-terminal.js +822 -0
  27. package/dist/commands/connect.d.ts +31 -0
  28. package/dist/commands/connect.js +3181 -0
  29. package/dist/commands/flow.d.ts +685 -0
  30. package/dist/commands/flow.js +697 -0
  31. package/dist/commands/install.d.ts +91 -0
  32. package/dist/commands/install.js +258 -0
  33. package/dist/commands/llm-config.d.ts +103 -0
  34. package/dist/commands/llm-config.js +452 -0
  35. package/dist/commands/manifest.d.ts +68 -0
  36. package/dist/commands/manifest.js +90 -0
  37. package/dist/commands/mcp.d.ts +59 -0
  38. package/dist/commands/mcp.js +122 -44
  39. package/dist/commands/oauth-login.d.ts +33 -0
  40. package/dist/commands/oauth-login.js +333 -0
  41. package/dist/commands/prompt.d.ts +96 -0
  42. package/dist/commands/prompt.js +397 -0
  43. package/dist/commands/serve-results.d.ts +61 -0
  44. package/dist/commands/serve-results.js +245 -0
  45. package/dist/commands/serve-signin.d.ts +28 -0
  46. package/dist/commands/serve-signin.js +90 -0
  47. package/dist/commands/signin-page.d.ts +49 -0
  48. package/dist/commands/signin-page.js +250 -0
  49. package/dist/commands/skills.js +87 -50
  50. package/dist/commands/tool-help.d.ts +69 -0
  51. package/dist/commands/tool-help.js +189 -0
  52. package/dist/commands/tool-input.d.ts +22 -0
  53. package/dist/commands/tool-input.js +220 -0
  54. package/dist/commands/tools.d.ts +12 -0
  55. package/dist/commands/tools.js +307 -0
  56. package/dist/commands/update.d.ts +11 -0
  57. package/dist/commands/update.js +92 -0
  58. package/dist/commands/vscode.d.ts +65 -0
  59. package/dist/commands/vscode.js +173 -0
  60. package/dist/commands/wrappers.d.ts +23 -0
  61. package/dist/commands/wrappers.js +102 -0
  62. package/dist/completion.js +3 -80
  63. package/dist/config.js +76 -2
  64. package/dist/constants.d.ts +51 -38
  65. package/dist/constants.js +53 -109
  66. package/dist/index.js +152 -355
  67. package/dist/json-text.d.ts +12 -0
  68. package/dist/json-text.js +60 -0
  69. package/dist/router.d.ts +39 -0
  70. package/dist/router.js +59 -0
  71. package/dist/skills-registry.js +12 -3
  72. package/dist/utils/banner.d.ts +9 -0
  73. package/dist/utils/banner.js +41 -0
  74. package/dist/utils/browser.d.ts +19 -0
  75. package/dist/utils/browser.js +31 -0
  76. package/dist/utils/exec.d.ts +43 -0
  77. package/dist/utils/exec.js +61 -0
  78. package/dist/utils/http.d.ts +53 -0
  79. package/dist/utils/http.js +124 -0
  80. package/dist/utils/install-method.d.ts +36 -0
  81. package/dist/utils/install-method.js +48 -0
  82. package/dist/utils/journal.d.ts +57 -0
  83. package/dist/utils/journal.js +188 -0
  84. package/dist/utils/key-check.d.ts +20 -0
  85. package/dist/utils/key-check.js +24 -0
  86. package/dist/utils/update-check.d.ts +23 -0
  87. package/dist/utils/update-check.js +116 -0
  88. package/dist/utils/width.d.ts +32 -0
  89. package/dist/utils/width.js +73 -0
  90. package/package.json +8 -4
  91. package/vscode-extension/README.md +6 -0
  92. package/vscode-extension/aisa-vscode.vsix +0 -0
  93. package/dist/commands/finance.d.ts +0 -17
  94. package/dist/commands/finance.js +0 -342
  95. package/dist/commands/run.d.ts +0 -28
  96. package/dist/commands/run.js +0 -182
  97. package/dist/commands/search.d.ts +0 -9
  98. package/dist/commands/search.js +0 -81
  99. package/dist/commands/twitter.d.ts +0 -138
  100. package/dist/commands/twitter.js +0 -374
  101. package/dist/commands/video.d.ts +0 -49
  102. package/dist/commands/video.js +0 -311
@@ -1,311 +0,0 @@
1
- import { writeFile } from "node:fs/promises";
2
- import ora from "ora";
3
- import chalk from "chalk";
4
- import { requireApiKey } from "../config.js";
5
- import { apiRequest } from "../api.js";
6
- import { error, formatJson, hint, success } from "../utils/display.js";
7
- import { DEFAULT_VIDEO_MODEL, MEDIA_TYPES, VIDEO_MODELS, videoModelSpec, } from "../constants.js";
8
- /** Statuses after which the task will never change again. */
9
- const TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
10
- const POLL_INTERVAL_MS = 5000;
11
- const MAX_WAIT_MS = 15 * 60 * 1000;
12
- const VIDEO_KEY = /video.*url|url.*video/i;
13
- const URL_KEY = /url$/i;
14
- /** Keys that carry a still image rather than the clip — thumbnails end in "url" too. */
15
- const NOT_VIDEO_KEY = /thumb|cover|preview|poster|image|img|icon|logo|snapshot|avatar|first_frame|last_frame/i;
16
- function searchUrl(value, keyMatches, depth = 0) {
17
- if (depth > 8 || value == null)
18
- return undefined;
19
- if (Array.isArray(value)) {
20
- for (const item of value) {
21
- const found = searchUrl(item, keyMatches, depth + 1);
22
- if (found)
23
- return found;
24
- }
25
- return undefined;
26
- }
27
- if (typeof value !== "object")
28
- return undefined;
29
- const entries = Object.entries(value);
30
- for (const [key, val] of entries) {
31
- if (typeof val === "string" && keyMatches(key) && /^https?:\/\//.test(val))
32
- return val;
33
- }
34
- for (const [, val] of entries) {
35
- const found = searchUrl(val, keyMatches, depth + 1);
36
- if (found)
37
- return found;
38
- }
39
- return undefined;
40
- }
41
- /**
42
- * Each upstream nests the finished video somewhere different (DashScope puts it
43
- * at result.output.video_url, Seedance under result.data[].content[].url), so
44
- * search rather than hardcode a path.
45
- *
46
- * Every pass covers the whole tree before the next one starts, and each is
47
- * narrower than a plain "ends in url" match would be. Responses ship thumbnails
48
- * and cover images beside the clip, and `thumbnail_url` both ends in "url" and
49
- * tends to sit closer to the root than the video does — matching it would make
50
- * `--output` download a still image.
51
- */
52
- export function findVideoUrl(value) {
53
- // The still-image exclusion applies to every pass: `video_thumbnail_url`
54
- // matches the video-named pattern too, and saving it via --output would hand
55
- // the user a JPEG with an .mp4 name.
56
- return (
57
- // 1. Explicitly video-named: output.video_url
58
- searchUrl(value, (k) => VIDEO_KEY.test(k) && !NOT_VIDEO_KEY.test(k)) ??
59
- // 2. A bare `url`, which is what a media-item object uses: content[].url
60
- searchUrl(value, (k) => k.toLowerCase() === "url") ??
61
- // 3. Any other *_url, minus the ones that name a still image
62
- searchUrl(value, (k) => URL_KEY.test(k) && !NOT_VIDEO_KEY.test(k)));
63
- }
64
- function printTask(task, fallbackId) {
65
- const url = task.result ? findVideoUrl(task.result) : undefined;
66
- console.log(` Task: ${task.id || fallbackId}`);
67
- console.log(` Status: ${task.status || "unknown"}`);
68
- if (task.model)
69
- console.log(` Model: ${task.model}`);
70
- if (task.progress && !TERMINAL_STATUSES.has(task.status || "")) {
71
- console.log(` Progress: ${task.progress}`);
72
- }
73
- if (url)
74
- console.log(` URL: ${chalk.cyan(url)}`);
75
- if (task.error?.message)
76
- console.log(` Error: ${chalk.red(task.error.message)}`);
77
- }
78
- async function downloadVideo(url, outputPath) {
79
- const res = await fetch(url);
80
- if (!res.ok)
81
- throw new Error(`Download failed: ${res.status}`);
82
- const buffer = Buffer.from(await res.arrayBuffer());
83
- await writeFile(outputPath, buffer);
84
- }
85
- /** Parse `--media first_frame=https://…`, and bare URLs from `--image`. */
86
- export function parseMedia(specs = [], images = []) {
87
- const media = images.map((url) => ({ type: "first_frame", url }));
88
- for (const spec of specs) {
89
- const eq = spec.indexOf("=");
90
- if (eq < 0) {
91
- throw new Error(`Invalid --media "${spec}". Use type=url, e.g. first_frame=https://…`);
92
- }
93
- const type = spec.slice(0, eq);
94
- const url = spec.slice(eq + 1);
95
- if (!MEDIA_TYPES.includes(type)) {
96
- throw new Error(`Unknown media type "${type}". Valid: ${MEDIA_TYPES.join(", ")}`);
97
- }
98
- if (!url)
99
- throw new Error(`Missing URL in --media "${spec}"`);
100
- media.push({ type, url });
101
- }
102
- return media;
103
- }
104
- /**
105
- * Build the request body for a model's vendor. The gateway rewrites only the
106
- * top-level `model` and forwards the rest verbatim, so a Bailian body sent to a
107
- * Seedance model is rejected with a 400.
108
- */
109
- export function buildVideoBody(model, prompt, media, options) {
110
- const spec = videoModelSpec(model);
111
- if (spec.family === "seedance") {
112
- // BytePlus takes a multimodal content array rather than input/parameters.
113
- return {
114
- model,
115
- content: [
116
- { type: "text", text: prompt },
117
- ...media.map((m) => ({ type: "image_url", image_url: { url: m.url } })),
118
- ],
119
- };
120
- }
121
- return {
122
- model,
123
- input: { prompt, ...(media.length > 0 ? { media } : {}) },
124
- parameters: {
125
- resolution: options.resolution || "720P",
126
- duration: options.duration ? parseInt(options.duration) : 5,
127
- },
128
- };
129
- }
130
- /**
131
- * Parse a `--body` payload. The option means "send this verbatim", so the model
132
- * it names wins over the CLI's default — only an explicit `--model` overrides
133
- * it. Anything else would silently redirect a valid request to another model.
134
- */
135
- export function parseBodyOverride(raw, explicitModel) {
136
- let parsed;
137
- try {
138
- parsed = JSON.parse(raw);
139
- }
140
- catch {
141
- throw new Error("--body must be valid JSON");
142
- }
143
- // JSON.parse accepts null, arrays and numbers; none of them are request bodies.
144
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
145
- throw new Error("--body must be a JSON object");
146
- }
147
- const body = { ...parsed };
148
- if (explicitModel)
149
- body.model = explicitModel;
150
- else if (!body.model)
151
- body.model = DEFAULT_VIDEO_MODEL;
152
- return body;
153
- }
154
- export async function videoCreateAction(prompt, options) {
155
- const key = requireApiKey();
156
- let body;
157
- if (options.body) {
158
- // Escape hatch for shapes the CLI does not model; the gateway is a pass-through.
159
- try {
160
- body = parseBodyOverride(options.body, options.model);
161
- }
162
- catch (err) {
163
- error(err.message);
164
- return;
165
- }
166
- }
167
- else {
168
- const model = options.model || DEFAULT_VIDEO_MODEL;
169
- const spec = videoModelSpec(model);
170
- let media;
171
- try {
172
- media = parseMedia(options.media, options.image);
173
- }
174
- catch (err) {
175
- error(err.message);
176
- return;
177
- }
178
- // Image- and clip-driven models are accepted at submission time and only
179
- // fail a minute later, after billing. Refuse before spending anything —
180
- // and check for the type the model actually wants, since supplying a
181
- // first_clip to an i2v model fails exactly the same way as supplying none.
182
- if (spec.requiresMedia && !media.some((m) => m.type === spec.requiresMedia)) {
183
- const shorthand = spec.requiresMedia === "first_frame" ? " (or --image <url>)" : "";
184
- error(media.length === 0
185
- ? `${model} needs source media.`
186
- : `${model} needs ${spec.requiresMedia} media, got ${media.map((m) => m.type).join(", ")}.`);
187
- hint(`Pass --media ${spec.requiresMedia}=<url>${shorthand}`);
188
- return;
189
- }
190
- if (spec.family === "seedance" && media.length > 0) {
191
- hint("Seedance media handling is unverified — use --body if the request is rejected");
192
- }
193
- body = buildVideoBody(model, prompt, media, options);
194
- }
195
- const spinner = ora("Creating video generation task...").start();
196
- const res = await apiRequest(key, "video/generations", {
197
- method: "POST",
198
- body,
199
- });
200
- if (!res.success || !res.data) {
201
- spinner.fail("Failed to create video task");
202
- error(res.error || "Unknown error");
203
- if ((res.error || "").includes("model not found")) {
204
- hint(`Available video models: ${Object.keys(VIDEO_MODELS).join(", ")}`);
205
- }
206
- return;
207
- }
208
- spinner.stop();
209
- const taskId = res.data.id;
210
- // --raw changes the output format; it must not cancel work the user asked
211
- // for. Only skip polling when nothing was requested beyond submission.
212
- if (!options.wait || !taskId) {
213
- if (options.raw) {
214
- console.log(JSON.stringify(res.data));
215
- return;
216
- }
217
- if (!taskId) {
218
- console.log(formatJson(res.data));
219
- return;
220
- }
221
- success(`Task created: ${taskId}`);
222
- hint(`Check status: aisa video status ${taskId}`);
223
- return;
224
- }
225
- if (!options.raw)
226
- success(`Task created: ${taskId}`);
227
- const pollSpinner = options.raw ? undefined : ora("Generating video...").start();
228
- const deadline = Date.now() + MAX_WAIT_MS;
229
- let task = res.data;
230
- while (!TERMINAL_STATUSES.has(task.status || "")) {
231
- if (Date.now() > deadline) {
232
- pollSpinner?.warn(`Still ${task.status || "pending"} after 15 minutes — giving up on waiting`);
233
- if (options.raw)
234
- console.log(JSON.stringify(task));
235
- else
236
- hint(`Check later: aisa video status ${taskId}`);
237
- return;
238
- }
239
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
240
- const pollRes = await apiRequest(key, `video/generations/${taskId}`);
241
- if (!pollRes.success || !pollRes.data) {
242
- pollSpinner?.fail("Failed to check status");
243
- error(pollRes.error || "Unknown error");
244
- return;
245
- }
246
- task = pollRes.data;
247
- if (pollSpinner) {
248
- pollSpinner.text = `Generating video... (${task.status}${task.progress ? ` ${task.progress}` : ""})`;
249
- }
250
- }
251
- const videoUrl = findVideoUrl(task.result);
252
- if (task.status !== "completed") {
253
- pollSpinner?.fail(`Video generation ${task.status}`);
254
- if (options.raw)
255
- console.log(JSON.stringify(task));
256
- else if (task.error?.message)
257
- error(task.error.message);
258
- return;
259
- }
260
- pollSpinner?.succeed("Video generated!");
261
- if (options.output) {
262
- await download(videoUrl, options.output, options.raw);
263
- }
264
- if (options.raw) {
265
- console.log(JSON.stringify(task));
266
- return;
267
- }
268
- if (videoUrl)
269
- console.log(` URL: ${chalk.cyan(videoUrl)}`);
270
- else
271
- console.log(formatJson(task.result));
272
- }
273
- /** Download to `output`, reporting through a spinner unless output is raw JSON. */
274
- async function download(videoUrl, output, raw) {
275
- if (!videoUrl) {
276
- error("No video URL available to download");
277
- return;
278
- }
279
- const spinner = raw ? undefined : ora(`Downloading to ${output}...`).start();
280
- try {
281
- await downloadVideo(videoUrl, output);
282
- spinner?.succeed(`Saved to ${output}`);
283
- }
284
- catch (err) {
285
- const message = `Download failed: ${err.message}`;
286
- if (spinner)
287
- spinner.fail(message);
288
- else
289
- error(message);
290
- }
291
- }
292
- export async function videoStatusAction(taskId, options) {
293
- const key = requireApiKey();
294
- const spinner = ora("Checking status...").start();
295
- const res = await apiRequest(key, `video/generations/${taskId}`);
296
- if (!res.success || !res.data) {
297
- spinner.fail("Failed to check status");
298
- error(res.error || "Unknown error");
299
- return;
300
- }
301
- spinner.stop();
302
- // --raw selects the output format; it must not skip a requested download.
303
- if (options.output) {
304
- await download(findVideoUrl(res.data.result), options.output, options.raw);
305
- }
306
- if (options.raw) {
307
- console.log(JSON.stringify(res.data));
308
- return;
309
- }
310
- printTask(res.data, taskId);
311
- }