ds-01 1.0.5 → 1.0.6

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,SAuTd,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")
@@ -20,10 +21,15 @@ export const login = new Command()
20
21
  // --------------------------------------------------
21
22
  s.start("Preparing secure device identity...");
22
23
  await createDeviceKey();
23
- // Only the PUBLIC certificate/key leaves the machine.
24
+ // Only the PUBLIC key/certificate leaves
25
+ // the user's machine.
24
26
  const publicKey = await getDevicePublicKey();
25
27
  // Existing machine fingerprint.
26
28
  const hardwareId = machineIdSync();
29
+ // Detect the cryptographic key type.
30
+ const publicKeyType = process.platform === "win32"
31
+ ? "windows-rsa"
32
+ : "macos-ec";
27
33
  // --------------------------------------------------
28
34
  // 2. Start device authorization
29
35
  // --------------------------------------------------
@@ -36,7 +42,7 @@ export const login = new Command()
36
42
  body: JSON.stringify({
37
43
  machineId: hardwareId,
38
44
  publicKey,
39
- publicKeyType: process.platform === "win32" ? "windows-rsa" : "macos-ec",
45
+ publicKeyType,
40
46
  }),
41
47
  });
42
48
  if (!initResponse.ok) {
@@ -44,8 +50,10 @@ export const login = new Command()
44
50
  cancel(`Server responded with HTTP ${initResponse.status}`);
45
51
  process.exit(1);
46
52
  }
47
- const { deviceCode, userCode, verificationUrl, interval } = await initResponse.json();
48
- if (!deviceCode || !userCode || !verificationUrl) {
53
+ const { deviceCode, userCode, verificationUrl, interval, } = await initResponse.json();
54
+ if (!deviceCode ||
55
+ !userCode ||
56
+ !verificationUrl) {
49
57
  throw new Error("Authorization server returned an invalid device response.");
50
58
  }
51
59
  s.stop(pc.green("Secure device identity registered."));
@@ -58,15 +66,16 @@ export const login = new Command()
58
66
  await open(verificationUrl);
59
67
  }
60
68
  catch {
61
- // Browser launch failure is non-fatal.
69
+ // Browser launch failure
70
+ // is non-fatal.
62
71
  }
63
72
  // --------------------------------------------------
64
73
  // 4. Poll for authorization
65
74
  // --------------------------------------------------
66
75
  s.start("Waiting for web authorization...");
67
- let token = null;
68
76
  const pollInterval = (interval || 5) * 1000;
69
- while (!token) {
77
+ let token = null;
78
+ while (true) {
70
79
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
71
80
  const tokenResponse = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
72
81
  method: "POST",
@@ -78,27 +87,69 @@ export const login = new Command()
78
87
  machineId: hardwareId,
79
88
  }),
80
89
  });
81
- const tokenData = await tokenResponse.json();
82
- if (tokenResponse.ok && tokenData.accessToken) {
83
- token = tokenData.accessToken;
90
+ let tokenData;
91
+ try {
92
+ tokenData =
93
+ await tokenResponse.json();
84
94
  }
85
- else if (tokenData.error !== "authorization_pending") {
86
- s.stop(pc.red("Authorization failed or expired."));
87
- cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
95
+ catch {
96
+ s.stop(pc.red("Authorization server returned invalid JSON."));
97
+ cancel("Unable to read authorization response.");
88
98
  process.exit(1);
89
99
  }
100
+ // --------------------------------------------------
101
+ // SUCCESS
102
+ // --------------------------------------------------
103
+ if (tokenResponse.ok &&
104
+ typeof tokenData.accessToken ===
105
+ "string" &&
106
+ tokenData.accessToken.length > 0) {
107
+ token =
108
+ 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
+ break;
115
+ }
116
+ // --------------------------------------------------
117
+ // STILL WAITING
118
+ // --------------------------------------------------
119
+ if (tokenData.error ===
120
+ "authorization_pending") {
121
+ continue;
122
+ }
123
+ // --------------------------------------------------
124
+ // FAILED / EXPIRED
125
+ // --------------------------------------------------
126
+ s.stop(pc.red("Authorization failed or expired."));
127
+ cancel(`Reason: ${tokenData.error ||
128
+ "Unknown authorization error"}`);
129
+ process.exit(1);
90
130
  }
91
131
  // --------------------------------------------------
92
- // 5. Save CLI token + machine ID
132
+ // 5. Make absolutely sure token exists
133
+ // --------------------------------------------------
134
+ if (!token) {
135
+ throw new Error("Authorization completed without receiving an access token.");
136
+ }
137
+ // --------------------------------------------------
138
+ // 6. Save CLI token + machine ID
93
139
  // --------------------------------------------------
94
140
  saveToken(token, hardwareId);
95
- s.stop(pc.green("Hardware signature linked & token verified!"));
141
+ // --------------------------------------------------
142
+ // 7. Success
143
+ // --------------------------------------------------
96
144
  outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
97
145
  pc.gray("Secure device key and session token are ready."));
