@king-3/file-kit 1.3.1 → 1.4.1
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/dist/cli.js +44 -20
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,8 @@ import fs from "node:fs/promises";
|
|
|
6
6
|
import { Buffer } from "node:buffer";
|
|
7
7
|
import { accessSync, existsSync, mkdirSync } from "node:fs";
|
|
8
8
|
import { confirm, intro, isCancel, outro, password, select, spinner, text } from "@clack/prompts";
|
|
9
|
-
import { createCipheriv, createDecipheriv,
|
|
9
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
10
|
+
import { argon2id, hash } from "argon2";
|
|
10
11
|
import path$1 from "node:path/posix";
|
|
11
12
|
import ffmpegPath from "@ffmpeg-installer/ffmpeg";
|
|
12
13
|
import { execa } from "execa";
|
|
@@ -164,8 +165,16 @@ const base64ToBuffer = (data) => Buffer.from(data, "base64");
|
|
|
164
165
|
//#endregion
|
|
165
166
|
//#region src/utils/time.ts
|
|
166
167
|
function nowUTC8() {
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
return (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", {
|
|
169
|
+
timeZone: "Asia/Shanghai",
|
|
170
|
+
year: "numeric",
|
|
171
|
+
month: "2-digit",
|
|
172
|
+
day: "2-digit",
|
|
173
|
+
hour: "2-digit",
|
|
174
|
+
minute: "2-digit",
|
|
175
|
+
second: "2-digit",
|
|
176
|
+
hour12: false
|
|
177
|
+
}).replace(/\//g, "-");
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
//#endregion
|
|
@@ -225,7 +234,7 @@ async function base64ToFile(archiveData, outputDir = ".") {
|
|
|
225
234
|
//#endregion
|
|
226
235
|
//#region src/config/defaults.ts
|
|
227
236
|
const CLI_NAME = "File Kit";
|
|
228
|
-
const CLI_VERSION = "1.
|
|
237
|
+
const CLI_VERSION = "1.4.1";
|
|
229
238
|
const CLI_ALIAS = "fkt";
|
|
230
239
|
/**
|
|
231
240
|
* 默认配置
|
|
@@ -349,7 +358,7 @@ async function getInputPath(providedPath, options) {
|
|
|
349
358
|
}
|
|
350
359
|
return inputPath;
|
|
351
360
|
}
|
|
352
|
-
async function getPassword(providedPwd) {
|
|
361
|
+
async function getPassword(providedPwd, options) {
|
|
353
362
|
let userPwd = providedPwd;
|
|
354
363
|
if (!userPwd) {
|
|
355
364
|
userPwd = await password$1({
|
|
@@ -359,14 +368,14 @@ async function getPassword(providedPwd) {
|
|
|
359
368
|
if (value.length < 6) return "密码长度至少 6 位";
|
|
360
369
|
}
|
|
361
370
|
});
|
|
362
|
-
await password$1({
|
|
371
|
+
if (options?.confirm) await password$1({
|
|
363
372
|
message: "请再次输入密码:",
|
|
364
373
|
validate: (value) => {
|
|
365
374
|
if (value !== userPwd) return "两次密码不一致";
|
|
366
375
|
}
|
|
367
376
|
});
|
|
368
377
|
} else if (typeof userPwd === "string" && userPwd.length < 6) {
|
|
369
|
-
logger.error(
|
|
378
|
+
logger.error("密码长度至少 6 位");
|
|
370
379
|
process.exit(1);
|
|
371
380
|
}
|
|
372
381
|
return userPwd;
|
|
@@ -441,7 +450,7 @@ var base64_default = defineCommand({
|
|
|
441
450
|
const typedArgs = args;
|
|
442
451
|
const ctx = createCommandContext(rawArgs);
|
|
443
452
|
ctx.showIntro();
|
|
444
|
-
tryCatch(async () => {
|
|
453
|
+
await tryCatch(async () => {
|
|
445
454
|
const inputPath = await ctx.getInput(typedArgs.input, {
|
|
446
455
|
message: "请输入文件路径",
|
|
447
456
|
placeholder: "file.txt"
|
|
@@ -459,9 +468,14 @@ var base64_default = defineCommand({
|
|
|
459
468
|
//#region src/config/crypto-algorithm.ts
|
|
460
469
|
const CRYPTO_ALGORITHM = {
|
|
461
470
|
name: "aes-256-gcm",
|
|
462
|
-
ivLength:
|
|
471
|
+
ivLength: 12,
|
|
463
472
|
saltLength: 32,
|
|
464
|
-
|
|
473
|
+
keyLength: 32,
|
|
474
|
+
argon2: {
|
|
475
|
+
memoryCost: 131072,
|
|
476
|
+
timeCost: 3,
|
|
477
|
+
parallelism: 4
|
|
478
|
+
}
|
|
465
479
|
};
|
|
466
480
|
|
|
467
481
|
//#endregion
|
|
@@ -469,8 +483,18 @@ const CRYPTO_ALGORITHM = {
|
|
|
469
483
|
/**
|
|
470
484
|
* 从密码派生密钥
|
|
471
485
|
*/
|
|
472
|
-
const deriveKey = (password$2, salt) => {
|
|
473
|
-
|
|
486
|
+
const deriveKey = async (password$2, salt) => {
|
|
487
|
+
const { argon2: argon2Config } = CRYPTO_ALGORITHM;
|
|
488
|
+
const result = await hash(password$2, {
|
|
489
|
+
type: argon2id,
|
|
490
|
+
salt,
|
|
491
|
+
memoryCost: argon2Config.memoryCost,
|
|
492
|
+
timeCost: argon2Config.timeCost,
|
|
493
|
+
parallelism: argon2Config.parallelism,
|
|
494
|
+
hashLength: CRYPTO_ALGORITHM.keyLength,
|
|
495
|
+
raw: true
|
|
496
|
+
});
|
|
497
|
+
return Buffer.from(result);
|
|
474
498
|
};
|
|
475
499
|
/**
|
|
476
500
|
* 加密文件
|
|
@@ -485,7 +509,7 @@ async function encrypt(filePath, outputPath, options) {
|
|
|
485
509
|
const fileExt = getFileExt(filePath);
|
|
486
510
|
const salt = randomBytes(CRYPTO_ALGORITHM.saltLength);
|
|
487
511
|
const iv = randomBytes(CRYPTO_ALGORITHM.ivLength);
|
|
488
|
-
const key = deriveKey(options.password, salt);
|
|
512
|
+
const key = await deriveKey(options.password, salt);
|
|
489
513
|
const cipher = createCipheriv(CRYPTO_ALGORITHM.name, key, iv);
|
|
490
514
|
const encrypted = Buffer.concat([cipher.update(fileBuffer), cipher.final()]);
|
|
491
515
|
const authTag = cipher.getAuthTag();
|
|
@@ -526,7 +550,7 @@ async function decrypt(archiveData, outputDir = ".", options) {
|
|
|
526
550
|
const iv = base64ToBuffer(file.iv);
|
|
527
551
|
const authTag = base64ToBuffer(file.authTag);
|
|
528
552
|
const encrypted = base64ToBuffer(file.encrypted);
|
|
529
|
-
const key = deriveKey(options.password, salt);
|
|
553
|
+
const key = await deriveKey(options.password, salt);
|
|
530
554
|
const decipher = createDecipheriv(CRYPTO_ALGORITHM.name, key, iv);
|
|
531
555
|
decipher.setAuthTag(authTag);
|
|
532
556
|
let decrypted;
|
|
@@ -573,14 +597,14 @@ var decrypt_default = defineCommand({
|
|
|
573
597
|
const typedArgs = args;
|
|
574
598
|
const ctx = createCommandContext(rawArgs);
|
|
575
599
|
ctx.showIntro();
|
|
576
|
-
tryCatch(async () => {
|
|
600
|
+
await tryCatch(async () => {
|
|
577
601
|
const inputPath = await ctx.getInput(typedArgs.input, {
|
|
578
602
|
message: "请输入文件路径",
|
|
579
603
|
placeholder: "*.crypto.txt",
|
|
580
604
|
validateExtension: ".crypto.txt"
|
|
581
605
|
});
|
|
582
606
|
const outputDir = await ctx.getOutput(typedArgs.output, { defaultDir: path.dirname(inputPath) });
|
|
583
|
-
const password$2 = await getPassword(typedArgs.password);
|
|
607
|
+
const password$2 = await getPassword(typedArgs.password, { confirm: false });
|
|
584
608
|
const loading$1 = ctx.loading("正在解密");
|
|
585
609
|
const outputPath = await decrypt(await loadArchive(inputPath, "crypto"), outputDir, { password: password$2 });
|
|
586
610
|
loading$1.close(`文件已解密到: ${cyan(outputPath)}`);
|
|
@@ -617,13 +641,13 @@ var encrypt_default = defineCommand({
|
|
|
617
641
|
const typedArgs = args;
|
|
618
642
|
const ctx = createCommandContext(rawArgs);
|
|
619
643
|
ctx.showIntro();
|
|
620
|
-
tryCatch(async () => {
|
|
644
|
+
await tryCatch(async () => {
|
|
621
645
|
const inputPath = await ctx.getInput(typedArgs.input, {
|
|
622
646
|
message: "请输入文件路径",
|
|
623
647
|
placeholder: "file.txt"
|
|
624
648
|
});
|
|
625
649
|
const outputDir = await ctx.getOutput(typedArgs.output, { defaultDir: path$1.dirname(inputPath) });
|
|
626
|
-
const password$2 = await getPassword(typedArgs.password);
|
|
650
|
+
const password$2 = await getPassword(typedArgs.password, { confirm: true });
|
|
627
651
|
console.log(gray("│"));
|
|
628
652
|
logger.warn(yellow("请妥善保管密码,丢失后无法恢复文件!"));
|
|
629
653
|
const outputPath = buildOutputPath(inputPath, outputDir, "crypto.txt");
|
|
@@ -657,7 +681,7 @@ var restore_default = defineCommand({
|
|
|
657
681
|
const typedArgs = args;
|
|
658
682
|
const ctx = createCommandContext(rawArgs);
|
|
659
683
|
ctx.showIntro();
|
|
660
|
-
tryCatch(async () => {
|
|
684
|
+
await tryCatch(async () => {
|
|
661
685
|
const inputPath = await ctx.getInput(typedArgs.input, {
|
|
662
686
|
message: "请输入文件路径",
|
|
663
687
|
placeholder: "*.base64.txt",
|
|
@@ -899,7 +923,7 @@ var video_to_audio_default = defineCommand({
|
|
|
899
923
|
const typedArgs = args;
|
|
900
924
|
const ctx = createCommandContext(rawArgs);
|
|
901
925
|
ctx.showIntro();
|
|
902
|
-
tryCatch(async () => {
|
|
926
|
+
await tryCatch(async () => {
|
|
903
927
|
const inputPath = await ctx.getInput(typedArgs.input, {
|
|
904
928
|
message: "请输入视频文件路径",
|
|
905
929
|
placeholder: "video.mp4"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@king-3/file-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "多功能文件工具箱(Base64 转换 · 视频转音频 · 加密/解密)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@clack/prompts": "^0.11.0",
|
|
42
42
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
43
43
|
"ansis": "^4.2.0",
|
|
44
|
+
"argon2": "^0.44.0",
|
|
44
45
|
"citty": "^0.1.6",
|
|
45
46
|
"execa": "^9.6.1"
|
|
46
47
|
},
|