@tq1086/urpf-cli 2.0.0 → 2.1.0

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.
Files changed (3) hide show
  1. package/dist/index.js +2763 -426
  2. package/package.json +16 -2
  3. package/readme.md +664 -423
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/pack.ts
7
- import path2 from "path";
8
- import fs3 from "fs/promises";
7
+ import path3 from "path";
8
+ import fs4 from "fs/promises";
9
9
 
10
10
  // src/core/scanner/file-scanner.ts
11
11
  import * as fs from "fs/promises";
@@ -554,8 +554,8 @@ var IgnoreRuleParser = class {
554
554
  * @returns 忽略规则列表
555
555
  */
556
556
  async parseFileFromPath(filePath) {
557
- const fs14 = await import("fs/promises");
558
- const content = await fs14.readFile(filePath, "utf-8");
557
+ const fs15 = await import("fs/promises");
558
+ const content = await fs15.readFile(filePath, "utf-8");
559
559
  return this.parseFile(content, filePath);
560
560
  }
561
561
  /**
@@ -582,13 +582,13 @@ var IgnoreRuleParser = class {
582
582
  * @returns 忽略规则列表
583
583
  */
584
584
  async findAndParseIgnoreFiles(rootPath, ignoreFileNames = [".gitignore", ".iflowignore", ".npmignore"]) {
585
- const fs14 = await import("fs/promises");
586
- const path7 = await import("path");
585
+ const fs15 = await import("fs/promises");
586
+ const path8 = await import("path");
587
587
  const rules = [];
588
588
  for (const fileName of ignoreFileNames) {
589
- const filePath = path7.join(rootPath, fileName);
589
+ const filePath = path8.join(rootPath, fileName);
590
590
  try {
591
- await fs14.access(filePath);
591
+ await fs15.access(filePath);
592
592
  const fileRules = await this.parseFileFromPath(filePath);
593
593
  rules.push(...fileRules);
594
594
  } catch {
@@ -602,11 +602,11 @@ var IgnoreRuleParser = class {
602
602
  * @returns 白名单规则列表,如果文件不存在则返回空数组
603
603
  */
604
604
  async parseWhiteList(rootPath) {
605
- const fs14 = await import("fs/promises");
606
- const path7 = await import("path");
607
- const whiteListPath = path7.join(rootPath, ".urpfallow");
605
+ const fs15 = await import("fs/promises");
606
+ const path8 = await import("path");
607
+ const whiteListPath = path8.join(rootPath, ".urpfallow");
608
608
  try {
609
- await fs14.access(whiteListPath);
609
+ await fs15.access(whiteListPath);
610
610
  return await this.parseFileFromPath(whiteListPath);
611
611
  } catch {
612
612
  return [];
@@ -618,11 +618,11 @@ var IgnoreRuleParser = class {
618
618
  * @returns 黑名单规则列表,如果文件不存在则返回空数组
619
619
  */
620
620
  async parseBlackList(rootPath) {
621
- const fs14 = await import("fs/promises");
622
- const path7 = await import("path");
623
- const blackListPath = path7.join(rootPath, ".urpfforbid");
621
+ const fs15 = await import("fs/promises");
622
+ const path8 = await import("path");
623
+ const blackListPath = path8.join(rootPath, ".urpfforbid");
624
624
  try {
625
- await fs14.access(blackListPath);
625
+ await fs15.access(blackListPath);
626
626
  return await this.parseFileFromPath(blackListPath);
627
627
  } catch {
628
628
  return [];
@@ -634,11 +634,11 @@ var IgnoreRuleParser = class {
634
634
  * @returns 是否存在白名单文件
635
635
  */
636
636
  async hasWhiteList(rootPath) {
637
- const fs14 = await import("fs/promises");
638
- const path7 = await import("path");
639
- const whiteListPath = path7.join(rootPath, ".urpfallow");
637
+ const fs15 = await import("fs/promises");
638
+ const path8 = await import("path");
639
+ const whiteListPath = path8.join(rootPath, ".urpfallow");
640
640
  try {
641
- await fs14.access(whiteListPath);
641
+ await fs15.access(whiteListPath);
642
642
  return true;
643
643
  } catch {
644
644
  return false;
@@ -689,14 +689,14 @@ var URPFGenerator = class {
689
689
  /**
690
690
  * 生成属性部分
691
691
  */
692
- generatePropertySection(properties) {
692
+ generatePropertySection(properties2) {
693
693
  const newline = this.getNewline();
694
694
  const lines = [];
695
- for (const prop of properties) {
695
+ for (const prop of properties2) {
696
696
  if (prop.name.includes(" ")) {
697
- lines.push(`\${${prop.name}}=${prop.value}`);
697
+ lines.push(`${prop.name}=${prop.value}`);
698
698
  } else {
699
- lines.push(`$${prop.name}=${prop.value}`);
699
+ lines.push(`${prop.name}=${prop.value}`);
700
700
  }
701
701
  }
702
702
  return lines.join(newline);
@@ -705,7 +705,19 @@ var URPFGenerator = class {
705
705
  * 生成资源头部
706
706
  */
707
707
  generateResourceHeader(resource) {
708
- const header = `@${resource.header.udrsReference} ${resource.header.encoding} ${resource.header.permissions} ${resource.header.newlineType}`;
708
+ const udrsRef = resource.header.udrsReference;
709
+ let udrsRefString;
710
+ if (typeof udrsRef === "string") {
711
+ udrsRefString = udrsRef;
712
+ } else if (udrsRef.protocol && udrsRef.uri) {
713
+ udrsRefString = `${udrsRef.protocol}://${udrsRef.uri}`;
714
+ if (udrsRef.fragment) {
715
+ udrsRefString += `#${udrsRef.fragment}`;
716
+ }
717
+ } else {
718
+ throw new Error(`Invalid UDRS reference: ${JSON.stringify(udrsRef)}`);
719
+ }
720
+ const header = `@${udrsRefString} ${resource.header.encoding} ${resource.header.permissions} ${resource.header.newlineType}`;
709
721
  return header;
710
722
  }
711
723
  /**
@@ -727,9 +739,9 @@ var URPFGenerator = class {
727
739
  if (this.state.includeBoundaries) {
728
740
  parts.push(this.generateStartBoundary());
729
741
  }
730
- const properties = "propertySection" in document ? document.propertySection?.properties : "properties" in document ? document.properties : void 0;
731
- if (properties && properties.length > 0) {
732
- parts.push(this.generatePropertySection(properties));
742
+ const properties2 = "propertySection" in document ? document.propertySection?.properties : "properties" in document ? document.properties : void 0;
743
+ if (properties2 && properties2.length > 0) {
744
+ parts.push(this.generatePropertySection(properties2));
733
745
  parts.push(this.generateDelimiter());
734
746
  }
735
747
  const resources = "resourceSections" in document ? document.resourceSections : "resources" in document ? document.resources : [];
@@ -745,10 +757,11 @@ var URPFGenerator = class {
745
757
  }
746
758
  const content = parts.join(newline);
747
759
  const duration = Date.now() - startTime;
760
+ const resourcesForCount = "resourceSections" in document ? document.resourceSections : "resources" in document ? document.resources : [];
748
761
  return {
749
762
  content,
750
763
  size: Buffer.byteLength(content, "utf-8"),
751
- resourceCount: document.resources.length,
764
+ resourceCount: resourcesForCount.length,
752
765
  duration
753
766
  };
754
767
  }
@@ -796,12 +809,7 @@ var ResourceSerializer = class {
796
809
  Buffer.from(buffer.toString("utf-8"), "utf-8");
797
810
  return "utf-8";
798
811
  } catch {
799
- try {
800
- Buffer.from(buffer.toString("gbk"), "gbk");
801
- return "gbk";
802
- } catch {
803
- return "binary";
804
- }
812
+ return "binary";
805
813
  }
806
814
  }
807
815
  /**
@@ -837,6 +845,36 @@ var ResourceSerializer = class {
837
845
  const permissions = mode & 511;
838
846
  return permissions.toString(8).padStart(3, "0");
839
847
  }
848
+ /**
849
+ * 解析 UDRS 引用字符串为 UDRSReference 对象
850
+ */
851
+ parseUDRSReference(udrsRefString) {
852
+ const match = udrsRefString.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/(.*)$/);
853
+ if (!match) {
854
+ throw new Error(`\u65E0\u6548\u7684 UDRS \u5F15\u7528\u683C\u5F0F: ${udrsRefString}`);
855
+ }
856
+ const protocol = match[1];
857
+ const uriAndFragment = match[2];
858
+ const fragmentIndex = uriAndFragment.indexOf("#");
859
+ const uri = fragmentIndex !== -1 ? uriAndFragment.substring(0, fragmentIndex) : uriAndFragment;
860
+ const fragment = fragmentIndex !== -1 ? uriAndFragment.substring(fragmentIndex + 1) : void 0;
861
+ let fragmentType;
862
+ if (fragment) {
863
+ if (fragment.startsWith("::line=")) {
864
+ fragmentType = "raw-line";
865
+ } else if (fragment.startsWith("::byte=")) {
866
+ fragmentType = "raw-byte";
867
+ } else {
868
+ fragmentType = "structured";
869
+ }
870
+ }
871
+ return {
872
+ protocol,
873
+ uri,
874
+ fragment,
875
+ fragmentType
876
+ };
877
+ }
840
878
  /**
841
879
  * 读取文件内容
842
880
  */
@@ -864,8 +902,9 @@ var ResourceSerializer = class {
864
902
  }
865
903
  const content = await this.readFile(filePath, encoding);
866
904
  const permissions = this.convertPermissions(metadata.permissions);
905
+ const udrsRef = this.parseUDRSReference(udrsReference);
867
906
  const header = {
868
- udrsReference,
907
+ udrsReference: udrsRef,
869
908
  encoding,
870
909
  permissions,
871
910
  newlineType
@@ -1175,6 +1214,9 @@ var LineOperations = class {
1175
1214
  */
1176
1215
  static applyCLILineRange(content, range) {
1177
1216
  const normalizedContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1217
+ if (normalizedContent === "") {
1218
+ throw new Error(`\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u63D0\u53D6\u884C`);
1219
+ }
1178
1220
  const lines = normalizedContent.split("\n");
1179
1221
  const totalLines = lines.length;
1180
1222
  const { start, end } = range;
@@ -1385,184 +1427,1527 @@ var LineOperations = class {
1385
1427
  }
1386
1428
  };
1387
1429
 
1388
- // src/commands/pack.ts
1389
- import clipboardy from "clipboardy";
1390
-
1391
- // src/commands/types.ts
1392
- function normalizeURPFPath(filePath) {
1393
- if (filePath.endsWith(".urpf.txt") || filePath.endsWith(".urpf")) {
1394
- return filePath;
1430
+ // src/core/engine/key-extractor.ts
1431
+ import * as fs3 from "fs";
1432
+ import * as path2 from "path";
1433
+ import * as yaml from "js-yaml";
1434
+ import * as toml from "toml";
1435
+ import * as xml2js from "xml2js";
1436
+ import * as ini from "ini";
1437
+ var KeyExtractor = class {
1438
+ constructor(options = {}) {
1439
+ this.options = {
1440
+ preserveFormat: options.preserveFormat ?? true,
1441
+ onError: options.onError ?? ((error) => {
1442
+ throw error;
1443
+ })
1444
+ };
1395
1445
  }
1396
- return `${filePath}.urpf.txt`;
1397
- }
1398
- async function findURPFFile(filePath) {
1399
- const fs14 = await import("fs/promises");
1400
- try {
1401
- const stats = await fs14.stat(filePath);
1402
- if (stats.isFile()) {
1403
- return filePath;
1446
+ /**
1447
+ * 从文件中提取指定 key 的值
1448
+ *
1449
+ * @param filePath - 文件路径
1450
+ * @param keyPath - key 路径(如 "config.server.port")
1451
+ * @returns 提取结果
1452
+ */
1453
+ async extractFromFile(filePath, keyPath) {
1454
+ if (!fs3.existsSync(filePath)) {
1455
+ const error = new Error(`File not found: ${filePath}`);
1456
+ this.options.onError(error);
1457
+ throw error;
1458
+ }
1459
+ const format = this.detectFormat(filePath);
1460
+ const content = fs3.readFileSync(filePath, "utf-8");
1461
+ let value;
1462
+ switch (format) {
1463
+ case "json" /* JSON */:
1464
+ value = await this.extractFromJSON(content, keyPath);
1465
+ break;
1466
+ case "yaml" /* YAML */:
1467
+ value = await this.extractFromYAML(content, keyPath);
1468
+ break;
1469
+ case "toml" /* TOML */:
1470
+ value = await this.extractFromTOML(content, keyPath);
1471
+ break;
1472
+ case "xml" /* XML */:
1473
+ value = await this.extractFromXML(content, keyPath);
1474
+ break;
1475
+ case "properties" /* PROPERTIES */:
1476
+ value = await this.extractFromProperties(content, keyPath);
1477
+ break;
1478
+ case "ini" /* INI */:
1479
+ value = await this.extractFromINI(content, keyPath);
1480
+ break;
1481
+ default: {
1482
+ const error = new Error(`Unsupported file format: ${format}`);
1483
+ this.options.onError(error);
1484
+ throw error;
1485
+ }
1404
1486
  }
1405
- } catch {
1487
+ return {
1488
+ value,
1489
+ type: this.getValueType(value),
1490
+ format,
1491
+ keyPath
1492
+ };
1406
1493
  }
1407
- if (!filePath.endsWith(".urpf.txt") && !filePath.endsWith(".urpf")) {
1408
- const newPath = `${filePath}.urpf.txt`;
1494
+ /**
1495
+ * JSON 内容中提取指定 key 的值
1496
+ *
1497
+ * @param content - JSON 内容
1498
+ * @param keyPath - key 路径
1499
+ * @returns 提取的值
1500
+ */
1501
+ async extractFromJSON(content, keyPath) {
1502
+ const data = this.parseJSON(content);
1503
+ return this.extractByPath(data, keyPath);
1504
+ }
1505
+ /**
1506
+ * 从 YAML 内容中提取指定 key 的值
1507
+ *
1508
+ * @param content - YAML 内容
1509
+ * @param keyPath - key 路径
1510
+ * @returns 提取的值
1511
+ */
1512
+ async extractFromYAML(content, keyPath) {
1513
+ const data = this.parseYAML(content);
1514
+ return this.extractByPath(data, keyPath);
1515
+ }
1516
+ /**
1517
+ * 从 TOML 内容中提取指定 key 的值
1518
+ *
1519
+ * @param content - TOML 内容
1520
+ * @param keyPath - key 路径
1521
+ * @returns 提取的值
1522
+ */
1523
+ async extractFromTOML(content, keyPath) {
1524
+ const data = this.parseTOML(content);
1525
+ return this.extractByPath(data, keyPath);
1526
+ }
1527
+ /**
1528
+ * 从 XML 内容中提取指定 key 的值
1529
+ *
1530
+ * @param content - XML 内容
1531
+ * @param keyPath - key 路径
1532
+ * @returns 提取的值
1533
+ */
1534
+ async extractFromXML(content, keyPath) {
1535
+ const data = await this.parseXML(content);
1536
+ return this.extractByPath(data, keyPath);
1537
+ }
1538
+ /**
1539
+ * 从 Properties 内容中提取指定 key 的值
1540
+ *
1541
+ * @param content - Properties 内容
1542
+ * @param keyPath - key 路径
1543
+ * @returns 提取的值
1544
+ */
1545
+ async extractFromProperties(content, keyPath) {
1546
+ const data = this.parseProperties(content);
1547
+ return this.extractByPath(data, keyPath);
1548
+ }
1549
+ /**
1550
+ * 从 INI 内容中提取指定 key 的值
1551
+ *
1552
+ * @param content - INI 内容
1553
+ * @param keyPath - key 路径
1554
+ * @returns 提取的值
1555
+ */
1556
+ async extractFromINI(content, keyPath) {
1557
+ const data = this.parseINI(content);
1558
+ return this.extractByPath(data, keyPath);
1559
+ }
1560
+ /**
1561
+ * 解析 JSON 内容
1562
+ *
1563
+ * @param content - JSON 内容
1564
+ * @returns 解析后的对象
1565
+ */
1566
+ parseJSON(content) {
1409
1567
  try {
1410
- const stats = await fs14.stat(newPath);
1411
- if (stats.isFile()) {
1412
- return newPath;
1413
- }
1414
- } catch {
1568
+ return JSON.parse(content);
1569
+ } catch (error) {
1570
+ const parsedError = new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`);
1571
+ this.options.onError(parsedError);
1572
+ throw parsedError;
1415
1573
  }
1416
- const oldPath = `${filePath}.urpf`;
1574
+ }
1575
+ /**
1576
+ * 解析 YAML 内容
1577
+ *
1578
+ * @param content - YAML 内容
1579
+ * @returns 解析后的对象
1580
+ */
1581
+ parseYAML(content) {
1417
1582
  try {
1418
- const stats = await fs14.stat(oldPath);
1419
- if (stats.isFile()) {
1420
- return oldPath;
1421
- }
1422
- } catch {
1583
+ return yaml.load(content);
1584
+ } catch (error) {
1585
+ const parsedError = new Error(`Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
1586
+ this.options.onError(parsedError);
1587
+ throw parsedError;
1423
1588
  }
1424
1589
  }
1425
- return null;
1426
- }
1427
-
1428
- // src/commands/pack.ts
1429
- var PackCommand = class {
1430
1590
  /**
1431
- * 执行 pack 命令
1591
+ * 解析 TOML 内容
1592
+ *
1593
+ * @param content - TOML 内容
1594
+ * @returns 解析后的对象
1432
1595
  */
1433
- async execute(inputPath, options = {}) {
1434
- const startTime = Date.now();
1596
+ parseTOML(content) {
1435
1597
  try {
1436
- const resolvedPath = path2.resolve(inputPath);
1437
- await this.validatePath(resolvedPath);
1438
- this.validateSliceOptions(options);
1439
- const outputPath = options.output || this.generateOutputPath(resolvedPath);
1440
- const scanResult = await this.scanFiles(resolvedPath, options);
1441
- const urpfDocument = await this.generateURPFDocument(scanResult, resolvedPath, options);
1442
- const urpfResult = await this.saveURPFDocument(urpfDocument, outputPath, options);
1443
- const duration = Date.now() - startTime;
1444
- await this.handleOutputOptions(urpfResult.content, outputPath, options);
1445
- if (options.verbose) {
1446
- this.logVerbose(scanResult, urpfResult, outputPath, duration);
1447
- }
1448
- return {
1449
- success: true,
1450
- outputPath,
1451
- fileCount: scanResult.files.length,
1452
- directoryCount: scanResult.directories.length,
1453
- skippedCount: scanResult.skippedFiles.length,
1454
- urpfSize: urpfResult.size,
1455
- duration
1456
- };
1457
- } catch (err) {
1458
- const duration = Date.now() - startTime;
1459
- const errorMessage = err instanceof Error ? err.message : String(err);
1460
- if (options.verbose) {
1461
- console.error(`\u9519\u8BEF: ${errorMessage}`);
1462
- }
1463
- return {
1464
- success: false,
1465
- outputPath: "",
1466
- fileCount: 0,
1467
- directoryCount: 0,
1468
- skippedCount: 0,
1469
- urpfSize: 0,
1470
- duration,
1471
- error: errorMessage
1472
- };
1598
+ return toml.parse(content);
1599
+ } catch (error) {
1600
+ const parsedError = new Error(`Failed to parse TOML: ${error instanceof Error ? error.message : String(error)}`);
1601
+ this.options.onError(parsedError);
1602
+ throw parsedError;
1473
1603
  }
1474
1604
  }
1475
1605
  /**
1476
- * 验证输入路径
1606
+ * 解析 XML 内容
1607
+ *
1608
+ * @param content - XML 内容
1609
+ * @returns 解析后的对象
1477
1610
  */
1478
- async validatePath(filePath) {
1611
+ async parseXML(content) {
1479
1612
  try {
1480
- const stats = await fs3.stat(filePath);
1481
- if (!stats.isFile() && !stats.isDirectory()) {
1482
- throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${filePath}`);
1483
- }
1484
- } catch {
1485
- throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u8DEF\u5F84: ${filePath}`);
1613
+ const parser = new xml2js.Parser();
1614
+ return await parser.parseStringPromise(content);
1615
+ } catch (error) {
1616
+ const parsedError = new Error(`Failed to parse XML: ${error instanceof Error ? error.message : String(error)}`);
1617
+ this.options.onError(parsedError);
1618
+ throw parsedError;
1486
1619
  }
1487
1620
  }
1488
1621
  /**
1489
- * 验证切片选项
1622
+ * 解析 Properties 内容
1623
+ *
1624
+ * @param content - Properties 内容
1625
+ * @returns 解析后的对象(包含 section)
1490
1626
  */
1491
- validateSliceOptions(options) {
1492
- if ((options.byteStart !== void 0 || options.byteEnd !== void 0) && (options.lineStart !== void 0 || options.lineEnd !== void 0)) {
1493
- throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --byte \u548C --line \u5207\u7247\u9009\u9879");
1494
- }
1495
- if (options.byteEnd !== void 0 && options.byteStart === void 0) {
1496
- throw new Error("--byte \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u4F4D\u7F6E");
1497
- }
1498
- if (options.lineEnd !== void 0 && options.lineStart === void 0) {
1499
- throw new Error("--line \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u884C\u53F7");
1627
+ parseProperties(content) {
1628
+ try {
1629
+ const result = {};
1630
+ const lines = content.split("\n");
1631
+ for (const line of lines) {
1632
+ const trimmedLine = line.trim();
1633
+ if (!trimmedLine || trimmedLine.startsWith("#") || trimmedLine.startsWith("!")) {
1634
+ continue;
1635
+ }
1636
+ const processedLine = trimmedLine.replace(/\\:/g, ":").replace(/\\=/g, "=").replace(/\\#/g, "#").replace(/\\!/g, "!");
1637
+ let separatorIndex = -1;
1638
+ for (let i = 0; i < processedLine.length; i++) {
1639
+ if (processedLine[i] === "=" || processedLine[i] === ":") {
1640
+ separatorIndex = i;
1641
+ break;
1642
+ }
1643
+ }
1644
+ if (separatorIndex === -1) {
1645
+ result[processedLine] = "";
1646
+ continue;
1647
+ }
1648
+ const key = processedLine.substring(0, separatorIndex).trim();
1649
+ let value = processedLine.substring(separatorIndex + 1).trim();
1650
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1651
+ value = value.substring(1, value.length - 1);
1652
+ }
1653
+ result[key] = value;
1654
+ }
1655
+ const nestedResult = {};
1656
+ for (const [key, value] of Object.entries(result)) {
1657
+ const parts = key.split(".");
1658
+ if (parts.length > 1) {
1659
+ const section = parts[0];
1660
+ const sectionKey = parts.slice(1).join(".");
1661
+ if (!(nestedResult[section] && typeof nestedResult[section] === "object")) {
1662
+ nestedResult[section] = {};
1663
+ }
1664
+ const sectionObj = nestedResult[section];
1665
+ sectionObj[sectionKey] = value;
1666
+ } else {
1667
+ nestedResult[key] = value;
1668
+ }
1669
+ }
1670
+ return nestedResult;
1671
+ } catch (error) {
1672
+ const parsedError = new Error(`Failed to parse Properties: ${error instanceof Error ? error.message : String(error)}`);
1673
+ this.options.onError(parsedError);
1674
+ throw parsedError;
1500
1675
  }
1501
1676
  }
1502
1677
  /**
1503
- * 生成输出路径
1678
+ * 解析 INI 内容
1679
+ *
1680
+ * @param content - INI 内容
1681
+ * @returns 解析后的对象(包含 section)
1504
1682
  */
1505
- generateOutputPath(inputPath) {
1506
- return normalizeURPFPath(inputPath);
1683
+ parseINI(content) {
1684
+ try {
1685
+ return ini.parse(content);
1686
+ } catch (error) {
1687
+ const parsedError = new Error(`Failed to parse INI: ${error instanceof Error ? error.message : String(error)}`);
1688
+ this.options.onError(parsedError);
1689
+ throw parsedError;
1690
+ }
1507
1691
  }
1508
1692
  /**
1509
- * 扫描文件
1510
- */
1511
- async scanFiles(rootPath, options) {
1512
- const stats = await fs3.stat(rootPath);
1513
- if (stats.isFile()) {
1514
- const metadata = await this.collectFileMetadata(rootPath);
1515
- return {
1516
- files: [metadata],
1517
- directories: [],
1518
- skippedFiles: [],
1519
- statistics: {
1520
- totalFiles: 1,
1521
- totalDirectories: 0,
1522
- skippedFiles: 0
1693
+ * 根据路径从对象中提取值
1694
+ *
1695
+ * @param data - 数据对象
1696
+ * @param keyPath - key 路径
1697
+ * @returns 提取的值
1698
+ */
1699
+ extractByPath(data, keyPath) {
1700
+ const pathSegments = this.parseKeyPath(keyPath);
1701
+ let current = data;
1702
+ for (const segment of pathSegments) {
1703
+ if (current == null) {
1704
+ const error = new Error(`Key path "${keyPath}" not found: cannot access property "${segment}" of ${current}`);
1705
+ this.options.onError(error);
1706
+ throw error;
1707
+ }
1708
+ if (typeof segment === "number") {
1709
+ if (Array.isArray(current)) {
1710
+ if (segment >= current.length) {
1711
+ const error = new Error(`Array index ${segment} out of bounds (length: ${current.length})`);
1712
+ this.options.onError(error);
1713
+ throw error;
1714
+ }
1715
+ current = current[segment];
1716
+ } else {
1717
+ const error = new Error(`Cannot access array index ${segment} on non-array value`);
1718
+ this.options.onError(error);
1719
+ throw error;
1523
1720
  }
1524
- };
1721
+ } else {
1722
+ if (typeof current === "object" && current !== null) {
1723
+ if (!(segment in current)) {
1724
+ const error = new Error(`Key "${segment}" not found in path "${keyPath}"`);
1725
+ this.options.onError(error);
1726
+ throw error;
1727
+ }
1728
+ current = current[segment];
1729
+ } else {
1730
+ const error = new Error(`Cannot access property "${segment}" on non-object value`);
1731
+ this.options.onError(error);
1732
+ throw error;
1733
+ }
1734
+ }
1525
1735
  }
1526
- let ignoreRules = [];
1527
- if (options.ignoreFile) {
1528
- const parser = new IgnoreRuleParser();
1529
- const content = await fs3.readFile(options.ignoreFile, "utf-8");
1530
- ignoreRules = parser.parseFile(content);
1531
- } else {
1532
- const defaultIgnoreFiles = [".gitignore", ".iflowignore"];
1533
- for (const ignoreFile of defaultIgnoreFiles) {
1534
- const ignorePath = path2.join(rootPath, ignoreFile);
1535
- try {
1536
- await fs3.access(ignorePath);
1537
- const parser = new IgnoreRuleParser();
1538
- const content = await fs3.readFile(ignorePath, "utf-8");
1539
- ignoreRules = [...ignoreRules, ...parser.parseFile(content)];
1540
- break;
1541
- } catch {
1736
+ return current;
1737
+ }
1738
+ /**
1739
+ * 解析 key 路径,支持点分隔符和数组索引
1740
+ *
1741
+ * @param keyPath - key 路径(如 "config.server.port" 或 "items[0].name")
1742
+ * @returns 解析后的路径段数组
1743
+ */
1744
+ parseKeyPath(keyPath) {
1745
+ const segments = [];
1746
+ let current = "";
1747
+ let inBrackets = false;
1748
+ let bracketContent = "";
1749
+ for (let i = 0; i < keyPath.length; i++) {
1750
+ const char = keyPath[i];
1751
+ if (char === "[") {
1752
+ if (inBrackets) {
1753
+ const error = new Error(`Invalid key path: nested brackets at position ${i}`);
1754
+ this.options.onError(error);
1755
+ throw error;
1756
+ }
1757
+ if (current.trim()) {
1758
+ segments.push(current.trim());
1759
+ }
1760
+ inBrackets = true;
1761
+ bracketContent = "";
1762
+ current = "";
1763
+ } else if (char === "]") {
1764
+ if (!inBrackets) {
1765
+ const error = new Error(`Invalid key path: closing bracket without opening bracket at position ${i}`);
1766
+ this.options.onError(error);
1767
+ throw error;
1768
+ }
1769
+ inBrackets = false;
1770
+ const index = parseInt(bracketContent.trim(), 10);
1771
+ if (isNaN(index)) {
1772
+ const error = new Error(`Invalid array index: "${bracketContent}" is not a number`);
1773
+ this.options.onError(error);
1774
+ throw error;
1775
+ }
1776
+ segments.push(index);
1777
+ current = "";
1778
+ } else if (char === "." && !inBrackets) {
1779
+ if (current.trim()) {
1780
+ const trimmed = current.trim();
1781
+ const numIndex = parseInt(trimmed, 10);
1782
+ if (!isNaN(numIndex) && trimmed === numIndex.toString()) {
1783
+ segments.push(numIndex);
1784
+ } else {
1785
+ segments.push(trimmed);
1786
+ }
1787
+ }
1788
+ current = "";
1789
+ } else {
1790
+ if (inBrackets) {
1791
+ bracketContent += char;
1792
+ } else {
1793
+ current += char;
1542
1794
  }
1543
1795
  }
1544
1796
  }
1545
- const effectiveMaxDepth = options.noRecurse ? 1 : options.maxDepth;
1546
- return await FileScanner.scanDirectory(rootPath, {
1547
- ignoreRules,
1548
- followSymlinks: options.followSymlinks || false,
1549
- maxDepth: effectiveMaxDepth
1550
- // undefined 或 1
1551
- });
1797
+ if (inBrackets) {
1798
+ const error = new Error("Invalid key path: unclosed bracket");
1799
+ this.options.onError(error);
1800
+ throw error;
1801
+ }
1802
+ if (current.trim()) {
1803
+ const trimmed = current.trim();
1804
+ const numIndex = parseInt(trimmed, 10);
1805
+ if (!isNaN(numIndex) && trimmed === numIndex.toString()) {
1806
+ segments.push(numIndex);
1807
+ } else {
1808
+ segments.push(trimmed);
1809
+ }
1810
+ }
1811
+ return segments;
1552
1812
  }
1553
1813
  /**
1554
- * 收集文件元数据
1555
- */
1556
- async collectFileMetadata(filePath) {
1557
- const stats = await fs3.stat(filePath);
1558
- return {
1559
- path: filePath,
1560
- name: path2.basename(filePath),
1814
+ * 根据文件扩展名检测文件格式
1815
+ *
1816
+ * @param filePath - 文件路径
1817
+ * @returns 文件格式
1818
+ */
1819
+ detectFormat(filePath) {
1820
+ const ext = path2.extname(filePath).toLowerCase();
1821
+ switch (ext) {
1822
+ case ".json":
1823
+ return "json" /* JSON */;
1824
+ case ".yaml":
1825
+ case ".yml":
1826
+ return "yaml" /* YAML */;
1827
+ case ".toml":
1828
+ return "toml" /* TOML */;
1829
+ case ".xml":
1830
+ return "xml" /* XML */;
1831
+ case ".properties":
1832
+ return "properties" /* PROPERTIES */;
1833
+ case ".ini":
1834
+ return "ini" /* INI */;
1835
+ default: {
1836
+ const error = new Error(`Unsupported file extension: ${ext}`);
1837
+ this.options.onError(error);
1838
+ throw error;
1839
+ }
1840
+ }
1841
+ }
1842
+ /**
1843
+ * 获取值的类型
1844
+ *
1845
+ * @param value - 值
1846
+ * @returns 类型字符串
1847
+ */
1848
+ getValueType(value) {
1849
+ if (value === null) return "null";
1850
+ if (Array.isArray(value)) return "array";
1851
+ return typeof value;
1852
+ }
1853
+ };
1854
+
1855
+ // src/core/engine/wildcard-matcher.ts
1856
+ var WildcardMatcher = class {
1857
+ constructor(options = {}) {
1858
+ this.options = {
1859
+ caseSensitive: options.caseSensitive ?? false
1860
+ };
1861
+ }
1862
+ /**
1863
+ * 检查单个 key 是否匹配通配符模式
1864
+ *
1865
+ * @param pattern - 通配符模式(如 "config.*.port" 或 "data.**.value")
1866
+ * @param key - 要检查的 key 路径
1867
+ * @returns 是否匹配
1868
+ */
1869
+ matches(pattern, key) {
1870
+ const patternSegments = this.splitPattern(pattern);
1871
+ const keySegments = this.splitKeyPath(key);
1872
+ return this.matchSegments(patternSegments, keySegments, 0, 0);
1873
+ }
1874
+ /**
1875
+ * 分割通配符模式为段数组
1876
+ */
1877
+ splitPattern(pattern) {
1878
+ const segments = [];
1879
+ let current = "";
1880
+ let i = 0;
1881
+ let inBrackets = false;
1882
+ while (i < pattern.length) {
1883
+ const char = pattern[i];
1884
+ if (char === "[") {
1885
+ if (current) {
1886
+ segments.push(current);
1887
+ current = "";
1888
+ }
1889
+ inBrackets = true;
1890
+ current = "[";
1891
+ i += 1;
1892
+ } else if (char === "]") {
1893
+ current += char;
1894
+ inBrackets = false;
1895
+ segments.push(current);
1896
+ current = "";
1897
+ i += 1;
1898
+ } else if (char === "*" && pattern[i + 1] === "*" && !inBrackets) {
1899
+ if (current) {
1900
+ segments.push(current);
1901
+ current = "";
1902
+ }
1903
+ segments.push("**");
1904
+ i += 2;
1905
+ } else if (char === "*" && !inBrackets) {
1906
+ current += char;
1907
+ i += 1;
1908
+ } else if (char === "." && !inBrackets) {
1909
+ if (current) {
1910
+ segments.push(current);
1911
+ current = "";
1912
+ }
1913
+ i += 1;
1914
+ } else {
1915
+ current += char;
1916
+ i += 1;
1917
+ }
1918
+ }
1919
+ if (current) {
1920
+ segments.push(current);
1921
+ }
1922
+ return segments;
1923
+ }
1924
+ /**
1925
+ * 分割 key 路径为段数组
1926
+ */
1927
+ splitKeyPath(key) {
1928
+ const segments = [];
1929
+ let current = "";
1930
+ let inBrackets = false;
1931
+ for (let i = 0; i < key.length; i++) {
1932
+ const char = key[i];
1933
+ if (char === "[") {
1934
+ if (current) {
1935
+ segments.push(current);
1936
+ current = "";
1937
+ }
1938
+ inBrackets = true;
1939
+ current = "[";
1940
+ } else if (char === "]") {
1941
+ current += char;
1942
+ inBrackets = false;
1943
+ segments.push(current);
1944
+ current = "";
1945
+ } else if (char === "." && !inBrackets) {
1946
+ if (current) {
1947
+ segments.push(current);
1948
+ current = "";
1949
+ }
1950
+ } else {
1951
+ current += char;
1952
+ }
1953
+ }
1954
+ if (current) {
1955
+ segments.push(current);
1956
+ }
1957
+ return segments;
1958
+ }
1959
+ /**
1960
+ * 递归匹配段
1961
+ */
1962
+ matchSegments(patternSegments, keySegments, patternIndex, keyIndex) {
1963
+ if (patternIndex >= patternSegments.length && keyIndex >= keySegments.length) {
1964
+ return true;
1965
+ }
1966
+ if (patternIndex >= patternSegments.length) {
1967
+ return false;
1968
+ }
1969
+ const patternSegment = patternSegments[patternIndex];
1970
+ if (patternSegment === "**") {
1971
+ if (this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex)) {
1972
+ return true;
1973
+ }
1974
+ if (keyIndex < keySegments.length) {
1975
+ if (this.matchSegments(patternSegments, keySegments, patternIndex, keyIndex + 1)) {
1976
+ return true;
1977
+ }
1978
+ }
1979
+ return false;
1980
+ }
1981
+ if (keyIndex >= keySegments.length) {
1982
+ for (let i = patternIndex; i < patternSegments.length; i++) {
1983
+ if (patternSegments[i] !== "**") {
1984
+ return false;
1985
+ }
1986
+ }
1987
+ return true;
1988
+ }
1989
+ const keySegment = keySegments[keyIndex];
1990
+ if (patternSegment === "*") {
1991
+ return this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex + 1);
1992
+ }
1993
+ if (this.matchPatternSegment(patternSegment, keySegment)) {
1994
+ return this.matchSegments(patternSegments, keySegments, patternIndex + 1, keyIndex + 1);
1995
+ }
1996
+ return false;
1997
+ }
1998
+ /**
1999
+ * 匹配单个模式段和 key 段
2000
+ */
2001
+ matchPatternSegment(patternSegment, keySegment) {
2002
+ if (patternSegment.includes("*") || patternSegment.includes("?")) {
2003
+ return this.matchWildcardPattern(patternSegment, keySegment);
2004
+ }
2005
+ if (this.options.caseSensitive) {
2006
+ return patternSegment === keySegment;
2007
+ } else {
2008
+ return patternSegment.toLowerCase() === keySegment.toLowerCase();
2009
+ }
2010
+ }
2011
+ /**
2012
+ * 匹配通配符模式(不包含 globstar)
2013
+ */
2014
+ matchWildcardPattern(pattern, text) {
2015
+ const pLen = pattern.length;
2016
+ const tLen = text.length;
2017
+ let pIndex = 0;
2018
+ let tIndex = 0;
2019
+ let starIndex = -1;
2020
+ let matchIndex = -1;
2021
+ while (tIndex < tLen) {
2022
+ if (pIndex < pLen && pattern[pIndex] === "*") {
2023
+ starIndex = pIndex;
2024
+ matchIndex = tIndex;
2025
+ pIndex++;
2026
+ } else if (pIndex < pLen && this.matchChar(pattern[pIndex], text[tIndex])) {
2027
+ pIndex++;
2028
+ tIndex++;
2029
+ } else if (starIndex !== -1) {
2030
+ pIndex = starIndex + 1;
2031
+ matchIndex++;
2032
+ tIndex = matchIndex;
2033
+ } else {
2034
+ return false;
2035
+ }
2036
+ }
2037
+ while (pIndex < pLen && pattern[pIndex] === "*") {
2038
+ pIndex++;
2039
+ }
2040
+ return pIndex === pLen;
2041
+ }
2042
+ /**
2043
+ * 匹配单个字符
2044
+ */
2045
+ matchChar(patternChar, textChar) {
2046
+ if (patternChar === "?") {
2047
+ return true;
2048
+ }
2049
+ if (this.options.caseSensitive) {
2050
+ return patternChar === textChar;
2051
+ } else {
2052
+ return patternChar.toLowerCase() === textChar.toLowerCase();
2053
+ }
2054
+ }
2055
+ /**
2056
+ * 从 key 列表中找出所有匹配通配符模式的 key
2057
+ *
2058
+ * @param pattern - 通配符模式
2059
+ * @param keys - key 列表
2060
+ * @returns 匹配结果
2061
+ */
2062
+ matchKeys(pattern, keys) {
2063
+ const matches = [];
2064
+ for (const key of keys) {
2065
+ if (this.matches(pattern, key)) {
2066
+ matches.push(key);
2067
+ }
2068
+ }
2069
+ return {
2070
+ matched: matches.length > 0,
2071
+ matches,
2072
+ stats: {
2073
+ total: keys.length,
2074
+ matched: matches.length
2075
+ }
2076
+ };
2077
+ }
2078
+ /**
2079
+ * 从对象中提取所有匹配通配符模式的 key 路径
2080
+ *
2081
+ * @param pattern - 通配符模式
2082
+ * @param obj - 要搜索的对象
2083
+ * @param prefix - 当前路径前缀(内部使用)
2084
+ * @returns 匹配结果
2085
+ */
2086
+ extractMatchingKeys(pattern, obj, prefix = "") {
2087
+ const matches = [];
2088
+ const allKeys = [];
2089
+ this.collectKeys(obj, prefix, allKeys);
2090
+ for (const key of allKeys) {
2091
+ if (this.matches(pattern, key)) {
2092
+ matches.push(key);
2093
+ }
2094
+ }
2095
+ return {
2096
+ matched: matches.length > 0,
2097
+ matches,
2098
+ stats: {
2099
+ total: allKeys.length,
2100
+ matched: matches.length
2101
+ }
2102
+ };
2103
+ }
2104
+ /**
2105
+ * 从对象中提取匹配通配符模式的 key 值对
2106
+ *
2107
+ * @param pattern - 通配符模式
2108
+ * @param obj - 要搜索的对象
2109
+ * @returns 匹配的 key 值对
2110
+ */
2111
+ extractMatchingValues(pattern, obj) {
2112
+ const result = {};
2113
+ const matchResult = this.extractMatchingKeys(pattern, obj);
2114
+ for (const key of matchResult.matches) {
2115
+ try {
2116
+ const value = this.getValueByPath(obj, key);
2117
+ result[key] = value;
2118
+ } catch {
2119
+ }
2120
+ }
2121
+ return result;
2122
+ }
2123
+ /**
2124
+ * 递归收集对象中的所有 key 路径
2125
+ *
2126
+ * @param obj - 对象
2127
+ * @param prefix - 路径前缀
2128
+ * @param keys - 收集的 key 列表
2129
+ */
2130
+ collectKeys(obj, prefix, keys) {
2131
+ if (obj == null || typeof obj !== "object") {
2132
+ return;
2133
+ }
2134
+ if (Array.isArray(obj)) {
2135
+ for (let i = 0; i < obj.length; i++) {
2136
+ const key = prefix ? `${prefix}[${i}]` : `[${i}]`;
2137
+ keys.push(key);
2138
+ this.collectKeys(obj[i], key, keys);
2139
+ }
2140
+ } else {
2141
+ for (const [prop, value] of Object.entries(obj)) {
2142
+ const key = prefix ? `${prefix}.${prop}` : prop;
2143
+ keys.push(key);
2144
+ this.collectKeys(value, key, keys);
2145
+ }
2146
+ }
2147
+ }
2148
+ /**
2149
+ * 根据路径从对象中获取值
2150
+ *
2151
+ * @param obj - 对象
2152
+ * @param path - 路径
2153
+ * @returns 值
2154
+ */
2155
+ getValueByPath(obj, path8) {
2156
+ const segments = [];
2157
+ let current = "";
2158
+ let inBrackets = false;
2159
+ let bracketContent = "";
2160
+ for (let i = 0; i < path8.length; i++) {
2161
+ const char = path8[i];
2162
+ if (char === "[") {
2163
+ if (inBrackets) {
2164
+ throw new Error(`Invalid path: nested brackets at position ${i}`);
2165
+ }
2166
+ if (current.trim()) {
2167
+ segments.push(current.trim());
2168
+ }
2169
+ inBrackets = true;
2170
+ bracketContent = "";
2171
+ current = "";
2172
+ } else if (char === "]") {
2173
+ if (!inBrackets) {
2174
+ throw new Error(`Invalid path: closing bracket without opening bracket at position ${i}`);
2175
+ }
2176
+ inBrackets = false;
2177
+ const index = parseInt(bracketContent.trim(), 10);
2178
+ if (isNaN(index)) {
2179
+ throw new Error(`Invalid array index: "${bracketContent}" is not a number`);
2180
+ }
2181
+ segments.push(index);
2182
+ current = "";
2183
+ } else if (char === "." && !inBrackets) {
2184
+ if (current.trim()) {
2185
+ segments.push(current.trim());
2186
+ }
2187
+ current = "";
2188
+ } else {
2189
+ if (inBrackets) {
2190
+ bracketContent += char;
2191
+ } else {
2192
+ current += char;
2193
+ }
2194
+ }
2195
+ }
2196
+ if (inBrackets) {
2197
+ throw new Error("Invalid path: unclosed bracket");
2198
+ }
2199
+ if (current.trim()) {
2200
+ segments.push(current.trim());
2201
+ }
2202
+ let value = obj;
2203
+ for (const segment of segments) {
2204
+ if (value == null) {
2205
+ throw new Error(`Path "${path8}" not found`);
2206
+ }
2207
+ if (typeof segment === "number") {
2208
+ if (Array.isArray(value)) {
2209
+ if (segment >= value.length) {
2210
+ throw new Error(`Array index ${segment} out of bounds`);
2211
+ }
2212
+ value = value[segment];
2213
+ } else {
2214
+ throw new Error(`Cannot access array index ${segment} on non-array value`);
2215
+ }
2216
+ } else {
2217
+ if (typeof value === "object" && value !== null) {
2218
+ if (!(segment in value)) {
2219
+ throw new Error(`Key "${segment}" not found`);
2220
+ }
2221
+ value = value[segment];
2222
+ } else {
2223
+ throw new Error(`Cannot access property "${segment}" on non-object value`);
2224
+ }
2225
+ }
2226
+ }
2227
+ return value;
2228
+ }
2229
+ /**
2230
+ * 检查模式是否包含通配符
2231
+ *
2232
+ * @param pattern - 模式
2233
+ * @returns 是否包含通配符
2234
+ */
2235
+ hasWildcard(pattern) {
2236
+ return /[*?]/.test(pattern);
2237
+ }
2238
+ /**
2239
+ * 检查模式是否包含多层级通配符
2240
+ *
2241
+ * @param pattern - 模式
2242
+ * @returns 是否包含多层级通配符
2243
+ */
2244
+ hasGlobstar(pattern) {
2245
+ return pattern.includes("**");
2246
+ }
2247
+ };
2248
+ var defaultWildcardMatcher = new WildcardMatcher({
2249
+ caseSensitive: false
2250
+ });
2251
+
2252
+ // src/core/engine/batch-key-extractor.ts
2253
+ var BatchKeyExtractor = class {
2254
+ constructor(options = {}) {
2255
+ this.options = {
2256
+ preserveFormat: options.preserveFormat ?? true,
2257
+ onError: options.onError ?? ((error) => {
2258
+ throw error;
2259
+ }),
2260
+ enableWildcard: options.enableWildcard ?? true,
2261
+ warnOnNoMatch: options.warnOnNoMatch ?? true,
2262
+ outputFormat: options.outputFormat ?? "array"
2263
+ };
2264
+ this.keyExtractor = new KeyExtractor({
2265
+ preserveFormat: this.options.preserveFormat,
2266
+ onError: this.options.onError
2267
+ });
2268
+ this.wildcardMatcher = new WildcardMatcher({
2269
+ caseSensitive: false
2270
+ });
2271
+ }
2272
+ /**
2273
+ * 从文件中批量提取多个 key 的值
2274
+ *
2275
+ * @param filePath - 文件路径
2276
+ * @param keyPaths - key 路径列表(支持逗号分隔)
2277
+ * @returns 批量提取结果
2278
+ */
2279
+ async extractFromFile(filePath, keyPaths) {
2280
+ const keys = this.parseKeyPaths(keyPaths);
2281
+ const results = [];
2282
+ const errors = [];
2283
+ for (const keyPath of keys) {
2284
+ try {
2285
+ if (this.options.enableWildcard && this.wildcardMatcher.hasWildcard(keyPath)) {
2286
+ const wildcardResults = await this.extractWithWildcard(filePath, keyPath);
2287
+ results.push(...wildcardResults.results);
2288
+ if (wildcardResults.errors.length > 0) {
2289
+ errors.push(...wildcardResults.errors);
2290
+ }
2291
+ } else {
2292
+ const result = await this.keyExtractor.extractFromFile(filePath, keyPath);
2293
+ results.push(result);
2294
+ }
2295
+ } catch (error) {
2296
+ const errorMessage = error instanceof Error ? error.message : String(error);
2297
+ errors.push({ key: keyPath, error: errorMessage });
2298
+ }
2299
+ }
2300
+ return {
2301
+ results,
2302
+ success: errors.length === 0,
2303
+ successCount: results.length,
2304
+ failureCount: errors.length,
2305
+ errors
2306
+ };
2307
+ }
2308
+ /**
2309
+ * 使用通配符模式提取 key
2310
+ *
2311
+ * @param filePath - 文件路径
2312
+ * @param pattern - 通配符模式
2313
+ * @returns 批量提取结果
2314
+ */
2315
+ async extractWithWildcard(filePath, pattern) {
2316
+ const fs15 = await import("fs");
2317
+ const content = fs15.readFileSync(filePath, "utf-8");
2318
+ let obj;
2319
+ const format = this.keyExtractor.detectFormat(filePath);
2320
+ switch (format) {
2321
+ case "json":
2322
+ obj = this.keyExtractor.parseJSON(content);
2323
+ break;
2324
+ case "yaml":
2325
+ obj = this.keyExtractor.parseYAML(content);
2326
+ break;
2327
+ case "toml":
2328
+ obj = this.keyExtractor.parseTOML(content);
2329
+ break;
2330
+ case "xml":
2331
+ obj = await this.keyExtractor.parseXML(content);
2332
+ break;
2333
+ case "properties":
2334
+ obj = this.keyExtractor.parseProperties(content);
2335
+ break;
2336
+ case "ini":
2337
+ obj = this.keyExtractor.parseINI(content);
2338
+ break;
2339
+ default:
2340
+ throw new Error(`Unsupported file format: ${format}`);
2341
+ }
2342
+ const matchedValues = this.wildcardMatcher.extractMatchingValues(pattern, obj);
2343
+ const results = [];
2344
+ const errors = [];
2345
+ for (const [key, value] of Object.entries(matchedValues)) {
2346
+ results.push({
2347
+ value,
2348
+ type: this.getValueType(value),
2349
+ format,
2350
+ keyPath: key
2351
+ });
2352
+ }
2353
+ if (results.length === 0 && this.options.warnOnNoMatch) {
2354
+ console.warn(`\u26A0\uFE0F \u8B66\u544A: \u901A\u914D\u7B26\u6A21\u5F0F "${pattern}" \u672A\u5339\u914D\u5230\u4EFB\u4F55 key`);
2355
+ }
2356
+ return {
2357
+ results,
2358
+ success: errors.length === 0,
2359
+ successCount: results.length,
2360
+ failureCount: errors.length,
2361
+ errors
2362
+ };
2363
+ }
2364
+ /**
2365
+ * 解析 key 路径列表(支持逗号分隔)
2366
+ *
2367
+ * @param keyPaths - key 路径(字符串或字符串数组)
2368
+ * @returns 解析后的 key 路径数组
2369
+ */
2370
+ parseKeyPaths(keyPaths) {
2371
+ if (Array.isArray(keyPaths)) {
2372
+ const result = [];
2373
+ for (const kp of keyPaths) {
2374
+ const parts = kp.split(",").map((s) => s.trim()).filter((s) => s);
2375
+ result.push(...parts);
2376
+ }
2377
+ return result;
2378
+ } else {
2379
+ return keyPaths.split(",").map((s) => s.trim()).filter((s) => s);
2380
+ }
2381
+ }
2382
+ /**
2383
+ * 获取值的类型
2384
+ *
2385
+ * @param value - 值
2386
+ * @returns 类型字符串
2387
+ */
2388
+ getValueType(value) {
2389
+ if (value === null) return "null";
2390
+ if (Array.isArray(value)) return "array";
2391
+ return typeof value;
2392
+ }
2393
+ /**
2394
+ * 格式化输出结果
2395
+ *
2396
+ * @param result - 批量提取结果
2397
+ * @returns 格式化后的输出
2398
+ */
2399
+ formatOutput(result) {
2400
+ if (this.options.outputFormat === "object") {
2401
+ const obj = {};
2402
+ for (const r of result.results) {
2403
+ obj[r.keyPath] = r.value;
2404
+ }
2405
+ return JSON.stringify(obj, null, 2);
2406
+ } else {
2407
+ return JSON.stringify(result.results.map((r) => ({
2408
+ key: r.keyPath,
2409
+ value: r.value,
2410
+ type: r.type
2411
+ })), null, 2);
2412
+ }
2413
+ }
2414
+ /**
2415
+ * 打印批量提取结果的摘要
2416
+ *
2417
+ * @param result - 批量提取结果
2418
+ */
2419
+ printSummary(result) {
2420
+ console.log(`\u2705 \u6210\u529F\u63D0\u53D6: ${result.successCount} \u4E2A key`);
2421
+ if (result.failureCount > 0) {
2422
+ console.log(`\u274C \u5931\u8D25: ${result.failureCount} \u4E2A key`);
2423
+ for (const err of result.errors) {
2424
+ console.log(` - ${err.key}: ${err.error}`);
2425
+ }
2426
+ }
2427
+ }
2428
+ };
2429
+ var defaultBatchKeyExtractor = new BatchKeyExtractor();
2430
+
2431
+ // src/core/formatter/output-formatter.ts
2432
+ import yaml2 from "js-yaml";
2433
+ import TOML from "toml";
2434
+ import xml2js2 from "xml2js";
2435
+ var OutputFormatter = class {
2436
+ /**
2437
+ * 格式化数据为指定格式
2438
+ * @param data 要格式化的数据
2439
+ * @param options 格式化选项
2440
+ * @returns 格式化后的字符串
2441
+ */
2442
+ format(data, options) {
2443
+ const { format } = options;
2444
+ switch (format) {
2445
+ case "json":
2446
+ return this.formatJSON(data, options);
2447
+ case "yaml":
2448
+ case "yml":
2449
+ return this.formatYAML(data, options);
2450
+ case "toml":
2451
+ return this.formatTOML(data, options);
2452
+ case "xml":
2453
+ return this.formatXML(data, options);
2454
+ case "properties":
2455
+ return this.formatProperties(data, options);
2456
+ case "ini":
2457
+ return this.formatINI(data, options);
2458
+ case "raw":
2459
+ return this.formatRaw(data, options);
2460
+ default:
2461
+ throw new Error(`Unsupported format: ${format}`);
2462
+ }
2463
+ }
2464
+ /**
2465
+ * 格式化为 JSON
2466
+ */
2467
+ formatJSON(data, options) {
2468
+ const indent = options.indent ?? 2;
2469
+ return JSON.stringify(data, null, indent);
2470
+ }
2471
+ /**
2472
+ * 格式化为 YAML
2473
+ */
2474
+ formatYAML(data, options) {
2475
+ const indent = options.indent ?? 2;
2476
+ return yaml2.dump(data, {
2477
+ indent,
2478
+ lineWidth: -1,
2479
+ // 不限制行宽
2480
+ noRefs: true,
2481
+ // 不支持引用
2482
+ sortKeys: false
2483
+ // 不排序键
2484
+ });
2485
+ }
2486
+ /**
2487
+ * 格式化为 TOML
2488
+ */
2489
+ formatTOML(data, _options) {
2490
+ return TOML.stringify(data);
2491
+ }
2492
+ /**
2493
+ * 格式化为 XML
2494
+ */
2495
+ formatXML(data, options) {
2496
+ const rootName = options.xmlRootName ?? "root";
2497
+ const indent = options.indent ?? 2;
2498
+ const builder = new xml2js2.Builder({
2499
+ rootName,
2500
+ xmldec: { version: "1.0", encoding: "UTF-8" },
2501
+ renderOpts: { pretty: true, indent: " ".repeat(indent) }
2502
+ });
2503
+ return builder.buildObject(data);
2504
+ }
2505
+ /**
2506
+ * 格式化为 Properties 格式
2507
+ */
2508
+ formatProperties(data, options) {
2509
+ const prefix = options.keyPrefix ?? "";
2510
+ const lines = [];
2511
+ const flatten = (obj, currentPrefix) => {
2512
+ for (const [key, value] of Object.entries(obj)) {
2513
+ const fullKey = currentPrefix ? `${currentPrefix}.${key}` : key;
2514
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
2515
+ flatten(value, fullKey);
2516
+ } else {
2517
+ const stringValue = this.escapePropertiesValue(String(value));
2518
+ lines.push(`${prefix}${fullKey}=${stringValue}`);
2519
+ }
2520
+ }
2521
+ };
2522
+ if (typeof data === "object" && data !== null) {
2523
+ flatten(data, "");
2524
+ } else {
2525
+ const stringValue = this.escapePropertiesValue(String(data));
2526
+ lines.push(`${prefix}value=${stringValue}`);
2527
+ }
2528
+ return lines.join("\n");
2529
+ }
2530
+ /**
2531
+ * 转义 Properties 值中的特殊字符
2532
+ */
2533
+ escapePropertiesValue(value) {
2534
+ return value.replace(/\\/g, "").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\f/g, "\\f");
2535
+ }
2536
+ /**
2537
+ * 格式化为 INI 格式
2538
+ */
2539
+ formatINI(data, _options) {
2540
+ const lines = [];
2541
+ const flatten = (obj, section) => {
2542
+ for (const [key, value] of Object.entries(obj)) {
2543
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
2544
+ flatten(value, key);
2545
+ } else {
2546
+ const stringValue = this.escapeINIValue(String(value));
2547
+ if (section) {
2548
+ lines.push(`[${section}]`);
2549
+ lines.push(`${key}=${stringValue}`);
2550
+ section = null;
2551
+ } else {
2552
+ lines.push(`${key}=${stringValue}`);
2553
+ }
2554
+ }
2555
+ }
2556
+ };
2557
+ if (typeof data === "object" && data !== null) {
2558
+ flatten(data, null);
2559
+ } else {
2560
+ const stringValue = this.escapeINIValue(String(data));
2561
+ lines.push(`value=${stringValue}`);
2562
+ }
2563
+ return lines.join("\n");
2564
+ }
2565
+ /**
2566
+ * 转义 INI 值中的特殊字符
2567
+ */
2568
+ escapeINIValue(value) {
2569
+ return value.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
2570
+ }
2571
+ /**
2572
+ * 格式化为原始格式(直接输出值)
2573
+ */
2574
+ formatRaw(data, _options) {
2575
+ if (typeof data === "string") {
2576
+ return data;
2577
+ }
2578
+ if (typeof data === "number" || typeof data === "boolean") {
2579
+ return String(data);
2580
+ }
2581
+ if (data === null) {
2582
+ return "";
2583
+ }
2584
+ if (typeof data === "object") {
2585
+ return JSON.stringify(data);
2586
+ }
2587
+ return String(data);
2588
+ }
2589
+ /**
2590
+ * 验证格式字符串是否有效
2591
+ */
2592
+ static isValidFormat(format) {
2593
+ const validFormats = ["json", "yaml", "yml", "toml", "xml", "properties", "ini", "raw"];
2594
+ return validFormats.includes(format);
2595
+ }
2596
+ /**
2597
+ * 获取所有支持的格式列表
2598
+ */
2599
+ static getSupportedFormats() {
2600
+ return ["json", "yaml", "yml", "toml", "xml", "properties", "ini", "raw"];
2601
+ }
2602
+ };
2603
+
2604
+ // src/core/validator/key-validator.ts
2605
+ var KeyValidator = class {
2606
+ constructor() {
2607
+ this.keyExtractor = new KeyExtractor();
2608
+ this.batchKeyExtractor = new BatchKeyExtractor();
2609
+ }
2610
+ /**
2611
+ * 验证单个 key 是否存在
2612
+ * @param data 配置数据对象
2613
+ * @param keyPath key 路径
2614
+ * @returns 验证结果
2615
+ */
2616
+ validate(data, keyPath) {
2617
+ try {
2618
+ const value = this.extractValueFromData(data, keyPath);
2619
+ if (value !== void 0) {
2620
+ return {
2621
+ key: keyPath,
2622
+ exists: true,
2623
+ value
2624
+ };
2625
+ } else {
2626
+ return {
2627
+ key: keyPath,
2628
+ exists: false
2629
+ };
2630
+ }
2631
+ } catch (error) {
2632
+ return {
2633
+ key: keyPath,
2634
+ exists: false,
2635
+ error: error instanceof Error ? error.message : String(error)
2636
+ };
2637
+ }
2638
+ }
2639
+ /**
2640
+ * 从数据对象中提取值(辅助方法)
2641
+ */
2642
+ extractValueFromData(data, keyPath) {
2643
+ if (!keyPath) {
2644
+ throw new Error("Key path cannot be empty");
2645
+ }
2646
+ const parts = keyPath.split(".");
2647
+ let current = data;
2648
+ for (const part of parts) {
2649
+ if (current === null || current === void 0) {
2650
+ return void 0;
2651
+ }
2652
+ if (typeof current !== "object") {
2653
+ return void 0;
2654
+ }
2655
+ if (part.includes("[") && part.includes("]")) {
2656
+ const [arrayKey, indexStr] = part.split("[");
2657
+ const index = parseInt(indexStr.replace("]", ""), 10);
2658
+ const obj = current;
2659
+ current = obj[arrayKey];
2660
+ if (Array.isArray(current)) {
2661
+ current = current[index];
2662
+ }
2663
+ } else {
2664
+ const obj = current;
2665
+ current = obj[part];
2666
+ }
2667
+ }
2668
+ return current;
2669
+ }
2670
+ /**
2671
+ * 批量验证多个 key 是否存在
2672
+ * @param data 配置数据对象
2673
+ * @param keyPaths key 路径数组(可能包含逗号分隔的字符串)
2674
+ * @returns 批量验证结果
2675
+ */
2676
+ validateBatch(data, keyPaths) {
2677
+ const results = [];
2678
+ const allKeys = [];
2679
+ for (const kp of keyPaths) {
2680
+ const parts = kp.split(",").map((s) => s.trim()).filter((s) => s);
2681
+ allKeys.push(...parts);
2682
+ }
2683
+ for (const keyPath of allKeys) {
2684
+ const result = this.validate(data, keyPath);
2685
+ results.push(result);
2686
+ }
2687
+ const existsCount = results.filter((r) => r.exists).length;
2688
+ const notExistsCount = results.length - existsCount;
2689
+ return {
2690
+ allExists: notExistsCount === 0,
2691
+ results,
2692
+ existsCount,
2693
+ notExistsCount
2694
+ };
2695
+ }
2696
+ /**
2697
+ * 格式化验证结果为人类可读的字符串
2698
+ * @param result 验证结果
2699
+ * @returns 格式化后的字符串
2700
+ */
2701
+ formatResult(result) {
2702
+ if (result.exists) {
2703
+ const valueStr = this.formatValue(result.value);
2704
+ return `\u2705 Key '${result.key}' exists${valueStr ? `: ${valueStr}` : ""}`;
2705
+ } else {
2706
+ if (result.error) {
2707
+ return `\u274C Key '${result.key}' error: ${result.error}`;
2708
+ }
2709
+ return `\u274C Key '${result.key}' not found`;
2710
+ }
2711
+ }
2712
+ /**
2713
+ * 格式化批量验证结果为人类可读的字符串
2714
+ * @param result 批量验证结果
2715
+ * @returns 格式化后的字符串
2716
+ */
2717
+ formatBatchResult(result) {
2718
+ const lines = [];
2719
+ for (const r of result.results) {
2720
+ lines.push(this.formatResult(r));
2721
+ }
2722
+ lines.push("");
2723
+ lines.push(`\u{1F4CA} Summary:`);
2724
+ lines.push(` \u2705 Exists: ${result.existsCount}`);
2725
+ lines.push(` \u274C Not found: ${result.notExistsCount}`);
2726
+ lines.push(` \u{1F4C8} Total: ${result.results.length}`);
2727
+ return lines.join("\n");
2728
+ }
2729
+ /**
2730
+ * 格式化值为字符串
2731
+ */
2732
+ formatValue(value) {
2733
+ if (value === null) {
2734
+ return "null";
2735
+ }
2736
+ if (typeof value === "string") {
2737
+ return `"${value}"`;
2738
+ }
2739
+ if (typeof value === "boolean" || typeof value === "number") {
2740
+ return String(value);
2741
+ }
2742
+ if (typeof value === "object") {
2743
+ return JSON.stringify(value);
2744
+ }
2745
+ return String(value);
2746
+ }
2747
+ /**
2748
+ * 根据验证结果确定退出码
2749
+ * @param result 验证结果
2750
+ * @returns 退出码(0 表示全部存在,1 表示有不存在或错误)
2751
+ */
2752
+ getExitCode(result) {
2753
+ if ("allExists" in result) {
2754
+ return result.allExists ? 0 : 1;
2755
+ } else {
2756
+ return result.exists ? 0 : 1;
2757
+ }
2758
+ }
2759
+ };
2760
+
2761
+ // src/commands/pack.ts
2762
+ import clipboardy from "clipboardy";
2763
+
2764
+ // src/commands/types.ts
2765
+ function normalizeURPFPath(filePath) {
2766
+ if (filePath.endsWith(".urpf.txt") || filePath.endsWith(".urpf")) {
2767
+ return filePath;
2768
+ }
2769
+ return `${filePath}.urpf.txt`;
2770
+ }
2771
+ async function findURPFFile(filePath) {
2772
+ const fs15 = await import("fs/promises");
2773
+ try {
2774
+ const stats = await fs15.stat(filePath);
2775
+ if (stats.isFile()) {
2776
+ return filePath;
2777
+ }
2778
+ } catch {
2779
+ }
2780
+ if (!filePath.endsWith(".urpf.txt") && !filePath.endsWith(".urpf")) {
2781
+ const newPath = `${filePath}.urpf.txt`;
2782
+ try {
2783
+ const stats = await fs15.stat(newPath);
2784
+ if (stats.isFile()) {
2785
+ return newPath;
2786
+ }
2787
+ } catch {
2788
+ }
2789
+ const oldPath = `${filePath}.urpf`;
2790
+ try {
2791
+ const stats = await fs15.stat(oldPath);
2792
+ if (stats.isFile()) {
2793
+ return oldPath;
2794
+ }
2795
+ } catch {
2796
+ }
2797
+ }
2798
+ return null;
2799
+ }
2800
+
2801
+ // src/commands/pack.ts
2802
+ var PackCommand = class {
2803
+ /**
2804
+ * 执行 pack 命令
2805
+ */
2806
+ async execute(inputPath, options = {}) {
2807
+ const startTime = Date.now();
2808
+ try {
2809
+ const resolvedPath = path3.resolve(inputPath);
2810
+ await this.validatePath(resolvedPath);
2811
+ this.validateSliceOptions(options);
2812
+ const outputPath = options.output || this.generateOutputPath(resolvedPath);
2813
+ const scanResult = await this.scanFiles(resolvedPath, options);
2814
+ const urpfDocument = await this.generateURPFDocument(scanResult, resolvedPath, options);
2815
+ const urpfResult = await this.saveURPFDocument(urpfDocument, outputPath, options);
2816
+ const duration = Date.now() - startTime;
2817
+ await this.handleOutputOptions(urpfResult.content, outputPath, options);
2818
+ if (options.verbose) {
2819
+ this.logVerbose(scanResult, urpfResult, outputPath, duration);
2820
+ }
2821
+ return {
2822
+ success: true,
2823
+ outputPath,
2824
+ fileCount: scanResult.files.length,
2825
+ directoryCount: scanResult.directories.length,
2826
+ skippedCount: scanResult.skippedFiles.length,
2827
+ urpfSize: urpfResult.size,
2828
+ duration
2829
+ };
2830
+ } catch (err) {
2831
+ const duration = Date.now() - startTime;
2832
+ const errorMessage = err instanceof Error ? err.message : String(err);
2833
+ if (options.verbose) {
2834
+ console.error(`\u9519\u8BEF: ${errorMessage}`);
2835
+ }
2836
+ return {
2837
+ success: false,
2838
+ outputPath: "",
2839
+ fileCount: 0,
2840
+ directoryCount: 0,
2841
+ skippedCount: 0,
2842
+ urpfSize: 0,
2843
+ duration,
2844
+ error: errorMessage
2845
+ };
2846
+ }
2847
+ }
2848
+ /**
2849
+ * 验证输入路径
2850
+ */
2851
+ async validatePath(filePath) {
2852
+ try {
2853
+ const stats = await fs4.stat(filePath);
2854
+ if (!stats.isFile() && !stats.isDirectory()) {
2855
+ throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${filePath}`);
2856
+ }
2857
+ } catch {
2858
+ throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u8DEF\u5F84: ${filePath}`);
2859
+ }
2860
+ }
2861
+ /**
2862
+ * 验证切片选项
2863
+ */
2864
+ validateSliceOptions(options) {
2865
+ if ((options.byteStart !== void 0 || options.byteEnd !== void 0) && (options.lineStart !== void 0 || options.lineEnd !== void 0)) {
2866
+ throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --byte \u548C --line \u5207\u7247\u9009\u9879");
2867
+ }
2868
+ if (options.byteEnd !== void 0 && options.byteStart === void 0) {
2869
+ throw new Error("--byte \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u4F4D\u7F6E");
2870
+ }
2871
+ if (options.lineEnd !== void 0 && options.lineStart === void 0) {
2872
+ throw new Error("--line \u9009\u9879\u5FC5\u987B\u6307\u5B9A\u8D77\u59CB\u884C\u53F7");
2873
+ }
2874
+ if (options.key !== void 0 && (options.byteStart !== void 0 || options.byteEnd !== void 0)) {
2875
+ throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --key \u548C --byte \u9009\u9879");
2876
+ }
2877
+ if (options.key !== void 0 && (options.lineStart !== void 0 || options.lineEnd !== void 0)) {
2878
+ throw new Error("\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528 --key \u548C --line \u9009\u9879");
2879
+ }
2880
+ }
2881
+ /**
2882
+ * 生成输出路径
2883
+ */
2884
+ generateOutputPath(inputPath) {
2885
+ return normalizeURPFPath(inputPath);
2886
+ }
2887
+ /**
2888
+ * 扫描文件
2889
+ */
2890
+ async scanFiles(rootPath, options) {
2891
+ const stats = await fs4.stat(rootPath);
2892
+ if (stats.isFile()) {
2893
+ const basePath = path3.dirname(rootPath);
2894
+ const metadata = await this.collectFileMetadata(rootPath, basePath);
2895
+ return {
2896
+ rootPath: basePath,
2897
+ // 设置正确的 rootPath
2898
+ files: [metadata],
2899
+ directories: [],
2900
+ skippedFiles: [],
2901
+ startTime: Date.now(),
2902
+ endTime: Date.now(),
2903
+ duration: 0
2904
+ };
2905
+ }
2906
+ let ignoreRules = [];
2907
+ if (options.ignoreFile) {
2908
+ const parser = new IgnoreRuleParser();
2909
+ const content = await fs4.readFile(options.ignoreFile, "utf-8");
2910
+ ignoreRules = parser.parseFile(content);
2911
+ } else {
2912
+ const defaultIgnoreFiles = [".gitignore", ".iflowignore"];
2913
+ for (const ignoreFile of defaultIgnoreFiles) {
2914
+ const ignorePath = path3.join(rootPath, ignoreFile);
2915
+ try {
2916
+ await fs4.access(ignorePath);
2917
+ const parser = new IgnoreRuleParser();
2918
+ const content = await fs4.readFile(ignorePath, "utf-8");
2919
+ ignoreRules = [...ignoreRules, ...parser.parseFile(content)];
2920
+ break;
2921
+ } catch {
2922
+ }
2923
+ }
2924
+ }
2925
+ const effectiveMaxDepth = options.noRecurse ? 1 : options.maxDepth;
2926
+ return await FileScanner.scanDirectory(rootPath, {
2927
+ ignoreRules,
2928
+ followSymlinks: options.followSymlinks || false,
2929
+ maxDepth: effectiveMaxDepth
2930
+ // undefined 或 1
2931
+ });
2932
+ }
2933
+ /**
2934
+ * 收集文件元数据
2935
+ */
2936
+ async collectFileMetadata(filePath, rootPath) {
2937
+ const stats = await fs4.stat(filePath);
2938
+ const relativePath = path3.relative(rootPath, filePath);
2939
+ return {
2940
+ path: relativePath,
2941
+ name: path3.basename(filePath),
2942
+ isDirectory: false,
2943
+ isSymlink: false,
1561
2944
  size: stats.size,
1562
- permissions: stats.mode,
1563
- mtime: stats.mtime,
2945
+ permissions: stats.mode.toString(8).padStart(3, "0"),
2946
+ mtime: stats.mtime.getTime(),
2947
+ ctime: stats.ctime.getTime(),
1564
2948
  encoding: "utf-8",
1565
- newlineType: "lf"
2949
+ newlineType: "lf",
2950
+ isBinary: false
1566
2951
  };
1567
2952
  }
1568
2953
  /**
@@ -1571,22 +2956,28 @@ var PackCommand = class {
1571
2956
  async generateURPFDocument(scanResult, rootPath, options) {
1572
2957
  const serializer = new ResourceSerializer();
1573
2958
  const files = scanResult.files.map((file) => {
1574
- const isAbsolutePath = path2.isAbsolute(file.path);
1575
- const filePath = isAbsolutePath ? file.path : path2.join(rootPath, file.path);
1576
- const relativePath = isAbsolutePath ? path2.relative(rootPath, file.path) : file.path;
2959
+ const relativePath = file.path;
2960
+ const basePath = scanResult.rootPath || rootPath;
2961
+ const filePath = path3.join(basePath, relativePath);
2962
+ const udrsRef = `file://${relativePath}`;
1577
2963
  return {
1578
2964
  filePath,
1579
2965
  metadata: file,
1580
- udrsReference: `file://${relativePath}`
2966
+ udrsReference: udrsRef
1581
2967
  };
1582
2968
  });
1583
2969
  const resources = await serializer.serializeFiles(files);
1584
- const processedResources = await this.applySliceToResources(resources, options);
1585
- const properties = this.buildProperties(options);
2970
+ let processedResources = resources;
2971
+ if (options.key !== void 0) {
2972
+ processedResources = await this.handleKeyExtraction(resources, files, options);
2973
+ } else {
2974
+ processedResources = await this.applySliceToResources(resources, options);
2975
+ }
2976
+ const properties2 = this.buildProperties(options);
1586
2977
  return {
1587
2978
  boundaryToken: "",
1588
2979
  // 将由生成器自动生成
1589
- properties,
2980
+ properties: properties2,
1590
2981
  resources: processedResources
1591
2982
  };
1592
2983
  }
@@ -1599,7 +2990,6 @@ var PackCommand = class {
1599
2990
  }
1600
2991
  if (options.byteStart !== void 0) {
1601
2992
  return resources.map((resource) => {
1602
- const { start, end } = options;
1603
2993
  const byteRange = { start: options.byteStart, end: options.byteEnd };
1604
2994
  let content;
1605
2995
  if (Buffer.isBuffer(resource.content)) {
@@ -1636,22 +3026,150 @@ var PackCommand = class {
1636
3026
  }
1637
3027
  return resources;
1638
3028
  }
3029
+ /**
3030
+ * 处理 Key 提取逻辑
3031
+ */
3032
+ async handleKeyExtraction(resources, files, options) {
3033
+ if (!options.key) {
3034
+ return resources;
3035
+ }
3036
+ if (options.validate) {
3037
+ return this.handleKeyValidation(resources, files, options);
3038
+ }
3039
+ const isBatch = Array.isArray(options.key) || typeof options.key === "string" && options.key.includes(",");
3040
+ const processedResources = [];
3041
+ const formatter = options.format ? new OutputFormatter() : null;
3042
+ if (isBatch) {
3043
+ const batchExtractor = new BatchKeyExtractor({
3044
+ outputFormat: "array",
3045
+ enableWildcard: true
3046
+ });
3047
+ for (let i = 0; i < resources.length; i++) {
3048
+ const resource = resources[i];
3049
+ const file = files[i];
3050
+ const absolutePath = file.filePath;
3051
+ try {
3052
+ const result = await batchExtractor.extractFromFile(absolutePath, options.key);
3053
+ const extractedData = result.results.map((r) => r.value);
3054
+ let extractedContent;
3055
+ if (formatter && options.format) {
3056
+ extractedContent = formatter.format(extractedData, { format: options.format });
3057
+ } else {
3058
+ extractedContent = batchExtractor.formatOutput(result);
3059
+ }
3060
+ processedResources.push({
3061
+ ...resource,
3062
+ content: extractedContent
3063
+ });
3064
+ if (options.verbose) {
3065
+ batchExtractor.printSummary(result);
3066
+ console.log(` \u2713 \u4ECE ${absolutePath} \u6279\u91CF\u63D0\u53D6 ${result.successCount} \u4E2A key`);
3067
+ }
3068
+ if (result.errors.length > 0) {
3069
+ throw new Error(`\u6279\u91CF\u63D0\u53D6\u5931\u8D25: ${result.errors[0].error}`);
3070
+ }
3071
+ } catch (error) {
3072
+ const errorMessage = error instanceof Error ? error.message : String(error);
3073
+ throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${absolutePath} \u6279\u91CF\u63D0\u53D6 key: ${errorMessage}`);
3074
+ }
3075
+ }
3076
+ } else {
3077
+ const keyExtractor = new KeyExtractor({
3078
+ onError: (error) => {
3079
+ throw error;
3080
+ }
3081
+ });
3082
+ for (let i = 0; i < resources.length; i++) {
3083
+ const resource = resources[i];
3084
+ const file = files[i];
3085
+ const absolutePath = file.filePath;
3086
+ try {
3087
+ const result = await keyExtractor.extractFromFile(absolutePath, options.key);
3088
+ const extractedValue = result.value;
3089
+ let extractedContent;
3090
+ if (formatter && options.format) {
3091
+ extractedContent = formatter.format(extractedValue, { format: options.format });
3092
+ } else if (typeof extractedValue === "object" && extractedValue !== null) {
3093
+ extractedContent = JSON.stringify(extractedValue, null, 2);
3094
+ } else if (typeof extractedValue === "string") {
3095
+ extractedContent = extractedValue;
3096
+ } else if (extractedValue === null) {
3097
+ extractedContent = "null";
3098
+ } else if (extractedValue === void 0) {
3099
+ extractedContent = "undefined";
3100
+ } else {
3101
+ extractedContent = String(extractedValue);
3102
+ }
3103
+ processedResources.push({
3104
+ ...resource,
3105
+ content: extractedContent
3106
+ });
3107
+ if (options.verbose) {
3108
+ console.log(` \u2713 \u4ECE ${absolutePath} \u63D0\u53D6 key "${options.key}": ${result.type} \u7C7B\u578B`);
3109
+ }
3110
+ } catch (error) {
3111
+ const errorMessage = error instanceof Error ? error.message : String(error);
3112
+ throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${absolutePath} \u63D0\u53D6 key "${options.key}": ${errorMessage}`);
3113
+ }
3114
+ }
3115
+ }
3116
+ return processedResources;
3117
+ }
3118
+ /**
3119
+ * 处理 Key 验证逻辑
3120
+ */
3121
+ async handleKeyValidation(resources, files, options) {
3122
+ const validator = new KeyValidator();
3123
+ for (let i = 0; i < resources.length; i++) {
3124
+ const file = files[i];
3125
+ const absolutePath = file.filePath;
3126
+ try {
3127
+ const fileContent = await fs4.readFile(absolutePath, "utf-8");
3128
+ const data = JSON.parse(fileContent);
3129
+ const keyPaths = Array.isArray(options.key) ? options.key : [options.key];
3130
+ const isBatch = keyPaths.length > 1 || typeof options.key === "string" && options.key.includes(",");
3131
+ let result;
3132
+ if (isBatch) {
3133
+ const batchResult = validator.validateBatch(data, keyPaths);
3134
+ console.log(validator.formatBatchResult(batchResult));
3135
+ result = { allExists: batchResult.allExists };
3136
+ } else {
3137
+ const singleResult = validator.validate(data, options.key);
3138
+ console.log(validator.formatResult(singleResult));
3139
+ result = { allExists: singleResult.exists };
3140
+ }
3141
+ if (!result.allExists) {
3142
+ process.exit(1);
3143
+ }
3144
+ return [];
3145
+ } catch (error) {
3146
+ const errorMessage = error instanceof Error ? error.message : String(error);
3147
+ console.error(`\u274C \u9A8C\u8BC1\u5931\u8D25: ${errorMessage}`);
3148
+ process.exit(1);
3149
+ }
3150
+ }
3151
+ return [];
3152
+ }
1639
3153
  /**
1640
3154
  * 构建属性列表(包括环境变量)
1641
3155
  */
1642
3156
  buildProperties(options) {
1643
- const properties = [];
3157
+ const properties2 = [];
3158
+ properties2.push({
3159
+ name: "version",
3160
+ value: "1.0"
3161
+ });
1644
3162
  if (options.envVars && Object.keys(options.envVars).length > 0) {
1645
3163
  for (const [name, value] of Object.entries(options.envVars)) {
1646
3164
  const envValue = value !== void 0 ? value : process.env[name] || "";
1647
- properties.push({
3165
+ properties2.push({
1648
3166
  name,
1649
3167
  value: envValue,
1650
3168
  isExtended: true
1651
3169
  });
1652
3170
  }
1653
3171
  }
1654
- return properties.length > 0 ? properties : void 0;
3172
+ return properties2;
1655
3173
  }
1656
3174
  /**
1657
3175
  * 保存 URPF 文件
@@ -1659,10 +3177,10 @@ var PackCommand = class {
1659
3177
  async saveURPFDocument(document, outputPath, options) {
1660
3178
  const generator = new URPFGenerator();
1661
3179
  const result = await generator.generate(document);
1662
- const outputDir = path2.dirname(outputPath);
1663
- await fs3.mkdir(outputDir, { recursive: true });
3180
+ const outputDir = path3.dirname(outputPath);
3181
+ await fs4.mkdir(outputDir, { recursive: true });
1664
3182
  try {
1665
- await fs3.access(outputPath);
3183
+ await fs4.access(outputPath);
1666
3184
  if (!options.force) {
1667
3185
  throw new Error(`\u8F93\u51FA\u6587\u4EF6\u5DF2\u5B58\u5728: ${outputPath}\u3002\u8BF7\u4F7F\u7528 --force \u9009\u9879\u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u6307\u5B9A\u4E0D\u540C\u7684\u8F93\u51FA\u8DEF\u5F84\u3002`);
1668
3186
  }
@@ -1671,7 +3189,7 @@ var PackCommand = class {
1671
3189
  throw error;
1672
3190
  }
1673
3191
  }
1674
- await fs3.writeFile(outputPath, result.content, "utf-8");
3192
+ await fs4.writeFile(outputPath, result.content, "utf-8");
1675
3193
  return result;
1676
3194
  }
1677
3195
  /**
@@ -1690,7 +3208,7 @@ var PackCommand = class {
1690
3208
  /**
1691
3209
  * 处理输出选项(stdout 和 clipboard)
1692
3210
  */
1693
- async handleOutputOptions(content, outputPath, options) {
3211
+ async handleOutputOptions(content, _outputPath, options) {
1694
3212
  if (options.stdout) {
1695
3213
  console.log(content);
1696
3214
  }
@@ -1708,9 +3226,9 @@ var PackCommand = class {
1708
3226
  }
1709
3227
  };
1710
3228
 
1711
- // src/commands/unpack.ts
1712
- import path3 from "path";
1713
- import fs4 from "fs/promises";
3229
+ // src/commands/unpack.ts
3230
+ import path4 from "path";
3231
+ import fs5 from "fs/promises";
1714
3232
 
1715
3233
  // src/core/parser/types.ts
1716
3234
  var URPFParseError = class extends Error {
@@ -1766,12 +3284,11 @@ var BoundaryParser = class _BoundaryParser {
1766
3284
  return _BoundaryParser.BOUNDARY_REGEX.test(line);
1767
3285
  }
1768
3286
  /**
1769
- * 生成边界行
1770
3287
  * @param token 边界令牌
1771
3288
  * @param type 边界类型
1772
3289
  * @returns 边界行字符串
1773
3290
  */
1774
- static generateBoundaryLine(token, type) {
3291
+ static generateBoundaryLine(token, _type) {
1775
3292
  return `${_BoundaryParser.BOUNDARY_PREFIX}${token}${_BoundaryParser.BOUNDARY_SUFFIX}`;
1776
3293
  }
1777
3294
  /**
@@ -1873,11 +3390,11 @@ var BoundaryParser = class _BoundaryParser {
1873
3390
  var PropertyParser = class _PropertyParser {
1874
3391
  static {
1875
3392
  /** 简单变量名正则表达式($name 或 $env:name) */
1876
- this.SIMPLE_NAME_REGEX = /^\$(?:([a-zA-Z0-9_]+)|(env:)([a-zA-Z0-9_]+))$/;
3393
+ this.SIMPLE_NAME_REGEX = /^\$(?:([a-zA-Z0-9_-]+)|(env:)([a-zA-Z0-9_-]+))$/;
1877
3394
  }
1878
3395
  static {
1879
3396
  /** 带空格变量名正则表达式(${full name} 或 ${env:full name}) */
1880
- this.EXTENDED_NAME_REGEX = /^\$\{(?:([a-zA-Z0-9_ ]+)|(env:)([a-zA-Z0-9_ ]+))\}$/;
3397
+ this.EXTENDED_NAME_REGEX = /^\$\{(?:([a-zA-Z0-9_ -]+)|(env:)([a-zA-Z0-9_ -]+))\}$/;
1881
3398
  }
1882
3399
  static {
1883
3400
  /** 属性行正则表达式 */
@@ -1943,7 +3460,7 @@ var PropertyParser = class _PropertyParser {
1943
3460
  */
1944
3461
  static parsePropertySection(content, startLineNo = 1) {
1945
3462
  const lines = content.split("\n");
1946
- const properties = [];
3463
+ const properties2 = [];
1947
3464
  const nameSet = /* @__PURE__ */ new Set();
1948
3465
  lines.forEach((line, index) => {
1949
3466
  const lineNo = startLineNo + index;
@@ -1959,9 +3476,9 @@ var PropertyParser = class _PropertyParser {
1959
3476
  );
1960
3477
  }
1961
3478
  nameSet.add(property.name);
1962
- properties.push(property);
3479
+ properties2.push(property);
1963
3480
  });
1964
- return { properties };
3481
+ return { properties: properties2 };
1965
3482
  }
1966
3483
  /**
1967
3484
  * 检查内容是否为属性部分
@@ -2212,9 +3729,9 @@ var VariableReplacer = class _VariableReplacer {
2212
3729
  * @param properties 属性列表
2213
3730
  * @returns 变量上下文
2214
3731
  */
2215
- static createContext(properties) {
3732
+ static createContext(properties2) {
2216
3733
  const map = /* @__PURE__ */ new Map();
2217
- properties.forEach((p) => map.set(p.name, p.value));
3734
+ properties2.forEach((p) => map.set(p.name, p.value));
2218
3735
  return {
2219
3736
  get(name) {
2220
3737
  return map.get(name);
@@ -2394,8 +3911,8 @@ var URPFParser = class _URPFParser {
2394
3911
  * @throws {URPFParseError} 如果解析失败
2395
3912
  */
2396
3913
  static async parseFile(filePath, options = {}) {
2397
- const fs14 = await import("fs/promises");
2398
- const content = await fs14.readFile(filePath, "utf-8");
3914
+ const fs15 = await import("fs/promises");
3915
+ const content = await fs15.readFile(filePath, "utf-8");
2399
3916
  return _URPFParser.parse(content, options);
2400
3917
  }
2401
3918
  /**
@@ -2463,7 +3980,7 @@ function matchesAnyPattern(filePath, patterns) {
2463
3980
  if (filePath.startsWith(pattern + "/")) {
2464
3981
  return true;
2465
3982
  }
2466
- const dirMatch = regex.test(path3.dirname(filePath));
3983
+ const dirMatch = regex.test(path4.dirname(filePath));
2467
3984
  if (dirMatch) {
2468
3985
  return true;
2469
3986
  }
@@ -2508,14 +4025,14 @@ var UnpackCommand = class {
2508
4025
  async execute(urpfFilePath, options = {}) {
2509
4026
  const startTime = Date.now();
2510
4027
  try {
2511
- const resolvedPath = path3.resolve(urpfFilePath);
4028
+ const resolvedPath = path4.resolve(urpfFilePath);
2512
4029
  await this.validateURPFFile(resolvedPath);
2513
4030
  const actualPath = await findURPFFile(resolvedPath);
2514
4031
  if (actualPath === null) {
2515
4032
  throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolvedPath}`);
2516
4033
  }
2517
- const outputDir = options.output ? path3.resolve(options.output) : process.cwd();
2518
- await fs4.mkdir(outputDir, { recursive: true });
4034
+ const outputDir = options.output ? path4.resolve(options.output) : process.cwd();
4035
+ await fs5.mkdir(outputDir, { recursive: true });
2519
4036
  const parseResult = await URPFParser.parseFile(actualPath);
2520
4037
  const envVars = parseResult.document.propertySection ? extractEnvironmentVariables(parseResult.document.propertySection) : [];
2521
4038
  const vars = parseResult.document.propertySection ? extractVariables(parseResult.document.propertySection) : [];
@@ -2574,7 +4091,7 @@ var UnpackCommand = class {
2574
4091
  }
2575
4092
  return {
2576
4093
  success: false,
2577
- outputDir: options.output ? path3.resolve(options.output) : process.cwd(),
4094
+ outputDir: options.output ? path4.resolve(options.output) : process.cwd(),
2578
4095
  files: [],
2579
4096
  createdCount: 0,
2580
4097
  overwrittenCount: 0,
@@ -2674,7 +4191,7 @@ var UnpackCommand = class {
2674
4191
  }
2675
4192
  throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
2676
4193
  }
2677
- const stats = await fs4.stat(actualPath);
4194
+ const stats = await fs5.stat(actualPath);
2678
4195
  if (!stats.isFile()) {
2679
4196
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
2680
4197
  }
@@ -2685,15 +4202,16 @@ var UnpackCommand = class {
2685
4202
  async extractResource(resource, outputDir, options, variableContext) {
2686
4203
  try {
2687
4204
  const filePath = this.extractFilePath(resource.header.udrsReference.uri);
2688
- const fullPath = path3.join(outputDir, filePath);
2689
- const parentDir = path3.dirname(fullPath);
2690
- await fs4.mkdir(parentDir, { recursive: true });
4205
+ const actualFilePath = filePath || `resource-${Date.now()}.txt`;
4206
+ const fullPath = path4.join(outputDir, actualFilePath);
4207
+ const parentDir = path4.dirname(fullPath);
4208
+ await fs5.mkdir(parentDir, { recursive: true });
2691
4209
  const fileExists = await this.fileExists(fullPath);
2692
4210
  if (fileExists) {
2693
4211
  if (options.force) {
2694
4212
  await this.writeFile(resource, fullPath, options, variableContext);
2695
4213
  return {
2696
- path: filePath,
4214
+ path: actualFilePath,
2697
4215
  success: true,
2698
4216
  action: "overwritten"
2699
4217
  };
@@ -2703,27 +4221,27 @@ var UnpackCommand = class {
2703
4221
  if (sourceMtime > targetMtime) {
2704
4222
  await this.writeFile(resource, fullPath, options, variableContext);
2705
4223
  return {
2706
- path: filePath,
4224
+ path: actualFilePath,
2707
4225
  success: true,
2708
4226
  action: "overwritten"
2709
4227
  };
2710
4228
  } else {
2711
4229
  if (options.verbose) {
2712
- console.log(`\u8DF3\u8FC7\uFF08\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\uFF09: ${filePath}`);
4230
+ console.log(`\u8DF3\u8FC7\uFF08\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\uFF09: ${actualFilePath}`);
2713
4231
  }
2714
4232
  return {
2715
- path: filePath,
4233
+ path: actualFilePath,
2716
4234
  success: true,
2717
4235
  action: "skipped"
2718
4236
  };
2719
4237
  }
2720
4238
  } else {
2721
- throw new Error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}\u3002\u8BF7\u4F7F\u7528 --force \u9009\u9879\u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u4F7F\u7528 --force-when-newer \u9009\u9879\u4EC5\u8986\u76D6\u66F4\u65B0\u7684\u6587\u4EF6\u3002`);
4239
+ throw new Error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${actualFilePath}\u3002\u8BF7\u4F7F\u7528 --force \u9009\u9879\u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u4F7F\u7528 --force-when-newer \u9009\u9879\u4EC5\u8986\u76D6\u66F4\u65B0\u7684\u6587\u4EF6\u3002`);
2722
4240
  }
2723
4241
  } else {
2724
4242
  await this.writeFile(resource, fullPath, options, variableContext);
2725
4243
  return {
2726
- path: filePath,
4244
+ path: actualFilePath,
2727
4245
  success: true,
2728
4246
  action: "created"
2729
4247
  };
@@ -2731,7 +4249,7 @@ var UnpackCommand = class {
2731
4249
  } catch (error) {
2732
4250
  const errorMessage = error instanceof Error ? error.message : String(error);
2733
4251
  return {
2734
- path: this.extractFilePath(resource.header.udrsReference.uri),
4252
+ path: this.extractFilePath(resource.header.udrsReference.uri) || `resource-${Date.now()}.txt`,
2735
4253
  success: false,
2736
4254
  action: "error",
2737
4255
  error: errorMessage
@@ -2747,7 +4265,6 @@ var UnpackCommand = class {
2747
4265
  filePath = filePath.substring(1);
2748
4266
  }
2749
4267
  if (/^[A-Za-z]:/.test(filePath)) {
2750
- filePath = filePath;
2751
4268
  }
2752
4269
  return filePath;
2753
4270
  }
@@ -2756,7 +4273,7 @@ var UnpackCommand = class {
2756
4273
  */
2757
4274
  async fileExists(filePath) {
2758
4275
  try {
2759
- await fs4.access(filePath);
4276
+ await fs5.access(filePath);
2760
4277
  return true;
2761
4278
  } catch {
2762
4279
  return false;
@@ -2766,7 +4283,7 @@ var UnpackCommand = class {
2766
4283
  * 获取文件修改时间
2767
4284
  */
2768
4285
  async getFileMtime(filePath) {
2769
- const stats = await fs4.stat(filePath);
4286
+ const stats = await fs5.stat(filePath);
2770
4287
  return stats.mtime;
2771
4288
  }
2772
4289
  /**
@@ -2774,7 +4291,7 @@ var UnpackCommand = class {
2774
4291
  * 注意:URPF 规范中不直接存储 mtime,这里我们使用一个合理的时间戳
2775
4292
  * 在实际应用中,可能需要从 URPF 文件的属性部分读取
2776
4293
  */
2777
- extractMtime(resource) {
4294
+ extractMtime(_resource) {
2778
4295
  return new Date(Date.now() - 36e5);
2779
4296
  }
2780
4297
  /**
@@ -2795,7 +4312,21 @@ var UnpackCommand = class {
2795
4312
  content = resource.content;
2796
4313
  } else {
2797
4314
  content = resource.content;
2798
- if (options.applyVariables && variableContext) {
4315
+ if (options.key) {
4316
+ try {
4317
+ const extractedContent = await this.applyKeyExtraction(
4318
+ content,
4319
+ filePath,
4320
+ options.key,
4321
+ options.verbose ?? false
4322
+ );
4323
+ content = extractedContent;
4324
+ } catch (error) {
4325
+ const errorMessage = error instanceof Error ? error.message : String(error);
4326
+ throw new Error(`\u65E0\u6CD5\u4ECE\u6587\u4EF6 ${filePath} \u63D0\u53D6 key "${options.key}": ${errorMessage}`);
4327
+ }
4328
+ }
4329
+ if (options.applyVariables === true && variableContext) {
2799
4330
  try {
2800
4331
  const originalContent = content;
2801
4332
  content = VariableReplacer.replace(originalContent, variableContext);
@@ -2813,24 +4344,176 @@ var UnpackCommand = class {
2813
4344
  }
2814
4345
  }
2815
4346
  if (Buffer.isBuffer(content)) {
2816
- await fs4.writeFile(filePath, content);
4347
+ await fs5.writeFile(filePath, content);
2817
4348
  } else {
2818
- await fs4.writeFile(filePath, content, {
4349
+ await fs5.writeFile(filePath, content, {
2819
4350
  encoding: resource.header.encoding
2820
4351
  });
2821
4352
  }
2822
4353
  if (resource.header.permissions) {
2823
4354
  const permissions = parseInt(resource.header.permissions, 8);
2824
- await fs4.chmod(filePath, permissions);
4355
+ await fs5.chmod(filePath, permissions);
2825
4356
  }
2826
4357
  if (options.preserveMtime) {
2827
4358
  const mtime = this.extractMtime(resource);
2828
- await fs4.utimes(filePath, mtime, mtime);
4359
+ await fs5.utimes(filePath, mtime, mtime);
2829
4360
  }
2830
4361
  if (options.verbose) {
2831
4362
  console.log(`\u5DF2${await this.fileExists(filePath) ? "\u8986\u76D6" : "\u521B\u5EFA"}: ${filePath}`);
2832
4363
  }
2833
4364
  }
4365
+ /**
4366
+ * 应用 key 提取到文件内容
4367
+ * @param content - 文件内容
4368
+ * @param filePath - 文件路径
4369
+ * @param keyPath - key 路径(可以是字符串或字符串数组,支持通配符和逗号分隔)
4370
+ * @param verbose - 是否显示详细日志
4371
+ * @param format - 输出格式(可选)
4372
+ * @param validate - 是否仅验证(可选)
4373
+ * @returns 提取后的内容
4374
+ */
4375
+ async applyKeyExtraction(content, filePath, keyPath, verbose, format, validate) {
4376
+ let fileFormat = this.detectFileFormat(filePath);
4377
+ if (!fileFormat) {
4378
+ fileFormat = this.detectFormatFromContent(content);
4379
+ }
4380
+ if (!fileFormat) {
4381
+ console.warn(`\u26A0\uFE0F \u8B66\u544A: \u6587\u4EF6 ${filePath} \u4E0D\u662F\u652F\u6301\u7684\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\uFF0C\u8DF3\u8FC7 key \u63D0\u53D6`);
4382
+ return content;
4383
+ }
4384
+ const tempDir = path4.join(process.cwd(), ".urpf-temp");
4385
+ await fs5.mkdir(tempDir, { recursive: true });
4386
+ const tempFilePath = path4.join(tempDir, `temp.${fileFormat}`);
4387
+ try {
4388
+ await fs5.writeFile(tempFilePath, content, "utf-8");
4389
+ if (validate) {
4390
+ return this.applyKeyValidation(tempFilePath, filePath, keyPath, verbose);
4391
+ }
4392
+ const formatter = format ? new OutputFormatter() : null;
4393
+ const isBatch = Array.isArray(keyPath) || typeof keyPath === "string" && keyPath.includes(",");
4394
+ let extractedContent;
4395
+ if (isBatch) {
4396
+ const batchExtractor = new BatchKeyExtractor({
4397
+ outputFormat: "array",
4398
+ enableWildcard: true
4399
+ });
4400
+ const result = await batchExtractor.extractFromFile(tempFilePath, keyPath);
4401
+ const extractedData = result.results.map((r) => r.value);
4402
+ if (formatter && format) {
4403
+ extractedContent = formatter.format(extractedData, { format });
4404
+ } else {
4405
+ extractedContent = batchExtractor.formatOutput(result);
4406
+ }
4407
+ if (verbose) {
4408
+ batchExtractor.printSummary(result);
4409
+ console.log(` \u2713 \u4ECE ${filePath} \u6279\u91CF\u63D0\u53D6 ${result.successCount} \u4E2A key`);
4410
+ }
4411
+ if (result.errors.length > 0) {
4412
+ throw new Error(`\u6279\u91CF\u63D0\u53D6\u5931\u8D25: ${result.errors[0].error}`);
4413
+ }
4414
+ } else {
4415
+ const keyExtractor = new KeyExtractor({
4416
+ onError: (error) => {
4417
+ throw error;
4418
+ }
4419
+ });
4420
+ const result = await keyExtractor.extractFromFile(tempFilePath, keyPath);
4421
+ const extractedValue = result.value;
4422
+ if (formatter && format) {
4423
+ extractedContent = formatter.format(extractedValue, { format });
4424
+ } else if (typeof extractedValue === "object" && extractedValue !== null) {
4425
+ extractedContent = JSON.stringify(extractedValue, null, 2);
4426
+ } else if (typeof extractedValue === "string") {
4427
+ extractedContent = extractedValue;
4428
+ } else if (extractedValue === null) {
4429
+ extractedContent = "null";
4430
+ } else if (extractedValue === void 0) {
4431
+ extractedContent = "undefined";
4432
+ } else {
4433
+ extractedContent = String(extractedValue);
4434
+ }
4435
+ if (verbose) {
4436
+ console.log(` \u2713 \u4ECE ${filePath} \u63D0\u53D6 key "${keyPath}": ${result.type} \u7C7B\u578B`);
4437
+ }
4438
+ }
4439
+ return extractedContent;
4440
+ } finally {
4441
+ try {
4442
+ await fs5.unlink(tempFilePath);
4443
+ } catch {
4444
+ }
4445
+ }
4446
+ }
4447
+ /**
4448
+ * 应用 Key 验证逻辑
4449
+ */
4450
+ async applyKeyValidation(tempFilePath, _filePath, keyPath, _verbose) {
4451
+ const validator = new KeyValidator();
4452
+ try {
4453
+ const fileContent = await fs5.readFile(tempFilePath, "utf-8");
4454
+ const data = JSON.parse(fileContent);
4455
+ const keyPaths = Array.isArray(keyPath) ? keyPath : [keyPath];
4456
+ const isBatch = keyPaths.length > 1 || typeof keyPath === "string" && keyPath.includes(",");
4457
+ if (isBatch) {
4458
+ const batchResult = validator.validateBatch(data, keyPaths);
4459
+ console.log(validator.formatBatchResult(batchResult));
4460
+ } else {
4461
+ const singleResult = validator.validate(data, keyPath);
4462
+ console.log(validator.formatResult(singleResult));
4463
+ }
4464
+ return fileContent;
4465
+ } catch (error) {
4466
+ const errorMessage = error instanceof Error ? error.message : String(error);
4467
+ console.error(`\u274C \u9A8C\u8BC1\u5931\u8D25: ${errorMessage}`);
4468
+ throw error;
4469
+ }
4470
+ }
4471
+ /**
4472
+ * 检测文件格式
4473
+ * @param filePath - 文件路径
4474
+ * @returns 文件格式,如果不支持则返回 null
4475
+ */
4476
+ detectFileFormat(filePath) {
4477
+ const ext = path4.extname(filePath).toLowerCase();
4478
+ const supportedFormats = [
4479
+ ".json",
4480
+ ".yaml",
4481
+ ".yml",
4482
+ ".toml",
4483
+ ".xml",
4484
+ ".properties",
4485
+ ".ini"
4486
+ ];
4487
+ if (supportedFormats.includes(ext)) {
4488
+ return ext.substring(1);
4489
+ }
4490
+ return null;
4491
+ }
4492
+ /**
4493
+ * 从内容检测文件格式
4494
+ * @param content - 文件内容
4495
+ * @returns 文件格式,如果不支持则返回 null
4496
+ */
4497
+ detectFormatFromContent(content) {
4498
+ const trimmedContent = content.trim();
4499
+ if (trimmedContent.startsWith("{") || trimmedContent.startsWith("[")) {
4500
+ try {
4501
+ JSON.parse(trimmedContent);
4502
+ return "json";
4503
+ } catch {
4504
+ }
4505
+ }
4506
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*\s*:/.test(trimmedContent)) {
4507
+ return "yaml";
4508
+ }
4509
+ if (trimmedContent.startsWith("<?xml") || trimmedContent.startsWith("<")) {
4510
+ return "xml";
4511
+ }
4512
+ if (/^[a-zA-Z0-9_-]+\s*=\s*/.test(trimmedContent)) {
4513
+ return "properties";
4514
+ }
4515
+ return null;
4516
+ }
2834
4517
  /**
2835
4518
  * 输出详细日志
2836
4519
  */
@@ -2859,8 +4542,8 @@ var UnpackCommand = class {
2859
4542
  };
2860
4543
 
2861
4544
  // src/commands/pack-add.ts
2862
- import path4 from "path";
2863
- import fs5 from "fs/promises";
4545
+ import path5 from "path";
4546
+ import fs6 from "fs/promises";
2864
4547
  var PackAddCommand = class {
2865
4548
  /**
2866
4549
  * 执行 pack add 命令(支持多文件/目录添加)
@@ -2871,7 +4554,7 @@ var PackAddCommand = class {
2871
4554
  async execute(urpfFilePath, targetPaths, options = {}) {
2872
4555
  const startTime = Date.now();
2873
4556
  try {
2874
- const resolvedUrpfPath = path4.resolve(urpfFilePath);
4557
+ const resolvedUrpfPath = path5.resolve(urpfFilePath);
2875
4558
  await this.validateUrpfFile(resolvedUrpfPath);
2876
4559
  const actualUrpfPath = await findURPFFile(resolvedUrpfPath);
2877
4560
  if (actualUrpfPath === null) {
@@ -2881,7 +4564,7 @@ var PackAddCommand = class {
2881
4564
  if (paths.length === 0) {
2882
4565
  throw new Error("\u81F3\u5C11\u9700\u8981\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84");
2883
4566
  }
2884
- const urpfContent = await fs5.readFile(actualUrpfPath, "utf-8");
4567
+ const urpfContent = await fs6.readFile(actualUrpfPath, "utf-8");
2885
4568
  const parseResult = URPFParser.parse(urpfContent, { strict: false });
2886
4569
  const document = parseResult.document;
2887
4570
  const variableContext = document.propertySection ? VariableReplacer.createContext(document.propertySection.properties) : void 0;
@@ -2889,14 +4572,14 @@ var PackAddCommand = class {
2889
4572
  const allSkippedFiles = [];
2890
4573
  const allDirectories = [];
2891
4574
  for (const targetPath of paths) {
2892
- const resolvedTargetPath = path4.resolve(targetPath);
4575
+ const resolvedTargetPath = path5.resolve(targetPath);
2893
4576
  await this.validatePath(resolvedTargetPath);
2894
4577
  const scanResult = await this.scanFiles(resolvedTargetPath, options, variableContext);
2895
4578
  const filesWithBasePath = scanResult.files.map((file) => {
2896
- const isAbsolutePath = path4.isAbsolute(file.path);
4579
+ const isAbsolutePath = path5.isAbsolute(file.path);
2897
4580
  return {
2898
4581
  ...file,
2899
- basePath: isAbsolutePath ? path4.dirname(file.path) : resolvedTargetPath,
4582
+ basePath: isAbsolutePath ? path5.dirname(file.path) : resolvedTargetPath,
2900
4583
  isAbsolutePath
2901
4584
  };
2902
4585
  });
@@ -2919,44 +4602,33 @@ var PackAddCommand = class {
2919
4602
  }
2920
4603
  const serializer = new ResourceSerializer();
2921
4604
  const filesToAdd = allFiles.map((file) => {
4605
+ const fileWithBase = file;
2922
4606
  let absoluteFilePath;
2923
- if (file.isAbsolutePath) {
2924
- absoluteFilePath = file.path;
4607
+ if (fileWithBase.isAbsolutePath) {
4608
+ absoluteFilePath = fileWithBase.path;
2925
4609
  } else {
2926
- absoluteFilePath = path4.join(file.basePath, file.path);
4610
+ absoluteFilePath = path5.join(fileWithBase.basePath, fileWithBase.path);
2927
4611
  }
2928
- const relativePath = path4.relative(process.cwd(), absoluteFilePath);
4612
+ const relativePath = path5.relative(process.cwd(), absoluteFilePath);
2929
4613
  return {
2930
4614
  filePath: absoluteFilePath,
2931
- metadata: file,
4615
+ metadata: fileWithBase,
2932
4616
  udrsReference: `file://${relativePath}`
2933
4617
  };
2934
4618
  });
2935
4619
  const newResources = await serializer.serializeFiles(filesToAdd);
2936
- const generatorDoc = {
4620
+ const updatedDocument = {
2937
4621
  boundaryToken: document.boundaryToken,
2938
- properties: document.propertySection?.properties.map((p) => ({
2939
- name: p.name,
2940
- value: p.value,
2941
- isExtended: p.isExtended
2942
- })),
2943
- resources: document.resourceSections.map((rs) => ({
2944
- header: {
2945
- udrsReference: rs.header.udrsReference.protocol + "://" + rs.header.udrsReference.uri + (rs.header.udrsReference.fragment ? "#" + rs.header.udrsReference.fragment : ""),
2946
- encoding: rs.header.encoding,
2947
- permissions: rs.header.permissions,
2948
- newlineType: rs.header.newlineType
2949
- },
2950
- content: rs.content
2951
- }))
4622
+ propertySection: document.propertySection,
4623
+ resourceSections: [...document.resourceSections]
2952
4624
  };
2953
- generatorDoc.resources = [...generatorDoc.resources, ...newResources];
4625
+ updatedDocument.resourceSections = [...updatedDocument.resourceSections, ...newResources];
2954
4626
  const generator = new URPFGenerator({
2955
- boundaryToken: generatorDoc.boundaryToken,
4627
+ boundaryToken: document.boundaryToken,
2956
4628
  includeBoundaries: true
2957
4629
  });
2958
- const result = await generator.generate(generatorDoc);
2959
- await fs5.writeFile(actualUrpfPath, result.content, "utf-8");
4630
+ const result = await generator.generate(updatedDocument);
4631
+ await fs6.writeFile(actualUrpfPath, result.content, "utf-8");
2960
4632
  const duration = Date.now() - startTime;
2961
4633
  const totalBytes = allFiles.reduce((sum, f) => sum + f.size, 0);
2962
4634
  if (options.verbose) {
@@ -2982,7 +4654,7 @@ var PackAddCommand = class {
2982
4654
  }
2983
4655
  return {
2984
4656
  success: false,
2985
- urpfFilePath: path4.resolve(urpfFilePath),
4657
+ urpfFilePath: path5.resolve(urpfFilePath),
2986
4658
  addedCount: 0,
2987
4659
  skippedCount: 0,
2988
4660
  totalBytes: 0,
@@ -2996,7 +4668,7 @@ var PackAddCommand = class {
2996
4668
  */
2997
4669
  async validatePath(filePath) {
2998
4670
  try {
2999
- const stats = await fs5.stat(filePath);
4671
+ const stats = await fs6.stat(filePath);
3000
4672
  if (!stats.isFile() && !stats.isDirectory()) {
3001
4673
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${filePath}`);
3002
4674
  }
@@ -3018,7 +4690,7 @@ var PackAddCommand = class {
3018
4690
  }
3019
4691
  throw new Error(`\u65E0\u6CD5\u8BBF\u95EE URPF \u6587\u4EF6: ${filePath}`);
3020
4692
  }
3021
- const stats = await fs5.stat(actualPath);
4693
+ const stats = await fs6.stat(actualPath);
3022
4694
  if (!stats.isFile()) {
3023
4695
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
3024
4696
  }
@@ -3027,33 +4699,32 @@ var PackAddCommand = class {
3027
4699
  * 扫描文件
3028
4700
  */
3029
4701
  async scanFiles(rootPath, options, _variableContext) {
3030
- const stats = await fs5.stat(rootPath);
4702
+ const stats = await fs6.stat(rootPath);
3031
4703
  if (stats.isFile()) {
3032
4704
  const metadata = await this.collectFileMetadata(rootPath);
3033
4705
  return {
4706
+ rootPath: path5.dirname(rootPath),
3034
4707
  files: [metadata],
3035
4708
  directories: [],
3036
4709
  skippedFiles: [],
3037
- statistics: {
3038
- totalFiles: 1,
3039
- totalDirectories: 0,
3040
- skippedFiles: 0
3041
- }
4710
+ startTime: Date.now(),
4711
+ endTime: Date.now(),
4712
+ duration: 0
3042
4713
  };
3043
4714
  }
3044
4715
  let ignoreRules = [];
3045
4716
  if (options.ignoreFile) {
3046
4717
  const parser = new IgnoreRuleParser();
3047
- const content = await fs5.readFile(options.ignoreFile, "utf-8");
4718
+ const content = await fs6.readFile(options.ignoreFile, "utf-8");
3048
4719
  ignoreRules = parser.parseFile(content);
3049
4720
  } else {
3050
4721
  const defaultIgnoreFiles = [".gitignore", ".iflowignore"];
3051
4722
  for (const ignoreFile of defaultIgnoreFiles) {
3052
- const ignorePath = path4.join(rootPath, ignoreFile);
4723
+ const ignorePath = path5.join(rootPath, ignoreFile);
3053
4724
  try {
3054
- await fs5.access(ignorePath);
4725
+ await fs6.access(ignorePath);
3055
4726
  const parser = new IgnoreRuleParser();
3056
- const content = await fs5.readFile(ignorePath, "utf-8");
4727
+ const content = await fs6.readFile(ignorePath, "utf-8");
3057
4728
  ignoreRules = [...ignoreRules, ...parser.parseFile(content)];
3058
4729
  break;
3059
4730
  } catch {
@@ -3071,22 +4742,26 @@ var PackAddCommand = class {
3071
4742
  * 收集文件元数据
3072
4743
  */
3073
4744
  async collectFileMetadata(filePath) {
3074
- const stats = await fs5.stat(filePath);
4745
+ const stats = await fs6.stat(filePath);
3075
4746
  return {
3076
4747
  path: filePath,
3077
- name: path4.basename(filePath),
4748
+ name: path5.basename(filePath),
4749
+ isDirectory: false,
4750
+ isSymlink: false,
3078
4751
  size: stats.size,
3079
- permissions: stats.mode,
3080
- mtime: stats.mtime,
4752
+ permissions: stats.mode.toString(8).padStart(3, "0"),
4753
+ mtime: stats.mtime.getTime(),
4754
+ ctime: stats.ctime.getTime(),
3081
4755
  encoding: "utf-8",
3082
- newlineType: "lf"
4756
+ newlineType: "lf",
4757
+ isBinary: false
3083
4758
  };
3084
4759
  }
3085
4760
  };
3086
4761
 
3087
4762
  // src/commands/pack-remove.ts
3088
- import path5 from "path";
3089
- import fs6 from "fs/promises";
4763
+ import path6 from "path";
4764
+ import fs7 from "fs/promises";
3090
4765
  var PackRemoveCommand = class {
3091
4766
  /**
3092
4767
  * 执行 pack remove 命令(支持多文件移除)
@@ -3097,7 +4772,7 @@ var PackRemoveCommand = class {
3097
4772
  async execute(urpfFilePath, filenamePatterns, options = {}) {
3098
4773
  const startTime = Date.now();
3099
4774
  try {
3100
- const resolvedUrpfPath = path5.resolve(urpfFilePath);
4775
+ const resolvedUrpfPath = path6.resolve(urpfFilePath);
3101
4776
  await this.validateUrpfFile(resolvedUrpfPath);
3102
4777
  const actualUrpfPath = await findURPFFile(resolvedUrpfPath);
3103
4778
  if (actualUrpfPath === null) {
@@ -3107,26 +4782,14 @@ var PackRemoveCommand = class {
3107
4782
  if (patterns.length === 0) {
3108
4783
  throw new Error("\u81F3\u5C11\u9700\u8981\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u540D\u6216\u6A21\u5F0F");
3109
4784
  }
3110
- const urpfContent = await fs6.readFile(actualUrpfPath, "utf-8");
4785
+ const urpfContent = await fs7.readFile(actualUrpfPath, "utf-8");
3111
4786
  const parseResult = URPFParser.parse(urpfContent, { strict: false });
3112
4787
  const document = parseResult.document;
3113
4788
  const variableContext = document.propertySection ? VariableReplacer.createContext(document.propertySection.properties) : void 0;
3114
- const generatorDoc = {
4789
+ const updatedDocument = {
3115
4790
  boundaryToken: document.boundaryToken,
3116
- properties: document.propertySection?.properties.map((p) => ({
3117
- name: p.name,
3118
- value: p.value,
3119
- isExtended: p.isExtended
3120
- })),
3121
- resources: document.resourceSections.map((rs) => ({
3122
- header: {
3123
- udrsReference: rs.header.udrsReference.protocol + "://" + rs.header.udrsReference.uri + (rs.header.udrsReference.fragment ? "#" + rs.header.udrsReference.fragment : ""),
3124
- encoding: rs.header.encoding,
3125
- permissions: rs.header.permissions,
3126
- newlineType: rs.header.newlineType
3127
- },
3128
- content: rs.content
3129
- }))
4791
+ propertySection: document.propertySection,
4792
+ resourceSections: [...document.resourceSections]
3130
4793
  };
3131
4794
  const removedResources = [];
3132
4795
  let notFoundCount = 0;
@@ -3145,7 +4808,7 @@ var PackRemoveCommand = class {
3145
4808
  throw error;
3146
4809
  }
3147
4810
  }
3148
- const { removedResources: removed, notFoundCount: notFound } = this.removeResources(generatorDoc, processedPattern);
4811
+ const { removedResources: removed, notFoundCount: notFound } = this.removeResources(updatedDocument, processedPattern);
3149
4812
  removedResources.push(...removed);
3150
4813
  notFoundCount += notFound;
3151
4814
  if (options.verbose) {
@@ -3162,11 +4825,11 @@ var PackRemoveCommand = class {
3162
4825
  };
3163
4826
  }
3164
4827
  const generator = new URPFGenerator({
3165
- boundaryToken: generatorDoc.boundaryToken,
4828
+ boundaryToken: document.boundaryToken,
3166
4829
  includeBoundaries: true
3167
4830
  });
3168
- const result = await generator.generate(generatorDoc);
3169
- await fs6.writeFile(actualUrpfPath, result.content, "utf-8");
4831
+ const result = await generator.generate(updatedDocument);
4832
+ await fs7.writeFile(actualUrpfPath, result.content, "utf-8");
3170
4833
  const duration = Date.now() - startTime;
3171
4834
  if (options.verbose) {
3172
4835
  console.log(`Successfully removed ${removedResources.length} files: ${actualUrpfPath}`);
@@ -3192,7 +4855,7 @@ var PackRemoveCommand = class {
3192
4855
  }
3193
4856
  return {
3194
4857
  success: false,
3195
- urpfFilePath: path5.resolve(urpfFilePath),
4858
+ urpfFilePath: path6.resolve(urpfFilePath),
3196
4859
  removedCount: 0,
3197
4860
  notFoundCount: 0,
3198
4861
  duration,
@@ -3214,25 +4877,25 @@ var PackRemoveCommand = class {
3214
4877
  }
3215
4878
  throw new Error(`\u65E0\u6CD5\u8BBF\u95EE URPF \u6587\u4EF6: ${filePath}`);
3216
4879
  }
3217
- const stats = await fs6.stat(actualPath);
4880
+ const stats = await fs7.stat(actualPath);
3218
4881
  if (!stats.isFile()) {
3219
4882
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}`);
3220
4883
  }
3221
4884
  }
3222
4885
  /**
3223
- * 从文档中移除匹配的资源
3224
- */
4886
+ * 从文档中移除匹配的资源
4887
+ */
3225
4888
  removeResources(document, pattern) {
3226
4889
  const removedResources = [];
3227
4890
  let notFoundCount = 0;
3228
4891
  const normalizedPattern = pattern.startsWith("/") || pattern.startsWith("\\") ? pattern.slice(1) : pattern;
3229
4892
  const normalizedPatternWithSlash = normalizedPattern.replace(/\\/g, "/");
3230
- for (let i = document.resources.length - 1; i >= 0; i--) {
3231
- const resource = document.resources[i];
3232
- const udrsRefString = resource.header.udrsReference;
4893
+ for (let i = document.resourceSections.length - 1; i >= 0; i--) {
4894
+ const resource = document.resourceSections[i];
4895
+ const udrsRefString = resource.header.udrsReference.protocol + "://" + resource.header.udrsReference.uri + (resource.header.udrsReference.fragment ? "#" + resource.header.udrsReference.fragment : "");
3233
4896
  if (this.matchResource(udrsRefString, normalizedPatternWithSlash)) {
3234
4897
  removedResources.push(resource);
3235
- document.resources.splice(i, 1);
4898
+ document.resourceSections.splice(i, 1);
3236
4899
  }
3237
4900
  }
3238
4901
  if (removedResources.length === 0) {
@@ -3260,7 +4923,7 @@ var PackRemoveCommand = class {
3260
4923
  };
3261
4924
 
3262
4925
  // src/commands/list.ts
3263
- import fs7 from "fs/promises";
4926
+ import fs8 from "fs/promises";
3264
4927
  function wildcardToRegex2(pattern) {
3265
4928
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
3266
4929
  const regexPattern = "^" + escaped.replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
@@ -3334,7 +4997,7 @@ var ListCommand = class {
3334
4997
  error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
3335
4998
  };
3336
4999
  }
3337
- const content = await fs7.readFile(actualFilePath, "utf-8");
5000
+ const content = await fs8.readFile(actualFilePath, "utf-8");
3338
5001
  let parseResult;
3339
5002
  try {
3340
5003
  parseResult = URPFParser.parse(content, { strict: false });
@@ -3443,7 +5106,7 @@ var ListCommand = class {
3443
5106
  };
3444
5107
 
3445
5108
  // src/commands/info.ts
3446
- import fs8 from "fs/promises";
5109
+ import fs9 from "fs/promises";
3447
5110
  function formatFileSize2(bytes) {
3448
5111
  if (bytes === 0) return "0 B";
3449
5112
  const k = 1024;
@@ -3513,7 +5176,7 @@ var InfoCommand = class {
3513
5176
  error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
3514
5177
  };
3515
5178
  }
3516
- const content = await fs8.readFile(actualFilePath, "utf-8");
5179
+ const content = await fs9.readFile(actualFilePath, "utf-8");
3517
5180
  let parseResult;
3518
5181
  try {
3519
5182
  parseResult = URPFParser.parse(content, { strict: false });
@@ -3534,21 +5197,23 @@ var InfoCommand = class {
3534
5197
  error: `\u89E3\u6790\u5931\u8D25: ${errorMessage}`
3535
5198
  };
3536
5199
  }
5200
+ const versionProp = parseResult.document.propertySection?.properties?.find((p) => p.name === "version");
5201
+ const version = versionProp ? versionProp.value : "1.0";
3537
5202
  const info = {
3538
5203
  fileCount: parseResult.document.resourceSections.length,
3539
5204
  totalSize: parseResult.document.resourceSections.reduce((sum, section) => sum + section.content.length, 0),
3540
- version: parseResult.document.propertySection?.properties.version || "1.0",
5205
+ version,
3541
5206
  boundaryToken: parseResult.document.boundaryToken,
3542
- propertyCount: parseResult.document.propertySection?.properties ? Object.keys(parseResult.document.propertySection.properties).length : 0,
5207
+ propertyCount: parseResult.document.propertySection?.properties?.length || 0,
3543
5208
  resourceCount: parseResult.document.resourceSections.length
3544
5209
  };
3545
5210
  if (options.verbose) {
3546
5211
  if (parseResult.document.propertySection?.properties) {
3547
- const properties = {};
5212
+ const properties2 = {};
3548
5213
  for (const prop of parseResult.document.propertySection.properties) {
3549
- properties[prop.name] = prop.value;
5214
+ properties2[prop.name] = prop.value;
3550
5215
  }
3551
- info.properties = properties;
5216
+ info.properties = properties2;
3552
5217
  }
3553
5218
  const textFiles = parseResult.document.resourceSections.filter((s) => s.header.encoding !== "binary").length;
3554
5219
  const binaryFiles = parseResult.document.resourceSections.filter((s) => s.header.encoding === "binary").length;
@@ -3608,7 +5273,7 @@ var InfoCommand = class {
3608
5273
  };
3609
5274
 
3610
5275
  // src/commands/verify.ts
3611
- import fs9 from "fs/promises";
5276
+ import fs10 from "fs/promises";
3612
5277
  var URPFVerifier = class {
3613
5278
  constructor() {
3614
5279
  this.issues = [];
@@ -3697,7 +5362,7 @@ var URPFVerifier = class {
3697
5362
  /**
3698
5363
  * 验证属性部分
3699
5364
  */
3700
- verifyProperties(content, boundaryToken) {
5365
+ verifyProperties(content, _boundaryToken) {
3701
5366
  try {
3702
5367
  const boundaries = BoundaryParser.findBoundaries(content);
3703
5368
  const sections = BoundaryParser.splitByBoundaries(content, boundaries);
@@ -3716,13 +5381,12 @@ var URPFVerifier = class {
3716
5381
  try {
3717
5382
  const propertySection = PropertyParser.parsePropertySection(sections[0], boundaries[0].line + 1);
3718
5383
  const propertyNames = /* @__PURE__ */ new Set();
3719
- for (const property of propertySection.properties) {
5384
+ for (const property of properties) {
3720
5385
  if (propertyNames.has(property.name)) {
3721
5386
  this.addIssue({
3722
5387
  type: "property_duplicate",
3723
5388
  message: `\u91CD\u590D\u7684\u5C5E\u6027\u540D: ${property.name}`,
3724
- severity: "error",
3725
- line: property.line
5389
+ severity: "error"
3726
5390
  }, false);
3727
5391
  }
3728
5392
  propertyNames.add(property.name);
@@ -3782,18 +5446,6 @@ var URPFVerifier = class {
3782
5446
  line: boundaries[i].line + 1
3783
5447
  });
3784
5448
  }
3785
- const expectedSize = parseInt(resourceSection.header.udrsReference.attributes.size || "0", 10);
3786
- const actualSize = resourceSection.content.length;
3787
- if (expectedSize > 0 && actualSize !== expectedSize) {
3788
- this.addIssue({
3789
- type: "resource_size_mismatch",
3790
- message: `\u6587\u4EF6\u5927\u5C0F\u4E0D\u5339\u914D: ${resourceSection.header.udrsReference.uri}`,
3791
- severity: "warning",
3792
- filePath: resourceSection.header.udrsReference.uri,
3793
- expected: expectedSize.toString(),
3794
- actual: actualSize.toString()
3795
- }, true);
3796
- }
3797
5449
  const encoding = resourceSection.header.encoding;
3798
5450
  if (!encoding) {
3799
5451
  this.addIssue({
@@ -3824,38 +5476,8 @@ var URPFVerifier = class {
3824
5476
  /**
3825
5477
  * 尝试修复问题
3826
5478
  */
3827
- async tryFix(content, issues) {
3828
- let fixedContent = content;
3829
- for (const issue of issues) {
3830
- if (!issue.fixable) {
3831
- continue;
3832
- }
3833
- if (issue.type === "resource_size_mismatch" && issue.filePath) {
3834
- try {
3835
- const boundaries = BoundaryParser.findBoundaries(fixedContent);
3836
- const sections = BoundaryParser.splitByBoundaries(fixedContent, boundaries);
3837
- let startSectionIndex = 0;
3838
- if (sections.length > 0 && PropertyParser.isPropertySection(sections[0])) {
3839
- startSectionIndex = 1;
3840
- }
3841
- for (let i = startSectionIndex; i < sections.length; i++) {
3842
- try {
3843
- const resourceSection = ResourceParser.parseResourceSection(sections[i], boundaries[i].line + 1);
3844
- if (resourceSection.header.udrsReference.uri === issue.filePath) {
3845
- const actualSize = resourceSection.content.length;
3846
- const sizePattern = new RegExp(`(size=${issue.expected})`, "g");
3847
- fixedContent = fixedContent.replace(sizePattern, `size=${actualSize}`);
3848
- this.fixedCount++;
3849
- break;
3850
- }
3851
- } catch {
3852
- }
3853
- }
3854
- } catch {
3855
- }
3856
- }
3857
- }
3858
- return fixedContent;
5479
+ async tryFix(content, _issues) {
5480
+ return content;
3859
5481
  }
3860
5482
  /**
3861
5483
  * 执行验证
@@ -3967,11 +5589,11 @@ var VerifyCommand = class {
3967
5589
  error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
3968
5590
  };
3969
5591
  }
3970
- let content = await fs9.readFile(actualFilePath, "utf-8");
5592
+ const content = await fs10.readFile(actualFilePath, "utf-8");
3971
5593
  const verifier = new URPFVerifier();
3972
5594
  const verifyResult = await verifier.verify(content, options);
3973
5595
  if (options.fix && verifyResult.fixedContent && verifyResult.fixedCount > 0) {
3974
- await fs9.writeFile(actualFilePath, verifyResult.fixedContent, "utf-8");
5596
+ await fs10.writeFile(actualFilePath, verifyResult.fixedContent, "utf-8");
3975
5597
  }
3976
5598
  let totalFiles = 0;
3977
5599
  let validFiles = 0;
@@ -4031,13 +5653,13 @@ var VerifyCommand = class {
4031
5653
  };
4032
5654
 
4033
5655
  // src/commands/diff.ts
4034
- import fs10 from "fs/promises";
5656
+ import fs11 from "fs/promises";
4035
5657
  async function extractFileInfo(filePath) {
4036
- const content = await fs10.readFile(filePath, "utf-8");
5658
+ const content = await fs11.readFile(filePath, "utf-8");
4037
5659
  let parseResult;
4038
5660
  try {
4039
5661
  parseResult = URPFParser.parse(content, { strict: false });
4040
- } catch (parseError) {
5662
+ } catch {
4041
5663
  return [];
4042
5664
  }
4043
5665
  if (!parseResult.document.resourceSections || parseResult.document.resourceSections.length === 0) {
@@ -4068,19 +5690,19 @@ function compareFileLists(files1, files2) {
4068
5690
  const removed = [];
4069
5691
  const modified = [];
4070
5692
  const unchanged = [];
4071
- for (const path7 of allPaths) {
4072
- const file1 = fileMap1.get(path7);
4073
- const file2 = fileMap2.get(path7);
5693
+ for (const path8 of allPaths) {
5694
+ const file1 = fileMap1.get(path8);
5695
+ const file2 = fileMap2.get(path8);
4074
5696
  if (!file1 && file2) {
4075
5697
  added.push({
4076
- path: path7,
5698
+ path: path8,
4077
5699
  changeType: "added",
4078
5700
  size2: file2.size,
4079
5701
  encoding2: file2.encoding
4080
5702
  });
4081
5703
  } else if (file1 && !file2) {
4082
5704
  removed.push({
4083
- path: path7,
5705
+ path: path8,
4084
5706
  changeType: "removed",
4085
5707
  size1: file1.size,
4086
5708
  encoding1: file1.encoding
@@ -4088,7 +5710,7 @@ function compareFileLists(files1, files2) {
4088
5710
  } else if (file1 && file2) {
4089
5711
  if (file1.size !== file2.size || file1.encoding !== file2.encoding) {
4090
5712
  modified.push({
4091
- path: path7,
5713
+ path: path8,
4092
5714
  changeType: "modified",
4093
5715
  size1: file1.size,
4094
5716
  size2: file2.size,
@@ -4098,7 +5720,7 @@ function compareFileLists(files1, files2) {
4098
5720
  });
4099
5721
  } else {
4100
5722
  unchanged.push({
4101
- path: path7,
5723
+ path: path8,
4102
5724
  changeType: "unchanged",
4103
5725
  size1: file1.size,
4104
5726
  size2: file2.size,
@@ -4280,8 +5902,8 @@ var DiffCommand = class {
4280
5902
  };
4281
5903
 
4282
5904
  // src/commands/merge.ts
4283
- import path6 from "path";
4284
- import fs11 from "fs/promises";
5905
+ import path7 from "path";
5906
+ import fs12 from "fs/promises";
4285
5907
  var MergeCommand = class {
4286
5908
  /**
4287
5909
  * 执行 merge 命令
@@ -4302,10 +5924,10 @@ var MergeCommand = class {
4302
5924
  parsedDocuments,
4303
5925
  options.verbose
4304
5926
  );
4305
- const resolvedOutputPath = path6.resolve(options.output);
4306
- await fs11.mkdir(path6.dirname(resolvedOutputPath), { recursive: true });
4307
- await fs11.writeFile(resolvedOutputPath, outputContent, "utf-8");
4308
- const outputStats = await fs11.stat(resolvedOutputPath);
5927
+ const resolvedOutputPath = path7.resolve(options.output);
5928
+ await fs12.mkdir(path7.dirname(resolvedOutputPath), { recursive: true });
5929
+ await fs12.writeFile(resolvedOutputPath, outputContent, "utf-8");
5930
+ const outputStats = await fs12.stat(resolvedOutputPath);
4309
5931
  const duration = Date.now() - startTime;
4310
5932
  const result = {
4311
5933
  success: true,
@@ -4364,7 +5986,7 @@ var MergeCommand = class {
4364
5986
  async resolveInputFiles(inputPaths) {
4365
5987
  const resolvedFiles = [];
4366
5988
  for (const inputPath of inputPaths) {
4367
- const resolvedPath = path6.resolve(inputPath);
5989
+ const resolvedPath = path7.resolve(inputPath);
4368
5990
  const actualPath = await findURPFFile(resolvedPath);
4369
5991
  if (!actualPath) {
4370
5992
  throw new Error(`\u627E\u4E0D\u5230 URPF \u6587\u4EF6: ${inputPath}`);
@@ -4380,7 +6002,7 @@ var MergeCommand = class {
4380
6002
  const documents = [];
4381
6003
  for (const filePath of filePaths) {
4382
6004
  try {
4383
- const content = await fs11.readFile(filePath, "utf-8");
6005
+ const content = await fs12.readFile(filePath, "utf-8");
4384
6006
  const parseResult = URPFParser.parse(content);
4385
6007
  documents.push({
4386
6008
  filePath,
@@ -4412,7 +6034,7 @@ var MergeCommand = class {
4412
6034
  const resourceSections = document.resourceSections || [];
4413
6035
  if (verbose) {
4414
6036
  console.log(`
4415
- \u5904\u7406\u6587\u4EF6: ${path6.basename(filePath)} (${resourceSections.length} \u4E2A\u8D44\u6E90)`);
6037
+ \u5904\u7406\u6587\u4EF6: ${path7.basename(filePath)} (${resourceSections.length} \u4E2A\u8D44\u6E90)`);
4416
6038
  }
4417
6039
  for (const section of resourceSections) {
4418
6040
  const udrsRef = section.header.udrsReference;
@@ -4463,9 +6085,9 @@ var MergeCommand = class {
4463
6085
  let counter = 1;
4464
6086
  let newPath;
4465
6087
  do {
4466
- const ext = path6.extname(udrsReference);
4467
- const baseName = path6.basename(udrsReference, ext);
4468
- const dirName = path6.dirname(udrsReference);
6088
+ const ext = path7.extname(udrsReference);
6089
+ const baseName = path7.basename(udrsReference, ext);
6090
+ const dirName = path7.dirname(udrsReference);
4469
6091
  const newBaseName = `${baseName}-${counter}`;
4470
6092
  newPath = dirName === "." ? `${newBaseName}${ext}` : `${dirName}/${newBaseName}${ext}`;
4471
6093
  counter++;
@@ -4496,11 +6118,14 @@ var MergeCommand = class {
4496
6118
  */
4497
6119
  async generateMergedURPF(mergedResources, sourceDocuments, verbose) {
4498
6120
  const propertySection = sourceDocuments[0]?.document?.propertySection;
4499
- const properties = propertySection?.properties || [];
6121
+ const properties2 = propertySection?.properties || [];
4500
6122
  const generatorResources = mergedResources.map((resource) => {
4501
6123
  return {
4502
6124
  header: {
4503
- udrsReference: resource.udrsReference,
6125
+ udrsReference: {
6126
+ protocol: "file",
6127
+ uri: resource.udrsReference
6128
+ },
4504
6129
  encoding: resource.encoding,
4505
6130
  permissions: resource.permissions,
4506
6131
  newlineType: resource.newlineType
@@ -4510,18 +6135,23 @@ var MergeCommand = class {
4510
6135
  });
4511
6136
  const generator = new URPFGenerator();
4512
6137
  const document = {
4513
- properties: properties.map((prop) => ({
4514
- name: prop.name,
4515
- value: prop.value
4516
- })),
4517
- resources: generatorResources
6138
+ boundaryToken: "",
6139
+ // 将由生成器自动生成
6140
+ propertySection: {
6141
+ properties: properties2.map((prop) => ({
6142
+ name: prop.name,
6143
+ value: prop.value,
6144
+ isExtended: false
6145
+ }))
6146
+ },
6147
+ resourceSections: generatorResources
4518
6148
  };
4519
6149
  const result = await generator.generate(document);
4520
6150
  if (verbose) {
4521
6151
  console.log(`
4522
6152
  \u751F\u6210\u5B8C\u6210:`);
4523
6153
  console.log(` \u8FB9\u754C\u4EE4\u724C: ${generator.getBoundaryToken()}`);
4524
- console.log(` \u5C5E\u6027\u6570\u91CF: ${properties.length}`);
6154
+ console.log(` \u5C5E\u6027\u6570\u91CF: ${properties2.length}`);
4525
6155
  console.log(` \u8D44\u6E90\u6570\u91CF: ${generatorResources.length}`);
4526
6156
  }
4527
6157
  return result.content;
@@ -4544,17 +6174,17 @@ var MergeCommand = class {
4544
6174
  };
4545
6175
 
4546
6176
  // src/commands/var.ts
4547
- import * as fs12 from "fs/promises";
6177
+ import * as fs13 from "fs/promises";
4548
6178
  function parseResultToDocument(parseResult) {
4549
- const properties = {};
6179
+ const properties2 = {};
4550
6180
  if (parseResult.document.propertySection) {
4551
6181
  for (const prop of parseResult.document.propertySection.properties) {
4552
- properties[prop.name] = prop.value;
6182
+ properties2[prop.name] = prop.value;
4553
6183
  }
4554
6184
  }
4555
6185
  return {
4556
6186
  boundaryToken: parseResult.document.boundaryToken,
4557
- properties,
6187
+ properties: properties2,
4558
6188
  resources: parseResult.document.resourceSections
4559
6189
  };
4560
6190
  }
@@ -4585,7 +6215,7 @@ async function executeVarCommand(options) {
4585
6215
  error: `URPF \u6587\u4EF6\u4E0D\u5B58\u5728: ${options.filePath}`
4586
6216
  };
4587
6217
  }
4588
- const urpfContent = await fs12.readFile(filePath, "utf-8");
6218
+ const urpfContent = await fs13.readFile(filePath, "utf-8");
4589
6219
  const parseResult = URPFParser.parse(urpfContent);
4590
6220
  const urpf = parseResultToDocument(parseResult);
4591
6221
  let result;
@@ -4664,7 +6294,7 @@ async function varAdd(urpf, filePath, options) {
4664
6294
  const urpfDocument = documentToURPFDocument(urpf);
4665
6295
  const generateResult = await generator.generate(urpfDocument);
4666
6296
  const newURPFContent = generateResult.content;
4667
- await fs12.writeFile(filePath, newURPFContent, "utf-8");
6297
+ await fs13.writeFile(filePath, newURPFContent, "utf-8");
4668
6298
  return {
4669
6299
  success: true,
4670
6300
  action: "add",
@@ -4677,8 +6307,9 @@ async function varAdd(urpf, filePath, options) {
4677
6307
  async function varList(urpf, filePath, _options) {
4678
6308
  const startTime = Date.now();
4679
6309
  const variables = {};
6310
+ const systemProperties = ["version", "boundary", "encoding", "line-ending"];
4680
6311
  for (const [key, value] of Object.entries(urpf.properties)) {
4681
- if (!key.startsWith("env:")) {
6312
+ if (!key.startsWith("env:") && !systemProperties.includes(key)) {
4682
6313
  variables[key] = value;
4683
6314
  }
4684
6315
  }
@@ -4716,7 +6347,7 @@ async function varRemove(urpf, filePath, options) {
4716
6347
  const urpfDocument = documentToURPFDocument(urpf);
4717
6348
  const generateResult = await generator.generate(urpfDocument);
4718
6349
  const newURPFContent = generateResult.content;
4719
- await fs12.writeFile(filePath, newURPFContent, "utf-8");
6350
+ await fs13.writeFile(filePath, newURPFContent, "utf-8");
4720
6351
  return {
4721
6352
  success: true,
4722
6353
  action: "remove",
@@ -4747,7 +6378,7 @@ async function varClear(urpf, filePath, _options) {
4747
6378
  const urpfDocument = documentToURPFDocument(urpf);
4748
6379
  const generateResult = await generator.generate(urpfDocument);
4749
6380
  const newURPFContent = generateResult.content;
4750
- await fs12.writeFile(filePath, newURPFContent, "utf-8");
6381
+ await fs13.writeFile(filePath, newURPFContent, "utf-8");
4751
6382
  return {
4752
6383
  success: true,
4753
6384
  action: "clear",
@@ -4758,17 +6389,17 @@ async function varClear(urpf, filePath, _options) {
4758
6389
  }
4759
6390
 
4760
6391
  // src/commands/env.ts
4761
- import * as fs13 from "fs/promises";
6392
+ import * as fs14 from "fs/promises";
4762
6393
  function parseResultToDocument2(parseResult) {
4763
- const properties = {};
6394
+ const properties2 = {};
4764
6395
  if (parseResult.document.propertySection) {
4765
6396
  for (const prop of parseResult.document.propertySection.properties) {
4766
- properties[prop.name] = prop.value;
6397
+ properties2[prop.name] = prop.value;
4767
6398
  }
4768
6399
  }
4769
6400
  return {
4770
6401
  boundaryToken: parseResult.document.boundaryToken,
4771
- properties,
6402
+ properties: properties2,
4772
6403
  resources: parseResult.document.resourceSections
4773
6404
  };
4774
6405
  }
@@ -4799,7 +6430,7 @@ async function executeEnvCommand(options) {
4799
6430
  error: `URPF \u6587\u4EF6\u4E0D\u5B58\u5728: ${options.filePath}`
4800
6431
  };
4801
6432
  }
4802
- const urpfContent = await fs13.readFile(filePath, "utf-8");
6433
+ const urpfContent = await fs14.readFile(filePath, "utf-8");
4803
6434
  const parseResult = URPFParser.parse(urpfContent);
4804
6435
  const urpf = parseResultToDocument2(parseResult);
4805
6436
  let result;
@@ -4873,7 +6504,7 @@ async function envAdd(urpf, filePath, options) {
4873
6504
  const urpfDocument = documentToURPFDocument2(urpf);
4874
6505
  const generateResult = await generator.generate(urpfDocument);
4875
6506
  const newURPFContent = generateResult.content;
4876
- await fs13.writeFile(filePath, newURPFContent, "utf-8");
6507
+ await fs14.writeFile(filePath, newURPFContent, "utf-8");
4877
6508
  return {
4878
6509
  success: true,
4879
6510
  action: "add",
@@ -4928,7 +6559,7 @@ async function envRemove(urpf, filePath, options) {
4928
6559
  const urpfDocument = documentToURPFDocument2(urpf);
4929
6560
  const generateResult = await generator.generate(urpfDocument);
4930
6561
  const newURPFContent = generateResult.content;
4931
- await fs13.writeFile(filePath, newURPFContent, "utf-8");
6562
+ await fs14.writeFile(filePath, newURPFContent, "utf-8");
4932
6563
  return {
4933
6564
  success: true,
4934
6565
  action: "remove",
@@ -4959,7 +6590,7 @@ async function envClear(urpf, filePath, _options) {
4959
6590
  const urpfDocument = documentToURPFDocument2(urpf);
4960
6591
  const generateResult = await generator.generate(urpfDocument);
4961
6592
  const newURPFContent = generateResult.content;
4962
- await fs13.writeFile(filePath, newURPFContent, "utf-8");
6593
+ await fs14.writeFile(filePath, newURPFContent, "utf-8");
4963
6594
  return {
4964
6595
  success: true,
4965
6596
  action: "clear",
@@ -4969,6 +6600,682 @@ async function envClear(urpf, filePath, _options) {
4969
6600
  };
4970
6601
  }
4971
6602
 
6603
+ // src/commands/completion.ts
6604
+ function generateBashCompletion() {
6605
+ const script = `#!/usr/bin/env bash
6606
+
6607
+ ###-begin-urpf-cli-completions-###
6608
+ #
6609
+ # urpf-cli command completion script
6610
+ #
6611
+ # Installation: urpf-cli completion bash >> ~/.bashrc
6612
+ # or: urpf-cli completion bash >> ~/.bash_profile on macOS
6613
+ #
6614
+
6615
+ _urpf_cli_completion() {
6616
+ local cur words cword
6617
+ _init_completion || return
6618
+
6619
+ # urpf-cli \u4E3B\u547D\u4EE4
6620
+ if [[ $cword -eq 1 ]]; then
6621
+ COMPREPLY=($(compgen -W "pack unpack list info verify diff merge var env help --help --version" -- "$cur"))
6622
+ return
6623
+ fi
6624
+
6625
+ # pack \u5B50\u547D\u4EE4
6626
+ if [[ "$words[1]" == "pack" ]]; then
6627
+ if [[ $cword -eq 2 ]]; then
6628
+ COMPREPLY=($(compgen -W "create add remove help" -- "$cur"))
6629
+ return
6630
+ fi
6631
+
6632
+ if [[ "$words[2]" == "create" ]]; then
6633
+ # pack create \u5B50\u547D\u4EE4
6634
+ if [[ $cword -eq 3 ]]; then
6635
+ _filedir
6636
+ return
6637
+ fi
6638
+ # pack create \u9009\u9879\u8865\u5168
6639
+ COMPREPLY=($(compgen -W "-o --output -v --verbose -i --ignore -f --follow-symlinks -d --max-depth --no-recurse --force --byte --line -s --stdout -c --clipboard -e --env --key --format --validate --help" -- "$cur"))
6640
+ return
6641
+ fi
6642
+
6643
+ if [[ "$words[2]" == "add" ]]; then
6644
+ # pack add \u5B50\u547D\u4EE4
6645
+ if [[ $cword -eq 3 ]]; then
6646
+ _filedir
6647
+ return
6648
+ fi
6649
+ if [[ $cword -ge 4 ]]; then
6650
+ _filedir
6651
+ return
6652
+ fi
6653
+ # pack add \u9009\u9879\u8865\u5168
6654
+ COMPREPLY=($(compgen -W "-v --verbose -i --ignore -f --follow-symlinks -d --max-depth --no-recurse --help" -- "$cur"))
6655
+ return
6656
+ fi
6657
+
6658
+ if [[ "$words[2]" == "remove" ]]; then
6659
+ # pack remove \u5B50\u547D\u4EE4
6660
+ if [[ $cword -eq 3 ]]; then
6661
+ _filedir
6662
+ return
6663
+ fi
6664
+ if [[ $cword -ge 4 ]]; then
6665
+ _filedir
6666
+ return
6667
+ fi
6668
+ # pack remove \u9009\u9879\u8865\u5168
6669
+ COMPREPLY=($(compgen -W "-v --verbose --no-apply-variables --help" -- "$cur"))
6670
+ return
6671
+ fi
6672
+ fi
6673
+
6674
+ # unpack \u5B50\u547D\u4EE4
6675
+ if [[ "$words[1]" == "unpack" ]]; then
6676
+ if [[ $cword -eq 2 ]]; then
6677
+ _filedir
6678
+ return
6679
+ fi
6680
+ # unpack \u9009\u9879\u8865\u5168
6681
+ COMPREPLY=($(compgen -W "-o --output -v --verbose --force --force-when-newer --preserve-mtime --unpack-env --apply-variables --packed-path --key --format --validate --help" -- "$cur"))
6682
+ return
6683
+ fi
6684
+
6685
+ # list \u5B50\u547D\u4EE4
6686
+ if [[ "$words[1]" == "list" ]]; then
6687
+ if [[ $cword -eq 2 ]]; then
6688
+ _filedir
6689
+ return
6690
+ fi
6691
+ COMPREPLY=($(compgen -W "-v --verbose -p --pattern -j --json --help" -- "$cur"))
6692
+ return
6693
+ fi
6694
+
6695
+ # info \u5B50\u547D\u4EE4
6696
+ if [[ "$words[1]" == "info" ]]; then
6697
+ if [[ $cword -eq 2 ]]; then
6698
+ _filedir
6699
+ return
6700
+ fi
6701
+ COMPREPLY=($(compgen -W "-v --verbose -j --json --help" -- "$cur"))
6702
+ return
6703
+ fi
6704
+
6705
+ # verify \u5B50\u547D\u4EE4
6706
+ if [[ "$words[1]" == "verify" ]]; then
6707
+ if [[ $cword -eq 2 ]]; then
6708
+ _filedir
6709
+ return
6710
+ fi
6711
+ COMPREPLY=($(compgen -W "-f --fix -v --verbose -j --json --help" -- "$cur"))
6712
+ return
6713
+ fi
6714
+
6715
+ # diff \u5B50\u547D\u4EE4
6716
+ if [[ "$words[1]" == "diff" ]]; then
6717
+ if [[ $cword -eq 2 || $cword -eq 3 ]]; then
6718
+ _filedir
6719
+ return
6720
+ fi
6721
+ COMPREPLY=($(compgen -W "-v --verbose -j --json --help" -- "$cur"))
6722
+ return
6723
+ fi
6724
+
6725
+ # merge \u5B50\u547D\u4EE4
6726
+ if [[ "$words[1]" == "merge" ]]; then
6727
+ if [[ $cword -eq 2 ]]; then
6728
+ _filedir
6729
+ return
6730
+ fi
6731
+ if [[ $cword -ge 3 ]]; then
6732
+ _filedir
6733
+ return
6734
+ fi
6735
+ COMPREPLY=($(compgen -W "-s --strategy -v --verbose --help" -- "$cur"))
6736
+ return
6737
+ fi
6738
+
6739
+ # var \u5B50\u547D\u4EE4
6740
+ if [[ "$words[1]" == "var" ]]; then
6741
+ if [[ $cword -eq 2 ]]; then
6742
+ COMPREPLY=($(compgen -W "add list remove clear help" -- "$cur"))
6743
+ return
6744
+ fi
6745
+
6746
+ if [[ "$words[2]" == "add" || "$words[2]" == "remove" ]]; then
6747
+ if [[ $cword -eq 3 ]]; then
6748
+ _filedir
6749
+ return
6750
+ fi
6751
+ COMPREPLY=($(compgen -W "-v --verbose --force --help" -- "$cur"))
6752
+ return
6753
+ fi
6754
+
6755
+ if [[ "$words[2]" == "list" || "$words[2]" == "clear" ]]; then
6756
+ if [[ $cword -eq 3 ]]; then
6757
+ _filedir
6758
+ return
6759
+ fi
6760
+ COMPREPLY=($(compgen -W "-v --verbose --help" -- "$cur"))
6761
+ return
6762
+ fi
6763
+ fi
6764
+
6765
+ # env \u5B50\u547D\u4EE4
6766
+ if [[ "$words[1]" == "env" ]]; then
6767
+ if [[ $cword -eq 2 ]]; then
6768
+ COMPREPLY=($(compgen -W "add list remove clear help" -- "$cur"))
6769
+ return
6770
+ fi
6771
+
6772
+ if [[ "$words[2]" == "add" || "$words[2]" == "remove" ]]; then
6773
+ if [[ $cword -eq 3 ]]; then
6774
+ _filedir
6775
+ return
6776
+ fi
6777
+ COMPREPLY=($(compgen -W "-v --verbose --force --help" -- "$cur"))
6778
+ return
6779
+ fi
6780
+
6781
+ if [[ "$words[2]" == "list" || "$words[2]" == "clear" ]]; then
6782
+ if [[ $cword -eq 3 ]]; then
6783
+ _filedir
6784
+ return
6785
+ fi
6786
+ COMPREPLY=($(compgen -W "-v --verbose --help" -- "$cur"))
6787
+ return
6788
+ fi
6789
+ fi
6790
+ }
6791
+
6792
+ complete -F _urpf_cli_completion urpf-cli
6793
+ ###-end-urpf-cli-completions-###
6794
+ `;
6795
+ return script;
6796
+ }
6797
+ function generateZshCompletion() {
6798
+ const script = `#compdef urpf-cli
6799
+
6800
+ # urpf-cli zsh completion script
6801
+ # Installation: urpf-cli completion zsh >> ~/.zshrc
6802
+
6803
+ _urpf_cli() {
6804
+ local -a commands subcommands
6805
+
6806
+ commands=(
6807
+ 'pack:URPF \u5305\u7BA1\u7406\u547D\u4EE4'
6808
+ 'unpack:\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55'
6809
+ 'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u53CA\u5176\u5143\u6570\u636E'
6810
+ 'info:\u663E\u793A URPF \u5305\u7684\u6458\u8981\u4FE1\u606F'
6811
+ 'verify:\u9A8C\u8BC1 URPF \u5305\u7684\u6587\u4EF6\u5B8C\u6574\u6027'
6812
+ 'diff:\u6BD4\u8F83\u4E24\u4E2A URPF \u5305\u4E4B\u95F4\u7684\u5DEE\u5F02'
6813
+ 'merge:\u5408\u5E76\u591A\u4E2A URPF \u5305\u5230\u4E00\u4E2A\u8F93\u51FA\u6587\u4EF6'
6814
+ 'var:URPF \u5305\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
6815
+ 'env:URPF \u5305\u73AF\u5883\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
6816
+ 'help:\u663E\u793A\u5E2E\u52A9\u4FE1\u606F'
6817
+ )
6818
+
6819
+ if (( CURRENT == 2 )); then
6820
+ _describe 'command' commands
6821
+ else
6822
+ case $words[2] in
6823
+ pack)
6824
+ subcommands=(
6825
+ 'create:\u521B\u5EFA\u65B0\u7684 URPF \u5305'
6826
+ 'add:\u5411\u5DF2\u6709 URPF \u5305\u6DFB\u52A0\u591A\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55'
6827
+ 'remove:\u4ECE URPF \u5305\u4E2D\u79FB\u9664\u591A\u4E2A\u6587\u4EF6'
6828
+ )
6829
+ if (( CURRENT == 3 )); then
6830
+ _describe 'subcommand' subcommands
6831
+ else
6832
+ case $words[3] in
6833
+ create)
6834
+ _arguments -S \\
6835
+ '(-o --output)'{-o,--output}'[\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84]' \\
6836
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6837
+ '(-i --ignore)'{-i,--ignore}'[\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84]' \\
6838
+ '(-f --follow-symlinks)'{-f,--follow-symlinks}'[\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5]' \\
6839
+ '(-d --max-depth)'{-d,--max-depth}'[\u6700\u5927\u626B\u63CF\u6DF1\u5EA6]' \\
6840
+ '(--no-recurse)--no-recurse[\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55]' \\
6841
+ '(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u8F93\u51FA\u6587\u4EF6]' \\
6842
+ '(--byte)--byte[\u6309\u5B57\u8282\u504F\u79FB\u91CF\u5207\u7247]' \\
6843
+ '(--line)--line[\u6309\u884C\u53F7\u5207\u7247]' \\
6844
+ '(-s --stdout)'{-s,--stdout}'[\u8F93\u51FA URPF \u5185\u5BB9\u5230\u5C4F\u5E55]' \\
6845
+ '(-c --clipboard)'{-c,--clipboard}'[\u590D\u5236 URPF \u5185\u5BB9\u5230\u526A\u8D34\u677F]' \\
6846
+ '(-e --env)'{-e,--env}'[\u6253\u5305\u73AF\u5883\u53D8\u91CF]' \\
6847
+ '(--key)--key[\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C]' \\
6848
+ '(--format)--format[\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)]' \\
6849
+ '(--validate)--validate[\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)]' \\
6850
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6851
+ ;;
6852
+ add)
6853
+ _arguments -S \\
6854
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6855
+ '(-i --ignore)'{-i,--ignore}'[\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84]' \\
6856
+ '(-f --follow-symlinks)'{-f,--follow-symlinks}'[\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5]' \\
6857
+ '(-d --max-depth)'{-d,--max-depth}'[\u6700\u5927\u626B\u63CF\u6DF1\u5EA6]' \\
6858
+ '(--no-recurse)--no-recurse[\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55]' \\
6859
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6860
+ ;;
6861
+ remove)
6862
+ _arguments -S \\
6863
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6864
+ '(--no-apply-variables)--no-apply-variables[\u4E0D\u5E94\u7528\u53D8\u91CF\u66FF\u6362]' \\
6865
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6866
+ ;;
6867
+ esac
6868
+ fi
6869
+ ;;
6870
+ unpack)
6871
+ _arguments -S \\
6872
+ '(-o --output)'{-o,--output}'[\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84]' \\
6873
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6874
+ '(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6]' \\
6875
+ '(--force-when-newer)--force-when-newer[\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6]' \\
6876
+ '(--preserve-mtime)--preserve-mtime[\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4]' \\
6877
+ '(--unpack-env)--unpack-env[\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E]' \\
6878
+ '(--apply-variables)--apply-variables[\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9]' \\
6879
+ '(--packed-path)--packed-path[\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55]' \\
6880
+ '(--key)--key[\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C]' \\
6881
+ '(--format)--format[\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)]' \\
6882
+ '(--validate)--validate[\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)]' \\
6883
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6884
+ ;;
6885
+ list)
6886
+ _arguments -S \\
6887
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6888
+ '(-p --pattern)'{-p,--pattern}'[\u6309\u6A21\u5F0F\u8FC7\u6EE4\u6587\u4EF6]' \\
6889
+ '(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
6890
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6891
+ ;;
6892
+ info)
6893
+ _arguments -S \\
6894
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F]' \\
6895
+ '(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
6896
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6897
+ ;;
6898
+ verify)
6899
+ _arguments -S \\
6900
+ '(-f --fix)'{-f,--fix}'[\u5C1D\u8BD5\u4FEE\u590D\u5E38\u89C1\u95EE\u9898]' \\
6901
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6902
+ '(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
6903
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6904
+ ;;
6905
+ diff)
6906
+ _arguments -S \\
6907
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6908
+ '(-j --json)'{-j,--json}'[\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA]' \\
6909
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6910
+ ;;
6911
+ merge)
6912
+ _arguments -S \\
6913
+ '(-s --strategy)'{-s,--strategy}'[\u5408\u5E76\u7B56\u7565(skip/overwrite/rename)]' \\
6914
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6915
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6916
+ ;;
6917
+ var)
6918
+ subcommands=(
6919
+ 'add:\u6DFB\u52A0\u53D8\u91CF\u5230 URPF \u5305'
6920
+ 'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
6921
+ 'remove:\u4ECE URPF \u5305\u4E2D\u5220\u9664\u53D8\u91CF'
6922
+ 'clear:\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
6923
+ )
6924
+ if (( CURRENT == 3 )); then
6925
+ _describe 'subcommand' subcommands
6926
+ else
6927
+ case $words[3] in
6928
+ add|remove)
6929
+ _arguments -S \\
6930
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6931
+ '(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u53D8\u91CF]' \\
6932
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6933
+ ;;
6934
+ list|clear)
6935
+ _arguments -S \\
6936
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6937
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6938
+ ;;
6939
+ esac
6940
+ fi
6941
+ ;;
6942
+ env)
6943
+ subcommands=(
6944
+ 'add:\u6DFB\u52A0\u73AF\u5883\u53D8\u91CF\u5230 URPF \u5305'
6945
+ 'list:\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
6946
+ 'remove:\u4ECE URPF \u5305\u4E2D\u5220\u9664\u73AF\u5883\u53D8\u91CF'
6947
+ 'clear:\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
6948
+ )
6949
+ if (( CURRENT == 3 )); then
6950
+ _describe 'subcommand' subcommands
6951
+ else
6952
+ case $words[3] in
6953
+ add|remove)
6954
+ _arguments -S \\
6955
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6956
+ '(--force)--force[\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u73AF\u5883\u53D8\u91CF]' \\
6957
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6958
+ ;;
6959
+ list|clear)
6960
+ _arguments -S \\
6961
+ '(-v --verbose)'{-v,--verbose}'[\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7]' \\
6962
+ '(-h --help)'{-h,--help}'[\u663E\u793A\u5E2E\u52A9\u4FE1\u606F]'
6963
+ ;;
6964
+ esac
6965
+ fi
6966
+ ;;
6967
+ esac
6968
+ fi
6969
+ }
6970
+
6971
+ _urpf_cli
6972
+ `;
6973
+ return script;
6974
+ }
6975
+ function generateFishCompletion() {
6976
+ const script = `#!/usr/bin/env fish
6977
+
6978
+ # urpf-cli fish completion script
6979
+ # Installation: urpf-cli completion fish > ~/.config/fish/completions/urpf-cli.fish
6980
+
6981
+ # Main commands
6982
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a pack -d 'URPF \u5305\u7BA1\u7406\u547D\u4EE4'
6983
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a unpack -d '\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55'
6984
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u53CA\u5176\u5143\u6570\u636E'
6985
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a info -d '\u663E\u793A URPF \u5305\u7684\u6458\u8981\u4FE1\u606F'
6986
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a verify -d '\u9A8C\u8BC1 URPF \u5305\u7684\u6587\u4EF6\u5B8C\u6574\u6027'
6987
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a diff -d '\u6BD4\u8F83\u4E24\u4E2A URPF \u5305\u4E4B\u95F4\u7684\u5DEE\u5F02'
6988
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a merge -d '\u5408\u5E76\u591A\u4E2A URPF \u5305\u5230\u4E00\u4E2A\u8F93\u51FA\u6587\u4EF6'
6989
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a var -d 'URPF \u5305\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
6990
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a env -d 'URPF \u5305\u73AF\u5883\u53D8\u91CF\u7BA1\u7406\u547D\u4EE4'
6991
+ complete -c urpf-cli -f -n "__fish_use_subcommand" -a help -d '\u663E\u793A\u5E2E\u52A9\u4FE1\u606F'
6992
+
6993
+ # pack subcommands
6994
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a create -d '\u521B\u5EFA\u65B0\u7684 URPF \u5305'
6995
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a add -d '\u5411\u5DF2\u6709 URPF \u5305\u6DFB\u52A0\u591A\u4E2A\u6587\u4EF6\u6216\u76EE\u5F55'
6996
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack" -a remove -d '\u4ECE URPF \u5305\u4E2D\u79FB\u9664\u591A\u4E2A\u6587\u4EF6'
6997
+
6998
+ # pack create options
6999
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l output -s o -d '\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84'
7000
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7001
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l ignore -s i -d '\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84'
7002
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l follow-symlinks -s f -d '\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5'
7003
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l max-depth -s d -d '\u6700\u5927\u626B\u63CF\u6DF1\u5EA6'
7004
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l no-recurse -d '\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55'
7005
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u8F93\u51FA\u6587\u4EF6'
7006
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l byte -d '\u6309\u5B57\u8282\u504F\u79FB\u91CF\u5207\u7247'
7007
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l line -d '\u6309\u884C\u53F7\u5207\u7247'
7008
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l stdout -s s -d '\u8F93\u51FA URPF \u5185\u5BB9\u5230\u5C4F\u5E55'
7009
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l clipboard -s c -d '\u590D\u5236 URPF \u5185\u5BB9\u5230\u526A\u8D34\u677F'
7010
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l env -s e -d '\u6253\u5305\u73AF\u5883\u53D8\u91CF'
7011
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l key -d '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C'
7012
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l format -d '\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)'
7013
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from create" -l validate -d '\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)'
7014
+
7015
+ # pack add options
7016
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7017
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l ignore -s i -d '\u6307\u5B9A\u5FFD\u7565\u89C4\u5219\u6587\u4EF6\u8DEF\u5F84'
7018
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l follow-symlinks -s f -d '\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5'
7019
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l max-depth -s d -d '\u6700\u5927\u626B\u63CF\u6DF1\u5EA6'
7020
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from add" -l no-recurse -d '\u4E0D\u9012\u5F52\u626B\u63CF\u5B50\u76EE\u5F55'
7021
+
7022
+ # pack remove options
7023
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7024
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from pack; and __fish_seen_subcommand_from remove" -l no-apply-variables -d '\u4E0D\u5E94\u7528\u53D8\u91CF\u66FF\u6362'
7025
+
7026
+ # unpack options
7027
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l output -s o -d '\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84'
7028
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7029
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6'
7030
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l force-when-newer -d '\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6'
7031
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l preserve-mtime -d '\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4'
7032
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l unpack-env -d '\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E'
7033
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l apply-variables -d '\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9'
7034
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l packed-path -d '\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55'
7035
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l key -d '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C'
7036
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l format -d '\u8F93\u51FA\u683C\u5F0F(json/yaml/toml/xml/properties/ini/raw)'
7037
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from unpack" -l validate -d '\u9A8C\u8BC1\u6A21\u5F0F(\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728)'
7038
+
7039
+ # list options
7040
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7041
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l pattern -s p -d '\u6309\u6A21\u5F0F\u8FC7\u6EE4\u6587\u4EF6'
7042
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from list" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
7043
+
7044
+ # info options
7045
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from info" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F'
7046
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from info" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
7047
+
7048
+ # verify options
7049
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l fix -s f -d '\u5C1D\u8BD5\u4FEE\u590D\u5E38\u89C1\u95EE\u9898'
7050
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7051
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from verify" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
7052
+
7053
+ # diff options
7054
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from diff" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7055
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from diff" -l json -s j -d '\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA'
7056
+
7057
+ # merge options
7058
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from merge" -l strategy -s s -d '\u5408\u5E76\u7B56\u7565(skip/overwrite/rename)'
7059
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from merge" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7060
+
7061
+ # var subcommands
7062
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a add -d '\u6DFB\u52A0\u53D8\u91CF\u5230 URPF \u5305'
7063
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
7064
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a remove -d '\u4ECE URPF \u5305\u4E2D\u5220\u9664\u53D8\u91CF'
7065
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var" -a clear -d '\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u53D8\u91CF'
7066
+
7067
+ # var add/remove options
7068
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7069
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from add" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u53D8\u91CF'
7070
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7071
+
7072
+ # var list/clear options
7073
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7074
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from var; and __fish_seen_subcommand_from clear" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7075
+
7076
+ # env subcommands
7077
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a add -d '\u6DFB\u52A0\u73AF\u5883\u53D8\u91CF\u5230 URPF \u5305'
7078
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a list -d '\u5217\u51FA URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
7079
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a remove -d '\u4ECE URPF \u5305\u4E2D\u5220\u9664\u73AF\u5883\u53D8\u91CF'
7080
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env" -a clear -d '\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6240\u6709\u73AF\u5883\u53D8\u91CF'
7081
+
7082
+ # env add/remove options
7083
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from add" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7084
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from add" -l force -d '\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u73AF\u5883\u53D8\u91CF'
7085
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from remove" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7086
+
7087
+ # env list/clear options
7088
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from list" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7089
+ complete -c urpf-cli -f -n "__fish_seen_subcommand_from env; and __fish_seen_subcommand_from clear" -l verbose -s v -d '\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7'
7090
+ `;
7091
+ return script;
7092
+ }
7093
+ function generatePowerShellCompletion() {
7094
+ const script = `# urpf-cli PowerShell completion script
7095
+ # Installation: urpf-cli completion powershell >> $PROFILE
7096
+
7097
+ Register-ArgumentCompleter -Native -CommandName urpf-cli -ScriptBlock {
7098
+ param($wordToComplete, $commandAst, $cursorPosition)
7099
+
7100
+ $commands = @('pack', 'unpack', 'list', 'info', 'verify', 'diff', 'merge', 'var', 'env', 'help')
7101
+
7102
+ # Get the current command
7103
+ $commandElements = $commandAst.CommandElements
7104
+ $currentCommand = if ($commandElements.Count -gt 1) { $commandElements[1].Value } else { '' }
7105
+
7106
+ # Main command completion
7107
+ if ($commandElements.Count -eq 2) {
7108
+ $commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7109
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7110
+ }
7111
+ return
7112
+ }
7113
+
7114
+ # pack subcommand completion
7115
+ if ($currentCommand -eq 'pack' -and $commandElements.Count -eq 3) {
7116
+ $subcommands = @('create', 'add', 'remove')
7117
+ $subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7118
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7119
+ }
7120
+ return
7121
+ }
7122
+
7123
+ # pack create options
7124
+ if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'create') {
7125
+ $options = @('-o', '--output', '-v', '--verbose', '-i', '--ignore', '-f', '--follow-symlinks', '-d', '--max-depth', '--no-recurse', '--force', '--byte', '--line', '-s', '--stdout', '-c', '--clipboard', '-e', '--env', '--key', '--format', '--validate')
7126
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7127
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7128
+ }
7129
+ return
7130
+ }
7131
+
7132
+ # pack add options
7133
+ if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'add') {
7134
+ $options = @('-v', '--verbose', '-i', '--ignore', '-f', '--follow-symlinks', '-d', '--max-depth', '--no-recurse')
7135
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7136
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7137
+ }
7138
+ return
7139
+ }
7140
+
7141
+ # pack remove options
7142
+ if ($currentCommand -eq 'pack' -and $commandElements.Count -gt 3 -and $commandElements[2].Value -eq 'remove') {
7143
+ $options = @('-v', '--verbose', '--no-apply-variables')
7144
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7145
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7146
+ }
7147
+ return
7148
+ }
7149
+
7150
+ # unpack options
7151
+ if ($currentCommand -eq 'unpack' -and $commandElements.Count -gt 2) {
7152
+ $options = @('-o', '--output', '-v', '--verbose', '--force', '--force-when-newer', '--preserve-mtime', '--unpack-env', '--apply-variables', '--packed-path', '--key', '--format', '--validate')
7153
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7154
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7155
+ }
7156
+ return
7157
+ }
7158
+
7159
+ # list options
7160
+ if ($currentCommand -eq 'list' -and $commandElements.Count -gt 2) {
7161
+ $options = @('-v', '--verbose', '-p', '--pattern', '-j', '--json')
7162
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7163
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7164
+ }
7165
+ return
7166
+ }
7167
+
7168
+ # info options
7169
+ if ($currentCommand -eq 'info' -and $commandElements.Count -gt 2) {
7170
+ $options = @('-v', '--verbose', '-j', '--json')
7171
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7172
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7173
+ }
7174
+ return
7175
+ }
7176
+
7177
+ # verify options
7178
+ if ($currentCommand -eq 'verify' -and $commandElements.Count -gt 2) {
7179
+ $options = @('-f', '--fix', '-v', '--verbose', '-j', '--json')
7180
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7181
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7182
+ }
7183
+ return
7184
+ }
7185
+
7186
+ # diff options
7187
+ if ($currentCommand -eq 'diff' -and $commandElements.Count -gt 2) {
7188
+ $options = @('-v', '--verbose', '-j', '--json')
7189
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7190
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7191
+ }
7192
+ return
7193
+ }
7194
+
7195
+ # merge options
7196
+ if ($currentCommand -eq 'merge' -and $commandElements.Count -gt 2) {
7197
+ $options = @('-s', '--strategy', '-v', '--verbose')
7198
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7199
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7200
+ }
7201
+ return
7202
+ }
7203
+
7204
+ # var subcommand completion
7205
+ if ($currentCommand -eq 'var' -and $commandElements.Count -eq 3) {
7206
+ $subcommands = @('add', 'list', 'remove', 'clear')
7207
+ $subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7208
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7209
+ }
7210
+ return
7211
+ }
7212
+
7213
+ # var add/remove options
7214
+ if ($currentCommand -eq 'var' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'add' -or $commandElements[2].Value -eq 'remove')) {
7215
+ $options = @('-v', '--verbose', '--force')
7216
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7217
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7218
+ }
7219
+ return
7220
+ }
7221
+
7222
+ # var list/clear options
7223
+ if ($currentCommand -eq 'var' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'list' -or $commandElements[2].Value -eq 'clear')) {
7224
+ $options = @('-v', '--verbose')
7225
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7226
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7227
+ }
7228
+ return
7229
+ }
7230
+
7231
+ # env subcommand completion
7232
+ if ($currentCommand -eq 'env' -and $commandElements.Count -eq 3) {
7233
+ $subcommands = @('add', 'list', 'remove', 'clear')
7234
+ $subcommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7235
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7236
+ }
7237
+ return
7238
+ }
7239
+
7240
+ # env add/remove options
7241
+ if ($currentCommand -eq 'env' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'add' -or $commandElements[2].Value -eq 'remove')) {
7242
+ $options = @('-v', '--verbose', '--force')
7243
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7244
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7245
+ }
7246
+ return
7247
+ }
7248
+
7249
+ # env list/clear options
7250
+ if ($currentCommand -eq 'env' -and $commandElements.Count -gt 3 -and ($commandElements[2].Value -eq 'list' -or $commandElements[2].Value -eq 'clear')) {
7251
+ $options = @('-v', '--verbose')
7252
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
7253
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
7254
+ }
7255
+ return
7256
+ }
7257
+ }
7258
+ `;
7259
+ return script;
7260
+ }
7261
+ function generateCompletion(shell) {
7262
+ switch (shell) {
7263
+ case "bash":
7264
+ return generateBashCompletion();
7265
+ case "zsh":
7266
+ return generateZshCompletion();
7267
+ case "fish":
7268
+ return generateFishCompletion();
7269
+ case "powershell":
7270
+ return generatePowerShellCompletion();
7271
+ default:
7272
+ throw new Error(`Unsupported shell: ${shell}`);
7273
+ }
7274
+ }
7275
+ function getSupportedShells() {
7276
+ return ["bash", "zsh", "fish", "powershell"];
7277
+ }
7278
+
4972
7279
  // src/index.ts
4973
7280
  var program = new Command();
4974
7281
  program.name("urpf-cli").description("URPF CLI \u5DE5\u5177 - \u57FA\u4E8E URPF \u89C4\u8303\u7684\u547D\u4EE4\u884C\u6253\u5305\u5DE5\u5177").version("0.1.0");
@@ -4985,7 +7292,10 @@ packCommand.command("create").description("\u5C06\u6587\u4EF6\u6216\u76EE\u5F55\
4985
7292
  previous[name] = void 0;
4986
7293
  }
4987
7294
  return previous;
4988
- }, {}).action(async (file, options) => {
7295
+ }, {}).option("--key <key-path>", '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\uFF08\u652F\u6301\u5D4C\u5957\u548C\u6570\u7EC4\u8BBF\u95EE\uFF0C\u5982 "config.server.port" \u6216 "items[0].name"\uFF1B\u652F\u6301\u901A\u914D\u7B26 * \u548C **\uFF1B\u652F\u6301\u9017\u53F7\u5206\u9694\u591A\u4E2A key\uFF1B\u53EF\u591A\u6B21\u6307\u5B9A\uFF09', (value, previous = []) => {
7296
+ previous.push(value);
7297
+ return previous;
7298
+ }).option("--format <format>", "\u8F93\u51FA\u683C\u5F0F\uFF08json\u3001yaml\u3001toml\u3001xml\u3001properties\u3001ini\u3001raw\uFF09", "json").option("--validate", "\u9A8C\u8BC1\u6A21\u5F0F\uFF08\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u63D0\u53D6\uFF09").action(async (file, options) => {
4989
7299
  try {
4990
7300
  const envVars = options.env;
4991
7301
  const packOptions = {
@@ -5006,7 +7316,12 @@ packCommand.command("create").description("\u5C06\u6587\u4EF6\u6216\u76EE\u5F55\
5006
7316
  stdout: options.stdout,
5007
7317
  clipboard: options.clipboard,
5008
7318
  // 环境变量
5009
- envVars: Object.keys(envVars).length > 0 ? envVars : void 0
7319
+ envVars: Object.keys(envVars).length > 0 ? envVars : void 0,
7320
+ // key 提取
7321
+ key: options.key,
7322
+ // 格式化和验证
7323
+ format: options.format,
7324
+ validate: options.validate
5010
7325
  };
5011
7326
  const command = new PackCommand();
5012
7327
  const result = await command.execute(file, packOptions);
@@ -5074,15 +7389,20 @@ packCommand.command("remove").description("\u4ECE URPF \u5305\u4E2D\u79FB\u9664\
5074
7389
  process.exit(1);
5075
7390
  }
5076
7391
  });
5077
- program.command("unpack").description("\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55\n\n\u73AF\u5883\u53D8\u91CF\u652F\u6301:\n \u5982\u679C URPF \u5305\u5305\u542B\u73AF\u5883\u53D8\u91CF\uFF0C\u89E3\u5305\u65F6\u4F1A\u63D0\u793A\u7528\u6237\u662F\u5426\u8BBE\u7F6E\n \u4F7F\u7528 --unpack-env \u9009\u9879\u53EF\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09\n \u4F7F\u7528 --apply-variables \u9009\u9879\u53EF\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9").argument("<file.urpf>", "\u8981\u89E3\u5305\u7684 URPF \u6587\u4EF6\u8DEF\u5F84").option("-o, --output <path>", "\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6").option("--force-when-newer", "\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6").option("--preserve-mtime", "\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4").option("--unpack-env", "\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09").option("--apply-variables", "\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9").option("--packed-path <path>", "\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55\uFF08\u652F\u6301\u901A\u914D\u7B26 * \u548C ?\uFF0C\u53EF\u591A\u6B21\u4F7F\u7528\uFF09", (value, previous = []) => {
7392
+ program.command("unpack").description('\u89E3\u5305 URPF \u6587\u4EF6\u5230\u6307\u5B9A\u76EE\u5F55\n\n\u73AF\u5883\u53D8\u91CF\u652F\u6301:\n \u5982\u679C URPF \u5305\u5305\u542B\u73AF\u5883\u53D8\u91CF\uFF0C\u89E3\u5305\u65F6\u4F1A\u63D0\u793A\u7528\u6237\u662F\u5426\u8BBE\u7F6E\n \u4F7F\u7528 --unpack-env \u9009\u9879\u53EF\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09\n \u4F7F\u7528 --apply-variables \u9009\u9879\u53EF\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9\n\nKey \u63D0\u53D6\u652F\u6301:\n \u4F7F\u7528 --key \u9009\u9879\u4ECE\u89E3\u5305\u540E\u7684\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\n \u652F\u6301\u5D4C\u5957\u952E\u8BBF\u95EE\uFF08\u5982 "config.server.port"\uFF09\n \u652F\u6301\u6570\u7EC4\u7D22\u5F15\u8BBF\u95EE\uFF08\u5982 "items[0].name"\uFF09').argument("<file.urpf>", "\u8981\u89E3\u5305\u7684 URPF \u6587\u4EF6\u8DEF\u5F84").option("-o, --output <path>", "\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6").option("--force-when-newer", "\u4EC5\u5F53\u6E90\u6587\u4EF6\u6BD4\u76EE\u6807\u6587\u4EF6\u66F4\u65B0\u65F6\u624D\u8986\u76D6").option("--preserve-mtime", "\u4FDD\u7559\u539F\u59CB\u4FEE\u6539\u65F6\u95F4").option("--unpack-env", "\u81EA\u52A8\u786E\u8BA4\u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\uFF08\u65E0\u9700\u7528\u6237\u4EA4\u4E92\uFF09").option("--apply-variables", "\u5E94\u7528\u53D8\u91CF\u66FF\u6362\u5230\u6587\u4EF6\u5185\u5BB9").option("--packed-path <path>", "\u53EA\u89E3\u538B\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u76EE\u5F55\uFF08\u652F\u6301\u901A\u914D\u7B26 * \u548C ?\uFF0C\u53EF\u591A\u6B21\u4F7F\u7528\uFF09", (value, previous = []) => {
7393
+ previous.push(value);
7394
+ return previous;
7395
+ }).option("--key <key-path>", '\u4ECE\u914D\u7F6E\u6587\u4EF6\u4E2D\u63D0\u53D6\u6307\u5B9A\u7684\u952E\u503C\uFF08\u652F\u6301\u5D4C\u5957\u548C\u6570\u7EC4\u8BBF\u95EE\uFF0C\u5982 "config.server.port" \u6216 "items[0].name"\uFF1B\u652F\u6301\u901A\u914D\u7B26 * \u548C **\uFF1B\u652F\u6301\u9017\u53F7\u5206\u9694\u591A\u4E2A key\uFF1B\u53EF\u591A\u6B21\u6307\u5B9A\uFF09', (value, previous = []) => {
5078
7396
  previous.push(value);
5079
7397
  return previous;
5080
- }).action(async (file, options) => {
7398
+ }).option("--format <format>", "\u8F93\u51FA\u683C\u5F0F\uFF08json\u3001yaml\u3001toml\u3001xml\u3001properties\u3001ini\u3001raw\uFF09", "json").option("--validate", "\u9A8C\u8BC1\u6A21\u5F0F\uFF08\u4EC5\u9A8C\u8BC1 key \u662F\u5426\u5B58\u5728\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u63D0\u53D6\uFF09").action(async (file, options) => {
5081
7399
  try {
5082
7400
  const unpackCommand = new UnpackCommand();
5083
7401
  const result = await unpackCommand.execute(file, {
5084
7402
  ...options,
5085
- packedPaths: options.packedPath
7403
+ packedPaths: options.packedPath,
7404
+ format: options.format,
7405
+ validate: options.validate
5086
7406
  });
5087
7407
  if (result.success) {
5088
7408
  console.log(`\u2705 URPF \u6587\u4EF6\u5DF2\u89E3\u5305\u5230: ${result.outputDir}`);
@@ -5426,4 +7746,21 @@ envCommand.command("clear").description("\u6E05\u9664 URPF \u5305\u4E2D\u7684\u6
5426
7746
  process.exit(1);
5427
7747
  }
5428
7748
  });
7749
+ program.command("completion").description("\u751F\u6210 Shell \u81EA\u52A8\u8865\u5168\u811A\u672C\n\n\u652F\u6301\u7684 Shell:\n bash - Bash shell\n zsh - Zsh shell\n fish - Fish shell\n powershell - PowerShell\n\n\u4F7F\u7528\u793A\u4F8B:\n urpf-cli completion bash >> ~/.bashrc\n urpf-cli completion zsh >> ~/.zshrc\n urpf-cli completion fish > ~/.config/fish/completions/urpf-cli.fish\n urpf-cli completion powershell >> $PROFILE").argument("<shell>", "Shell \u7C7B\u578B\uFF08bash\u3001zsh\u3001fish\u3001powershell\uFF09").action(async (shell) => {
7750
+ try {
7751
+ const shellType = shell.toLowerCase();
7752
+ const supportedShells = getSupportedShells();
7753
+ if (!supportedShells.includes(shellType)) {
7754
+ console.error(`\u274C \u4E0D\u652F\u6301\u7684 Shell \u7C7B\u578B: ${shell}`);
7755
+ console.error(`\u652F\u6301\u7684 Shell \u7C7B\u578B: ${supportedShells.join(", ")}`);
7756
+ process.exit(1);
7757
+ }
7758
+ const completionScript = generateCompletion(shellType);
7759
+ console.log(completionScript);
7760
+ } catch (error) {
7761
+ const errorMessage = error instanceof Error ? error.message : String(error);
7762
+ console.error(`\u274C \u751F\u6210\u8865\u5168\u811A\u672C\u5931\u8D25: ${errorMessage}`);
7763
+ process.exit(1);
7764
+ }
7765
+ });
5429
7766
  program.parse();