@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 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
- async function login() {
702
- console.log();
703
- console.log(chalk5.bold("Codea CLI Login"));
704
- console.log(chalk5.gray("Enter your Alia API key to get started."));
705
- console.log(chalk5.gray("Get your API key at: ") + chalk5.cyan("https://alia.onl/settings/api"));
706
- console.log();
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(`Logged in successfully!`);
808
+ printSuccess("Logged in successfully!");
733
809
  if (data.name) {
734
810
  console.log(chalk5.gray(`Welcome, ${data.name}!`));
735
811
  }
736
- console.log();
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 !== "login" && command !== "help" && !config.get("apiKey")) {
857
- console.log(chalk7.yellow("\nNo API key found. Please run `codea login` first.\n"));
858
- process.exit(1);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alia-codea/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Codea CLI - AI coding assistant for your terminal by Alia",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -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
- export async function login(): Promise<void> {
7
- console.log();
8
- console.log(chalk.bold('Codea CLI Login'));
9
- console.log(chalk.gray('Enter your Alia API key to get started.'));
10
- console.log(chalk.gray('Get your API key at: ') + chalk.cyan('https://alia.onl/settings/api'));
11
- console.log();
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(`Logged in successfully!`);
147
+ printSuccess('Logged in successfully!');
46
148
  if (data.name) {
47
149
  console.log(chalk.gray(`Welcome, ${data.name}!`));
48
150
  }
49
- console.log();
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 !== 'login' && command !== 'help' && !config.get('apiKey')) {
33
- console.log(chalk.yellow('\nNo API key found. Please run `codea login` first.\n'));
34
- process.exit(1);
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