ds-01 1.0.6 → 1.0.8

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 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgEpC,eAAO,MAAM,GAAG,SAkRZ,CAAC"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuDpC,eAAO,MAAM,GAAG,SA0MZ,CAAC"}
@@ -84,8 +84,9 @@ export const add = new Command()
84
84
  },
85
85
  });
86
86
  if (!response.ok) {
87
+ const errorData = await response.json().catch(() => null);
87
88
  s.stop(pc.red("Component fetch failed."));
88
- cancel(`Component not found or server error (HTTP ${response.status})`);
89
+ cancel(errorData?.error || `Server returned HTTP ${response.status}`);
89
90
  process.exit(1);
90
91
  }
91
92
  const componentData = await response.json();
@@ -118,8 +119,7 @@ export const add = new Command()
118
119
  // --------------------------------------------------
119
120
  // 7. Install dependencies
120
121
  // --------------------------------------------------
121
- if (componentData.dependencies &&
122
- componentData.dependencies.length > 0) {
122
+ if (componentData.dependencies && componentData.dependencies.length > 0) {
123
123
  const depsToInstall = componentData.dependencies.join(" ");
124
124
  s.start(`Installing missing dependencies: ${pc.cyan(depsToInstall)}...`);
125
125
  try {
@@ -136,16 +136,14 @@ export const add = new Command()
136
136
  // --------------------------------------------------
137
137
  // 8. Success
138
138
  // --------------------------------------------------
139
- const mainFile = bundledFileNames.find((name) => name.toLowerCase() ===
140
- componentName.toLowerCase()) || bundledFileNames[0];
139
+ const mainFile = bundledFileNames.find((name) => name.toLowerCase() === componentName.toLowerCase()) || bundledFileNames[0];
141
140
  outro(`${pc.white("✔")} ${pc.bold(`Component <${componentName} /> is ready!`)}\n` +
142
141
  pc.gray("Import it: ") +
143
142
  pc.cyan(`import { ${mainFile} } from "@/${config.componentsPath}/${componentName}/${mainFile}"`));
144
143
  }
145
144
  catch (error) {
146
145
  s.stop(pc.red("An error occurred during injection."));
147
- cancel(pc.gray(error?.message ||
148
- "Unknown CLI Error"));
146
+ cancel(pc.gray(error?.message || "Unknown CLI Error"));
149
147
  process.exit(1);
150
148
  }
151
149
  });
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,KAAK,SAuTd,CAAC"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,KAAK,SA6Td,CAAC"}
@@ -16,23 +16,19 @@ export const login = new Command()
16
16
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`);
17
17
  const s = spinner();
18
18
  try {
19
- // --------------------------------------------------
20
- // 1. Create / load the device's secure key
21
- // --------------------------------------------------
19
+ // ==================================================
20
+ // 1. CREATE / LOAD SECURE DEVICE KEY
21
+ // ==================================================
22
22
  s.start("Preparing secure device identity...");
23
23
  await createDeviceKey();
24
- // Only the PUBLIC key/certificate leaves
25
- // the user's machine.
26
24
  const publicKey = await getDevicePublicKey();
27
- // Existing machine fingerprint.
28
25
  const hardwareId = machineIdSync();
29
- // Detect the cryptographic key type.
30
26
  const publicKeyType = process.platform === "win32"
31
27
  ? "windows-rsa"
32
28
  : "macos-ec";
33
- // --------------------------------------------------
34
- // 2. Start device authorization
35
- // --------------------------------------------------
29
+ // ==================================================
30
+ // 2. REQUEST DEVICE CODE
31
+ // ==================================================
36
32
  s.start("Requesting pairing code...");
37
33
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
38
34
  method: "POST",
@@ -57,21 +53,20 @@ export const login = new Command()
57
53
  throw new Error("Authorization server returned an invalid device response.");
58
54
  }
59
55
  s.stop(pc.green("Secure device identity registered."));
60
- // --------------------------------------------------
61
- // 3. Ask user to approve in browser
62
- // --------------------------------------------------
56
+ // ==================================================
57
+ // 3. SHOW BROWSER AUTHORIZATION
58
+ // ==================================================
63
59
  note(`${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
64
60
  `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`, "Action Required");
