@hapico/cli 0.0.2
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/bin/index.js +460 -0
- package/bun.lock +177 -0
- package/dist/index.js +460 -0
- package/index.ts +553 -0
- package/package.json +36 -0
- package/tsconfig.json +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.compileES5 = void 0;
|
|
41
|
+
const commander_1 = require("commander");
|
|
42
|
+
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const unzipper_1 = __importDefault(require("unzipper"));
|
|
46
|
+
const ora_1 = __importDefault(require("ora"));
|
|
47
|
+
const ws_1 = require("ws");
|
|
48
|
+
const Babel = __importStar(require("@babel/standalone"));
|
|
49
|
+
const qrcode_terminal_1 = __importDefault(require("qrcode-terminal"));
|
|
50
|
+
// Directory to store the token and project config
|
|
51
|
+
const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".hapico");
|
|
52
|
+
const TOKEN_FILE = path.join(CONFIG_DIR, "auth_token.json");
|
|
53
|
+
// Ensure config directory exists
|
|
54
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
55
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
// Function to save token
|
|
58
|
+
const saveToken = (token) => {
|
|
59
|
+
fs.writeFileSync(TOKEN_FILE, JSON.stringify({ access_token: token }, null, 2), {
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
// Function to get stored token
|
|
64
|
+
const getStoredToken = () => {
|
|
65
|
+
if (fs.existsSync(TOKEN_FILE)) {
|
|
66
|
+
const data = fs.readFileSync(TOKEN_FILE, { encoding: "utf8" });
|
|
67
|
+
const json = JSON.parse(data);
|
|
68
|
+
return json.access_token || null;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
// Function to save project ID
|
|
73
|
+
const saveProjectId = (projectDir, id) => {
|
|
74
|
+
const configFile = path.join(projectDir, "hapico.config.json");
|
|
75
|
+
fs.writeFileSync(configFile, JSON.stringify({ projectId: id }, null, 2), { encoding: "utf8" });
|
|
76
|
+
};
|
|
77
|
+
// Function to get stored project ID
|
|
78
|
+
const getStoredProjectId = (projectDir) => {
|
|
79
|
+
const configFile = path.join(projectDir, "hapico.config.json");
|
|
80
|
+
if (fs.existsSync(configFile)) {
|
|
81
|
+
const data = fs.readFileSync(configFile, { encoding: "utf8" });
|
|
82
|
+
const json = JSON.parse(data);
|
|
83
|
+
return json.projectId || null;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
// Khởi tạo cache bằng Map để lưu trữ kết quả compile
|
|
88
|
+
const compileCache = new Map();
|
|
89
|
+
const compileES5 = (code, filePath) => {
|
|
90
|
+
if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
|
|
91
|
+
return code;
|
|
92
|
+
}
|
|
93
|
+
if (compileCache.has(code)) {
|
|
94
|
+
return compileCache.get(code);
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const result = Babel.transform(code, {
|
|
98
|
+
presets: [
|
|
99
|
+
["env", { targets: { esmodules: false } }],
|
|
100
|
+
"typescript",
|
|
101
|
+
"react",
|
|
102
|
+
],
|
|
103
|
+
filename: "file.tsx",
|
|
104
|
+
});
|
|
105
|
+
compileCache.set(code, result.code || "");
|
|
106
|
+
return result.code;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return "";
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
exports.compileES5 = compileES5;
|
|
113
|
+
class FileManager {
|
|
114
|
+
constructor(basePath, onFileChange) {
|
|
115
|
+
this.basePath = basePath;
|
|
116
|
+
this.onFileChange = onFileChange;
|
|
117
|
+
}
|
|
118
|
+
syncFiles(files) {
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
this.writeFile(file);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
listFiles() {
|
|
124
|
+
const files = [];
|
|
125
|
+
const traverseDirectory = (dir) => {
|
|
126
|
+
fs.readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
|
127
|
+
var _a;
|
|
128
|
+
const fullPath = path.join(dir, entry.name);
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
traverseDirectory(fullPath);
|
|
131
|
+
}
|
|
132
|
+
else if (entry.isFile()) {
|
|
133
|
+
const content = fs.readFileSync(fullPath, { encoding: "utf8" });
|
|
134
|
+
files.push({
|
|
135
|
+
path: fullPath.replace(this.basePath + path.sep, "./"),
|
|
136
|
+
content,
|
|
137
|
+
es5: (_a = (0, exports.compileES5)(content, fullPath)) !== null && _a !== void 0 ? _a : "",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
traverseDirectory(this.basePath);
|
|
143
|
+
return files;
|
|
144
|
+
}
|
|
145
|
+
writeFile(file) {
|
|
146
|
+
try {
|
|
147
|
+
const fullPath = path.join(this.basePath, file.path);
|
|
148
|
+
const dir = path.dirname(fullPath);
|
|
149
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
150
|
+
let hasChanged = true;
|
|
151
|
+
if (fs.existsSync(fullPath)) {
|
|
152
|
+
try {
|
|
153
|
+
const existingContent = fs.readFileSync(fullPath, {
|
|
154
|
+
encoding: "utf8",
|
|
155
|
+
});
|
|
156
|
+
hasChanged = existingContent !== file.content;
|
|
157
|
+
}
|
|
158
|
+
catch (readError) {
|
|
159
|
+
console.warn(`Warning: Could not read existing file ${fullPath}, treating as changed:`, readError);
|
|
160
|
+
hasChanged = true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (hasChanged) {
|
|
164
|
+
fs.writeFileSync(fullPath, file.content, { encoding: "utf8" });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(`Error processing file ${file.path}:`, error);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
startWatching() {
|
|
173
|
+
if (!this.onFileChange) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
this.watcher = fs.watch(this.basePath, { recursive: true }, (eventType, filename) => {
|
|
178
|
+
if (filename && eventType === "change") {
|
|
179
|
+
const fullPath = path.join(this.basePath, filename);
|
|
180
|
+
try {
|
|
181
|
+
const stats = fs.statSync(fullPath);
|
|
182
|
+
if (stats.isFile()) {
|
|
183
|
+
const content = fs.readFileSync(fullPath, { encoding: "utf8" });
|
|
184
|
+
this.onFileChange(fullPath, content);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.warn(`Error reading changed file ${fullPath}:`, error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
// console.error(`Error setting up file watcher for ${this.basePath}:`, error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
setOnFileChange(callback) {
|
|
198
|
+
this.onFileChange = callback;
|
|
199
|
+
if (!this.watcher && callback) {
|
|
200
|
+
this.startWatching();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
stopWatching() {
|
|
204
|
+
if (this.watcher) {
|
|
205
|
+
this.watcher.close();
|
|
206
|
+
this.watcher = undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
class RoomState {
|
|
211
|
+
constructor(roomId) {
|
|
212
|
+
this.files = [];
|
|
213
|
+
this.roomId = roomId;
|
|
214
|
+
this.state = {};
|
|
215
|
+
this.isConnected = false;
|
|
216
|
+
this.ws = null;
|
|
217
|
+
this.reconnectTimeout = null;
|
|
218
|
+
this.reconnectAttempts = 0;
|
|
219
|
+
}
|
|
220
|
+
connect(onConnected) {
|
|
221
|
+
if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN)
|
|
222
|
+
return;
|
|
223
|
+
if (this.reconnectTimeout) {
|
|
224
|
+
clearTimeout(this.reconnectTimeout);
|
|
225
|
+
}
|
|
226
|
+
this.ws = new ws_1.WebSocket(`https://base.myworkbeast.com/ws?room=${this.roomId}`);
|
|
227
|
+
this.ws.on("open", () => {
|
|
228
|
+
this.isConnected = true;
|
|
229
|
+
this.reconnectAttempts = 0;
|
|
230
|
+
onConnected === null || onConnected === void 0 ? void 0 : onConnected();
|
|
231
|
+
});
|
|
232
|
+
this.ws.on("close", () => {
|
|
233
|
+
this.isConnected = false;
|
|
234
|
+
this.reconnectAttempts++;
|
|
235
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
236
|
+
console.log(`Attempting to reconnect in ${delay / 1000}s...`);
|
|
237
|
+
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
|
238
|
+
});
|
|
239
|
+
this.ws.on("message", (data) => {
|
|
240
|
+
try {
|
|
241
|
+
const message = JSON.parse(data.toString());
|
|
242
|
+
if (message.type === "state" && message.state) {
|
|
243
|
+
this.state = message.state;
|
|
244
|
+
const view = this.state.view;
|
|
245
|
+
this.files = view;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
console.error("Error processing message:", e);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
this.ws.on("error", (err) => {
|
|
253
|
+
var _a;
|
|
254
|
+
console.error("WebSocket error:", err);
|
|
255
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
updateState(key, value) {
|
|
259
|
+
if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) {
|
|
260
|
+
this.ws.send(JSON.stringify({
|
|
261
|
+
type: "update",
|
|
262
|
+
state: { [key]: value },
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
console.log("Cannot update state: Not connected");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
disconnect() {
|
|
270
|
+
if (this.reconnectTimeout) {
|
|
271
|
+
clearTimeout(this.reconnectTimeout);
|
|
272
|
+
}
|
|
273
|
+
if (this.ws) {
|
|
274
|
+
this.ws.removeAllListeners("close");
|
|
275
|
+
this.ws.close();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
getState() {
|
|
279
|
+
return this.state;
|
|
280
|
+
}
|
|
281
|
+
getIsConnected() {
|
|
282
|
+
return this.isConnected;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
commander_1.program.version("1.0.0").description("Hapico CLI for project management");
|
|
286
|
+
commander_1.program
|
|
287
|
+
.command("clone <id>")
|
|
288
|
+
.description("Clone a project by ID")
|
|
289
|
+
.action(async (id) => {
|
|
290
|
+
const token = getStoredToken();
|
|
291
|
+
if (!token) {
|
|
292
|
+
console.error("You need to login first. Use 'hapico login' command.");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const projectDir = path.resolve(process.cwd(), id);
|
|
296
|
+
if (fs.existsSync(projectDir)) {
|
|
297
|
+
console.error(`Project directory "${id}" already exists.`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
let files = [];
|
|
301
|
+
const apiSpinner = (0, ora_1.default)("Fetching project data...").start();
|
|
302
|
+
try {
|
|
303
|
+
const response = await axios_1.default.get(`https://base.myworkbeast.com/api/views/${id}`);
|
|
304
|
+
files = response.data.files || [];
|
|
305
|
+
apiSpinner.succeed("Project data fetched successfully!");
|
|
306
|
+
const templateSpinner = (0, ora_1.default)("Downloading template...").start();
|
|
307
|
+
const TEMPLATE_URL = "https://files.hcm04.vstorage.vngcloud.vn/assets/template_zalominiapp_devmode.zip";
|
|
308
|
+
const templateResponse = await axios_1.default.get(TEMPLATE_URL, {
|
|
309
|
+
responseType: "arraybuffer",
|
|
310
|
+
});
|
|
311
|
+
templateSpinner.succeed("Template downloaded successfully!");
|
|
312
|
+
const outputDir = path.resolve(process.cwd(), id);
|
|
313
|
+
if (!fs.existsSync(outputDir)) {
|
|
314
|
+
fs.mkdirSync(outputDir);
|
|
315
|
+
}
|
|
316
|
+
const unzipSpinner = (0, ora_1.default)("Extracting template...").start();
|
|
317
|
+
await unzipper_1.default.Open.buffer(templateResponse.data).then((directory) => directory.extract({ path: outputDir }));
|
|
318
|
+
unzipSpinner.succeed("Template extracted successfully!");
|
|
319
|
+
const macosxDir = path.join(process.cwd(), id, "__MACOSX");
|
|
320
|
+
if (fs.existsSync(macosxDir)) {
|
|
321
|
+
fs.rmSync(macosxDir, { recursive: true, force: true });
|
|
322
|
+
}
|
|
323
|
+
// Save project ID to hapico.config.json
|
|
324
|
+
saveProjectId(outputDir, id);
|
|
325
|
+
console.log("Project cloned successfully!");
|
|
326
|
+
const saveSpinner = (0, ora_1.default)("Saving project files...").start();
|
|
327
|
+
files.forEach((file) => {
|
|
328
|
+
const filePath = path.join(process.cwd(), id, "src", file.path);
|
|
329
|
+
const dir = path.dirname(filePath);
|
|
330
|
+
if (!fs.existsSync(dir)) {
|
|
331
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
332
|
+
}
|
|
333
|
+
fs.writeFileSync(filePath, file.content);
|
|
334
|
+
});
|
|
335
|
+
saveSpinner.succeed("Project files saved successfully!");
|
|
336
|
+
console.log(`Run 'cd ${id} && npm install && hapico dev' to start the project.`);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
apiSpinner.fail(`Error cloning project: ${error.message}`);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
commander_1.program
|
|
343
|
+
.command("dev")
|
|
344
|
+
.description("Start the project in development mode")
|
|
345
|
+
.action(() => {
|
|
346
|
+
const token = getStoredToken();
|
|
347
|
+
if (!token) {
|
|
348
|
+
console.error("You need to login first. Use 'hapico login' command.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const devSpinner = (0, ora_1.default)("Starting the project in development mode...").start();
|
|
352
|
+
const pwd = process.cwd();
|
|
353
|
+
const srcDir = path.join(pwd, "src");
|
|
354
|
+
if (!fs.existsSync(srcDir)) {
|
|
355
|
+
devSpinner.fail("Source directory 'src' does not exist. Please clone a project first.");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const projectId = getStoredProjectId(pwd);
|
|
359
|
+
if (!projectId) {
|
|
360
|
+
devSpinner.fail("Project ID not found. Please ensure hapico.config.json exists in the project directory.");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
console.log(`Connecting to WebSocket server for room: ${projectId}`);
|
|
364
|
+
const room = new RoomState(`view_${projectId}`);
|
|
365
|
+
room.connect(async () => {
|
|
366
|
+
devSpinner.succeed("Project started in development mode!");
|
|
367
|
+
const fileManager = new FileManager(srcDir);
|
|
368
|
+
const initialFiles = fileManager.listFiles();
|
|
369
|
+
room.updateState("view", initialFiles);
|
|
370
|
+
fileManager.setOnFileChange((filePath, content) => {
|
|
371
|
+
const es5 = (0, exports.compileES5)(content, filePath);
|
|
372
|
+
console.log(`File changed: ${filePath === null || filePath === void 0 ? void 0 : filePath.replace(srcDir, ".")}`);
|
|
373
|
+
const updatedView = room.files.map((file) => {
|
|
374
|
+
if (path.join(srcDir, file.path) === filePath) {
|
|
375
|
+
return { ...file, content, es5 };
|
|
376
|
+
}
|
|
377
|
+
return file;
|
|
378
|
+
});
|
|
379
|
+
room.updateState("view", updatedView);
|
|
380
|
+
});
|
|
381
|
+
qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
|
|
382
|
+
console.log("Scan this QR code to connect to the project:");
|
|
383
|
+
console.log(qrcode);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
commander_1.program
|
|
388
|
+
.command("publish")
|
|
389
|
+
.description("Publish the project source code to the server")
|
|
390
|
+
.action(() => {
|
|
391
|
+
const token = getStoredToken();
|
|
392
|
+
if (!token) {
|
|
393
|
+
console.error("You need to login first. Use 'hapico login' command.");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const saveSpinner = (0, ora_1.default)("Saving project source code...").start();
|
|
397
|
+
const pwd = process.cwd();
|
|
398
|
+
const projectId = getStoredProjectId(pwd);
|
|
399
|
+
if (!projectId) {
|
|
400
|
+
saveSpinner.fail("Project ID not found. Please ensure hapico.config.json exists in the project directory.");
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const srcDir = path.join(pwd, "src");
|
|
404
|
+
if (!fs.existsSync(srcDir)) {
|
|
405
|
+
saveSpinner.fail("Source directory 'src' does not exist. Please clone a project first.");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const fileManager = new FileManager(srcDir);
|
|
409
|
+
const files = fileManager.listFiles();
|
|
410
|
+
const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
|
|
411
|
+
axios_1.default
|
|
412
|
+
.put(apiUrl, {
|
|
413
|
+
code: JSON.stringify({
|
|
414
|
+
files,
|
|
415
|
+
}),
|
|
416
|
+
}, {
|
|
417
|
+
headers: {
|
|
418
|
+
Authorization: `Bearer ${token}`,
|
|
419
|
+
"Content-Type": "application/json",
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
.then(() => {
|
|
423
|
+
saveSpinner.succeed("Project source code saved successfully!");
|
|
424
|
+
})
|
|
425
|
+
.catch((error) => {
|
|
426
|
+
saveSpinner.fail(`Error saving project: ${error.message}`);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
commander_1.program
|
|
430
|
+
.command("login")
|
|
431
|
+
.description("Login to the system")
|
|
432
|
+
.action(() => {
|
|
433
|
+
const token = getStoredToken();
|
|
434
|
+
if (token) {
|
|
435
|
+
console.log("You are already logged in.");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const readline = require("readline").createInterface({
|
|
439
|
+
input: process.stdin,
|
|
440
|
+
output: process.stdout,
|
|
441
|
+
});
|
|
442
|
+
readline.question("Please enter your token: ", (inputToken) => {
|
|
443
|
+
saveToken(inputToken);
|
|
444
|
+
console.log("Login successful!");
|
|
445
|
+
readline.close();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
commander_1.program
|
|
449
|
+
.command("logout")
|
|
450
|
+
.description("Logout from the system")
|
|
451
|
+
.action(() => {
|
|
452
|
+
const token = getStoredToken();
|
|
453
|
+
if (!token) {
|
|
454
|
+
console.log("You are not logged in.");
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
fs.unlinkSync(TOKEN_FILE);
|
|
458
|
+
console.log("Logout successful!");
|
|
459
|
+
});
|
|
460
|
+
commander_1.program.parse(process.argv);
|