taskair-cli 1.0.8 → 1.0.10
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 +1 -4
- package/dist/index.js +11 -44
- package/package.json +5 -3
- package/scripts/postinstall.js +168 -0
package/README.md
CHANGED
|
@@ -28,10 +28,7 @@ Run the configuration command and follow the prompt to open the browser for auth
|
|
|
28
28
|
taskair config
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
* **API URL**: The URL of your running TaskAir server (e.g., `http://localhost:3001` or your production domain).
|
|
33
|
-
|
|
34
|
-
After pressing Enter, a web browser window will open automatically. Complete your sign-in or enter your Master Password on the website to link your CLI terminal.
|
|
31
|
+
After running the command, a web browser window will open automatically. Complete your sign-in or enter your Master Password on the website to link your CLI terminal. The API URL is automatically preconfigured to point to our production endpoint (`https://api-taskair.vercel.app`).
|
|
35
32
|
|
|
36
33
|
### 2. Verify Session
|
|
37
34
|
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ function readCredentials() {
|
|
|
69
69
|
const raw = parseIni(content);
|
|
70
70
|
const rawExpires = raw.expires_at ? parseInt(raw.expires_at, 10) : void 0;
|
|
71
71
|
return {
|
|
72
|
-
api_url: raw.api_url || "
|
|
72
|
+
api_url: raw.api_url || "https://api-taskair.vercel.app",
|
|
73
73
|
email: raw.email || "",
|
|
74
74
|
access_token: raw.access_token || void 0,
|
|
75
75
|
refresh_token: raw.refresh_token || void 0,
|
|
@@ -383,35 +383,16 @@ function openBrowser(url) {
|
|
|
383
383
|
const cmd = process.platform === "win32" ? `start "" "${url}"` : `${start} "${url}"`;
|
|
384
384
|
exec(cmd);
|
|
385
385
|
}
|
|
386
|
-
function ConfigUI(
|
|
386
|
+
function ConfigUI() {
|
|
387
387
|
const { exit } = useApp();
|
|
388
|
-
const [stage, setStage] = useState4("
|
|
389
|
-
const
|
|
390
|
-
const [currentInput, setCurrentInput] = useState4(initialApiUrl);
|
|
388
|
+
const [stage, setStage] = useState4("waiting_for_browser");
|
|
389
|
+
const apiUrl = "https://api-taskair.vercel.app";
|
|
391
390
|
const [port, setPort] = useState4(0);
|
|
392
391
|
const [errorMsg, setErrorMsg] = useState4("");
|
|
393
392
|
const [authDetails, setAuthDetails] = useState4(null);
|
|
394
393
|
useInput((input, key) => {
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
if (!currentInput.trim()) {
|
|
398
|
-
setErrorMsg("API URL cannot be empty");
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
setErrorMsg("");
|
|
402
|
-
setApiUrl(currentInput.trim());
|
|
403
|
-
setStage("waiting_for_browser");
|
|
404
|
-
} else if (key.backspace || key.delete) {
|
|
405
|
-
setCurrentInput((v) => v.slice(0, -1));
|
|
406
|
-
} else if (key.escape) {
|
|
407
|
-
exit();
|
|
408
|
-
} else {
|
|
409
|
-
setCurrentInput((v) => v + input);
|
|
410
|
-
}
|
|
411
|
-
} else {
|
|
412
|
-
if (key.escape) {
|
|
413
|
-
exit();
|
|
414
|
-
}
|
|
394
|
+
if (key.escape) {
|
|
395
|
+
exit();
|
|
415
396
|
}
|
|
416
397
|
});
|
|
417
398
|
useEffect4(() => {
|
|
@@ -656,18 +637,7 @@ function ConfigUI({ initialApiUrl }) {
|
|
|
656
637
|
}, [stage, authDetails, apiUrl]);
|
|
657
638
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", padding: 1, children: [
|
|
658
639
|
/* @__PURE__ */ jsx4(AsciiHeader, {}),
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u2726 Configure TaskAir" }),
|
|
661
|
-
stage === "input_api_url" && /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Enter the API URL of your TaskAir instance. Press Enter to confirm." })
|
|
662
|
-
] }),
|
|
663
|
-
stage === "input_api_url" && /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
664
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u2192 " }),
|
|
665
|
-
/* @__PURE__ */ jsx4(Text4, { color: "white", children: "API URL: " }),
|
|
666
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
|
|
667
|
-
currentInput,
|
|
668
|
-
/* @__PURE__ */ jsx4(Text4, { color: "white", children: "\u258C" })
|
|
669
|
-
] })
|
|
670
|
-
] }),
|
|
640
|
+
/* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u2726 Configure TaskAir" }) }),
|
|
671
641
|
stage === "waiting_for_browser" && /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 0.5, children: [
|
|
672
642
|
/* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
|
|
673
643
|
"\u2713 API URL set to: ",
|
|
@@ -689,17 +659,14 @@ function ConfigUI({ initialApiUrl }) {
|
|
|
689
659
|
] }),
|
|
690
660
|
stage === "success" && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(StatusBadge, { type: "success", message: "Configuration saved successfully! E2E Encryption Active." }) }),
|
|
691
661
|
stage === "error" && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(StatusBadge, { type: "error", message: errorMsg }) }),
|
|
692
|
-
|
|
662
|
+
stage === "waiting_for_browser" && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "Press Esc to cancel" }) })
|
|
693
663
|
] });
|
|
694
664
|
}
|
|
695
665
|
function registerConfig(program2) {
|
|
696
666
|
program2.command("config").alias("configure").description("Authenticate and configure CLI via browser").action(async () => {
|
|
697
|
-
const existing = readCredentials();
|
|
698
667
|
const { render } = await import("ink");
|
|
699
668
|
render(
|
|
700
|
-
React4.createElement(ConfigUI
|
|
701
|
-
initialApiUrl: existing?.api_url ?? "http://localhost:3001"
|
|
702
|
-
})
|
|
669
|
+
React4.createElement(ConfigUI)
|
|
703
670
|
);
|
|
704
671
|
});
|
|
705
672
|
}
|
|
@@ -719,7 +686,7 @@ function LoginUI() {
|
|
|
719
686
|
const [errorMsg, setErrorMsg] = useState5("");
|
|
720
687
|
useEffect5(() => {
|
|
721
688
|
if (stage === "loading") {
|
|
722
|
-
const apiUrl = creds?.api_url ?? "
|
|
689
|
+
const apiUrl = creds?.api_url ?? "https://api-taskair.vercel.app";
|
|
723
690
|
apiLogin(apiUrl, email, password).then(async (res) => {
|
|
724
691
|
if (res.success && res.data) {
|
|
725
692
|
const accessToken = res.data.access_token;
|
|
@@ -2169,7 +2136,7 @@ import { Box as Box16, Text as Text16, useApp as useApp11 } from "ink";
|
|
|
2169
2136
|
import { exec as exec2 } from "child_process";
|
|
2170
2137
|
|
|
2171
2138
|
// src/lib/version.ts
|
|
2172
|
-
var CLI_VERSION = "1.0.
|
|
2139
|
+
var CLI_VERSION = "1.0.10";
|
|
2173
2140
|
|
|
2174
2141
|
// src/commands/upgrade.tsx
|
|
2175
2142
|
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskair-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Space-themed, privacy-first task management CLI with E2E encryption",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"taskair": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"scripts"
|
|
11
12
|
],
|
|
12
13
|
"publishConfig": {
|
|
13
14
|
"access": "public"
|
|
@@ -37,7 +38,8 @@
|
|
|
37
38
|
"build": "tsup src/index.ts --format esm --dts --out-dir dist --shims",
|
|
38
39
|
"start": "node dist/index.js",
|
|
39
40
|
"lint": "tsc --noEmit",
|
|
40
|
-
"taskair": "node --import tsx/esm src/index.ts"
|
|
41
|
+
"taskair": "node --import tsx/esm src/index.ts",
|
|
42
|
+
"postinstall": "node scripts/postinstall.js"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
45
|
"chalk": "^5.3.0",
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
|
|
3
|
+
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
4
|
+
|
|
5
|
+
const SEQUENCE = [
|
|
6
|
+
{ type: 'divider' },
|
|
7
|
+
{
|
|
8
|
+
type: 'bar',
|
|
9
|
+
labels: ['Scanning…', 'Analysing…', 'Detecting…', 'Profiling…'],
|
|
10
|
+
icons: ['✦', '◆', '✶', '❋', '✸'],
|
|
11
|
+
duration: 3000,
|
|
12
|
+
tokenTarget: 0.6, // shows "0.6k" counter
|
|
13
|
+
showPercent: true,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
type: 'lines',
|
|
17
|
+
lines: ['New Role Detected', 'New Team Detected', 'New Challenges'],
|
|
18
|
+
},
|
|
19
|
+
{ type: 'divider' },
|
|
20
|
+
{
|
|
21
|
+
type: 'bar',
|
|
22
|
+
labels: ['Booting…', 'Loading…', 'Preparing…', 'Initializing…'],
|
|
23
|
+
icons: ['✦', '◆', '✶', '❋', '✸'],
|
|
24
|
+
duration: 3000,
|
|
25
|
+
showPercent: false, // no percent, no tokens — clean spinner
|
|
26
|
+
},
|
|
27
|
+
{ type: 'message', text: "Let's build something great 🚀" },
|
|
28
|
+
{ type: 'divider' },
|
|
29
|
+
{
|
|
30
|
+
type: 'bar',
|
|
31
|
+
labels: ['Syncing…', 'Configuring…', 'Setting up…', 'Onboarding…'],
|
|
32
|
+
icons: ['◈', '◉', '⬡', '⬢', '◍'],
|
|
33
|
+
duration: 3000,
|
|
34
|
+
tokenTarget: 0.8, // tokens but no percent
|
|
35
|
+
showPercent: false,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const BG_COLOR = '\x1b[48;2;15;14;23m';
|
|
40
|
+
const RESET = '\x1b[0m';
|
|
41
|
+
|
|
42
|
+
function stripAnsi(str) {
|
|
43
|
+
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function printLine(contentStr = '') {
|
|
47
|
+
const rawLen = stripAnsi(contentStr).length;
|
|
48
|
+
const totalWidth = 52;
|
|
49
|
+
const paddingLeft = 4;
|
|
50
|
+
const paddingRight = Math.max(0, totalWidth - paddingLeft - rawLen);
|
|
51
|
+
const leftSpaces = ' '.repeat(paddingLeft);
|
|
52
|
+
const rightSpaces = ' '.repeat(paddingRight);
|
|
53
|
+
console.log(`${BG_COLOR}${leftSpaces}${contentStr}${rightSpaces}${RESET}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function printLineInPlace(contentStr) {
|
|
57
|
+
const rawLen = stripAnsi(contentStr).length;
|
|
58
|
+
const totalWidth = 52;
|
|
59
|
+
const paddingLeft = 4;
|
|
60
|
+
const paddingRight = Math.max(0, totalWidth - paddingLeft - rawLen);
|
|
61
|
+
const leftSpaces = ' '.repeat(paddingLeft);
|
|
62
|
+
const rightSpaces = ' '.repeat(paddingRight);
|
|
63
|
+
process.stdout.write(`\r${BG_COLOR}${leftSpaces}${contentStr}${rightSpaces}${RESET}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function drawDivider() {
|
|
67
|
+
const line = '─'.repeat(44);
|
|
68
|
+
const dividerStr = `\x1b[38;2;54;49;69m${line}\x1b[0m`;
|
|
69
|
+
printLine(dividerStr);
|
|
70
|
+
await delay(80);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function typeLines(lines) {
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
for (let i = 1; i <= line.length; i++) {
|
|
76
|
+
const textSoFar = line.slice(0, i);
|
|
77
|
+
const cursor = i < line.length ? '▊' : '';
|
|
78
|
+
const contentStr = `\x1b[38;2;230;230;235m${textSoFar}${cursor}\x1b[0m`;
|
|
79
|
+
printLineInPlace(contentStr);
|
|
80
|
+
await delay(40);
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write('\n');
|
|
83
|
+
await delay(200);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function printMessage(text) {
|
|
88
|
+
const contentStr = `\x1b[1m\x1b[38;2;160;130;255m${text}\x1b[0m`;
|
|
89
|
+
printLine(contentStr);
|
|
90
|
+
await delay(400);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function runProgressBar(phase) {
|
|
94
|
+
const { labels, icons, duration, tokenTarget, showPercent = true } = phase;
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
|
|
97
|
+
while (true) {
|
|
98
|
+
const elapsed = Date.now() - startTime;
|
|
99
|
+
const progress = Math.min(1, elapsed / duration);
|
|
100
|
+
|
|
101
|
+
const labelIdx = Math.min(
|
|
102
|
+
labels.length - 1,
|
|
103
|
+
Math.floor(progress * labels.length)
|
|
104
|
+
);
|
|
105
|
+
const label = labels[labelIdx];
|
|
106
|
+
|
|
107
|
+
const iconIdx = Math.floor((Date.now() / 150) % icons.length);
|
|
108
|
+
const icon = icons[iconIdx];
|
|
109
|
+
|
|
110
|
+
const barWidth = 16;
|
|
111
|
+
const filledWidth = Math.floor(progress * barWidth);
|
|
112
|
+
const emptyWidth = barWidth - filledWidth;
|
|
113
|
+
const filledStr = `\x1b[38;2;160;130;255m${'█'.repeat(filledWidth)}\x1b[0m`;
|
|
114
|
+
const emptyStr = `\x1b[38;2;54;49;69m${'░'.repeat(emptyWidth)}\x1b[0m`;
|
|
115
|
+
const barStr = `\x1b[38;2;54;49;69m[\x1b[0m${filledStr}${emptyStr}\x1b[38;2;54;49;69m]\x1b[0m`;
|
|
116
|
+
|
|
117
|
+
let suffix = '';
|
|
118
|
+
if (showPercent) {
|
|
119
|
+
const pct = Math.round(progress * 100);
|
|
120
|
+
suffix += ` \x1b[38;2;160;130;255m${pct}%\x1b[0m`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (tokenTarget !== undefined) {
|
|
124
|
+
const currentTokens = (progress * tokenTarget).toFixed(1);
|
|
125
|
+
suffix += ` \x1b[38;2;210;190;255m ${currentTokens}k tokens\x1b[0m`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const iconColor = `\x1b[38;2;160;130;255m`;
|
|
129
|
+
const labelColor = `\x1b[1m\x1b[38;2;255;255;255m`;
|
|
130
|
+
const output = `${iconColor}${icon}\x1b[0m ${labelColor}${label.padEnd(14)}\x1b[0m${barStr}${suffix}`;
|
|
131
|
+
|
|
132
|
+
printLineInPlace(output);
|
|
133
|
+
|
|
134
|
+
if (progress >= 1) {
|
|
135
|
+
process.stdout.write('\n');
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await delay(80);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function run() {
|
|
144
|
+
console.log('\n\x1b[1m\x1b[38;2;160;130;255m✦ Preparing TaskAir CLI Environment... ✦\x1b[0m\n');
|
|
145
|
+
|
|
146
|
+
printLine();
|
|
147
|
+
printLine();
|
|
148
|
+
|
|
149
|
+
for (const step of SEQUENCE) {
|
|
150
|
+
if (step.type === 'divider') {
|
|
151
|
+
await drawDivider();
|
|
152
|
+
} else if (step.type === 'bar') {
|
|
153
|
+
await runProgressBar(step);
|
|
154
|
+
await delay(150);
|
|
155
|
+
} else if (step.type === 'lines') {
|
|
156
|
+
await typeLines(step.lines);
|
|
157
|
+
} else if (step.type === 'message') {
|
|
158
|
+
await printMessage(step.text);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
printLine();
|
|
163
|
+
printLine();
|
|
164
|
+
|
|
165
|
+
console.log();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
run();
|