65
61
  try {
66
62
  await open(verificationUrl);
67
63
  }
68
64
  catch {
69
- // Browser launch failure
70
- // is non-fatal.
65
+ // Browser launch failure is non-fatal.
71
66
  }
72
- // --------------------------------------------------
73
- // 4. Poll for authorization
74
- // --------------------------------------------------
67
+ // ==================================================
68
+ // 4. POLL FOR AUTHORIZATION
69
+ // ==================================================
75
70
  s.start("Waiting for web authorization...");
76
71
  const pollInterval = (interval || 5) * 1000;
77
72
  let token = null;
@@ -97,56 +92,64 @@ export const login = new Command()
97
92
  cancel("Unable to read authorization response.");
98
93
  process.exit(1);
99
94
  }
100
- // --------------------------------------------------
101
- // SUCCESS
102
- // --------------------------------------------------
95
+ // ==================================================
96
+ // AUTHORIZATION SUCCESS
97
+ // ==================================================
103
98
  if (tokenResponse.ok &&
104
99
  typeof tokenData.accessToken ===
105
100
  "string" &&
106
101
  tokenData.accessToken.length > 0) {
107
102
  token =
108
103
  tokenData.accessToken;
109
- // IMPORTANT:
110
- // Stop the spinner immediately.
111
- s.stop(pc.green("Hardware signature linked & token verified!"));
112
- // IMPORTANT:
113
- // Leave the polling loop immediately.
114
104
  break;
115
105
  }
116
- // --------------------------------------------------
106
+ // ==================================================
117
107
  // STILL WAITING
118
- // --------------------------------------------------
108
+ // ==================================================
119
109
  if (tokenData.error ===
120
110
  "authorization_pending") {
121
111
  continue;
122
112
  }
123
- // --------------------------------------------------
124
- // FAILED / EXPIRED
125
- // --------------------------------------------------
113
+ // ==================================================
114
+ // AUTHORIZATION FAILED
115
+ // ==================================================
126
116
  s.stop(pc.red("Authorization failed or expired."));
127
117
  cancel(`Reason: ${tokenData.error ||
128
118
  "Unknown authorization error"}`);
129
119
  process.exit(1);
130
120
  }
131
- // --------------------------------------------------
132
- // 5. Make absolutely sure token exists
133
- // --------------------------------------------------
121
+ // ==================================================
122
+ // 5. STOP POLLING SPINNER
123
+ // ==================================================
124
+ s.stop(pc.green("Hardware signature linked & token verified!"));
125
+ // ==================================================
126
+ // 6. ENSURE TOKEN EXISTS
127
+ // ==================================================
134
128
  if (!token) {
135
129
  throw new Error("Authorization completed without receiving an access token.");
136
130
  }
137
- // --------------------------------------------------
138
- // 6. Save CLI token + machine ID
139
- // --------------------------------------------------
131
+ // ==================================================
132
+ // 7. SAVE TOKEN
133
+ // ==================================================
140
134
  saveToken(token, hardwareId);
141
- // --------------------------------------------------
142
- // 7. Success
143
- // --------------------------------------------------
135
+ // ==================================================
136
+ // 8. SUCCESS
137
+ // ==================================================
144
138
  outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
145
139
  pc.gray("Secure device key and session token are ready."));
140
+ // ==================================================
141
+ // 9. HARD EXIT
142
+ // ==================================================
143
+ //
144
+ // The login command is completely finished.
145
+ //
146
+ // This prevents any lingering CLI handles
147
+ // (spinner timers, readline handles, etc.)
148
+ // from keeping the Node process alive.
149
+ //
150
+ process.exit(0);
146
151
  }
