@juuno-sdk/cli 1.0.5 → 1.0.7

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.
Files changed (36) hide show
  1. package/README.md +204 -20
  2. package/bin/cli.js +79 -184
  3. package/dist/assets/main-B8xiNI5_.js +1 -0
  4. package/dist/assets/main-COSZCfRq.css +1 -0
  5. package/dist/cli/src/auth/index.d.ts +51 -0
  6. package/dist/cli/src/auth/index.d.ts.map +1 -0
  7. package/dist/cli/src/auth/index.js +156 -0
  8. package/dist/cli/src/deploy/index.d.ts +12 -0
  9. package/dist/cli/src/deploy/index.d.ts.map +1 -0
  10. package/dist/cli/src/deploy/index.js +129 -0
  11. package/dist/cli/src/info/index.d.ts +12 -0
  12. package/dist/cli/src/info/index.d.ts.map +1 -0
  13. package/dist/cli/src/info/index.js +111 -0
  14. package/dist/cli/src/list/index.d.ts +22 -0
  15. package/dist/cli/src/list/index.d.ts.map +1 -0
  16. package/dist/cli/src/list/index.js +102 -0
  17. package/dist/cli/src/login/index.d.ts +12 -0
  18. package/dist/cli/src/login/index.d.ts.map +1 -0
  19. package/dist/cli/src/login/index.js +61 -0
  20. package/dist/cli/src/logout/index.d.ts +5 -0
  21. package/dist/cli/src/logout/index.d.ts.map +1 -0
  22. package/dist/cli/src/logout/index.js +26 -0
  23. package/dist/cli/src/simulator/index.d.ts +13 -0
  24. package/dist/cli/src/simulator/index.d.ts.map +1 -0
  25. package/dist/cli/src/simulator/index.js +136 -0
  26. package/dist/cli/src/whoami/index.d.ts +5 -0
  27. package/dist/cli/src/whoami/index.d.ts.map +1 -0
  28. package/dist/cli/src/whoami/index.js +24 -0
  29. package/dist/cli/tsconfig.build.tsbuildinfo +1 -0
  30. package/dist/index.html +2 -2
  31. package/package.json +8 -7
  32. package/dist/assets/main-BIR58A5i.css +0 -1
  33. package/dist/assets/main-BYDwbwR9.js +0 -1
  34. package/src/App.vue +0 -350
  35. package/src/deploy/index.ts +0 -202
  36. package/src/main.ts +0 -5