98
146
  }
99
147
  catch (error) {
148
+ // Only stop the spinner if it is
149
+ // still running.
100
150
  s.stop(pc.red("An error occurred during login."));
101
- cancel(pc.gray(error?.message || "Unknown CLI Error"));
151
+ cancel(pc.gray(error?.message ||
152
+ "Unknown CLI Error"));
102
153
  process.exit(1);
103
154
  }
104
155
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ds-01",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
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
  );
@@ -32,125 +46,291 @@ export const login = new Command()
32
46
  // --------------------------------------------------
33
47
  // 1. Create / load the device's secure key
34
48
  // --------------------------------------------------
35
- s.start("Preparing secure device identity...");
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();
56
+ // Only the PUBLIC key/certificate leaves
57
+ // the user's machine.
58
+ const publicKey =
59
+ await getDevicePublicKey();
41
60
 
42
61
  // Existing machine fingerprint.
43
- const hardwareId = machineIdSync();
62
+ const hardwareId =
63
+ machineIdSync();
64
+
65
+ // Detect the cryptographic key type.
66
+ const publicKeyType =
67
+ process.platform === "win32"
68
+ ? "windows-rsa"
69
+ : "macos-ec";
44
70
 
45
71
  // --------------------------------------------------
46
72
  // 2. Start device authorization
47
73
  // --------------------------------------------------
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
- });
74
+
75
+ s.start(
76
+ "Requesting pairing code...",
77
+ );
78
+
79
+ const initResponse =
80
+ await fetch(
81
+ `${API_BASE_URL}/api/auth/device/code`,
82
+ {
83
+ method: "POST",
84
+
85
+ headers: {
86
+ "Content-Type":
87
+ "application/json",
88
+ },
89
+
90
+ body: JSON.stringify({
91
+ machineId:
92
+ hardwareId,
93
+
94
+ publicKey,
95
+
96
+ publicKeyType,
97
+ }),
98
+ },
99
+ );
62
100
 
63
101
  if (!initResponse.ok) {
64
- s.stop(pc.red("Failed to reach DS01 authorization server."));
102
+ s.stop(
103
+ pc.red(
104
+ "Failed to reach DS01 authorization server.",
105
+ ),
106
+ );
65
107
 
66
- cancel(`Server responded with HTTP ${initResponse.status}`);
108
+ cancel(
109
+ `Server responded with HTTP ${initResponse.status}`,
110
+ );
67
111
 
68
112
  process.exit(1);
69
113
  }
70
114
 
