ds-01 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.
@@ -1,3 +1,3 @@
1
1
  [?9001h[?1004h]0;C:\WINDOWS\system32\cmd.exe[?25h[?25l
2
- > ds-01@0.1.5 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
2
+ > ds-01@1.0.5 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
3
3
  > tsc[?25h[?9001l[?1004l
@@ -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;AAgBpC,eAAO,MAAM,KAAK,SA2Id,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"}
@@ -1,11 +1,12 @@
1
1
  import { Command } from "commander";
2
- import { intro, outro, spinner, note, cancel } from "@clack/prompts";
2
+ import { intro, outro, spinner, note, cancel, } from "@clack/prompts";
3
3
  import pc from "picocolors";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
6
  import { saveToken } from "../utils/config.js";
7
7
  import { createDeviceKey, getDevicePublicKey, } from "../utils/secureKeyStore.js";
8
- const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
8
+ const API_BASE_URL = process.env.DS01_API_URL ||
9
+ "https://ds-01.vercel.app";
9
10
  const { machineIdSync } = machineIdPkg;
10
11
  export const login = new Command()
11
12
  .name("login")
@@ -15,18 +16,19 @@ export const login = new Command()
15
16
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`);
16
17
  const s = spinner();
17
18
  try {
18
- // --------------------------------------------------
19
- // 1. Create / load the device's secure key
20
- // --------------------------------------------------
19
+ // ==================================================
20
+ // 1. CREATE / LOAD SECURE DEVICE KEY
21
+ // ==================================================
21
22
  s.start("Preparing secure device identity...");
22
23
  await createDeviceKey();
23
- // Only the PUBLIC certificate/key leaves the machine.
24
24
  const publicKey = await getDevicePublicKey();
25
- // Existing machine fingerprint.
26
25
  const hardwareId = machineIdSync();
27
- // --------------------------------------------------
28
- // 2. Start device authorization
29
- // --------------------------------------------------
26
+ const publicKeyType = process.platform === "win32"
27
+ ? "windows-rsa"
28
+ : "macos-ec";
29
+ // ==================================================
30
+ // 2. REQUEST DEVICE CODE
31
+ // ==================================================
30
32
  s.start("Requesting pairing code...");
31
33
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
32
34
  method: "POST",
@@ -36,7 +38,7 @@ export const login = new Command()
36
38
  body: JSON.stringify({
37
39
  machineId: hardwareId,
38
40
  publicKey,
39
- publicKeyType: process.platform === "win32" ? "windows-rsa" : "macos-ec",
41
+ publicKeyType,
40
42
  }),
41
43
  });
42
44
  if (!initResponse.ok) {
@@ -44,14 +46,16 @@ export const login = new Command()
44
46
  cancel(`Server responded with HTTP ${initResponse.status}`);
45
47
  process.exit(1);
46
48
  }
47
- const { deviceCode, userCode, verificationUrl, interval } = await initResponse.json();
48
- if (!deviceCode || !userCode || !verificationUrl) {
49
+ const { deviceCode, userCode, verificationUrl, interval, } = await initResponse.json();
50
+ if (!deviceCode ||
51
+ !userCode ||
52
+ !verificationUrl) {
49
53
  throw new Error("Authorization server returned an invalid device response.");
50
54
  }
51
55
  s.stop(pc.green("Secure device identity registered."));
52
- // --------------------------------------------------
53
- // 3. Ask user to approve in browser
54
- // --------------------------------------------------
56
+ // ==================================================
57
+ // 3. SHOW BROWSER AUTHORIZATION
58
+ // ==================================================
55
59
  note(`${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
56
60
  `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`, "Action Required");
57
61
  try {
@@ -60,13 +64,13 @@ export const login = new Command()
60
64
  catch {
61
65
  // Browser launch failure is non-fatal.
62
66
  }
63
- // --------------------------------------------------
64
- // 4. Poll for authorization
65
- // --------------------------------------------------
67
+ // ==================================================
68
+ // 4. POLL FOR AUTHORIZATION
69
+ // ==================================================
66
70
  s.start("Waiting for web authorization...");
67
- let token = null;
68
71
  const pollInterval = (interval || 5) * 1000;
69
- while (!token) {
72
+ let token = null;
73
+ while (true) {
70
74
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
71
75
  const tokenResponse = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
72
76
  method: "POST",
@@ -78,27 +82,77 @@ export const login = new Command()
78
82
  machineId: hardwareId,
79
83
  }),
80
84
  });
81
- const tokenData = await tokenResponse.json();
82
- if (tokenResponse.ok && tokenData.accessToken) {
83
- token = tokenData.accessToken;
85
+ let tokenData;
86
+ try {
87
+ tokenData =
88
+ await tokenResponse.json();
84
89
  }
85
- else if (tokenData.error !== "authorization_pending") {
86
- s.stop(pc.red("Authorization failed or expired."));
87
- cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
90
+ catch {
91
+ s.stop(pc.red("Authorization server returned invalid JSON."));
92
+ cancel("Unable to read authorization response.");
88
93
  process.exit(1);
89
94
  }
95
+ // ==================================================
96
+ // AUTHORIZATION SUCCESS
97
+ // ==================================================
98
+ if (tokenResponse.ok &&
99
+ typeof tokenData.accessToken ===
100
+ "string" &&
101
+ tokenData.accessToken.length > 0) {
102
+ token =
103
+ tokenData.accessToken;
104
+ break;
105
+ }
106
+ // ==================================================
107
+ // STILL WAITING
108
+ // ==================================================
109
+ if (tokenData.error ===
110
+ "authorization_pending") {
111
+ continue;
112
+ }
113
+ // ==================================================
114
+ // AUTHORIZATION FAILED
115
+ // ==================================================
116
+ s.stop(pc.red("Authorization failed or expired."));
117
+ cancel(`Reason: ${tokenData.error ||
118
+ "Unknown authorization error"}`);
119
+ process.exit(1);
90
120
  }
91
- // --------------------------------------------------
92
- // 5. Save CLI token + machine ID
93
- // --------------------------------------------------
94
- saveToken(token, hardwareId);
121
+ // ==================================================
122
+ // 5. STOP POLLING SPINNER
123
+ // ==================================================
95
124
  s.stop(pc.green("Hardware signature linked & token verified!"));
125
+ // ==================================================
126
+ // 6. ENSURE TOKEN EXISTS
127
+ // ==================================================
128
+ if (!token) {
129
+ throw new Error("Authorization completed without receiving an access token.");
130
+ }
131
+ // ==================================================
132
+ // 7. SAVE TOKEN
133
+ // ==================================================
134
+ saveToken(token, hardwareId);
135
+ // ==================================================
136
+ // 8. SUCCESS
137
+ // ==================================================
96
138
  outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
97
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);
98
151
  }
99
152
  catch (error) {
100
153
  s.stop(pc.red("An error occurred during login."));
101
- cancel(pc.gray(error?.message || "Unknown CLI Error"));
154
+ cancel(pc.gray(error?.message ||
155
+ "Unknown CLI Error"));
102
156
  process.exit(1);
103
157
  }
104
158
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ds-01",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Make your site best with DS01",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,11 @@
1
1
  import { Command } from "commander";
2
- import { intro, outro, spinner, note, cancel } from "@clack/prompts";
2
+ import {
3
+ intro,
4
+ outro,
5
+ spinner,
6
+ note,
7
+ cancel,
8
+ } from "@clack/prompts";
3
9
  import pc from "picocolors";
4
10
  import open from "open";
5
11
  import machineIdPkg from "node-machine-id";
@@ -10,18 +16,26 @@ import {
10
16
  getDevicePublicKey,
11
17
  } from "../utils/secureKeyStore.js";
12
18
 
13
- const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
19
+ const API_BASE_URL =
20
+ process.env.DS01_API_URL ||
21
+ "https://ds-01.vercel.app";
14
22
 
15
23
  const { machineIdSync } = machineIdPkg;
16
24
 
17
25
  export const login = new Command()
18
26
  .name("login")
19
- .description("Authenticate your terminal with DS01 via browser")
27
+ .description(
28
+ "Authenticate your terminal with DS01 via browser",
29
+ )
20
30
  .action(async () => {
21
31
  console.log();
22
32
 
23
33
  intro(
24
- `${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray(
34
+ `${pc.bgWhite(
35
+ pc.black(
36
+ pc.bold(" DS01 "),
37
+ ),
38
+ )} ${pc.gray(
25
39
  "v1.0.0 — Terminal Authentication",
26
40
  )}`,
27
41
  );
@@ -29,128 +43,300 @@ export const login = new Command()
29
43
  const s = spinner();
30
44
 
31
45
  try {
32
- // --------------------------------------------------
33
- // 1. Create / load the device's secure key
34
- // --------------------------------------------------
35
- s.start("Preparing secure device identity...");
46
+ // ==================================================
47
+ // 1. CREATE / LOAD SECURE DEVICE KEY
48
+ // ==================================================
49
+
50
+ s.start(
51
+ "Preparing secure device identity...",
52
+ );
36
53
 
37
54
  await createDeviceKey();
38
55
 
39
- // Only the PUBLIC certificate/key leaves the machine.
40
- const publicKey = await getDevicePublicKey();
41
-
42
- // Existing machine fingerprint.
43
- const hardwareId = machineIdSync();
44
-
45
- // --------------------------------------------------
46
- // 2. Start device authorization
47
- // --------------------------------------------------
48
- s.start("Requesting pairing code...");
49
-
50
- const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
51
- method: "POST",
52
- headers: {
53
- "Content-Type": "application/json",
54
- },
55
- body: JSON.stringify({
56
- machineId: hardwareId,
57
- publicKey,
58
- publicKeyType:
59
- process.platform === "win32" ? "windows-rsa" : "macos-ec",
60
- }),
61
- });
56
+ const publicKey =
57
+ await getDevicePublicKey();
58
+
59
+ const hardwareId =
60
+ machineIdSync();
61
+
62
+ const publicKeyType =
63
+ process.platform === "win32"
64
+ ? "windows-rsa"
65
+ : "macos-ec";
66
+
67
+ // ==================================================
68
+ // 2. REQUEST DEVICE CODE
69
+ // ==================================================
70
+
71
+ s.start(
72
+ "Requesting pairing code...",
73
+ );
74
+
75
+ const initResponse =
76
+ await fetch(
77
+ `${API_BASE_URL}/api/auth/device/code`,
78
+ {
79
+ method: "POST",
80
+
81
+ headers: {
82
+ "Content-Type":
83
+ "application/json",
84
+ },
85
+
86
+ body: JSON.stringify({
87
+ machineId:
88
+ hardwareId,
89
+
90
+ publicKey,
91
+
92
+ publicKeyType,
93
+ }),
94
+ },
95
+ );
62
96
 
63
97
  if (!initResponse.ok) {
64
- s.stop(pc.red("Failed to reach DS01 authorization server."));
98
+ s.stop(
99
+ pc.red(
100
+ "Failed to reach DS01 authorization server.",
101
+ ),
102
+ );
65
103
 
66
- cancel(`Server responded with HTTP ${initResponse.status}`);
104
+ cancel(
105
+ `Server responded with HTTP ${initResponse.status}`,
106
+ );
67
107
 
68
108
  process.exit(1);
69
109
  }
70
110
 
71
- const { deviceCode, userCode, verificationUrl, interval } =
111
+ const {
112
+ deviceCode,
113
+ userCode,
114
+ verificationUrl,
115
+ interval,
116
+ } =
72
117
  await initResponse.json();
73
118
 
74
- if (!deviceCode || !userCode || !verificationUrl) {
119
+ if (
120
+ !deviceCode ||
121
+ !userCode ||
122
+ !verificationUrl
123
+ ) {
75
124
  throw new Error(
76
125
  "Authorization server returned an invalid device response.",
77
126
  );
78
127
  }
79
128
 
80
- s.stop(pc.green("Secure device identity registered."));
129
+ s.stop(
130
+ pc.green(
131
+ "Secure device identity registered.",
132
+ ),
133
+ );
134
+
135
+ // ==================================================
136
+ // 3. SHOW BROWSER AUTHORIZATION
137
+ // ==================================================
81
138
 
82
- // --------------------------------------------------
83
- // 3. Ask user to approve in browser
84
- // --------------------------------------------------
85
139
  note(
86
- `${pc.bold("Pairing Code:")} ${pc.cyan(
87
- pc.bold(` ${userCode} `),
140
+ `${pc.bold(
141
+ "Pairing Code:",
142
+ )} ${pc.cyan(
143
+ pc.bold(
144
+ ` ${userCode} `,
145
+ ),
88
146
  )}\n` +
89
- `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`,
147
+ `${pc.bold(
148
+ "Verification URL:",
149
+ )} ${pc.underline(
150
+ verificationUrl,
151
+ )}`,
90
152
  "Action Required",
91
153
  );
92
154
 
93
155
  try {
94
- await open(verificationUrl);
156
+ await open(
157
+ verificationUrl,
158
+ );
95
159
  } catch {
96
160
  // Browser launch failure is non-fatal.
97
161
  }
98
162
 
99
- // --------------------------------------------------
100
- // 4. Poll for authorization
101
- // --------------------------------------------------
102
- s.start("Waiting for web authorization...");
163
+ // ==================================================
164
+ // 4. POLL FOR AUTHORIZATION
165
+ // ==================================================
103
166
 
104
- let token: string | null = null;
167
+ s.start(
168
+ "Waiting for web authorization...",
169
+ );
105
170
 
106
- const pollInterval = (interval || 5) * 1000;
171
+ const pollInterval =
172
+ (interval || 5) * 1000;
107
173
 
108
- while (!token) {
109
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
174
+ let token: string | null =
175
+ null;
110
176
 
111
- const tokenResponse = await fetch(
112
- `${API_BASE_URL}/api/auth/device/token`,
113
- {
114
- method: "POST",
115
- headers: {
116
- "Content-Type": "application/json",
117
- },
118
- body: JSON.stringify({
119
- deviceCode,
120
- machineId: hardwareId,
121
- }),
122
- },
177
+ while (true) {
178
+ await new Promise(
179
+ (resolve) =>
180
+ setTimeout(
181
+ resolve,
182
+ pollInterval,
183
+ ),
123
184
  );
124
185
 
125
- const tokenData = await tokenResponse.json();
186
+ const tokenResponse =
187
+ await fetch(
188
+ `${API_BASE_URL}/api/auth/device/token`,
189
+ {
190
+ method: "POST",
191
+
192
+ headers: {
193
+ "Content-Type":
194
+ "application/json",
195
+ },
196
+
197
+ body: JSON.stringify({
198
+ deviceCode,
199
+ machineId:
200
+ hardwareId,
201
+ }),
202
+ },
203
+ );
204
+
205
+ let tokenData: any;
126
206
 
127
- if (tokenResponse.ok && tokenData.accessToken) {
128
- token = tokenData.accessToken;
129
- } else if (tokenData.error !== "authorization_pending") {
130
- s.stop(pc.red("Authorization failed or expired."));
207
+ try {
208
+ tokenData =
209
+ await tokenResponse.json();
210
+ } catch {
211
+ s.stop(
212
+ pc.red(
213
+ "Authorization server returned invalid JSON.",
214
+ ),
215
+ );
131
216
 
132
- cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
217
+ cancel(
218
+ "Unable to read authorization response.",
219
+ );
133
220
 
134
221
  process.exit(1);
135
222
  }
223
+
224
+ // ==================================================
225
+ // AUTHORIZATION SUCCESS
226
+ // ==================================================
227
+
228
+ if (
229
+ tokenResponse.ok &&
230
+ typeof tokenData.accessToken ===
231
+ "string" &&
232
+ tokenData.accessToken.length > 0
233
+ ) {
234
+ token =
235
+ tokenData.accessToken;
236
+
237
+ break;
238
+ }
239
+
240
+ // ==================================================
241
+ // STILL WAITING
242
+ // ==================================================
243
+
244
+ if (
245
+ tokenData.error ===
246
+ "authorization_pending"
247
+ ) {
248
+ continue;
249
+ }
250
+
251
+ // ==================================================
252
+ // AUTHORIZATION FAILED
253
+ // ==================================================
254
+
255
+ s.stop(
256
+ pc.red(
257
+ "Authorization failed or expired.",
258
+ ),
259
+ );
260
+
261
+ cancel(
262
+ `Reason: ${
263
+ tokenData.error ||
264
+ "Unknown authorization error"
265
+ }`,
266
+ );
267
+
268
+ process.exit(1);
269
+ }
270
+
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
+ // ==================================================
284
+
285
+ if (!token) {
286
+ throw new Error(
287
+ "Authorization completed without receiving an access token.",
288
+ );
136
289
  }
137
290
 
138
- // --------------------------------------------------
139
- // 5. Save CLI token + machine ID
140
- // --------------------------------------------------
141
- saveToken(token, hardwareId);
291
+ // ==================================================
292
+ // 7. SAVE TOKEN
293
+ // ==================================================
294
+
295
+ saveToken(
296
+ token,
297
+ hardwareId,
298
+ );
142
299
 
143
- s.stop(pc.green("Hardware signature linked & token verified!"));
300
+ // ==================================================
301
+ // 8. SUCCESS
302
+ // ==================================================
144
303
 
145
304
  outro(
146
- `${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
147
- pc.gray("Secure device key and session token are ready."),
305
+ `${pc.white(
306
+ "",
307
+ )} ${pc.bold(
308
+ "Terminal synced successfully",
309
+ )}\n` +
310
+ pc.gray(
311
+ "Secure device key and session token are ready.",
312
+ ),
148
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);
149
326
  } catch (error: any) {
150
- s.stop(pc.red("An error occurred during login."));
327
+ s.stop(
328
+ pc.red(
329
+ "An error occurred during login.",
330
+ ),
331
+ );
151
332
 
152
- cancel(pc.gray(error?.message || "Unknown CLI Error"));
333
+ cancel(
334
+ pc.gray(
335
+ error?.message ||
336
+ "Unknown CLI Error",
337
+ ),
338
+ );
153
339
 
154
340
  process.exit(1);
155
341
  }
156
- });
342
+ });