@alia-codea/cli 1.0.0 → 1.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/dist/index.js +113 -18
- package/package.json +1 -1
- package/src/commands/auth.ts +138 -19
- package/src/index.ts +11 -5
package/dist/index.js
CHANGED
|
@@ -697,13 +697,91 @@ function formatToolArgs2(name, args) {
|
|
|
697
697
|
|
|
698
698
|
// src/commands/auth.ts
|
|
699
699
|
import * as readline3 from "readline";
|
|
700
|
+
import * as crypto from "crypto";
|
|
701
|
+
import * as http from "http";
|
|
702
|
+
import { exec as exec3 } from "child_process";
|
|
700
703
|
import chalk5 from "chalk";
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
704
|
+
function openBrowser(url) {
|
|
705
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? 'start ""' : "xdg-open";
|
|
706
|
+
exec3(`${cmd} "${url}"`);
|
|
707
|
+
}
|
|
708
|
+
async function loginWithBrowser() {
|
|
709
|
+
const codeVerifier = crypto.randomBytes(32).toString("base64url");
|
|
710
|
+
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
711
|
+
return new Promise((resolve2) => {
|
|
712
|
+
const server = http.createServer(async (req, res) => {
|
|
713
|
+
const url = new URL(req.url, `http://localhost`);
|
|
714
|
+
if (url.pathname !== "/callback") {
|
|
715
|
+
res.writeHead(404);
|
|
716
|
+
res.end();
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const code = url.searchParams.get("code");
|
|
720
|
+
const error = url.searchParams.get("error");
|
|
721
|
+
if (error || !code) {
|
|
722
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
723
|
+
res.end(
|
|
724
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Authorization cancelled</h2><p>You can close this window.</p></body></html>'
|
|
725
|
+
);
|
|
726
|
+
server.close();
|
|
727
|
+
resolve2(false);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
const baseUrl = config.get("apiBaseUrl") || "https://api.alia.onl";
|
|
732
|
+
const response = await fetch(`${baseUrl}/auth/token`, {
|
|
733
|
+
method: "POST",
|
|
734
|
+
headers: { "Content-Type": "application/json" },
|
|
735
|
+
body: JSON.stringify({
|
|
736
|
+
grant_type: "authorization_code",
|
|
737
|
+
code,
|
|
738
|
+
code_verifier: codeVerifier,
|
|
739
|
+
client_id: "codea"
|
|
740
|
+
})
|
|
741
|
+
});
|
|
742
|
+
const data = await response.json();
|
|
743
|
+
if (data.token) {
|
|
744
|
+
config.set("apiKey", data.token);
|
|
745
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
746
|
+
res.end(
|
|
747
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Logged in!</h2><p>You can close this window and return to the terminal.</p></body></html>'
|
|
748
|
+
);
|
|
749
|
+
console.log();
|
|
750
|
+
printSuccess("Logged in successfully!");
|
|
751
|
+
server.close();
|
|
752
|
+
resolve2(true);
|
|
753
|
+
} else {
|
|
754
|
+
throw new Error("No token received");
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
758
|
+
res.end(
|
|
759
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Login failed</h2><p>Please try again.</p></body></html>'
|
|
760
|
+
);
|
|
761
|
+
printError("Failed to exchange authorization code.");
|
|
762
|
+
server.close();
|
|
763
|
+
resolve2(false);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
server.listen(0, () => {
|
|
767
|
+
const port = server.address().port;
|
|
768
|
+
const callback = encodeURIComponent(`http://localhost:${port}/callback`);
|
|
769
|
+
const authorizeUrl = `https://alia.onl/authorize?app=codea&callback=${callback}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
|
|
770
|
+
printInfo("Opening browser for authorization...");
|
|
771
|
+
openBrowser(authorizeUrl);
|
|
772
|
+
console.log(
|
|
773
|
+
chalk5.gray("\nIf the browser doesn't open, visit:\n") + chalk5.cyan(authorizeUrl) + "\n"
|
|
774
|
+
);
|
|
775
|
+
console.log(chalk5.gray("Waiting for authorization..."));
|
|
776
|
+
});
|
|
777
|
+
setTimeout(() => {
|
|
778
|
+
server.close();
|
|
779
|
+
printError("Authorization timed out.");
|
|
780
|
+
resolve2(false);
|
|
781
|
+
}, 5 * 60 * 1e3);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
async function loginWithApiKey() {
|
|
707
785
|
const rl = readline3.createInterface({
|
|
708
786
|
input: process.stdin,
|
|
709
787
|
output: process.stdout
|
|
@@ -714,37 +792,48 @@ async function login() {
|
|
|
714
792
|
const trimmedKey = apiKey.trim();
|
|
715
793
|
if (!trimmedKey) {
|
|
716
794
|
printError("No API key provided.");
|
|
717
|
-
resolve2();
|
|
795
|
+
resolve2(false);
|
|
718
796
|
return;
|
|
719
797
|
}
|
|
720
798
|
printInfo("Validating API key...");
|
|
721
799
|
try {
|
|
722
800
|
const baseUrl = config.get("apiBaseUrl") || "https://api.alia.onl";
|
|
723
801
|
const response = await fetch(`${baseUrl}/codea/me`, {
|
|
724
|
-
headers: {
|
|
725
|
-
"Authorization": `Bearer ${trimmedKey}`
|
|
726
|
-
}
|
|
802
|
+
headers: { Authorization: `Bearer ${trimmedKey}` }
|
|
727
803
|
});
|
|
728
804
|
if (response.ok) {
|
|
729
805
|
const data = await response.json();
|
|
730
806
|
config.set("apiKey", trimmedKey);
|
|
731
807
|
console.log();
|
|
732
|
-
printSuccess(
|
|
808
|
+
printSuccess("Logged in successfully!");
|
|
733
809
|
if (data.name) {
|
|
734
810
|
console.log(chalk5.gray(`Welcome, ${data.name}!`));
|
|
735
811
|
}
|
|
736
|
-
|
|
737
|
-
console.log(chalk5.gray("Run ") + chalk5.cyan("codea") + chalk5.gray(" to start coding."));
|
|
812
|
+
resolve2(true);
|
|
738
813
|
} else {
|
|
739
814
|
printError("Invalid API key. Please check and try again.");
|
|
815
|
+
resolve2(false);
|
|
740
816
|
}
|
|
741
817
|
} catch (error) {
|
|
742
818
|
printError(`Could not validate API key: ${error.message}`);
|
|
819
|
+
resolve2(false);
|
|
743
820
|
}
|
|
744
|
-
resolve2();
|
|
745
821
|
});
|
|
746
822
|
});
|
|
747
823
|
}
|
|
824
|
+
async function login() {
|
|
825
|
+
console.log();
|
|
826
|
+
console.log(chalk5.bold("Codea CLI Login"));
|
|
827
|
+
console.log();
|
|
828
|
+
const success = await loginWithBrowser();
|
|
829
|
+
if (success) return true;
|
|
830
|
+
console.log();
|
|
831
|
+
console.log(
|
|
832
|
+
chalk5.gray("Alternatively, paste your API key from: ") + chalk5.cyan("https://alia.onl/settings/api")
|
|
833
|
+
);
|
|
834
|
+
console.log();
|
|
835
|
+
return loginWithApiKey();
|
|
836
|
+
}
|
|
748
837
|
|
|
749
838
|
// src/commands/sessions.ts
|
|
750
839
|
import chalk6 from "chalk";
|
|
@@ -851,11 +940,17 @@ ${chalk7.cyan(" | |__| (_) | (_| | __/ (_| |")}
|
|
|
851
940
|
${chalk7.cyan(" \\____\\___/ \\__,_|\\___|\\__,_|")}
|
|
852
941
|
${chalk7.gray(" AI Coding Assistant by Alia")}
|
|
853
942
|
`;
|
|
854
|
-
program.name("codea").description("Codea CLI - AI coding assistant for your terminal").version(VERSION).hook("preAction", () => {
|
|
943
|
+
program.name("codea").description("Codea CLI - AI coding assistant for your terminal").version(VERSION).hook("preAction", async () => {
|
|
855
944
|
const command = program.args[0];
|
|
856
|
-
if (command
|
|
857
|
-
|
|
858
|
-
|
|
945
|
+
if (command === "login" || command === "help") return;
|
|
946
|
+
if (!config.get("apiKey")) {
|
|
947
|
+
console.log(banner);
|
|
948
|
+
console.log(chalk7.yellow("No API key found. Let's get you logged in.\n"));
|
|
949
|
+
const success = await login();
|
|
950
|
+
if (!success) {
|
|
951
|
+
process.exit(1);
|
|
952
|
+
}
|
|
953
|
+
console.log();
|
|
859
954
|
}
|
|
860
955
|
});
|
|
861
956
|
program.command("chat", { isDefault: true }).description("Start an interactive chat session").option("-m, --model <model>", "Model to use (codea, codea-pro, codea-thinking)", "alia-v1-codea").option("--no-context", "Disable automatic codebase context").action(async (options) => {
|
package/package.json
CHANGED
package/src/commands/auth.ts
CHANGED
|
@@ -1,18 +1,124 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import { exec } from 'child_process';
|
|
2
5
|
import chalk from 'chalk';
|
|
3
6
|
import { config } from '../utils/config.js';
|
|
4
7
|
import { printSuccess, printError, printInfo } from '../utils/ui.js';
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
function openBrowser(url: string): void {
|
|
10
|
+
const cmd =
|
|
11
|
+
process.platform === 'darwin'
|
|
12
|
+
? 'open'
|
|
13
|
+
: process.platform === 'win32'
|
|
14
|
+
? 'start ""'
|
|
15
|
+
: 'xdg-open';
|
|
16
|
+
exec(`${cmd} "${url}"`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function loginWithBrowser(): Promise<boolean> {
|
|
20
|
+
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
21
|
+
const codeChallenge = crypto
|
|
22
|
+
.createHash('sha256')
|
|
23
|
+
.update(codeVerifier)
|
|
24
|
+
.digest('base64url');
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const server = http.createServer(async (req, res) => {
|
|
28
|
+
const url = new URL(req.url!, `http://localhost`);
|
|
29
|
+
if (url.pathname !== '/callback') {
|
|
30
|
+
res.writeHead(404);
|
|
31
|
+
res.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const code = url.searchParams.get('code');
|
|
36
|
+
const error = url.searchParams.get('error');
|
|
37
|
+
|
|
38
|
+
if (error || !code) {
|
|
39
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
40
|
+
res.end(
|
|
41
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px">' +
|
|
42
|
+
'<h2>Authorization cancelled</h2><p>You can close this window.</p></body></html>',
|
|
43
|
+
);
|
|
44
|
+
server.close();
|
|
45
|
+
resolve(false);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const baseUrl = config.get('apiBaseUrl') || 'https://api.alia.onl';
|
|
51
|
+
const response = await fetch(`${baseUrl}/auth/token`, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
grant_type: 'authorization_code',
|
|
56
|
+
code,
|
|
57
|
+
code_verifier: codeVerifier,
|
|
58
|
+
client_id: 'codea',
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
12
61
|
|
|
62
|
+
const data = (await response.json()) as { token?: string };
|
|
63
|
+
|
|
64
|
+
if (data.token) {
|
|
65
|
+
config.set('apiKey', data.token);
|
|
66
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
67
|
+
res.end(
|
|
68
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px">' +
|
|
69
|
+
'<h2>Logged in!</h2><p>You can close this window and return to the terminal.</p></body></html>',
|
|
70
|
+
);
|
|
71
|
+
console.log();
|
|
72
|
+
printSuccess('Logged in successfully!');
|
|
73
|
+
server.close();
|
|
74
|
+
resolve(true);
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error('No token received');
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
80
|
+
res.end(
|
|
81
|
+
'<html><body style="font-family:system-ui;text-align:center;padding:60px">' +
|
|
82
|
+
'<h2>Login failed</h2><p>Please try again.</p></body></html>',
|
|
83
|
+
);
|
|
84
|
+
printError('Failed to exchange authorization code.');
|
|
85
|
+
server.close();
|
|
86
|
+
resolve(false);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
server.listen(0, () => {
|
|
91
|
+
const port = (server.address() as { port: number }).port;
|
|
92
|
+
const callback = encodeURIComponent(`http://localhost:${port}/callback`);
|
|
93
|
+
const authorizeUrl =
|
|
94
|
+
`https://alia.onl/authorize?app=codea` +
|
|
95
|
+
`&callback=${callback}` +
|
|
96
|
+
`&code_challenge=${codeChallenge}` +
|
|
97
|
+
`&code_challenge_method=S256`;
|
|
98
|
+
|
|
99
|
+
printInfo('Opening browser for authorization...');
|
|
100
|
+
openBrowser(authorizeUrl);
|
|
101
|
+
console.log(
|
|
102
|
+
chalk.gray('\nIf the browser doesn\'t open, visit:\n') +
|
|
103
|
+
chalk.cyan(authorizeUrl) +
|
|
104
|
+
'\n',
|
|
105
|
+
);
|
|
106
|
+
console.log(chalk.gray('Waiting for authorization...'));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Timeout after 5 minutes
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
server.close();
|
|
112
|
+
printError('Authorization timed out.');
|
|
113
|
+
resolve(false);
|
|
114
|
+
}, 5 * 60 * 1000);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function loginWithApiKey(): Promise<boolean> {
|
|
13
119
|
const rl = readline.createInterface({
|
|
14
120
|
input: process.stdin,
|
|
15
|
-
output: process.stdout
|
|
121
|
+
output: process.stdout,
|
|
16
122
|
});
|
|
17
123
|
|
|
18
124
|
return new Promise((resolve) => {
|
|
@@ -20,46 +126,59 @@ export async function login(): Promise<void> {
|
|
|
20
126
|
rl.close();
|
|
21
127
|
|
|
22
128
|
const trimmedKey = apiKey.trim();
|
|
23
|
-
|
|
24
129
|
if (!trimmedKey) {
|
|
25
130
|
printError('No API key provided.');
|
|
26
|
-
resolve();
|
|
131
|
+
resolve(false);
|
|
27
132
|
return;
|
|
28
133
|
}
|
|
29
134
|
|
|
30
|
-
// Validate the API key by making a test request
|
|
31
135
|
printInfo('Validating API key...');
|
|
32
136
|
|
|
33
137
|
try {
|
|
34
138
|
const baseUrl = config.get('apiBaseUrl') || 'https://api.alia.onl';
|
|
35
139
|
const response = await fetch(`${baseUrl}/codea/me`, {
|
|
36
|
-
headers: {
|
|
37
|
-
'Authorization': `Bearer ${trimmedKey}`
|
|
38
|
-
}
|
|
140
|
+
headers: { Authorization: `Bearer ${trimmedKey}` },
|
|
39
141
|
});
|
|
40
142
|
|
|
41
143
|
if (response.ok) {
|
|
42
|
-
const data = await response.json();
|
|
144
|
+
const data = (await response.json()) as { name?: string };
|
|
43
145
|
config.set('apiKey', trimmedKey);
|
|
44
146
|
console.log();
|
|
45
|
-
printSuccess(
|
|
147
|
+
printSuccess('Logged in successfully!');
|
|
46
148
|
if (data.name) {
|
|
47
149
|
console.log(chalk.gray(`Welcome, ${data.name}!`));
|
|
48
150
|
}
|
|
49
|
-
|
|
50
|
-
console.log(chalk.gray('Run ') + chalk.cyan('codea') + chalk.gray(' to start coding.'));
|
|
151
|
+
resolve(true);
|
|
51
152
|
} else {
|
|
52
153
|
printError('Invalid API key. Please check and try again.');
|
|
154
|
+
resolve(false);
|
|
53
155
|
}
|
|
54
156
|
} catch (error: any) {
|
|
55
157
|
printError(`Could not validate API key: ${error.message}`);
|
|
158
|
+
resolve(false);
|
|
56
159
|
}
|
|
57
|
-
|
|
58
|
-
resolve();
|
|
59
160
|
});
|
|
60
161
|
});
|
|
61
162
|
}
|
|
62
163
|
|
|
164
|
+
export async function login(): Promise<boolean> {
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(chalk.bold('Codea CLI Login'));
|
|
167
|
+
console.log();
|
|
168
|
+
|
|
169
|
+
const success = await loginWithBrowser();
|
|
170
|
+
if (success) return true;
|
|
171
|
+
|
|
172
|
+
// Fallback to manual API key entry
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(
|
|
175
|
+
chalk.gray('Alternatively, paste your API key from: ') +
|
|
176
|
+
chalk.cyan('https://alia.onl/settings/api'),
|
|
177
|
+
);
|
|
178
|
+
console.log();
|
|
179
|
+
return loginWithApiKey();
|
|
180
|
+
}
|
|
181
|
+
|
|
63
182
|
export function logout(): void {
|
|
64
183
|
config.delete('apiKey');
|
|
65
184
|
printSuccess('Logged out successfully.');
|
package/src/index.ts
CHANGED
|
@@ -26,12 +26,18 @@ program
|
|
|
26
26
|
.name('codea')
|
|
27
27
|
.description('Codea CLI - AI coding assistant for your terminal')
|
|
28
28
|
.version(VERSION)
|
|
29
|
-
.hook('preAction', () => {
|
|
30
|
-
// Check for API key before running commands (except login)
|
|
29
|
+
.hook('preAction', async () => {
|
|
31
30
|
const command = program.args[0];
|
|
32
|
-
if (command
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (command === 'login' || command === 'help') return;
|
|
32
|
+
|
|
33
|
+
if (!config.get('apiKey')) {
|
|
34
|
+
console.log(banner);
|
|
35
|
+
console.log(chalk.yellow('No API key found. Let\'s get you logged in.\n'));
|
|
36
|
+
const success = await login();
|
|
37
|
+
if (!success) {
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
console.log();
|
|
35
41
|
}
|
|
36
42
|
});
|
|
37
43
|
|