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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +86 -32
- package/package.json +1 -1
- package/src/commands/login.ts +265 -79
package/.turbo/turbo-build.log
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/commands/login.js
CHANGED
|
@@ -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 ||
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 ||
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
let tokenData;
|
|
86
|
+
try {
|
|
87
|
+
tokenData =
|
|
88
|
+
await tokenResponse.json();
|
|
84
89
|
}
|
|
85
|
-
|
|
86
|
-
s.stop(pc.red("Authorization
|
|
87
|
-
cancel(
|
|
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.
|
|
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 ||
|
|
154
|
+
cancel(pc.gray(error?.message ||
|
|
155
|
+
"Unknown CLI Error"));
|
|
102
156
|
process.exit(1);
|
|
103
157
|
}
|
|
104
158
|
});
|
package/package.json
CHANGED
package/src/commands/login.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
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 =
|
|
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(
|
|
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(
|
|
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.
|
|
34
|
-
//
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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(
|
|
98
|
+
s.stop(
|
|
99
|
+
pc.red(
|
|
100
|
+
"Failed to reach DS01 authorization server.",
|
|
101
|
+
),
|
|
102
|
+
);
|
|
65
103
|
|
|
66
|
-
cancel(
|
|
104
|
+
cancel(
|
|
105
|
+
`Server responded with HTTP ${initResponse.status}`,
|
|
106
|
+
);
|
|
67
107
|
|
|
68
108
|
process.exit(1);
|
|
69
109
|
}
|
|
70
110
|
|
|
71
|
-
const {
|
|
111
|
+
const {
|
|
112
|
+
deviceCode,
|
|
113
|
+
userCode,
|
|
114
|
+
verificationUrl,
|
|
115
|
+
interval,
|
|
116
|
+
} =
|
|
72
117
|
await initResponse.json();
|
|
73
118
|
|
|
74
|
-
if (
|
|
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(
|
|
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(
|
|
87
|
-
|
|
140
|
+
`${pc.bold(
|
|
141
|
+
"Pairing Code:",
|
|
142
|
+
)} ${pc.cyan(
|
|
143
|
+
pc.bold(
|
|
144
|
+
` ${userCode} `,
|
|
145
|
+
),
|
|
88
146
|
)}\n` +
|
|
89
|
-
`${pc.bold(
|
|
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(
|
|
156
|
+
await open(
|
|
157
|
+
verificationUrl,
|
|
158
|
+
);
|
|
95
159
|
} catch {
|
|
96
160
|
// Browser launch failure is non-fatal.
|
|
97
161
|
}
|
|
98
162
|
|
|
99
|
-
//
|
|
100
|
-
// 4.
|
|
101
|
-
//
|
|
102
|
-
s.start("Waiting for web authorization...");
|
|
163
|
+
// ==================================================
|
|
164
|
+
// 4. POLL FOR AUTHORIZATION
|
|
165
|
+
// ==================================================
|
|
103
166
|
|
|
104
|
-
|
|
167
|
+
s.start(
|
|
168
|
+
"Waiting for web authorization...",
|
|
169
|
+
);
|
|
105
170
|
|
|
106
|
-
const pollInterval =
|
|
171
|
+
const pollInterval =
|
|
172
|
+
(interval || 5) * 1000;
|
|
107
173
|
|
|
108
|
-
|
|
109
|
-
|
|
174
|
+
let token: string | null =
|
|
175
|
+
null;
|
|
110
176
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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(
|
|
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
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
291
|
+
// ==================================================
|
|
292
|
+
// 7. SAVE TOKEN
|
|
293
|
+
// ==================================================
|
|
294
|
+
|
|
295
|
+
saveToken(
|
|
296
|
+
token,
|
|
297
|
+
hardwareId,
|
|
298
|
+
);
|
|
142
299
|
|
|
143
|
-
|
|
300
|
+
// ==================================================
|
|
301
|
+
// 8. SUCCESS
|
|
302
|
+
// ==================================================
|
|
144
303
|
|
|
145
304
|
outro(
|
|
146
|
-
`${pc.white(
|
|
147
|
-
|
|
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(
|
|
327
|
+
s.stop(
|
|
328
|
+
pc.red(
|
|
329
|
+
"An error occurred during login.",
|
|
330
|
+
),
|
|
331
|
+
);
|
|
151
332
|
|
|
152
|
-
cancel(
|
|
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
|
+
});
|