@wji.ai/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.
- package/README.md +242 -0
- package/dist/commands/agent.js +274 -0
- package/dist/commands/asset.js +241 -0
- package/dist/commands/auth.js +216 -0
- package/dist/commands/canvas.js +915 -0
- package/dist/commands/config.js +113 -0
- package/dist/commands/fsnode.js +252 -0
- package/dist/commands/gen.js +366 -0
- package/dist/commands/model.js +134 -0
- package/dist/commands/org.js +85 -0
- package/dist/commands/preset.js +176 -0
- package/dist/commands/profile.js +129 -0
- package/dist/commands/skill.js +272 -0
- package/dist/commands/workspace.js +305 -0
- package/dist/core/client.js +252 -0
- package/dist/core/config.js +95 -0
- package/dist/core/output.js +143 -0
- package/dist/core/prompt.js +118 -0
- package/dist/core/zip.js +87 -0
- package/dist/index.js +94 -0
- package/package.json +49 -0
- package/skill/wji/SKILL.md +398 -0
- package/skill/wji/reference/canvas-recipes.md +179 -0
- package/skill/wji/reference/commands.md +381 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const useColor = process.env.NO_COLOR == null && process.env.WJI_NO_COLOR == null && process.stdout.isTTY;
|
|
2
|
+
const wrap = (code) => (s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : s);
|
|
3
|
+
export const color = {
|
|
4
|
+
dim: wrap('2'),
|
|
5
|
+
bold: wrap('1'),
|
|
6
|
+
red: wrap('31'),
|
|
7
|
+
green: wrap('32'),
|
|
8
|
+
yellow: wrap('33'),
|
|
9
|
+
blue: wrap('34'),
|
|
10
|
+
cyan: wrap('36'),
|
|
11
|
+
};
|
|
12
|
+
/** Global --json: when on, every command prints raw JSON and nothing else. */
|
|
13
|
+
let jsonMode = false;
|
|
14
|
+
export function setJsonMode(on) {
|
|
15
|
+
jsonMode = on;
|
|
16
|
+
}
|
|
17
|
+
export function isJsonMode() {
|
|
18
|
+
return jsonMode;
|
|
19
|
+
}
|
|
20
|
+
export function printJson(value) {
|
|
21
|
+
process.stdout.write(JSON.stringify(value, null, 2) + '\n');
|
|
22
|
+
}
|
|
23
|
+
/** Print `value` as JSON in --json mode, otherwise run `human`. */
|
|
24
|
+
export function render(value, human) {
|
|
25
|
+
if (jsonMode)
|
|
26
|
+
return printJson(value);
|
|
27
|
+
human(value);
|
|
28
|
+
}
|
|
29
|
+
export function info(message) {
|
|
30
|
+
if (!jsonMode)
|
|
31
|
+
process.stderr.write(message + '\n');
|
|
32
|
+
}
|
|
33
|
+
export function success(message) {
|
|
34
|
+
if (!jsonMode)
|
|
35
|
+
process.stderr.write(color.green('✓ ') + message + '\n');
|
|
36
|
+
}
|
|
37
|
+
export function warn(message) {
|
|
38
|
+
process.stderr.write(color.yellow('! ') + message + '\n');
|
|
39
|
+
}
|
|
40
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
41
|
+
/**
|
|
42
|
+
* Terminal cell width. CJK and emoji occupy two columns, so counting code
|
|
43
|
+
* points misaligns every table that holds Chinese text.
|
|
44
|
+
*/
|
|
45
|
+
function charWidth(code) {
|
|
46
|
+
if (code === 0x200d)
|
|
47
|
+
return 0;
|
|
48
|
+
if (code >= 0x0300 && code <= 0x036f)
|
|
49
|
+
return 0; // combining marks
|
|
50
|
+
if (code >= 0xfe00 && code <= 0xfe0f)
|
|
51
|
+
return 0; // variation selectors
|
|
52
|
+
return (code >= 0x1100 && code <= 0x115f) ||
|
|
53
|
+
(code >= 0x2e80 && code <= 0x303e) ||
|
|
54
|
+
(code >= 0x3041 && code <= 0x33ff) ||
|
|
55
|
+
(code >= 0x3400 && code <= 0x4dbf) ||
|
|
56
|
+
(code >= 0x4e00 && code <= 0x9fff) ||
|
|
57
|
+
(code >= 0xa000 && code <= 0xa4cf) ||
|
|
58
|
+
(code >= 0xa960 && code <= 0xa97f) ||
|
|
59
|
+
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
60
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
61
|
+
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
62
|
+
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
63
|
+
(code >= 0xff00 && code <= 0xff60) ||
|
|
64
|
+
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
65
|
+
(code >= 0x1f300 && code <= 0x1f64f) ||
|
|
66
|
+
(code >= 0x1f900 && code <= 0x1f9ff) ||
|
|
67
|
+
(code >= 0x20000 && code <= 0x3fffd)
|
|
68
|
+
? 2
|
|
69
|
+
: 1;
|
|
70
|
+
}
|
|
71
|
+
export function displayWidth(text) {
|
|
72
|
+
let width = 0;
|
|
73
|
+
for (const char of stripAnsi(text))
|
|
74
|
+
width += charWidth(char.codePointAt(0));
|
|
75
|
+
return width;
|
|
76
|
+
}
|
|
77
|
+
const visibleLength = displayWidth;
|
|
78
|
+
export function table(rows, columns) {
|
|
79
|
+
if (!rows.length) {
|
|
80
|
+
info(color.dim('(empty)'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const cells = rows.map((row) => columns.map((c) => {
|
|
84
|
+
const v = c.value(row);
|
|
85
|
+
return v == null ? color.dim('-') : String(v);
|
|
86
|
+
}));
|
|
87
|
+
const widths = columns.map((c, i) => Math.max(visibleLength(c.header), ...cells.map((r) => visibleLength(r[i]))));
|
|
88
|
+
const pad = (s, w) => s + ' '.repeat(Math.max(0, w - displayWidth(s)));
|
|
89
|
+
process.stdout.write(columns.map((c, i) => color.dim(pad(c.header.toUpperCase(), widths[i]))).join(' ').trimEnd() +
|
|
90
|
+
'\n');
|
|
91
|
+
for (const row of cells) {
|
|
92
|
+
process.stdout.write(row.map((v, i) => pad(v, widths[i])).join(' ').trimEnd() + '\n');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Key/value block for detail views. */
|
|
96
|
+
export function detail(pairs) {
|
|
97
|
+
const width = Math.max(...pairs.map(([k]) => displayWidth(k)));
|
|
98
|
+
for (const [k, v] of pairs) {
|
|
99
|
+
if (v == null || v === '')
|
|
100
|
+
continue;
|
|
101
|
+
const text = typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
102
|
+
const padded = k + ' '.repeat(Math.max(0, width - displayWidth(k)));
|
|
103
|
+
process.stdout.write(`${color.dim(padded)} ${text}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Truncate to `max` terminal columns, not `max` code points. */
|
|
107
|
+
export function truncate(value, max = 48) {
|
|
108
|
+
if (value == null)
|
|
109
|
+
return null;
|
|
110
|
+
const s = String(value).replace(/\s+/g, ' ').trim();
|
|
111
|
+
if (displayWidth(s) <= max)
|
|
112
|
+
return s;
|
|
113
|
+
let out = '';
|
|
114
|
+
let width = 0;
|
|
115
|
+
for (const char of s) {
|
|
116
|
+
const next = width + charWidth(char.codePointAt(0));
|
|
117
|
+
if (next > max - 1)
|
|
118
|
+
break;
|
|
119
|
+
out += char;
|
|
120
|
+
width = next;
|
|
121
|
+
}
|
|
122
|
+
return out + '…';
|
|
123
|
+
}
|
|
124
|
+
export function relativeTime(value) {
|
|
125
|
+
if (!value)
|
|
126
|
+
return null;
|
|
127
|
+
const then = new Date(value).getTime();
|
|
128
|
+
if (Number.isNaN(then))
|
|
129
|
+
return null;
|
|
130
|
+
const diff = Date.now() - then;
|
|
131
|
+
const units = [
|
|
132
|
+
[86400000 * 365, 'y'],
|
|
133
|
+
[86400000 * 30, 'mo'],
|
|
134
|
+
[86400000, 'd'],
|
|
135
|
+
[3600000, 'h'],
|
|
136
|
+
[60000, 'm'],
|
|
137
|
+
];
|
|
138
|
+
for (const [ms, label] of units) {
|
|
139
|
+
if (Math.abs(diff) >= ms)
|
|
140
|
+
return `${Math.round(diff / ms)}${label} ago`;
|
|
141
|
+
}
|
|
142
|
+
return 'just now';
|
|
143
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { stdin, stdout } from 'node:process';
|
|
3
|
+
const ETX = '\u0003'; // Ctrl-C
|
|
4
|
+
const BACKSPACE = '\u007f';
|
|
5
|
+
export async function ask(question) {
|
|
6
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
7
|
+
try {
|
|
8
|
+
return (await rl.question(question)).trim();
|
|
9
|
+
}
|
|
10
|
+
finally {
|
|
11
|
+
rl.close();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Read a secret without echoing it. Falls back to a normal read on a non-TTY stdin. */
|
|
15
|
+
export async function askSecret(question) {
|
|
16
|
+
if (!stdin.isTTY)
|
|
17
|
+
return ask(question);
|
|
18
|
+
stdout.write(question);
|
|
19
|
+
stdin.setRawMode(true);
|
|
20
|
+
stdin.resume();
|
|
21
|
+
stdin.setEncoding('utf8');
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
let value = '';
|
|
24
|
+
const finish = (fn) => {
|
|
25
|
+
stdin.setRawMode(false);
|
|
26
|
+
stdin.pause();
|
|
27
|
+
stdin.removeListener('data', onData);
|
|
28
|
+
stdout.write('\n');
|
|
29
|
+
fn();
|
|
30
|
+
};
|
|
31
|
+
const onData = (chunk) => {
|
|
32
|
+
for (const char of chunk) {
|
|
33
|
+
if (char === '\r' || char === '\n')
|
|
34
|
+
return finish(() => resolve(value));
|
|
35
|
+
if (char === ETX)
|
|
36
|
+
return finish(() => reject(new Error('Cancelled')));
|
|
37
|
+
if (char === BACKSPACE || char === '\b') {
|
|
38
|
+
value = value.slice(0, -1);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
value += char;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
stdin.on('data', onData);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export async function confirm(question, force) {
|
|
48
|
+
if (force)
|
|
49
|
+
return true;
|
|
50
|
+
if (!stdin.isTTY) {
|
|
51
|
+
throw new Error(`Refusing to continue without confirmation on a non-TTY. Pass --yes. (${question})`);
|
|
52
|
+
}
|
|
53
|
+
const answer = (await ask(`${question} [y/N] `)).toLowerCase();
|
|
54
|
+
return answer === 'y' || answer === 'yes';
|
|
55
|
+
}
|
|
56
|
+
/** Read a value given inline, as `@file`, or as `-` for stdin. */
|
|
57
|
+
export async function readValue(value) {
|
|
58
|
+
const { readFile } = await import('node:fs/promises');
|
|
59
|
+
if (value === '-') {
|
|
60
|
+
const chunks = [];
|
|
61
|
+
for await (const chunk of stdin)
|
|
62
|
+
chunks.push(chunk);
|
|
63
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
64
|
+
}
|
|
65
|
+
if (value.startsWith('@'))
|
|
66
|
+
return readFile(value.slice(1), 'utf8');
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
/** Parse a JSON option that may be inline JSON, `@file` or `-`. */
|
|
70
|
+
export async function readJson(value) {
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
const text = await readValue(value);
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(text);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
throw new Error(`Invalid JSON: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Parse repeated `--set key=value` pairs into a nested object. */
|
|
82
|
+
export function parseKeyValues(pairs = []) {
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const pair of pairs) {
|
|
85
|
+
const index = pair.indexOf('=');
|
|
86
|
+
if (index === -1)
|
|
87
|
+
throw new Error(`Expected key=value, got "${pair}"`);
|
|
88
|
+
const key = pair.slice(0, index);
|
|
89
|
+
const raw = pair.slice(index + 1);
|
|
90
|
+
let value = raw;
|
|
91
|
+
if (raw === 'true')
|
|
92
|
+
value = true;
|
|
93
|
+
else if (raw === 'false')
|
|
94
|
+
value = false;
|
|
95
|
+
else if (raw === 'null')
|
|
96
|
+
value = null;
|
|
97
|
+
else if (raw !== '' && !Number.isNaN(Number(raw)))
|
|
98
|
+
value = Number(raw);
|
|
99
|
+
else if (raw.startsWith('{') || raw.startsWith('[')) {
|
|
100
|
+
try {
|
|
101
|
+
value = JSON.parse(raw);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* leave it as a string */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Dotted keys build nested objects, matching the API's qs allowDots parsing.
|
|
108
|
+
const path = key.split('.');
|
|
109
|
+
let cursor = out;
|
|
110
|
+
for (const segment of path.slice(0, -1)) {
|
|
111
|
+
if (typeof cursor[segment] !== 'object' || cursor[segment] === null)
|
|
112
|
+
cursor[segment] = {};
|
|
113
|
+
cursor = cursor[segment];
|
|
114
|
+
}
|
|
115
|
+
cursor[path[path.length - 1]] = value;
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
package/dist/core/zip.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ZIP writer — "stored" (no compression) entries only.
|
|
3
|
+
*
|
|
4
|
+
* The backend's skill importer keeps just the `.md` files and re-parses them, so
|
|
5
|
+
* compression buys nothing here; storing avoids pulling a zip dependency into an
|
|
6
|
+
* otherwise dependency-light CLI. Enough of the spec is implemented to produce a
|
|
7
|
+
* archive JSZip (what the backend uses) reads back correctly.
|
|
8
|
+
*/
|
|
9
|
+
const CRC_TABLE = (() => {
|
|
10
|
+
const table = new Uint32Array(256);
|
|
11
|
+
for (let n = 0; n < 256; n++) {
|
|
12
|
+
let c = n;
|
|
13
|
+
for (let k = 0; k < 8; k++)
|
|
14
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
15
|
+
table[n] = c >>> 0;
|
|
16
|
+
}
|
|
17
|
+
return table;
|
|
18
|
+
})();
|
|
19
|
+
function crc32(buf) {
|
|
20
|
+
let crc = 0xffffffff;
|
|
21
|
+
for (let i = 0; i < buf.length; i++)
|
|
22
|
+
crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
23
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
24
|
+
}
|
|
25
|
+
/** DOS date/time — a fixed, valid timestamp is fine; the backend ignores it. */
|
|
26
|
+
function dosDateTime(date = new Date()) {
|
|
27
|
+
const time = (date.getHours() << 11) | (date.getMinutes() << 5) | (Math.floor(date.getSeconds() / 2));
|
|
28
|
+
const day = (((date.getFullYear() - 1980) & 0x7f) << 9) | ((date.getMonth() + 1) << 5) | date.getDate();
|
|
29
|
+
return { time: time & 0xffff, day: day & 0xffff };
|
|
30
|
+
}
|
|
31
|
+
export function createZip(entries) {
|
|
32
|
+
const { time, day } = dosDateTime();
|
|
33
|
+
const locals = [];
|
|
34
|
+
const centrals = [];
|
|
35
|
+
let offset = 0;
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
const nameBuf = Buffer.from(entry.path.replace(/\\/g, '/'), 'utf8');
|
|
38
|
+
const data = Buffer.isBuffer(entry.content) ? entry.content : Buffer.from(entry.content, 'utf8');
|
|
39
|
+
const crc = crc32(data);
|
|
40
|
+
const local = Buffer.alloc(30 + nameBuf.length);
|
|
41
|
+
local.writeUInt32LE(0x04034b50, 0); // local file header signature
|
|
42
|
+
local.writeUInt16LE(20, 4); // version needed
|
|
43
|
+
local.writeUInt16LE(0, 6); // flags
|
|
44
|
+
local.writeUInt16LE(0, 8); // method: store
|
|
45
|
+
local.writeUInt16LE(time, 10);
|
|
46
|
+
local.writeUInt16LE(day, 12);
|
|
47
|
+
local.writeUInt32LE(crc, 14);
|
|
48
|
+
local.writeUInt32LE(data.length, 18); // compressed size
|
|
49
|
+
local.writeUInt32LE(data.length, 22); // uncompressed size
|
|
50
|
+
local.writeUInt16LE(nameBuf.length, 26);
|
|
51
|
+
local.writeUInt16LE(0, 28); // extra length
|
|
52
|
+
nameBuf.copy(local, 30);
|
|
53
|
+
locals.push(local, data);
|
|
54
|
+
const central = Buffer.alloc(46 + nameBuf.length);
|
|
55
|
+
central.writeUInt32LE(0x02014b50, 0); // central directory header signature
|
|
56
|
+
central.writeUInt16LE(20, 4); // version made by
|
|
57
|
+
central.writeUInt16LE(20, 6); // version needed
|
|
58
|
+
central.writeUInt16LE(0, 8); // flags
|
|
59
|
+
central.writeUInt16LE(0, 10); // method
|
|
60
|
+
central.writeUInt16LE(time, 12);
|
|
61
|
+
central.writeUInt16LE(day, 14);
|
|
62
|
+
central.writeUInt32LE(crc, 16);
|
|
63
|
+
central.writeUInt32LE(data.length, 20);
|
|
64
|
+
central.writeUInt32LE(data.length, 24);
|
|
65
|
+
central.writeUInt16LE(nameBuf.length, 28);
|
|
66
|
+
central.writeUInt16LE(0, 30); // extra length
|
|
67
|
+
central.writeUInt16LE(0, 32); // comment length
|
|
68
|
+
central.writeUInt16LE(0, 34); // disk number
|
|
69
|
+
central.writeUInt16LE(0, 36); // internal attrs
|
|
70
|
+
central.writeUInt32LE(0, 38); // external attrs
|
|
71
|
+
central.writeUInt32LE(offset, 42); // local header offset
|
|
72
|
+
nameBuf.copy(central, 46);
|
|
73
|
+
centrals.push(central);
|
|
74
|
+
offset += local.length + data.length;
|
|
75
|
+
}
|
|
76
|
+
const centralDir = Buffer.concat(centrals);
|
|
77
|
+
const end = Buffer.alloc(22);
|
|
78
|
+
end.writeUInt32LE(0x06054b50, 0); // end of central directory signature
|
|
79
|
+
end.writeUInt16LE(0, 4); // disk number
|
|
80
|
+
end.writeUInt16LE(0, 6); // central dir start disk
|
|
81
|
+
end.writeUInt16LE(entries.length, 8); // entries on this disk
|
|
82
|
+
end.writeUInt16LE(entries.length, 10); // total entries
|
|
83
|
+
end.writeUInt32LE(centralDir.length, 12);
|
|
84
|
+
end.writeUInt32LE(offset, 16); // central dir offset
|
|
85
|
+
end.writeUInt16LE(0, 20); // comment length
|
|
86
|
+
return Buffer.concat([...locals, centralDir, end]);
|
|
87
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { ApiError } from './core/client.js';
|
|
4
|
+
import { color, setJsonMode } from './core/output.js';
|
|
5
|
+
import { authCommand } from './commands/auth.js';
|
|
6
|
+
import { configCommand } from './commands/config.js';
|
|
7
|
+
import { orgCommand } from './commands/org.js';
|
|
8
|
+
import { workspaceCommand } from './commands/workspace.js';
|
|
9
|
+
import { datasetCommand, fsCommand } from './commands/fsnode.js';
|
|
10
|
+
import { assetCommand } from './commands/asset.js';
|
|
11
|
+
import { genCommand, historyCommand } from './commands/gen.js';
|
|
12
|
+
import { modelCommand } from './commands/model.js';
|
|
13
|
+
import { agentCommand } from './commands/agent.js';
|
|
14
|
+
import { profileCommand } from './commands/profile.js';
|
|
15
|
+
import { presetCommand } from './commands/preset.js';
|
|
16
|
+
import { skillCommand } from './commands/skill.js';
|
|
17
|
+
import { canvasCommand } from './commands/canvas.js';
|
|
18
|
+
const VERSION = '0.1.0';
|
|
19
|
+
const program = new Command();
|
|
20
|
+
program
|
|
21
|
+
.name('wji')
|
|
22
|
+
.description('Command line interface for the WJI AI platform')
|
|
23
|
+
.version(VERSION, '-v, --version')
|
|
24
|
+
.option('--profile <name>', 'config profile to use')
|
|
25
|
+
.option('--endpoint <url>', 'API base URL')
|
|
26
|
+
.option('--token <token>', 'bearer token or sk_ API key for this call')
|
|
27
|
+
.option('--org <orgId>', 'organization context (X-Org-Id)')
|
|
28
|
+
.option('--no-org', 'force personal context')
|
|
29
|
+
.option('--locale <locale>', 'response language (cht, chs, en)')
|
|
30
|
+
.option('--json', 'print raw JSON instead of tables')
|
|
31
|
+
.configureHelp({ sortSubcommands: true });
|
|
32
|
+
/**
|
|
33
|
+
* Global flags live on the root command, but subcommand actions run before
|
|
34
|
+
* we can read them off `program.opts()` in a nested scope — so hand every
|
|
35
|
+
* command a getter instead of a snapshot.
|
|
36
|
+
*/
|
|
37
|
+
const globals = () => {
|
|
38
|
+
const opts = program.opts();
|
|
39
|
+
return {
|
|
40
|
+
profile: opts.profile,
|
|
41
|
+
endpoint: opts.endpoint,
|
|
42
|
+
token: opts.token,
|
|
43
|
+
org: typeof opts.org === 'string' ? opts.org : undefined,
|
|
44
|
+
// commander turns `--no-org` into org === false
|
|
45
|
+
personal: opts.org === false,
|
|
46
|
+
locale: opts.locale,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
program.hook('preAction', () => {
|
|
50
|
+
setJsonMode(!!program.opts().json);
|
|
51
|
+
});
|
|
52
|
+
program.addCommand(authCommand(globals));
|
|
53
|
+
program.addCommand(configCommand(globals));
|
|
54
|
+
program.addCommand(orgCommand(globals));
|
|
55
|
+
program.addCommand(workspaceCommand(globals));
|
|
56
|
+
program.addCommand(fsCommand(globals));
|
|
57
|
+
program.addCommand(datasetCommand(globals));
|
|
58
|
+
program.addCommand(assetCommand(globals));
|
|
59
|
+
program.addCommand(genCommand(globals));
|
|
60
|
+
program.addCommand(historyCommand(globals));
|
|
61
|
+
program.addCommand(modelCommand(globals));
|
|
62
|
+
program.addCommand(agentCommand(globals));
|
|
63
|
+
program.addCommand(profileCommand(globals));
|
|
64
|
+
program.addCommand(presetCommand(globals));
|
|
65
|
+
program.addCommand(skillCommand(globals));
|
|
66
|
+
program.addCommand(canvasCommand(globals));
|
|
67
|
+
program.addHelpText('after', `
|
|
68
|
+
Examples:
|
|
69
|
+
wji auth login --email you@example.com
|
|
70
|
+
wji ws list
|
|
71
|
+
wji ws use 65f0a1b2c3d4e5f6a7b8c9d0
|
|
72
|
+
wji models list --feature image # pass the MODEL column to -m
|
|
73
|
+
wji gen image -m nano-banana -p "a rainy alley at dusk" --ratio 16:9 --wait
|
|
74
|
+
wji fs tree $(wji ws get --json | jq -r .id) --depth 2
|
|
75
|
+
wji asset upload ./reference.png --workspace <id>
|
|
76
|
+
wji canvas status # after pressing the link button on a canvas
|
|
77
|
+
|
|
78
|
+
Auth precedence: --token > $WJI_TOKEN / $WJI_API_KEY > profile apiKey > profile accessToken.
|
|
79
|
+
Config lives in ~/.wji/config.json (chmod 600).`);
|
|
80
|
+
async function main() {
|
|
81
|
+
try {
|
|
82
|
+
await program.parseAsync(process.argv);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (err instanceof ApiError) {
|
|
86
|
+
process.stderr.write(`${color.red('✗')} ${err.message}\n`);
|
|
87
|
+
process.stderr.write(color.dim(` ${err.status} ${err.path}\n`));
|
|
88
|
+
process.exit(err.status === 401 || err.status === 403 ? 77 : 1);
|
|
89
|
+
}
|
|
90
|
+
process.stderr.write(`${color.red('✗')} ${err.message}\n`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wji.ai/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Command line interface for the WJI AI platform",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"author": "WJI AI",
|
|
7
|
+
"homepage": "https://wji.ai",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ultra-instinct-ai/wji-cli.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"wji",
|
|
14
|
+
"cli",
|
|
15
|
+
"ai",
|
|
16
|
+
"image-generation",
|
|
17
|
+
"video-generation",
|
|
18
|
+
"claude-code"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"bin": {
|
|
22
|
+
"wji": "dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"skill",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
34
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"start": "node dist/index.js",
|
|
37
|
+
"prepublishOnly": "npm run build"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^12.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.10.0",
|
|
44
|
+
"typescript": "^5.7.2"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|