71
- const { deviceCode, userCode, verificationUrl, interval } =
72
- await initResponse.json();
73
-
74
- if (!deviceCode || !userCode || !verificationUrl) {
115
+ const {
116
+ deviceCode,
117
+ userCode,
118
+ verificationUrl,
119
+ interval,
120
+ } = await initResponse.json();
121
+
122
+ if (
123
+ !deviceCode ||
124
+ !userCode ||
125
+ !verificationUrl
126
+ ) {
75
127
  throw new Error(
76
128
  "Authorization server returned an invalid device response.",
77
129
  );
78
130
  }
79
131
 
80
- s.stop(pc.green("Secure device identity registered."));
132
+ s.stop(
133
+ pc.green(
134
+ "Secure device identity registered.",
135
+ ),
136
+ );
81
137
 
82
138
  // --------------------------------------------------
83
139
  // 3. Ask user to approve in browser
84
140
  // --------------------------------------------------
141
+
85
142
  note(
86
- `${pc.bold("Pairing Code:")} ${pc.cyan(
87
- pc.bold(` ${userCode} `),
143
+ `${pc.bold(
144
+ "Pairing Code:",
145
+ )} ${pc.cyan(
146
+ pc.bold(
147
+ ` ${userCode} `,
148
+ ),
88
149
  )}\n` +
89
- `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`,
150
+ `${pc.bold(
151
+ "Verification URL:",
152
+ )} ${pc.underline(
153
+ verificationUrl,
154
+ )}`,
90
155
  "Action Required",
91
156
  );
92
157
 
93
158
  try {
94
- await open(verificationUrl);
159
+ await open(
160
+ verificationUrl,
161
+ );
95
162
  } catch {
96
- // Browser launch failure is non-fatal.
163
+ // Browser launch failure
164
+ // is non-fatal.
97
165
  }
98
166
 
99
167
  // --------------------------------------------------
100
168
  // 4. Poll for authorization
101
169
  // --------------------------------------------------
102
- s.start("Waiting for web authorization...");
103
170
 
104
- let token: string | null = null;
171
+ s.start(
172
+ "Waiting for web authorization...",
173
+ );
105
174
 
106
- const pollInterval = (interval || 5) * 1000;
175
+ const pollInterval =
176
+ (interval || 5) * 1000;
107
177
 
108
- while (!token) {
109
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
178
+ let token: string | null =
179
+ null;
110
180
 
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
- },
181
+ while (true) {
182
+ await new Promise(
183
+ (resolve) =>
184
+ setTimeout(
185
+ resolve,
186
+ pollInterval,
187
+ ),
123
188
  );
124
189
 
125
- const tokenData = await tokenResponse.json();
190
+ const tokenResponse =
191
+ await fetch(
192
+ `${API_BASE_URL}/api/auth/device/token`,
193
+ {
194
+ method: "POST",
195
+
196
+ headers: {
197
+ "Content-Type":
198
+ "application/json",
199
+ },
200
+
201
+ body: JSON.stringify({
202
+ deviceCode,
203
+ machineId:
204
+ hardwareId,
205
+ }),
206
+ },
207
+ );
208
+
209
+ let tokenData: any;
126
210
 
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."));
211
+ try {
212
+ tokenData =
213
+ await tokenResponse.json();
214
+ } catch {
215
+ s.stop(
216
+ pc.red(
217
+ "Authorization server returned invalid JSON.",
218
+ ),
219
+ );
131
220
 
132
- cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
221
+ cancel(
222
+ "Unable to read authorization response.",
223
+ );
133
224
 
134
225
  process.exit(1);
135
226
  }
227
+
228
+ // --------------------------------------------------
229
+ // SUCCESS
230
+ // --------------------------------------------------
231
+
232
+ if (
233
+ tokenResponse.ok &&
234
+ typeof tokenData.accessToken ===
235
+ "string" &&
236
+ tokenData.accessToken.length > 0
237
+ ) {
238
+ token =
239
+ tokenData.accessToken;
240
+
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
+ break;
252
+ }
253
+
254
+ // --------------------------------------------------
255
+ // STILL WAITING
256
+ // --------------------------------------------------
257
+
258
+ if (
259
+ tokenData.error ===
260
+ "authorization_pending"
261
+ ) {
262
+ continue;
263
+ }
264
+
265
+ // --------------------------------------------------
266
+ // FAILED / EXPIRED
267
+ // --------------------------------------------------
268
+
269
+ s.stop(
270
+ pc.red(
271
+ "Authorization failed or expired.",
272
+ ),
273
+ );
274
+
275
+ cancel(
276
+ `Reason: ${
277
+ tokenData.error ||
278
+ "Unknown authorization error"
279
+ }`,
280
+ );
281
+
282
+ process.exit(1);
136
283
  }
137
284
 
138
285
  // --------------------------------------------------
139
- // 5. Save CLI token + machine ID
286
+ // 5. Make absolutely sure token exists
140
287
  // --------------------------------------------------
141
- saveToken(token, hardwareId);
142
288
 
143
- s.stop(pc.green("Hardware signature linked & token verified!"));
289
+ if (!token) {
290
+ throw new Error(
291
+ "Authorization completed without receiving an access token.",
292
+ );
293
+ }
294
+
295
+ // --------------------------------------------------
296
+ // 6. Save CLI token + machine ID
297
+ // --------------------------------------------------
298
+
299
+ saveToken(
300
+ token,
301
+ hardwareId,
302
+ );
303
+
304
+ // --------------------------------------------------
305
+ // 7. Success
306
+ // --------------------------------------------------
144
307
 
145
308
  outro(
146
- `${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
147
- pc.gray("Secure device key and session token are ready."),
309
+ `${pc.white(
310
+ "",
311
+ )} ${pc.bold(
312
+ "Terminal synced successfully",
313
+ )}\n` +
314
+ pc.gray(
315
+ "Secure device key and session token are ready.",
316
+ ),
148
317
  );
149
318
  } catch (error: any) {
150
- s.stop(pc.red("An error occurred during login."));
319
+ // Only stop the spinner if it is
320
+ // still running.
321
+ s.stop(
322
+ pc.red(
323
+ "An error occurred during login.",
324
+ ),
325
+ );
151
326
 
152
- cancel(pc.gray(error?.message || "Unknown CLI Error"));
327
+ cancel(
328
+ pc.gray(
329
+ error?.message ||
330
+ "Unknown CLI Error",
331
+ ),
332
+ );
153
333
 
154
334
  process.exit(1);
155
335
  }
156
- });
336
+ });