@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.
- package/CHANGELOG.md +143 -1
- package/LICENSE +21 -0
- package/README.md +151 -162
- package/dist/api.d.ts +10 -0
- package/dist/api.js +14 -3
- package/dist/catalog.d.ts +1 -18
- package/dist/catalog.js +1 -24
- package/dist/cli-error.d.ts +17 -0
- package/dist/cli-error.js +25 -0
- package/dist/cli-guidance.d.ts +14 -0
- package/dist/cli-guidance.js +55 -0
- package/dist/commands/account.d.ts +17 -0
- package/dist/commands/account.js +45 -1
- package/dist/commands/api.d.ts +0 -11
- package/dist/commands/api.js +6 -175
- package/dist/commands/auth.d.ts +2 -1
- package/dist/commands/auth.js +43 -9
- package/dist/commands/brand-logos.d.ts +6 -0
- package/dist/commands/brand-logos.js +21 -0
- package/dist/commands/configCmd.js +8 -3
- package/dist/commands/connect-shared.d.ts +136 -0
- package/dist/commands/connect-shared.js +143 -0
- package/dist/commands/connect-t2.d.ts +18 -0
- package/dist/commands/connect-t2.js +1599 -0
- package/dist/commands/connect-terminal.d.ts +65 -0
- package/dist/commands/connect-terminal.js +822 -0
- package/dist/commands/connect.d.ts +31 -0
- package/dist/commands/connect.js +3181 -0
- package/dist/commands/flow.d.ts +685 -0
- package/dist/commands/flow.js +697 -0
- package/dist/commands/install.d.ts +91 -0
- package/dist/commands/install.js +258 -0
- package/dist/commands/llm-config.d.ts +103 -0
- package/dist/commands/llm-config.js +452 -0
- package/dist/commands/manifest.d.ts +68 -0
- package/dist/commands/manifest.js +90 -0
- package/dist/commands/mcp.d.ts +59 -0
- package/dist/commands/mcp.js +122 -44
- package/dist/commands/oauth-login.d.ts +33 -0
- package/dist/commands/oauth-login.js +333 -0
- package/dist/commands/prompt.d.ts +96 -0
- package/dist/commands/prompt.js +397 -0
- package/dist/commands/serve-results.d.ts +61 -0
- package/dist/commands/serve-results.js +245 -0
- package/dist/commands/serve-signin.d.ts +28 -0
- package/dist/commands/serve-signin.js +90 -0
- package/dist/commands/signin-page.d.ts +49 -0
- package/dist/commands/signin-page.js +250 -0
- package/dist/commands/skills.js +87 -50
- package/dist/commands/tool-help.d.ts +69 -0
- package/dist/commands/tool-help.js +189 -0
- package/dist/commands/tool-input.d.ts +22 -0
- package/dist/commands/tool-input.js +220 -0
- package/dist/commands/tools.d.ts +12 -0
- package/dist/commands/tools.js +307 -0
- package/dist/commands/update.d.ts +11 -0
- package/dist/commands/update.js +92 -0
- package/dist/commands/vscode.d.ts +65 -0
- package/dist/commands/vscode.js +173 -0
- package/dist/commands/wrappers.d.ts +23 -0
- package/dist/commands/wrappers.js +102 -0
- package/dist/completion.js +3 -80
- package/dist/config.js +76 -2
- package/dist/constants.d.ts +51 -38
- package/dist/constants.js +53 -109
- package/dist/index.js +152 -355
- package/dist/json-text.d.ts +12 -0
- package/dist/json-text.js +60 -0
- package/dist/router.d.ts +39 -0
- package/dist/router.js +59 -0
- package/dist/skills-registry.js +12 -3
- package/dist/utils/banner.d.ts +9 -0
- package/dist/utils/banner.js +41 -0
- package/dist/utils/browser.d.ts +19 -0
- package/dist/utils/browser.js +31 -0
- package/dist/utils/exec.d.ts +43 -0
- package/dist/utils/exec.js +61 -0
- package/dist/utils/http.d.ts +53 -0
- package/dist/utils/http.js +124 -0
- package/dist/utils/install-method.d.ts +36 -0
- package/dist/utils/install-method.js +48 -0
- package/dist/utils/journal.d.ts +57 -0
- package/dist/utils/journal.js +188 -0
- package/dist/utils/key-check.d.ts +20 -0
- package/dist/utils/key-check.js +24 -0
- package/dist/utils/update-check.d.ts +23 -0
- package/dist/utils/update-check.js +116 -0
- package/dist/utils/width.d.ts +32 -0
- package/dist/utils/width.js +73 -0
- package/package.json +8 -4
- package/vscode-extension/README.md +6 -0
- package/vscode-extension/aisa-vscode.vsix +0 -0
- package/dist/commands/finance.d.ts +0 -17
- package/dist/commands/finance.js +0 -342
- package/dist/commands/run.d.ts +0 -28
- package/dist/commands/run.js +0 -182
- package/dist/commands/search.d.ts +0 -9
- package/dist/commands/search.js +0 -81
- package/dist/commands/twitter.d.ts +0 -138
- package/dist/commands/twitter.js +0 -374
- package/dist/commands/video.d.ts +0 -49
- package/dist/commands/video.js +0 -311
package/dist/commands/video.js
DELETED
|
@@ -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
|
-
}
|