147
152
  catch (error) {
148
- // Only stop the spinner if it is
149
- // still running.
150
153
  s.stop(pc.red("An error occurred during login."));
151
154
  cancel(pc.gray(error?.message ||
152
155
  "Unknown CLI Error"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ds-01",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Make your site best with DS01",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,8 +12,7 @@ import machineIdPkg from "node-machine-id";
12
12
 
13
13
  const { machineIdSync } = machineIdPkg;
14
14
 
15
- const API_BASE_URL =
16
- process.env.DS01_API_URL || "https://ds-01.vercel.app";
15
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
17
16
 
18
17
  // Helper to verify Tailwind exists before doing anything
19
18
  function checkTailwindInstallation() {
@@ -29,9 +28,7 @@ function checkTailwindInstallation() {
29
28
  process.exit(1);
30
29
  }
31
30
 
32
- const pkgJson = JSON.parse(
33
- fs.readFileSync(pkgJsonPath, "utf-8"),
34
- );
31
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
35
32
 
36
33
  const allDeps = {
37
34
  ...pkgJson.dependencies,
@@ -40,22 +37,16 @@ function checkTailwindInstallation() {
40
37
 
41
38
  if (!allDeps["tailwindcss"]) {
42
39
  cancel(
43
- pc.red(
44
- "Tailwind CSS is missing from your project dependencies.\n",
45
- ) +
40
+ pc.red("Tailwind CSS is missing from your project dependencies.\n") +
46
41
  pc.gray(
47
42
  "DS01 components rely strictly on Tailwind CSS for styling.\n\n",
48
43
  ) +
49
44
  pc.white(
50
45
  "Kindly install it and configure your project, then retry:\n",
51
46
  ) +
52
- pc.cyan(
53
- " npm install tailwindcss @tailwindcss/postcss postcss\n\n",
54
- ) +
47
+ pc.cyan(" npm install tailwindcss @tailwindcss/postcss postcss\n\n") +
55
48
  pc.gray("Official Setup Guide: ") +
56
- pc.underline(
57
- "https://tailwindcss.com/docs/installation",
58
- ),
49
+ pc.underline("https://tailwindcss.com/docs/installation"),
59
50
  );
60
51
 
61
52
  process.exit(1);
@@ -65,10 +56,7 @@ function checkTailwindInstallation() {
65
56
  export const add = new Command()
66
57
  .name("add")
67
58
  .description("Add a component from DS01 to your project")
68
- .argument(
69
- "<component>",
70
- "The name of the component (e.g., premium-section)",
71
- )
59
+ .argument("<component>", "The name of the component (e.g., premium-section)")
72
60
  .action(async (componentName: string) => {
73
61
  console.log();
74
62
 
@@ -82,45 +70,28 @@ export const add = new Command()
82
70
 
83
71
  const cwd = process.cwd();
84
72
 
85
- const configPath = path.join(
86
- cwd,
87
- "ds01.config.json",
88
- );
73
+ const configPath = path.join(cwd, "ds01.config.json");
89
74
 
90
75
  if (!fs.existsSync(configPath)) {
91
- cancel(
92
- pc.red(
93
- "ds01.config.json not found. Run 'npx ds-01 init' first.",
94
- ),
95
- );
76
+ cancel(pc.red("ds01.config.json not found. Run 'npx ds-01 init' first."));
96
77
 
97
78
  process.exit(1);
98
79
  }
99
80
 
100
- const config = JSON.parse(
101
- fs.readFileSync(configPath, "utf-8"),
102
- );
81
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
103
82
 
104
83
  const token = getToken();
105
84
  const machineId = machineIdSync();
106
85
 
107
86
  if (!token || !machineId) {
108
- cancel(
109
- pc.red(
110
- "You are not authenticated. Run 'npx ds-01 login' first.",
111
- ),
112
- );
87
+ cancel(pc.red("You are not authenticated. Run 'npx ds-01 login' first."));
113
88
 
114
89
  process.exit(1);
115
90
  }
116
91
 
117
92
  const s = spinner();
118
93
 
119
- s.start(
120
- `Fetching ${pc.cyan(
121
- `<${componentName} />`,
122
- )} from registry...`,
123
- );
94
+ s.start(`Fetching ${pc.cyan(`<${componentName} />`)} from registry...`);
124
95
 
125
96
  try {
126
97
  // --------------------------------------------------
@@ -144,8 +115,7 @@ export const add = new Command()
144
115
  new TextEncoder().encode(message),
145
116
  );
146
117
 
147
- const signatureBase64 =
148
- Buffer.from(signature).toString("base64");
118
+ const signatureBase64 = Buffer.from(signature).toString("base64");
149
119
 
150
120
  // --------------------------------------------------
151
121
  // 3. Send token + machine ID + signature
@@ -169,29 +139,22 @@ export const add = new Command()
169
139
  );
170
140
 
171
141
  if (!response.ok) {
172
- s.stop(
173
- pc.red("Component fetch failed."),
174
- );
142
+ const errorData = await response.json().catch(() => null);
175
143
 
176
- cancel(
177
- `Component not found or server error (HTTP ${response.status})`,
178
- );
144
+ s.stop(pc.red("Component fetch failed."));
145
+
146
+ cancel(errorData?.error || `Server returned HTTP ${response.status}`);
179
147
 
180
148
  process.exit(1);
181
149
  }
182
150
 
183
- const componentData =
184
- await response.json();
151
+ const componentData = await response.json();
185
152
 
186
153
  // --------------------------------------------------
187
154
  // 4. Create component folder
188
155
  // --------------------------------------------------
189
156
 
190
- const targetDir = path.join(
191
- cwd,
192
- config.componentsPath,
193
- componentName,
194
- );
157
+ const targetDir = path.join(cwd, config.componentsPath, componentName);
195
158
 
196
159
  if (!fs.existsSync(targetDir)) {
197
160
  fs.mkdirSync(targetDir, {
@@ -203,45 +166,29 @@ export const add = new Command()
203
166
  // 5. Extract bundled file names
204
167
  // --------------------------------------------------
205
168
 
206
- const bundledFileNames =
207
- componentData.files.map(
208
- (file: any) =>
209
- file.name.replace(
210
- /\.[^/.]+$/,
211
- "",
212
- ),
213
- );
169
+ const bundledFileNames = componentData.files.map((file: any) =>
170
+ file.name.replace(/\.[^/.]+$/, ""),
171
+ );
214
172
 
215
173
  // --------------------------------------------------
216
174
  // 6. Write component files
217
175
  // --------------------------------------------------
218
176
 
219
177
  for (const file of componentData.files) {
220
- const filePath = path.join(
221
- targetDir,
222
- file.name,
223
- );
178
+ const filePath = path.join(targetDir, file.name);
224
179
 
225
180
  let content = file.content;
226
181
 
227
- bundledFileNames.forEach(
228
- (fileName: string) => {
229
- const regex = new RegExp(
230
- `from\\s+["']\\.[^"']*?\\/${fileName}["']`,
231
- "g",
232
- );
233
-
234
- content = content.replace(
235
- regex,
236
- `from "./${fileName}"`,
237
- );
238
- },
239
- );
182
+ bundledFileNames.forEach((fileName: string) => {
183
+ const regex = new RegExp(
184
+ `from\\s+["']\\.[^"']*?\\/${fileName}["']`,
185
+ "g",
186
+ );
240
187
 
241
- fs.writeFileSync(
242
- filePath,
243
- content,
244
- );
188
+ content = content.replace(regex, `from "./${fileName}"`);
189
+ });
190
+
191
+ fs.writeFileSync(filePath, content);
245
192
  }
246
193
 
247
194
  s.stop(
@@ -256,45 +203,28 @@ export const add = new Command()
256
203
  // 7. Install dependencies
257
204
  // --------------------------------------------------
258
205
 
259
- if (
260
- componentData.dependencies &&
261
- componentData.dependencies.length > 0
262
- ) {
263
- const depsToInstall =
264
- componentData.dependencies.join(" ");
206
+ if (componentData.dependencies && componentData.dependencies.length > 0) {
207
+ const depsToInstall = componentData.dependencies.join(" ");
265
208
 
266
209
  s.start(
267
- `Installing missing dependencies: ${pc.cyan(
268
- depsToInstall,
269
- )}...`,
210
+ `Installing missing dependencies: ${pc.cyan(depsToInstall)}...`,
270
211
  );
271
212
 
272
213
  try {
273
- execSync(
274
- `npm install ${depsToInstall}`,
275
- {
276
- stdio: "ignore",
277
- },
278
- );
214
+ execSync(`npm install ${depsToInstall}`, {
215
+ stdio: "ignore",
216
+ });
279
217
 
280
218
  s.stop(
281
219
  pc.green(
282
- `Dependencies installed successfully: ${pc.gray(
283
- depsToInstall,
284
- )}`,
220
+ `Dependencies installed successfully: ${pc.gray(depsToInstall)}`,
285
221
  ),
286
222
  );
287
223
  } catch {
288
- s.stop(
289
- pc.red(
290
- "Failed to auto-install dependencies.",
291
- ),
292
- );
224
+ s.stop(pc.red("Failed to auto-install dependencies."));
293
225
 
294
226
  note(
295
- `Please run: ${pc.cyan(
296
- `npm install ${depsToInstall}`,
297
- )} manually.`,
227
+ `Please run: ${pc.cyan(`npm install ${depsToInstall}`)} manually.`,
298
228
  "Manual Action Required",
299
229
  );
300
230
  }
@@ -306,9 +236,7 @@ export const add = new Command()
306
236
 
307
237
  const mainFile =
308
238
  bundledFileNames.find(
309
- (name: string) =>
310
- name.toLowerCase() ===
311
- componentName.toLowerCase(),
239
+ (name: string) => name.toLowerCase() === componentName.toLowerCase(),
312
240
  ) || bundledFileNames[0];
313
241
 
314
242
  outro(
@@ -321,19 +249,10 @@ export const add = new Command()
321
249
  ),
322
250
  );
323
251
  } catch (error: any) {
324
- s.stop(
325
- pc.red(
326
- "An error occurred during injection.",
327
- ),
328
- );
252
+ s.stop(pc.red("An error occurred during injection."));
329
253
 
330
- cancel(
331
- pc.gray(
332
- error?.message ||
333
- "Unknown CLI Error",
334
- ),
335
- );
254
+ cancel(pc.gray(error?.message || "Unknown CLI Error"));
336
255
 
337
256
  process.exit(1);
338
257
  }
339
- });
258
+ });
@@ -43,9 +43,9 @@ export const login = new Command()
43
43
  const s = spinner();
44
44
 
45
45
  try {
46
- // --------------------------------------------------
47
- // 1. Create / load the device's secure key
48
- // --------------------------------------------------
46
+ // ==================================================
47
+ // 1. CREATE / LOAD SECURE DEVICE KEY
48
+ // ==================================================
49
49
 
50
50
  s.start(
51
51
  "Preparing secure device identity...",
@@ -53,24 +53,20 @@ export const login = new Command()
53
53
 
54
54
  await createDeviceKey();
55
55
 
56
- // Only the PUBLIC key/certificate leaves
57
- // the user's machine.
58
56
  const publicKey =
59
57
  await getDevicePublicKey();
60
58
 
61
- // Existing machine fingerprint.
62
59
  const hardwareId =
63
60
  machineIdSync();
64
61
 
65
- // Detect the cryptographic key type.
66
62
  const publicKeyType =
67
63
  process.platform === "win32"
68
64
  ? "windows-rsa"
69
65
  : "macos-ec";
70
66
 
71
- // --------------------------------------------------
72
- // 2. Start device authorization
73
- // --------------------------------------------------
67
+ // ==================================================
68
+ // 2. REQUEST DEVICE CODE
69
+ // ==================================================
74
70
 
75
71
  s.start(
76
72
  "Requesting pairing code...",
@@ -117,7 +113,8 @@ export const login = new Command()
117
113
  userCode,
118
114
  verificationUrl,
119
115
  interval,
120
- } = await initResponse.json();
116
+ } =
117
+ await initResponse.json();
121
118
 
122
119
  if (
123
120
  !deviceCode ||
@@ -135,9 +132,9 @@ export const login = new Command()
135
132
  ),
136
133
  );
137
134
 
138
- // --------------------------------------------------
139
- // 3. Ask user to approve in browser
140
- // --------------------------------------------------
135
+ // ==================================================
136
+ // 3. SHOW BROWSER AUTHORIZATION
137
+ // ==================================================
141
138
 
142
139
  note(
143
140
  `${pc.bold(
@@ -160,13 +157,12 @@ export const login = new Command()
160
157
  verificationUrl,
161
158
  );
162
159
  } catch {
163
- // Browser launch failure
164
- // is non-fatal.
160
+ // Browser launch failure is non-fatal.
165
161
  }
166
162
 
167
- // --------------------------------------------------
168
- // 4. Poll for authorization
169
- // --------------------------------------------------
163
+ // ==================================================
164
+ // 4. POLL FOR AUTHORIZATION
165
+ // ==================================================
170
166
 
171
167
  s.start(
172
168
  "Waiting for web authorization...",
@@ -225,9 +221,9 @@ export const login = new Command()
225
221
  process.exit(1);
226
222
  }
227
223
 
228
- // --------------------------------------------------
229
- // SUCCESS
230
- // --------------------------------------------------
224
+ // ==================================================
225
+ // AUTHORIZATION SUCCESS
226
+ // ==================================================
231
227
 
232
228
  if (
233
229
  tokenResponse.ok &&
@@ -238,22 +234,12 @@ export const login = new Command()
238
234
  token =
239
235
  tokenData.accessToken;
240
236
 
241
- // IMPORTANT:
242
- // Stop the spinner immediately.
243
- s.stop(
244
- pc.green(
245
- "Hardware signature linked & token verified!",
246
- ),
247
- );
248
-
249
- // IMPORTANT:
250
- // Leave the polling loop immediately.
251
237
  break;
252
238
  }
253
239
 
254
- // --------------------------------------------------
240
+ // ==================================================
255
241
  // STILL WAITING
256
- // --------------------------------------------------
242
+ // ==================================================
257
243
 
258
244
  if (
259
245
  tokenData.error ===
@@ -262,9 +248,9 @@ export const login = new Command()
262
248
  continue;
263
249
  }
264
250
 
265
- // --------------------------------------------------
266
- // FAILED / EXPIRED
267
- // --------------------------------------------------
251
+ // ==================================================
252
+ // AUTHORIZATION FAILED
253
+ // ==================================================
268
254
 
269
255
  s.stop(
270
256
  pc.red(
@@ -282,9 +268,19 @@ export const login = new Command()
282
268
  process.exit(1);
283
269
  }
284
270
 
285
- // --------------------------------------------------
286
- // 5. Make absolutely sure token exists
287
- // --------------------------------------------------
271
+ // ==================================================
272
+ // 5. STOP POLLING SPINNER
273
+ // ==================================================
274
+
275
+ s.stop(
276
+ pc.green(
277
+ "Hardware signature linked & token verified!",
278
+ ),
279
+ );
280
+
281
+ // ==================================================
282
+ // 6. ENSURE TOKEN EXISTS
283
+ // ==================================================
288
284
 
289
285
  if (!token) {
290
286
  throw new Error(
@@ -292,18 +288,18 @@ export const login = new Command()
292
288
  );
293
289
  }
294
290
 
295
- // --------------------------------------------------
296
- // 6. Save CLI token + machine ID
297
- // --------------------------------------------------
291
+ // ==================================================
292
+ // 7. SAVE TOKEN
293
+ // ==================================================
298
294
 
299
295
  saveToken(
300
296
  token,
301
297
  hardwareId,
302
298
  );
303
299
 
304
- // --------------------------------------------------
305
- // 7. Success
306
- // --------------------------------------------------
300
+ // ==================================================
301
+ // 8. SUCCESS
302
+ // ==================================================
307
303
 
308
304
  outro(
309
305
  `${pc.white(
@@ -315,9 +311,19 @@ export const login = new Command()
315
311
  "Secure device key and session token are ready.",
316
312
  ),
317
313
  );
314
+
315
+ // ==================================================
316
+ // 9. HARD EXIT
317
+ // ==================================================
318
+ //
319
+ // The login command is completely finished.
320
+ //
321
+ // This prevents any lingering CLI handles
322
+ // (spinner timers, readline handles, etc.)
323
+ // from keeping the Node process alive.
324
+ //
325
+ process.exit(0);
318
326
  } catch (error: any) {
319
- // Only stop the spinner if it is
320
- // still running.
321
327
  s.stop(
322
328
  pc.red(
323
329
  "An error occurred during login.",