@@ -0,0 +1,156 @@
1
+ import inquirer from 'inquirer';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, } from 'fs';
5
+ /**
6
+ * Gets the credentials file path.
7
+ */
8
+ function getCredentialsPath() {
9
+ const configDir = join(homedir(), '.juuno');
10
+ return join(configDir, 'credentials.json');
11
+ }
12
+ /**
13
+ * Ensures the credentials directory exists.
14
+ */
15
+ function ensureCredentialsDir() {
16
+ const configDir = join(homedir(), '.juuno');
17
+ if (!existsSync(configDir)) {
18
+ mkdirSync(configDir, { recursive: true, mode: 0o700 });
19
+ }
20
+ }
21
+ /**
22
+ * Saves authentication token to local credentials file.
23
+ */
24
+ export function saveToken(token) {
25
+ ensureCredentialsDir();
26
+ const credentials = {
27
+ token,
28
+ api_url: getApiUrl(),
29
+ created_at: new Date().toISOString(),
30
+ };
31
+ const credentialsPath = getCredentialsPath();
32
+ writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
33
+ mode: 0o600,
34
+ });
35
+ }
36
+ /**
37
+ * Loads authentication token from local credentials file.
38
+ */
39
+ export function loadToken() {
40
+ const credentialsPath = getCredentialsPath();
41
+ if (!existsSync(credentialsPath)) {
42
+ return null;
43
+ }
44
+ try {
45
+ const fileContent = readFileSync(credentialsPath, 'utf-8');
46
+ const credentials = JSON.parse(fileContent);
47
+ return credentials.token || null;
48
+ }
49
+ catch (error) {
50
+ return null;
51
+ }
52
+ }
53
+ /**
54
+ * Removes stored credentials file.
55
+ */
56
+ export function removeToken() {
57
+ const credentialsPath = getCredentialsPath();
58
+ if (existsSync(credentialsPath)) {
59
+ unlinkSync(credentialsPath);
60
+ }
61
+ }
62
+ /**
63
+ * Checks if user is currently logged in.
64
+ */
65
+ export function isLoggedIn() {
66
+ return loadToken() !== null;
67
+ }
68
+ /**
69
+ * Gets the API URL from environment or uses default.
70
+ */
71
+ export function getApiUrl() {
72
+ return process.env.JUUNO_API_URL || 'https://api.juuno.co';
73
+ }
74
+ /**
75
+ * Gets the authentication token from options, environment, or stored credentials.
76
+ */
77
+ export async function getAuthToken(options) {
78
+ if (options.token) {
79
+ return options.token;
80
+ }
81
+ const envToken = process.env.JUUNO_API_TOKEN;
82
+ if (envToken) {
83
+ return envToken;
84
+ }
85
+ const storedToken = loadToken();
86
+ if (storedToken) {
87
+ return storedToken;
88
+ }
89
+ if (options.email || options.password) {
90
+ const credentials = await getCredentials(options);
91
+ return await loginWithCredentials(credentials);
92
+ }
93
+ throw new Error('Not logged in. Please run "juuno-cli login" first or provide credentials via --email and --password');
94
+ }
95
+ /**
96
+ * Gets credentials from options or prompts the user.
97
+ */
98
+ async function getCredentials(options) {
99
+ if (options.email && options.password) {
100
+ return { email: options.email, password: options.password };
101
+ }
102
+ const answers = await inquirer.prompt([
103
+ {
104
+ type: 'input',
105
+ name: 'email',
106
+ message: 'Email:',
107
+ when: !options.email,
108
+ validate: (input) => input.length > 0 || 'Email is required',
109
+ },
110
+ {
111
+ type: 'password',
112
+ name: 'password',
113
+ message: 'Password:',
114
+ when: !options.password,
115
+ mask: '*',
116
+ },
117
+ ]);
118
+ return {
119
+ email: options.email || answers.email,
120
+ password: options.password || answers.password,
121
+ };
122
+ }
123
+ /**
124
+ * Authenticates with the developer API using email and password.
125
+ */
126
+ export async function loginWithCredentials(credentials) {
127
+ const apiUrl = getApiUrl();
128
+ const loginUrl = `${apiUrl}/api/v1/developer/auth/login`;
129
+ try {
130
+ const response = await fetch(loginUrl, {
131
+ method: 'POST',
132
+ headers: {
133
+ 'Content-Type': 'application/json',
134
+ },
135
+ body: JSON.stringify({
136
+ email: credentials.email,
137
+ password: credentials.password,
138
+ }),
139
+ });
140
+ if (!response.ok) {
141
+ const errorText = await response.text();
142
+ throw new Error(`Authentication failed: ${response.status} ${response.statusText}\n${errorText}`);
143
+ }
144
+ const data = (await response.json());
145
+ if (!data.success || !data.token) {
146
+ throw new Error('Authentication failed: Invalid response from server');
147
+ }
148
+ return data.token;
149
+ }
150
+ catch (error) {
151
+ if (error instanceof Error) {
152
+ throw new Error(`Authentication failed: ${error.message}`);
153
+ }
154
+ throw new Error('Authentication failed: Unknown error');
155
+ }
156
+ }
@@ -0,0 +1,12 @@
1
+ import { type AuthOptions } from '../auth/index.js';
2
+ /**
3
+ * Deploy options.
4
+ */
5
+ export interface DeployOptions extends AuthOptions {
6
+ buildDir: string;
7
+ }
8
+ /**
9
+ * Deploys an external app to the Juuno platform.
10
+ */
11
+ export declare function deployApp(options: DeployOptions): Promise<void>;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/deploy/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAa7E;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6ED;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyFrE"}
@@ -0,0 +1,129 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ import JSZip from 'jszip';
4
+ import { getAuthToken, getApiUrl } from '../auth/index.js';
5
+ /**
6
+ * Creates a zip bundle from the build directory.
7
+ */
8
+ async function createZipBundle(buildDir) {
9
+ const zip = new JSZip();
10
+ const files = readdirSync(buildDir);
11
+ for (const file of files) {
12
+ const filePath = join(buildDir, file);
13
+ const stats = statSync(filePath);
14
+ if (stats.isFile()) {
15
+ const content = readFileSync(filePath);
16
+ zip.file(file, content);
17
+ }
18
+ }
19
+ return await zip.generateAsync({ type: 'nodebuffer' });
20
+ }
21
+ /**
22
+ * Uploads the app bundle to the developer API.
23
+ */
24
+ async function uploadBundle(token, zipBuffer) {
25
+ const apiUrl = getApiUrl();
26
+ const uploadUrl = `${apiUrl}/api/v1/developer/apps/upload`;
27
+ const formData = new FormData();
28
+ const blob = new Blob([zipBuffer], { type: 'application/zip' });
29
+ formData.append('app_bundle', blob, 'app.zip');
30
+ try {
31
+ const response = await fetch(uploadUrl, {
32
+ method: 'POST',
33
+ headers: {
34
+ Authorization: `Bearer ${token}`,
35
+ Accept: 'application/json',
36
+ },
37
+ body: formData,
38
+ });
39
+ if (!response.ok) {
40
+ const errorText = await response.text();
41
+ throw new Error(`Upload failed: ${response.status} ${response.statusText}\n${errorText}`);
42
+ }
43
+ const data = (await response.json());
44
+ if (!data.success) {
45
+ throw new Error(`Upload failed: ${data.message || 'Unknown error'}`);
46
+ }
47
+ return data;
48
+ }
49
+ catch (error) {
50
+ if (error instanceof Error) {
51
+ throw new Error(`Upload failed: ${error.message}`);
52
+ }
53
+ throw new Error('Upload failed: Unknown error');
54
+ }
55
+ }
56
+ /**
57
+ * Deploys an external app to the Juuno platform.
58
+ */
59
+ export async function deployApp(options) {
60
+ const buildDir = options.buildDir;
61
+ console.log('📦 Juuno CLI - Deploy');
62
+ console.log('');
63
+ if (!existsSync(buildDir)) {
64
+ console.error(`❌ Build directory not found: ${buildDir}`);
65
+ console.error(' Run your build command first (e.g., npm run build)');
66
+ process.exit(1);
67
+ }
68
+ const manifestPath = join(buildDir, 'manifest.json');
69
+ if (!existsSync(manifestPath)) {
70
+ console.error(`❌ manifest.json not found in ${buildDir}`);
71
+ console.error(' Your build should generate a manifest.json file');
72
+ process.exit(1);
73
+ }
74
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
75
+ if (!manifest.id || !manifest.version || !manifest.name) {
76
+ console.error('❌ Invalid manifest.json - missing required fields (id, version, name)');
77
+ process.exit(1);
78
+ }
79
+ console.log(` App: ${manifest.name}`);
80
+ console.log(` Version: ${manifest.version}`);
81
+ console.log(` ID: ${manifest.id}`);
82
+ console.log('');
83
+ console.log('🔑 Authenticating...');
84
+ let token;
85
+ try {
86
+ token = await getAuthToken(options);
87
+ console.log(' ✓ Authenticated');
88
+ console.log('');
89
+ }
90
+ catch (error) {
91
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
92
+ process.exit(1);
93
+ }
94
+ console.log('📁 Creating bundle...');
95
+ let zipBuffer;
96
+ try {
97
+ zipBuffer = await createZipBundle(buildDir);
98
+ console.log(` ✓ Bundle created (${(zipBuffer.length / 1024).toFixed(1)} KB)`);
99
+ console.log('');
100
+ }
101
+ catch (error) {
102
+ console.error(`❌ Failed to create bundle: ${error instanceof Error ? error.message : String(error)}`);
103
+ process.exit(1);
104
+ }
105
+ console.log('☁️ Uploading to Juuno...');
106
+ let result;
107
+ try {
108
+ result = await uploadBundle(token, zipBuffer);
109
+ console.log(' ✓ Upload complete');
110
+ console.log('');
111
+ }
112
+ catch (error) {
113
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
114
+ process.exit(1);
115
+ }
116
+ console.log('✅ Deployment successful!');
117
+ console.log('');
118
+ if (result.data) {
119
+ console.log('📋 App Details:');
120
+ console.log(` ID: ${result.data.id}`);
121
+ console.log(` Name: ${result.data.name}`);
122
+ console.log(` Version: ${result.data.version}`);
123
+ console.log('');
124
+ }
125
+ if (result.message) {
126
+ console.log(`💡 ${result.message}`);
127
+ console.log('');
128
+ }
129
+ }
@@ -0,0 +1,12 @@
1
+ import { type AuthOptions } from '../auth/index.js';
2
+ /**
3
+ * Info command options.
4
+ */
5
+ export interface InfoOptions extends AuthOptions {
6
+ appId: string;
7
+ }
8
+ /**
9
+ * Gets and displays detailed information about a specific app.
10
+ */
11
+ export declare function getAppInfo(options: InfoOptions): Promise<void>;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/info/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,KAAK,EAAE,MAAM,CAAC;CACf;AA0HD;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCpE"}
@@ -0,0 +1,111 @@
1
+ import { getAuthToken, getApiUrl } from '../auth/index.js';
2
+ /**
3
+ * Fetches detailed information about a specific app.
4
+ */
5
+ async function fetchAppInfo(token, appId) {
6
+ const apiUrl = getApiUrl();
7
+ const infoUrl = `${apiUrl}/api/v1/developer/apps/${appId}`;
8
+ try {
9
+ const response = await fetch(infoUrl, {
10
+ method: 'GET',
11
+ headers: {
12
+ Authorization: `Bearer ${token}`,
13
+ Accept: 'application/json',
14
+ },
15
+ });
16
+ if (!response.ok) {
17
+ const errorText = await response.text();
18
+ throw new Error(`Failed to fetch app info: ${response.status} ${response.statusText}\n${errorText}`);
19
+ }
20
+ const data = (await response.json());
21
+ if (!data.success || !data.data) {
22
+ throw new Error(`Failed to fetch app info: ${data.message || 'Unknown error'}`);
23
+ }
24
+ return data.data;
25
+ }
26
+ catch (error) {
27
+ if (error instanceof Error) {
28
+ throw new Error(`Failed to fetch app info: ${error.message}`);
29
+ }
30
+ throw new Error('Failed to fetch app info: Unknown error');
31
+ }
32
+ }
33
+ /**
34
+ * Formats a date string for display.
35
+ */
36
+ function formatDate(dateString) {
37
+ if (!dateString) {
38
+ return 'N/A';
39
+ }
40
+ try {
41
+ const date = new Date(dateString);
42
+ return date.toLocaleString();
43
+ }
44
+ catch {
45
+ return dateString;
46
+ }
47
+ }
48
+ /**
49
+ * Displays detailed app information.
50
+ */
51
+ function displayAppInfo(app) {
52
+ console.log('📱 App Information');
53
+ console.log('');
54
+ console.log('─'.repeat(60));
55
+ console.log('');
56
+ console.log(`Name: ${app.name}`);
57
+ console.log(`ID: ${app.id}`);
58
+ if (app.description) {
59
+ console.log(`Description: ${app.description}`);
60
+ }
61
+ if (app.created_at) {
62
+ console.log(`Created: ${formatDate(app.created_at)}`);
63
+ }
64
+ if (app.updated_at) {
65
+ console.log(`Updated: ${formatDate(app.updated_at)}`);
66
+ }
67
+ console.log('');
68
+ console.log('─'.repeat(60));
69
+ console.log('');
70
+ if (app.manifest && Object.keys(app.manifest).length > 0) {
71
+ console.log('📋 Manifest:');
72
+ console.log('');
73
+ console.log(JSON.stringify(app.manifest, null, 2));
74
+ console.log('');
75
+ }
76
+ }
77
+ /**
78
+ * Gets and displays detailed information about a specific app.
79
+ */
80
+ export async function getAppInfo(options) {
81
+ console.log('📦 Juuno CLI - App Info');
82
+ console.log('');
83
+ if (!options.appId || options.appId.trim().length === 0) {
84
+ console.error('❌ App ID is required');
85
+ console.error(' Usage: juuno-cli info <app-id>');
86
+ process.exit(1);
87
+ }
88
+ console.log('🔑 Authenticating...');
89
+ let token;
90
+ try {
91
+ token = await getAuthToken(options);
92
+ console.log(' ✓ Authenticated');
93
+ console.log('');
94
+ }
95
+ catch (error) {
96
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
97
+ process.exit(1);
98
+ }
99
+ console.log(`📡 Fetching app info for: ${options.appId}`);
100
+ let appInfo;
101
+ try {
102
+ appInfo = await fetchAppInfo(token, options.appId);
103
+ console.log(' ✓ Info retrieved');
104
+ console.log('');
105
+ }
106
+ catch (error) {
107
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
108
+ process.exit(1);
109
+ }
110
+ displayAppInfo(appInfo);
111
+ }
@@ -0,0 +1,22 @@
1
+ import { type AuthOptions } from '../auth/index.js';
2
+ /**
3
+ * List command options.
4
+ */
5
+ export interface ListOptions extends AuthOptions {
6
+ }
7
+ /**
8
+ * App summary returned from the list endpoint.
9
+ */
10
+ export interface AppSummary {
11
+ id: string;
12
+ name: string;
13
+ version: string;
14
+ visibility: string;
15
+ role: string;
16
+ last_uploaded_at?: string;
17
+ }
18
+ /**
19
+ * Lists all developer apps.
20
+ */
21
+ export declare function listApps(options: ListOptions): Promise<void>;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/list/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;CAAG;AAEnD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAsGD;;GAEG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BlE"}
@@ -0,0 +1,102 @@
1
+ import { getAuthToken, getApiUrl } from '../auth/index.js';
2
+ /**
3
+ * Fetches the list of developer apps from the API.
4
+ */
5
+ async function fetchAppList(token) {
6
+ const apiUrl = getApiUrl();
7
+ const listUrl = `${apiUrl}/api/v1/developer/apps`;
8
+ try {
9
+ const response = await fetch(listUrl, {
10
+ method: 'GET',
11
+ headers: {
12
+ Authorization: `Bearer ${token}`,
13
+ Accept: 'application/json',
14
+ },
15
+ });
16
+ if (!response.ok) {
17
+ const errorText = await response.text();
18
+ throw new Error(`Failed to fetch apps: ${response.status} ${response.statusText}\n${errorText}`);
19
+ }
20
+ const data = (await response.json());
21
+ if (!data.success) {
22
+ throw new Error(`Failed to fetch apps: ${data.message || 'Unknown error'}`);
23
+ }
24
+ return data.data || [];
25
+ }
26
+ catch (error) {
27
+ if (error instanceof Error) {
28
+ throw new Error(`Failed to fetch apps: ${error.message}`);
29
+ }
30
+ throw new Error('Failed to fetch apps: Unknown error');
31
+ }
32
+ }
33
+ /**
34
+ * Formats and displays the app list.
35
+ */
36
+ function displayAppList(apps) {
37
+ if (apps.length === 0) {
38
+ console.log('No apps found.');
39
+ console.log('');
40
+ console.log('💡 Deploy your first app using: juuno-cli deploy');
41
+ console.log('');
42
+ return;
43
+ }
44
+ console.log('📱 Your Apps');
45
+ console.log('');
46
+ const maxIdLength = Math.max(...apps.map((a) => a.id.length), 2);
47
+ const maxNameLength = Math.max(...apps.map((a) => a.name.length), 4);
48
+ const maxVersionLength = Math.max(...apps.map((a) => a.version.length), 7);
49
+ const maxRoleLength = Math.max(...apps.map((a) => a.role.length), 4);
50
+ const maxLastUploadedAtLength = Math.max(...apps.map((a) => a.last_uploaded_at?.length || 0), 20);
51
+ const idCol = maxIdLength + 2;
52
+ const nameCol = maxNameLength + 2;
53
+ const versionCol = maxVersionLength + 2;
54
+ const roleCol = maxRoleLength + 2;
55
+ const lastUploadedAtCol = maxLastUploadedAtLength + 2;
56
+ const header = `${'ID'.padEnd(idCol)}${'Name'.padEnd(nameCol)}${'Version'.padEnd(versionCol)}${'Last Uploaded'.padEnd(lastUploadedAtCol)}${'Role'.padEnd(roleCol)}`;
57
+ const separator = '─'.repeat(header.length);
58
+ console.log(header);
59
+ console.log(separator);
60
+ for (const app of apps) {
61
+ const lastUploadedAt = app.last_uploaded_at
62
+ ? new Date(app.last_uploaded_at).toLocaleString()
63
+ : '';
64
+ const row = `${app.id.padEnd(idCol)}${app.name.padEnd(nameCol)}${app.version.padEnd(versionCol)}${lastUploadedAt.padEnd(lastUploadedAtCol)}${app.role.padEnd(roleCol)}`;
65
+ console.log(row);
66
+ }
67
+ console.log('');
68
+ console.log(`Total: ${apps.length} app${apps.length === 1 ? '' : 's'}`);
69
+ console.log('');
70
+ console.log('💡 View app details using: juuno-cli info <app-id>');
71
+ console.log('');
72
+ }
73
+ /**
74
+ * Lists all developer apps.
75
+ */
76
+ export async function listApps(options) {
77
+ console.log('📦 Juuno CLI - List Apps');
78
+ console.log('');
79
+ console.log('🔑 Authenticating...');
80
+ let token;
81
+ try {
82
+ token = await getAuthToken(options);
83
+ console.log(' ✓ Authenticated');
84
+ console.log('');
85
+ }
86
+ catch (error) {
87
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
88
+ process.exit(1);
89
+ }
90
+ console.log('📡 Fetching apps...');
91
+ let apps;
92
+ try {
93
+ apps = await fetchAppList(token);
94
+ console.log(' ✓ Apps retrieved');
95
+ console.log('');
96
+ }
97
+ catch (error) {
98
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
99
+ process.exit(1);
100
+ }
101
+ displayAppList(apps);
102
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Login command options.
3
+ */
4
+ export interface LoginOptions {
5
+ email?: string;
6
+ password?: string;
7
+ }
8
+ /**
9
+ * Logs in to Juuno and saves authentication token.
10
+ */
11
+ export declare function login(options: LoginOptions): Promise<void>;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/login/index.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAoEhE"}
@@ -0,0 +1,61 @@
1
+ import { loginWithCredentials, saveToken, getApiUrl, } from '../auth/index.js';
2
+ import inquirer from 'inquirer';
3
+ /**
4
+ * Logs in to Juuno and saves authentication token.
5
+ */
6
+ export async function login(options) {
7
+ console.log('📦 Juuno CLI - Login');
8
+ console.log('');
9
+ const apiUrl = getApiUrl();
10
+ console.log(` API: ${apiUrl}`);
11
+ console.log('');
12
+ let email = options.email;
13
+ let pwd = options.password;
14
+ if (!email || !pwd) {
15
+ const answers = await inquirer.prompt([
16
+ {
17
+ type: 'input',
18
+ name: 'email',
19
+ message: 'Email:',
20
+ when: !options.email,
21
+ validate: (input) => input.length > 0 || 'Email is required',
22
+ },
23
+ {
24
+ type: 'password',
25
+ name: 'password',
26
+ message: 'Password:',
27
+ when: !options.password,
28
+ mask: '*',
29
+ },
30
+ ]);
31
+ email = email || answers.email;
32
+ pwd = pwd || answers.password;
33
+ }
34
+ const credentials = {
35
+ email: email,
36
+ password: pwd,
37
+ };
38
+ console.log('');
39
+ console.log('🔑 Authenticating...');
40
+ let token;
41
+ try {
42
+ token = await loginWithCredentials(credentials);
43
+ }
44
+ catch (error) {
45
+ console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
46
+ process.exit(1);
47
+ }
48
+ try {
49
+ saveToken(token);
50
+ console.log(' ✓ Token saved to ~/.juuno/credentials.json');
51
+ console.log('');
52
+ }
53
+ catch (error) {
54
+ console.error(`❌ Failed to save token: ${error instanceof Error ? error.message : String(error)}`);
55
+ process.exit(1);
56
+ }
57
+ console.log('✅ Login successful!');
58
+ console.log('');
59
+ console.log('💡 You can now use deploy, list, and info commands without re-authenticating.');
60
+ console.log('');
61
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Logs out of Juuno by removing stored credentials.
3
+ */
4
+ export declare function logout(): Promise<void>;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/logout/index.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAwB5C"}
@@ -0,0 +1,26 @@
1
+ import { removeToken, isLoggedIn } from '../auth/index.js';
2
+ /**
3
+ * Logs out of Juuno by removing stored credentials.
4
+ */
5
+ export async function logout() {
6
+ console.log('📦 Juuno CLI - Logout');
7
+ console.log('');
8
+ if (!isLoggedIn()) {
9
+ console.log(' You are not currently logged in.');
10
+ console.log('');
11
+ return;
12
+ }
13
+ try {
14
+ removeToken();
15
+ console.log(' ✓ Credentials removed from ~/.juuno/credentials.json');
16
+ console.log('');
17
+ console.log('✅ Logged out successfully!');
18
+ console.log('');
19
+ console.log('💡 Run "juuno-cli login" to authenticate again.');
20
+ console.log('');
21
+ }
22
+ catch (error) {
23
+ console.error(`❌ Failed to remove credentials: ${error instanceof Error ? error.message : String(error)}`);
24
+ process.exit(1);
25
+ }
26
+ }