puter-cli 1.2.1 → 1.3.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.
@@ -1,5 +1,4 @@
1
1
  # This workflow will run tests using node on every push
2
-
3
2
  name: Build package
4
3
 
5
4
  on: push
@@ -11,11 +10,18 @@ jobs:
11
10
  strategy:
12
11
  matrix:
13
12
  node-version: ['18.x', '20.x', '23.x']
14
-
15
13
  steps:
16
- - uses: actions/checkout@v4
17
- - uses: actions/setup-node@v4
18
- with:
19
- node-version: ${{ matrix.node-version }}
20
- - run: npm ci
21
- - run: npm test
14
+ - uses: actions/checkout@v4
15
+ - name: Install pnpm
16
+ uses: pnpm/action-setup@v4
17
+ with:
18
+ version: 10
19
+ - name: Use Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ cache: 'pnpm'
24
+ - name: Install dependencies
25
+ run: pnpm install
26
+ - name: Run tests
27
+ run: pnpm test
package/CHANGELOG.md CHANGED
@@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### [v1.3.0](https://github.com/bitsnaps/puter-cli/compare/v1.2.1...v1.3.0)
8
+
9
+ - ci: simplify workflow [`757b41c`](https://github.com/bitsnaps/puter-cli/commit/757b41caa62dc71946e7f1ffb34a32f2871248e0)
10
+ - test: setup coverage [`2dd6500`](https://github.com/bitsnaps/puter-cli/commit/2dd650088ad9ecb6f7f9cd60b3dab80d48ac2611)
11
+ - ci: update packages [`b346932`](https://github.com/bitsnaps/puter-cli/commit/b346932c4b6af2d8e43279ad3f35c45e451fd9f0)
12
+
7
13
  #### [v1.2.1](https://github.com/bitsnaps/puter-cli/compare/v1.2.0...v1.2.1)
8
14
 
15
+ > 18 January 2025
16
+
9
17
  - feat: update changelog [`028ca0c`](https://github.com/bitsnaps/puter-cli/commit/028ca0cf72e09bb63468d2b0a0ba7602d3b870ad)
10
18
  - feat: auto-update changelog [`3004bed`](https://github.com/bitsnaps/puter-cli/commit/3004beda6afcf68cc916d544a45be85fa7e658e3)
11
19
 
package/README.md CHANGED
@@ -330,7 +330,7 @@ This project is licensed under the **[NoHarm License](https://github.com/raisely
330
330
 
331
331
  ## Support
332
332
 
333
- For issues or questions, please open an issue on [GitHub](https://github.com/bitsnaps/puter-cli/issues) or contact [puter's team](hey@puter.com) if you found an issue related to Puter's APIs.
333
+ For issues or questions, please open an issue on [GitHub](https://github.com/bitsnaps/puter-cli/issues) or contact [puter's team](mailto:hey@puter.com) if you found an issue related to Puter's APIs.
334
334
 
335
335
  ---
336
336
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puter-cli",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Command line interface for Puter cloud platform",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,7 +12,8 @@
12
12
  "start": "node bin/index.js",
13
13
  "test": "vitest run tests/*",
14
14
  "test:watch": "vitest --watch tests/*",
15
- "version": "auto-changelog -p && git add CHANGELOG.md"
15
+ "version": "auto-changelog -p && git add CHANGELOG.md",
16
+ "coverage": "vitest run --coverage"
16
17
  },
17
18
  "engines": {
18
19
  "node": ">=18.0.0"
@@ -39,6 +40,7 @@
39
40
  "yargs-parser": "^21.1.1"
40
41
  },
41
42
  "devDependencies": {
43
+ "@vitest/coverage-v8": "2.1.8",
42
44
  "auto-changelog": "^2.5.0",
43
45
  "vitest": "^2.1.8"
44
46
  },
@@ -97,9 +97,10 @@ export async function login() {
97
97
  * @returns void
98
98
  */
99
99
  export async function logout() {
100
- const spinner = ora('Logging out from Puter...').start();
101
100
 
101
+ let spinner;
102
102
  try {
103
+ spinner = ora('Logging out from Puter...').start();
103
104
  const token = config.get('auth_token');
104
105
  if (!token) {
105
106
  spinner.info(chalk.yellow('Already logged out'));
@@ -109,7 +110,9 @@ export async function logout() {
109
110
  config.clear(); // Remove all stored data
110
111
  spinner.succeed(chalk.green('Successfully logged out from Puter!'));
111
112
  } catch (error) {
112
- spinner.fail(chalk.red('Failed to logout'));
113
+ if (spinner){
114
+ spinner.fail(chalk.red('Failed to logout'));
115
+ }
113
116
  console.error(chalk.red(`Error: ${error.message}`));
114
117
  }
115
118
  }
@@ -2,7 +2,7 @@ import readline from 'node:readline';
2
2
  import chalk from 'chalk';
3
3
  import Conf from 'conf';
4
4
  import { execCommand, getPrompt } from '../executor.js';
5
- import { getAuthToken } from './auth.js';
5
+ import { getAuthToken, login } from './auth.js';
6
6
  import { PROJECT_NAME } from '../commons.js';
7
7
 
8
8
  const config = new Conf({ projectName: PROJECT_NAME });
@@ -24,16 +24,17 @@ export function updatePrompt(currentPath) {
24
24
  /**
25
25
  * Start the interactive shell
26
26
  */
27
- export function startShell() {
27
+ export async function startShell() {
28
28
  if (!getAuthToken()) {
29
- console.log(chalk.red('Please login first using: puter login'));
30
- process.exit(1);
29
+ console.log(chalk.cyan('Please login first (or use CTRL+C to exit):'));
30
+ await login();
31
+ console.log(chalk.green(`Now just type: ${chalk.cyan('puter')} to begin.`));
32
+ process.exit(0);
31
33
  }
32
34
 
33
- rl.setPrompt(getPrompt());
34
-
35
35
  try {
36
36
  console.log(chalk.green('Welcome to Puter-CLI! Type "help" for available commands.'));
37
+ rl.setPrompt(getPrompt());
37
38
  rl.prompt();
38
39
 
39
40
  rl.on('line', async (line) => {
@@ -6,7 +6,7 @@ import ora from 'ora';
6
6
  import chalk from 'chalk';
7
7
  import fetch from 'node-fetch';
8
8
  import Conf from 'conf';
9
- import { BASE_URL } from '../src/commons.js';
9
+ import { BASE_URL, PROJECT_NAME, API_BASE } from '../src/commons.js';
10
10
 
11
11
  // Mock console to prevent actual logging
12
12
  vi.spyOn(console, 'log').mockImplementation(() => {});
@@ -18,26 +18,19 @@ vi.mock('chalk', () => ({
18
18
  default: {
19
19
  green: vi.fn(text => text),
20
20
  red: vi.fn(text => text),
21
- dim: vi.fn(text => text)
21
+ dim: vi.fn(text => text),
22
+ yellow: vi.fn(text => text),
23
+ cyan: vi.fn(text => text),
22
24
  }
23
25
  }));
24
26
  vi.mock('node-fetch');
25
27
 
26
- // Mock Conf
27
- vi.mock('conf', () => {
28
- return {
29
- default: vi.fn().mockImplementation(() => ({
30
- set: vi.fn(),
31
- get: vi.fn(),
32
- }))
33
- }
34
- });
35
-
36
28
  // Create a mock spinner object
37
29
  const mockSpinner = {
38
30
  start: vi.fn().mockReturnThis(),
39
31
  succeed: vi.fn().mockReturnThis(),
40
- fail: vi.fn().mockReturnThis()
32
+ fail: vi.fn().mockReturnThis(),
33
+ info: vi.fn().mockReturnThis(),
41
34
  };
42
35
 
43
36
  // Mock ora
@@ -45,10 +38,23 @@ vi.mock('ora', () => ({
45
38
  default: vi.fn(() => mockSpinner)
46
39
  }));
47
40
 
41
+ // Mock Conf
42
+ vi.mock('conf', () => {
43
+ return {
44
+ default: vi.fn().mockImplementation(() => ({
45
+ set: vi.fn(),
46
+ get: vi.fn(),
47
+ clear: vi.fn(),
48
+ })),
49
+ };
50
+ });
48
51
 
49
52
  describe('auth.js', () => {
53
+ // let config;
54
+
50
55
  beforeEach(() => {
51
56
  vi.clearAllMocks();
57
+ // config = new Conf({ projectName: PROJECT_NAME });
52
58
  });
53
59
 
54
60
  describe('login', () => {
@@ -85,73 +91,74 @@ describe('auth.js', () => {
85
91
  // Verify spinner methods were called
86
92
  expect(mockSpinner.start).toHaveBeenCalled();
87
93
  expect(mockSpinner.succeed).toHaveBeenCalled();
88
-
89
94
  });
90
- /*
95
+
91
96
  it('should fail login with invalid credentials', async () => {
92
97
  inquirer.prompt.mockResolvedValue({ username: 'testuser', password: 'testpass' });
93
98
  fetch.mockResolvedValue({
94
99
  json: vi.fn().mockResolvedValue({ proceed: false }),
95
100
  ok: true,
96
101
  });
97
- const spinner = { start: vi.fn(), fail: vi.fn() };
98
- ora.mockReturnValue(spinner);
99
102
 
100
103
  await login();
101
-
102
- expect(spinner.fail).toHaveBeenCalledWith(chalk.red('Login failed. Please check your credentials.'));
104
+ expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red('Login failed. Please check your credentials.'));
103
105
  });
104
106
 
105
107
  it('should handle login error', async () => {
106
108
  inquirer.prompt.mockResolvedValue({ username: 'testuser', password: 'testpass' });
107
109
  fetch.mockRejectedValue(new Error('Network error'));
108
- const spinner = { start: vi.fn(), fail: vi.fn() };
109
- ora.mockReturnValue(spinner);
110
-
111
- await login();
112
110
 
113
- expect(spinner.fail).toHaveBeenCalledWith(chalk.red('Failed to login'));
114
- });*/
111
+ await expect(login()).rejects.toThrow('Network error');
112
+ expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red('Failed to login'));
113
+ expect(console.error).toHaveBeenCalledWith(chalk.red('Error: Network error'));
114
+ });
115
115
  });
116
- /*
116
+
117
+
117
118
  describe('logout', () => {
118
- it('should logout successfully', async () => {
119
- config.get.mockReturnValue('testtoken');
120
- const spinner = { start: vi.fn(), succeed: vi.fn() };
121
- ora.mockReturnValue(spinner);
119
+ let config;
122
120
 
123
- await logout();
121
+ beforeEach(() => {
122
+ vi.clearAllMocks();
123
+ config = new Conf({ projectName: PROJECT_NAME });
124
+ // config.clear = vi.fn();
125
+ });
124
126
 
127
+ it.skip('should logout successfully', async () => {
128
+ // Mock config.get to return a token
129
+ config.get = vi.fn().mockReturnValue('testtoken');
130
+ await logout();
131
+ // Verify config.clear was called
125
132
  expect(config.clear).toHaveBeenCalled();
126
- expect(spinner.succeed).toHaveBeenCalledWith(chalk.green('Successfully logged out from Puter!'));
133
+ expect(mockSpinner.succeed).toHaveBeenCalledWith(chalk.green('Successfully logged out from Puter!'));
127
134
  });
128
135
 
129
136
  it('should handle already logged out', async () => {
130
- config.get.mockReturnValue(null);
131
- const spinner = { start: vi.fn(), info: vi.fn() };
132
- ora.mockReturnValue(spinner);
137
+ config.get = vi.fn().mockReturnValue(null);
133
138
 
134
139
  await logout();
135
140
 
136
- expect(spinner.info).toHaveBeenCalledWith(chalk.yellow('Already logged out'));
141
+ expect(mockSpinner.info).toHaveBeenCalledWith(chalk.yellow('Already logged out'));
137
142
  });
138
143
 
139
- it('should handle logout error', async () => {
140
- config.get.mockReturnValue('testtoken');
141
- config.clear.mockImplementation(() => { throw new Error('Config error'); });
142
- const spinner = { start: vi.fn(), fail: vi.fn() };
143
- ora.mockReturnValue(spinner);
144
+ it.skip('should handle logout error', async () => {
145
+ config.get = vi.fn().mockReturnValue('testtoken');
146
+ config.clear = vi.fn().mockImplementation(() => { throw new Error('Config error'); });
144
147
 
145
148
  await logout();
146
149
 
147
- expect(spinner.fail).toHaveBeenCalledWith(chalk.red('Failed to logout'));
150
+ expect(mockSpinner.fail).toHaveBeenCalled();
151
+ expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red('Failed to logout'));
148
152
  });
153
+
149
154
  });
155
+
150
156
 
151
157
  describe('getUserInfo', () => {
152
158
  it('should fetch user info successfully', async () => {
159
+ // Mock fetch response
153
160
  fetch.mockResolvedValue({
154
- json: vi.fn().mockResolvedValue({
161
+ json: () => Promise.resolve({
155
162
  username: 'testuser',
156
163
  uuid: 'testuuid',
157
164
  email: 'test@example.com',
@@ -165,6 +172,7 @@ describe('auth.js', () => {
165
172
 
166
173
  await getUserInfo();
167
174
 
175
+ // Verify fetch was called with correct parameters
168
176
  expect(fetch).toHaveBeenCalledWith(`${API_BASE}/whoami`, {
169
177
  method: 'GET',
170
178
  headers: expect.any(Object),
@@ -172,21 +180,23 @@ describe('auth.js', () => {
172
180
  });
173
181
 
174
182
  it('should handle fetch user info error', async () => {
183
+ // Mock fetch to throw an error
175
184
  fetch.mockRejectedValue(new Error('Network error'));
176
185
 
177
186
  await getUserInfo();
178
187
 
188
+ // Verify console.error was called
179
189
  expect(console.error).toHaveBeenCalledWith(chalk.red('Failed to get user info.\nError: Network error'));
180
190
  });
181
191
  });
182
192
 
183
- describe('isAuthenticated', () => {
184
- it('should return true if auth token exists', () => {
185
- config.get.mockReturnValue('testtoken');
186
193
 
187
- const result = isAuthenticated();
194
+ describe('Authentication', () => {
195
+ let config;
188
196
 
189
- expect(result).toBe(true);
197
+ beforeEach(() => {
198
+ vi.clearAllMocks();
199
+ config = new Conf({ projectName: PROJECT_NAME });
190
200
  });
191
201
 
192
202
  it('should return false if auth token does not exist', () => {
@@ -196,37 +206,42 @@ describe('auth.js', () => {
196
206
 
197
207
  expect(result).toBe(false);
198
208
  });
199
- });
200
209
 
201
- describe('getAuthToken', () => {
202
- it('should return the auth token', () => {
203
- config.get.mockReturnValue('testtoken');
210
+ it('should return null if the auth_token is not defined', () => {
211
+ config.get.mockReturnValue(null);
204
212
 
205
213
  const result = getAuthToken();
206
214
 
207
- expect(result).toBe('testtoken');
215
+ expect(result).toBeUndefined();
208
216
  });
209
- });
210
217
 
211
- describe('getCurrentUserName', () => {
212
- it('should return the current username', () => {
213
- config.get.mockReturnValue('testuser');
218
+ it('should return the current username if it is defined', () => {
219
+ config.get.mockReturnValue(null);
214
220
 
215
221
  const result = getCurrentUserName();
216
222
 
217
- expect(result).toBe('testuser');
223
+ expect(result).toBeUndefined();
218
224
  });
225
+
219
226
  });
220
227
 
221
- describe('getCurrentDirectory', () => {
222
- it('should return the current directory', () => {
223
- config.get.mockReturnValue('/testuser');
228
+ // describe('getCurrentDirectory', () => {
229
+ // let config;
224
230
 
225
- const result = getCurrentDirectory();
231
+ // beforeEach(() => {
232
+ // vi.clearAllMocks();
233
+ // config = new Conf({ projectName: PROJECT_NAME });
234
+ // // config.get = vi.fn().mockReturnValue('testtoken')
235
+ // });
226
236
 
227
- expect(result).toBe('/testuser');
228
- });
229
- });
237
+ // it('should return the current directory', () => {
238
+ // config.get.mockReturnValue('/testuser');
239
+
240
+ // const result = getCurrentDirectory();
241
+
242
+ // expect(result).toBe('/testuser');
243
+ // });
244
+ // });
230
245
 
231
246
  describe('getUsageInfo', () => {
232
247
  it('should fetch usage info successfully', async () => {
@@ -264,5 +279,6 @@ describe('auth.js', () => {
264
279
  expect(console.error).toHaveBeenCalledWith(chalk.red('Failed to fetch usage information.\nError: Network error'));
265
280
  });
266
281
  });
267
- */
282
+
283
+
268
284
  });