mihomo-cli 3.2.0 → 3.4.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.
- package/CHANGELOG.md +79 -0
- package/README.md +51 -17
- package/dist/index.js +1522 -1092
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import fs4 from "fs";
|
|
|
6
6
|
|
|
7
7
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
8
8
|
var NOT_RESOLVED = /* @__PURE__ */ Symbol("NOT_RESOLVED");
|
|
9
|
-
var MERGE_KEY = /* @__PURE__ */ Symbol("MERGE_KEY");
|
|
10
9
|
function defineScalarTag(tagName, options) {
|
|
11
10
|
return {
|
|
12
11
|
tagName,
|
|
@@ -15,9 +14,9 @@ function defineScalarTag(tagName, options) {
|
|
|
15
14
|
matchByTagPrefix: options.matchByTagPrefix ?? false,
|
|
16
15
|
implicitFirstChars: options.implicitFirstChars ?? null,
|
|
17
16
|
resolve: options.resolve,
|
|
18
|
-
identify: options.identify
|
|
17
|
+
identify: options.identify,
|
|
19
18
|
represent: options.represent ?? ((data) => String(data)),
|
|
20
|
-
representTagName: options.representTagName ??
|
|
19
|
+
representTagName: options.representTagName ?? (() => tagName)
|
|
21
20
|
};
|
|
22
21
|
}
|
|
23
22
|
function defineSequenceTag(tagName, options) {
|
|
@@ -31,9 +30,9 @@ function defineSequenceTag(tagName, options) {
|
|
|
31
30
|
addItem: options.addItem,
|
|
32
31
|
finalize: options.finalize ?? ((carrier) => carrier),
|
|
33
32
|
carrierIsResult,
|
|
34
|
-
identify: options.identify
|
|
33
|
+
identify: options.identify,
|
|
35
34
|
represent: options.represent ?? ((data) => data),
|
|
36
|
-
representTagName: options.representTagName ??
|
|
35
|
+
representTagName: options.representTagName ?? (() => tagName)
|
|
37
36
|
};
|
|
38
37
|
}
|
|
39
38
|
function defineMappingTag(tagName, options) {
|
|
@@ -50,9 +49,9 @@ function defineMappingTag(tagName, options) {
|
|
|
50
49
|
get: options.get,
|
|
51
50
|
finalize: options.finalize ?? ((carrier) => carrier),
|
|
52
51
|
carrierIsResult,
|
|
53
|
-
identify: options.identify
|
|
52
|
+
identify: options.identify,
|
|
54
53
|
represent: options.represent ?? ((data) => data),
|
|
55
|
-
representTagName: options.representTagName ??
|
|
54
|
+
representTagName: options.representTagName ?? (() => tagName)
|
|
56
55
|
};
|
|
57
56
|
}
|
|
58
57
|
var strTag = defineScalarTag("tag:yaml.org,2002:str", {
|
|
@@ -401,9 +400,10 @@ var mergeTag = defineScalarTag("tag:yaml.org,2002:merge", {
|
|
|
401
400
|
implicit: true,
|
|
402
401
|
implicitFirstChars: ["<"],
|
|
403
402
|
resolve: (source, isExplicit) => {
|
|
404
|
-
if (source === "<<" || isExplicit && source === "") return
|
|
403
|
+
if (source === "<<" || isExplicit && source === "") return "<<";
|
|
405
404
|
return NOT_RESOLVED;
|
|
406
|
-
}
|
|
405
|
+
},
|
|
406
|
+
identify: () => false
|
|
407
407
|
});
|
|
408
408
|
var BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
409
409
|
function resolveYamlBinary(source) {
|
|
@@ -426,6 +426,11 @@ var binaryTag = defineScalarTag("tag:yaml.org,2002:binary", {
|
|
|
426
426
|
});
|
|
427
427
|
var YAML_DATE_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$");
|
|
428
428
|
var YAML_TIMESTAMP_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");
|
|
429
|
+
function makeUtcDate(year, month, day, hour = 0, minute = 0, second = 0, fraction = 0) {
|
|
430
|
+
const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));
|
|
431
|
+
date.setUTCFullYear(year, month, day);
|
|
432
|
+
return date;
|
|
433
|
+
}
|
|
429
434
|
function resolveYamlTimestamp(source) {
|
|
430
435
|
let match = YAML_DATE_REGEXP.exec(source);
|
|
431
436
|
if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(source);
|
|
@@ -434,7 +439,7 @@ function resolveYamlTimestamp(source) {
|
|
|
434
439
|
const month = +match[2] - 1;
|
|
435
440
|
const day = +match[3];
|
|
436
441
|
if (!match[4]) {
|
|
437
|
-
const date2 =
|
|
442
|
+
const date2 = makeUtcDate(year, month, day);
|
|
438
443
|
if (date2.getUTCFullYear() !== year || date2.getUTCMonth() !== month || date2.getUTCDate() !== day) return NOT_RESOLVED;
|
|
439
444
|
return date2;
|
|
440
445
|
}
|
|
@@ -448,7 +453,7 @@ function resolveYamlTimestamp(source) {
|
|
|
448
453
|
while (value.length < 3) value += "0";
|
|
449
454
|
fraction = +value;
|
|
450
455
|
}
|
|
451
|
-
const date =
|
|
456
|
+
const date = makeUtcDate(year, month, day, hour, minute, second, fraction);
|
|
452
457
|
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) return NOT_RESOLVED;
|
|
453
458
|
if (match[9]) {
|
|
454
459
|
const offsetHour = +match[10];
|
|
@@ -503,7 +508,8 @@ var omapTag = defineSequenceTag("tag:yaml.org,2002:omap", {
|
|
|
503
508
|
carrier.list.push(item);
|
|
504
509
|
return "";
|
|
505
510
|
},
|
|
506
|
-
finalize: (carrier) => carrier.list
|
|
511
|
+
finalize: (carrier) => carrier.list,
|
|
512
|
+
identify: () => false
|
|
507
513
|
});
|
|
508
514
|
var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", {
|
|
509
515
|
create: () => [],
|
|
@@ -519,7 +525,8 @@ var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", {
|
|
|
519
525
|
if (keys.length !== 1) return "cannot resolve a pairs item";
|
|
520
526
|
container.push([keys[0], object[keys[0]]]);
|
|
521
527
|
return "";
|
|
522
|
-
}
|
|
528
|
+
},
|
|
529
|
+
identify: () => false
|
|
523
530
|
});
|
|
524
531
|
var mapTag = defineMappingTag("tag:yaml.org,2002:map", {
|
|
525
532
|
create: () => ({}),
|
|
@@ -546,7 +553,11 @@ var mapTag = defineMappingTag("tag:yaml.org,2002:map", {
|
|
|
546
553
|
return Object.prototype.hasOwnProperty.call(container, String(key));
|
|
547
554
|
},
|
|
548
555
|
keys: (container) => Object.keys(container),
|
|
549
|
-
get: (container, key) =>
|
|
556
|
+
get: (container, key) => {
|
|
557
|
+
const normalizedKey = String(key);
|
|
558
|
+
if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null;
|
|
559
|
+
return container[normalizedKey];
|
|
560
|
+
}
|
|
550
561
|
});
|
|
551
562
|
var setTag = defineMappingTag("tag:yaml.org,2002:set", {
|
|
552
563
|
create: () => /* @__PURE__ */ new Set(),
|
|
@@ -567,9 +578,9 @@ var setTag = defineMappingTag("tag:yaml.org,2002:set", {
|
|
|
567
578
|
});
|
|
568
579
|
function createTagDefinitionMap() {
|
|
569
580
|
return {
|
|
570
|
-
scalar:
|
|
571
|
-
sequence:
|
|
572
|
-
mapping:
|
|
581
|
+
scalar: /* @__PURE__ */ Object.create(null),
|
|
582
|
+
sequence: /* @__PURE__ */ Object.create(null),
|
|
583
|
+
mapping: /* @__PURE__ */ Object.create(null)
|
|
573
584
|
};
|
|
574
585
|
}
|
|
575
586
|
function createTagDefinitionListMap() {
|
|
@@ -596,11 +607,35 @@ function compileTags(tags) {
|
|
|
596
607
|
}
|
|
597
608
|
var Schema = class Schema2 {
|
|
598
609
|
tags;
|
|
610
|
+
/** @internal */
|
|
599
611
|
implicitScalarTags;
|
|
612
|
+
/**
|
|
613
|
+
* Dispatch implicit scalar resolvers by `source.charAt(0)`. Each bucket holds
|
|
614
|
+
* the resolvers that may match that key, in schema order; a key absent from
|
|
615
|
+
* the map uses
|
|
616
|
+
* {@link Schema.implicitScalarAnyFirstChar}
|
|
617
|
+
* (resolvers that declared no first-char constraint, so they apply to any
|
|
618
|
+
* first character).
|
|
619
|
+
*/
|
|
600
620
|
implicitScalarByFirstChar;
|
|
601
621
|
implicitScalarAnyFirstChar;
|
|
622
|
+
/**
|
|
623
|
+
* The default scalar tag (`!!str`), resolved once so the composer's fallback
|
|
624
|
+
* for unresolved plain scalars avoids a keyed lookup per scalar.
|
|
625
|
+
*
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
602
628
|
defaultScalarTag;
|
|
629
|
+
/**
|
|
630
|
+
* The default container tags (`!!seq` / `!!map`), used by the dumper: when a
|
|
631
|
+
* value is identified by its default tag, the tag is implicit and not
|
|
632
|
+
* printed. Undefined if the schema does not define them (then such values
|
|
633
|
+
* can't be dumped).
|
|
634
|
+
*
|
|
635
|
+
* @internal
|
|
636
|
+
*/
|
|
603
637
|
defaultSequenceTag;
|
|
638
|
+
/** @internal */
|
|
604
639
|
defaultMappingTag;
|
|
605
640
|
exact;
|
|
606
641
|
prefix;
|
|
@@ -646,6 +681,52 @@ var Schema = class Schema2 {
|
|
|
646
681
|
this.exact = exact;
|
|
647
682
|
this.prefix = prefix;
|
|
648
683
|
}
|
|
684
|
+
/** @internal */
|
|
685
|
+
lookupScalarTag(tagName) {
|
|
686
|
+
const exactTag = this.exact.scalar[tagName];
|
|
687
|
+
if (exactTag) return exactTag;
|
|
688
|
+
for (const tag of this.prefix.scalar) if (tagName.startsWith(tag.tagName)) return tag;
|
|
689
|
+
}
|
|
690
|
+
/** @internal */
|
|
691
|
+
lookupSequenceTag(tagName) {
|
|
692
|
+
const exactTag = this.exact.sequence[tagName];
|
|
693
|
+
if (exactTag) return exactTag;
|
|
694
|
+
for (const tag of this.prefix.sequence) if (tagName.startsWith(tag.tagName)) return tag;
|
|
695
|
+
}
|
|
696
|
+
/** @internal */
|
|
697
|
+
lookupMappingTag(tagName) {
|
|
698
|
+
const exactTag = this.exact.mapping[tagName];
|
|
699
|
+
if (exactTag) return exactTag;
|
|
700
|
+
for (const tag of this.prefix.mapping) if (tagName.startsWith(tag.tagName)) return tag;
|
|
701
|
+
}
|
|
702
|
+
/** @internal */
|
|
703
|
+
resolveImplicitScalarTag(source) {
|
|
704
|
+
const candidates = this.implicitScalarByFirstChar.get(source.charAt(0)) ?? this.implicitScalarAnyFirstChar;
|
|
705
|
+
for (const tag2 of candidates) {
|
|
706
|
+
const value = tag2.resolve(source, false, tag2.tagName);
|
|
707
|
+
if (value !== NOT_RESOLVED) return {
|
|
708
|
+
value,
|
|
709
|
+
tag: tag2
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
const tag = this.defaultScalarTag;
|
|
713
|
+
return {
|
|
714
|
+
value: tag.resolve(source, false, tag.tagName),
|
|
715
|
+
tag
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Creates a new schema with the specified tags added. If a tag already
|
|
720
|
+
* exists, it is replaced by the specified tag.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
*
|
|
724
|
+
* ```javascript
|
|
725
|
+
* import { CORE_SCHEMA, mergeTag, realMapTag } from 'js-yaml'
|
|
726
|
+
*
|
|
727
|
+
* const schema = CORE_SCHEMA.withTags(mergeTag, realMapTag)
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
649
730
|
withTags(...tags) {
|
|
650
731
|
let flatTags = [];
|
|
651
732
|
for (const tag of tags) flatTags = flatTags.concat(tag);
|
|
@@ -684,6 +765,19 @@ var YAML11_SCHEMA = new Schema([
|
|
|
684
765
|
pairsTag,
|
|
685
766
|
setTag
|
|
686
767
|
]);
|
|
768
|
+
var DUMP_SCHEMA = YAML11_SCHEMA.withTags({
|
|
769
|
+
...intYaml11Tag,
|
|
770
|
+
resolve: (source, isExplicit, tagName) => {
|
|
771
|
+
const result = intYaml11Tag.resolve(source, isExplicit, tagName);
|
|
772
|
+
return result === NOT_RESOLVED ? intCoreTag.resolve(source, isExplicit, tagName) : result;
|
|
773
|
+
}
|
|
774
|
+
}, {
|
|
775
|
+
...floatYaml11Tag,
|
|
776
|
+
resolve: (source, isExplicit, tagName) => {
|
|
777
|
+
const result = floatYaml11Tag.resolve(source, isExplicit, tagName);
|
|
778
|
+
return result === NOT_RESOLVED ? floatCoreTag.resolve(source, isExplicit, tagName) : result;
|
|
779
|
+
}
|
|
780
|
+
});
|
|
687
781
|
var realMapTag = defineMappingTag("tag:yaml.org,2002:map", {
|
|
688
782
|
create: () => /* @__PURE__ */ new Map(),
|
|
689
783
|
addPair: (container, key, value) => {
|
|
@@ -739,7 +833,11 @@ var legacyMapTag = defineMappingTag("tag:yaml.org,2002:map", {
|
|
|
739
833
|
return normalizedKey !== null && Object.prototype.hasOwnProperty.call(container, normalizedKey);
|
|
740
834
|
},
|
|
741
835
|
keys: (container) => Object.keys(container),
|
|
742
|
-
get: (container, key) =>
|
|
836
|
+
get: (container, key) => {
|
|
837
|
+
const normalizedKey = String(key);
|
|
838
|
+
if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null;
|
|
839
|
+
return container[normalizedKey];
|
|
840
|
+
}
|
|
743
841
|
});
|
|
744
842
|
var DEFAULT_SNIPPET_OPTIONS = {
|
|
745
843
|
maxLength: 79,
|
|
@@ -816,9 +914,13 @@ function formatError(exception, compact) {
|
|
|
816
914
|
${exception.mark.snippet}`;
|
|
817
915
|
return `${exception.reason} ${where}`;
|
|
818
916
|
}
|
|
819
|
-
var YAMLException = class extends Error {
|
|
917
|
+
var YAMLException = class YAMLException2 extends Error {
|
|
820
918
|
reason;
|
|
821
919
|
mark;
|
|
920
|
+
/**
|
|
921
|
+
* Optional `mark` contains source snippet data. Usually, use
|
|
922
|
+
* {@link YAMLException.throwAt} instead of passing it directly.
|
|
923
|
+
*/
|
|
822
924
|
constructor(reason, mark) {
|
|
823
925
|
super();
|
|
824
926
|
this.name = "YAMLException";
|
|
@@ -827,34 +929,65 @@ var YAMLException = class extends Error {
|
|
|
827
929
|
this.message = formatError(this, false);
|
|
828
930
|
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
829
931
|
}
|
|
932
|
+
/**
|
|
933
|
+
* Returns the formatted error, omitting the source snippet in compact mode.
|
|
934
|
+
*/
|
|
830
935
|
toString(compact) {
|
|
831
936
|
return `${this.name}: ${formatError(this, compact)}`;
|
|
832
937
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
938
|
+
/**
|
|
939
|
+
* Builds a YAMLException with a source snippet and throws it. `source` is
|
|
940
|
+
* the raw input text; `position` is an offset into it.
|
|
941
|
+
*/
|
|
942
|
+
static throwAt(source, position, message, filename = "") {
|
|
943
|
+
let line = 0;
|
|
944
|
+
let lineStart = 0;
|
|
945
|
+
for (let index = 0; index < position; index++) {
|
|
946
|
+
const ch = source.charCodeAt(index);
|
|
947
|
+
if (ch === 10) {
|
|
948
|
+
line++;
|
|
949
|
+
lineStart = index + 1;
|
|
950
|
+
} else if (ch === 13) {
|
|
951
|
+
line++;
|
|
952
|
+
if (source.charCodeAt(index + 1) === 10) index++;
|
|
953
|
+
lineStart = index + 1;
|
|
954
|
+
}
|
|
846
955
|
}
|
|
956
|
+
const mark = {
|
|
957
|
+
name: filename,
|
|
958
|
+
buffer: source,
|
|
959
|
+
position,
|
|
960
|
+
line,
|
|
961
|
+
column: position - lineStart
|
|
962
|
+
};
|
|
963
|
+
mark.snippet = makeSnippet(mark);
|
|
964
|
+
throw new YAMLException2(message, mark);
|
|
847
965
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
966
|
+
};
|
|
967
|
+
var EVENT_ID = {
|
|
968
|
+
DOCUMENT: 1,
|
|
969
|
+
SEQUENCE: 2,
|
|
970
|
+
MAPPING: 3,
|
|
971
|
+
SCALAR: 4,
|
|
972
|
+
ALIAS: 5,
|
|
973
|
+
POP: 6
|
|
974
|
+
};
|
|
975
|
+
var SCALAR_STYLE = {
|
|
976
|
+
PLAIN: 1,
|
|
977
|
+
SINGLE_QUOTED: 2,
|
|
978
|
+
DOUBLE_QUOTED: 3,
|
|
979
|
+
LITERAL_BLOCK: 4,
|
|
980
|
+
FOLDED_BLOCK: 5
|
|
981
|
+
};
|
|
982
|
+
var COLLECTION_STYLE = {
|
|
983
|
+
BLOCK: 1,
|
|
984
|
+
FLOW: 2
|
|
985
|
+
};
|
|
986
|
+
var CHOMPING_MODE = {
|
|
987
|
+
CLIP: 1,
|
|
988
|
+
STRIP: 2,
|
|
989
|
+
KEEP: 3
|
|
990
|
+
};
|
|
858
991
|
var NO_RANGE$3 = -1;
|
|
859
992
|
function simpleEscapeSequence(c) {
|
|
860
993
|
switch (c) {
|
|
@@ -1052,8 +1185,8 @@ function getBlockValue(input, start2, end, indent, chomping, folded) {
|
|
|
1052
1185
|
didReadContent = true;
|
|
1053
1186
|
emptyLines = 0;
|
|
1054
1187
|
}
|
|
1055
|
-
if (chomping ===
|
|
1056
|
-
else if (chomping !==
|
|
1188
|
+
if (chomping === CHOMPING_MODE.KEEP) result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines);
|
|
1189
|
+
else if (chomping !== CHOMPING_MODE.STRIP) {
|
|
1057
1190
|
if (didReadContent) result += "\n";
|
|
1058
1191
|
}
|
|
1059
1192
|
return result;
|
|
@@ -1063,22 +1196,22 @@ function getScalarValue(input, scalar) {
|
|
|
1063
1196
|
const { valueStart, valueEnd } = scalar;
|
|
1064
1197
|
if (scalar.fast) return input.slice(valueStart, valueEnd);
|
|
1065
1198
|
switch (scalar.style) {
|
|
1066
|
-
case
|
|
1199
|
+
case SCALAR_STYLE.SINGLE_QUOTED:
|
|
1067
1200
|
return getSingleQuotedValue(input, valueStart, valueEnd);
|
|
1068
|
-
case
|
|
1201
|
+
case SCALAR_STYLE.DOUBLE_QUOTED:
|
|
1069
1202
|
return getDoubleQuotedValue(input, valueStart, valueEnd);
|
|
1070
|
-
case
|
|
1203
|
+
case SCALAR_STYLE.LITERAL_BLOCK:
|
|
1071
1204
|
return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, false);
|
|
1072
|
-
case
|
|
1205
|
+
case SCALAR_STYLE.FOLDED_BLOCK:
|
|
1073
1206
|
return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, true);
|
|
1074
1207
|
default:
|
|
1075
1208
|
return getPlainValue(input, valueStart, valueEnd);
|
|
1076
1209
|
}
|
|
1077
1210
|
}
|
|
1078
|
-
var DEFAULT_TAG_HANDLERS = {
|
|
1211
|
+
var DEFAULT_TAG_HANDLERS = Object.assign(/* @__PURE__ */ Object.create(null), {
|
|
1079
1212
|
"!": "!",
|
|
1080
1213
|
"!!": "tag:yaml.org,2002:"
|
|
1081
|
-
};
|
|
1214
|
+
});
|
|
1082
1215
|
function tagPercentEncode(source) {
|
|
1083
1216
|
return encodeURI(source).replace(/!/g, "%21");
|
|
1084
1217
|
}
|
|
@@ -1099,6 +1232,7 @@ function tagNameShort(fullTag) {
|
|
|
1099
1232
|
return `!<${tagPercentEncode(tag)}>`;
|
|
1100
1233
|
}
|
|
1101
1234
|
var NO_RANGE$2 = -1;
|
|
1235
|
+
var MERGE_TAG_NAME = "tag:yaml.org,2002:merge";
|
|
1102
1236
|
var DEFAULT_CONSTRUCTOR_OPTIONS = {
|
|
1103
1237
|
filename: "",
|
|
1104
1238
|
schema: CORE_SCHEMA,
|
|
@@ -1114,26 +1248,16 @@ function eventPosition$1(event) {
|
|
|
1114
1248
|
return 0;
|
|
1115
1249
|
}
|
|
1116
1250
|
function throwError$1(state, message) {
|
|
1117
|
-
|
|
1251
|
+
YAMLException.throwAt(state.source, state.position, message, state.filename);
|
|
1118
1252
|
}
|
|
1119
1253
|
function finalizeCollection(state, position, tag, carrier) {
|
|
1120
1254
|
try {
|
|
1121
1255
|
return tag.finalize(carrier);
|
|
1122
1256
|
} catch (error) {
|
|
1123
1257
|
if (error instanceof YAMLException) throw error;
|
|
1124
|
-
|
|
1258
|
+
YAMLException.throwAt(state.source, position, error instanceof Error ? error.message : String(error), state.filename);
|
|
1125
1259
|
}
|
|
1126
1260
|
}
|
|
1127
|
-
function lookupTag(exact, prefix, tagName) {
|
|
1128
|
-
const exactTag = exact[tagName];
|
|
1129
|
-
if (exactTag) return exactTag;
|
|
1130
|
-
for (const tag of prefix) if (tagName.startsWith(tag.tagName)) return tag;
|
|
1131
|
-
}
|
|
1132
|
-
function findExplicitTag(state, exact, prefix, tagName, nodeKind) {
|
|
1133
|
-
const tag = lookupTag(exact, prefix, tagName);
|
|
1134
|
-
if (tag) return tag;
|
|
1135
|
-
throwError$1(state, `unknown ${nodeKind} tag !<${tagName}>`);
|
|
1136
|
-
}
|
|
1137
1261
|
function constructScalar(state, event) {
|
|
1138
1262
|
const source = getScalarValue(state.source, event);
|
|
1139
1263
|
const rawTag = event.tagStart === NO_RANGE$2 ? "" : state.source.slice(event.tagStart, event.tagEnd);
|
|
@@ -1144,7 +1268,7 @@ function constructScalar(state, event) {
|
|
|
1144
1268
|
tag: strTag2
|
|
1145
1269
|
};
|
|
1146
1270
|
const tagName = tagNameFull(rawTag, state.tagHandlers);
|
|
1147
|
-
const scalarTag =
|
|
1271
|
+
const scalarTag = state.schema.lookupScalarTag(tagName);
|
|
1148
1272
|
if (scalarTag) {
|
|
1149
1273
|
const result = scalarTag.resolve(source, true, tagName);
|
|
1150
1274
|
if (result === NOT_RESOLVED) throwError$1(state, `cannot resolve a node with !<${tagName}> explicit tag`);
|
|
@@ -1153,7 +1277,7 @@ function constructScalar(state, event) {
|
|
|
1153
1277
|
tag: scalarTag
|
|
1154
1278
|
};
|
|
1155
1279
|
}
|
|
1156
|
-
const collectionTagDef =
|
|
1280
|
+
const collectionTagDef = state.schema.lookupMappingTag(tagName) ?? state.schema.lookupSequenceTag(tagName);
|
|
1157
1281
|
if (collectionTagDef) {
|
|
1158
1282
|
if (source !== "") throwError$1(state, `cannot resolve a node with !<${tagName}> explicit tag`);
|
|
1159
1283
|
const carrier = collectionTagDef.create(tagName);
|
|
@@ -1164,28 +1288,15 @@ function constructScalar(state, event) {
|
|
|
1164
1288
|
}
|
|
1165
1289
|
throwError$1(state, `unknown scalar tag !<${tagName}>`);
|
|
1166
1290
|
}
|
|
1167
|
-
if (event.style ===
|
|
1168
|
-
const candidates = state.schema.implicitScalarByFirstChar.get(source.charAt(0)) ?? state.schema.implicitScalarAnyFirstChar;
|
|
1169
|
-
for (const tag of candidates) {
|
|
1170
|
-
const result = tag.resolve(source, false, tag.tagName);
|
|
1171
|
-
if (result !== NOT_RESOLVED) return {
|
|
1172
|
-
value: result,
|
|
1173
|
-
tag
|
|
1174
|
-
};
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1291
|
+
if (event.style === SCALAR_STYLE.PLAIN) return state.schema.resolveImplicitScalarTag(source);
|
|
1177
1292
|
return {
|
|
1178
1293
|
value: strTag2.resolve(source, false, strTag2.tagName),
|
|
1179
1294
|
tag: strTag2
|
|
1180
1295
|
};
|
|
1181
1296
|
}
|
|
1182
|
-
function
|
|
1297
|
+
function collectionTagName(state, event, defaultTagName) {
|
|
1183
1298
|
const rawTag = event.tagStart === NO_RANGE$2 ? "" : state.source.slice(event.tagStart, event.tagEnd);
|
|
1184
|
-
|
|
1185
|
-
return {
|
|
1186
|
-
tagName,
|
|
1187
|
-
tag: findExplicitTag(state, exact, prefix, tagName, nodeKind)
|
|
1188
|
-
};
|
|
1299
|
+
return rawTag === "" || rawTag === "!" ? defaultTagName : tagNameFull(rawTag, state.tagHandlers);
|
|
1189
1300
|
}
|
|
1190
1301
|
function isMappingTag(tag) {
|
|
1191
1302
|
return tag.nodeKind === "mapping";
|
|
@@ -1202,12 +1313,16 @@ function mergeKeys(state, frame, source, sourceTag) {
|
|
|
1202
1313
|
function mergeSource(state, frame, source, sourceTag) {
|
|
1203
1314
|
state.position = frame.keyPosition;
|
|
1204
1315
|
if (isMappingTag(sourceTag)) mergeKeys(state, frame, source, sourceTag);
|
|
1205
|
-
else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source)
|
|
1316
|
+
else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source) {
|
|
1317
|
+
const elementTag = state.nodeTags.get(element);
|
|
1318
|
+
if (!elementTag) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
|
|
1319
|
+
mergeKeys(state, frame, element, elementTag);
|
|
1320
|
+
}
|
|
1206
1321
|
else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
|
|
1207
1322
|
}
|
|
1208
1323
|
function addMappingValue(state, frame, key, value, tag) {
|
|
1209
1324
|
state.position = frame.keyPosition;
|
|
1210
|
-
if (
|
|
1325
|
+
if (frame.keyIsMerge) {
|
|
1211
1326
|
mergeSource(state, frame, value, tag);
|
|
1212
1327
|
return;
|
|
1213
1328
|
}
|
|
@@ -1222,9 +1337,7 @@ function addValue(state, value, tag) {
|
|
|
1222
1337
|
frame.value = value;
|
|
1223
1338
|
frame.hasValue = true;
|
|
1224
1339
|
} else if (frame.kind === "sequence") {
|
|
1225
|
-
if (
|
|
1226
|
-
if (!isMappingTag(tag)) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
|
|
1227
|
-
}
|
|
1340
|
+
if (isMappingTag(tag)) state.nodeTags.set(value, tag);
|
|
1228
1341
|
const err = frame.tag.addItem(frame.value, value, frame.index++);
|
|
1229
1342
|
if (err) throwError$1(state, err);
|
|
1230
1343
|
} else if (frame.hasKey) {
|
|
@@ -1236,6 +1349,7 @@ function addValue(state, value, tag) {
|
|
|
1236
1349
|
frame.key = value;
|
|
1237
1350
|
frame.keyPosition = state.position;
|
|
1238
1351
|
frame.hasKey = true;
|
|
1352
|
+
frame.keyIsMerge = tag.tagName === MERGE_TAG_NAME;
|
|
1239
1353
|
}
|
|
1240
1354
|
}
|
|
1241
1355
|
function storeAnchor(state, event, value, tag, isValueFinal) {
|
|
@@ -1260,6 +1374,7 @@ function constructFromEvents(events, options) {
|
|
|
1260
1374
|
position: 0,
|
|
1261
1375
|
frames: [],
|
|
1262
1376
|
anchors: /* @__PURE__ */ new Map(),
|
|
1377
|
+
nodeTags: /* @__PURE__ */ new Map(),
|
|
1263
1378
|
tagHandlers: /* @__PURE__ */ Object.create(null),
|
|
1264
1379
|
totalMergeKeys: 0,
|
|
1265
1380
|
aliasCount: 0
|
|
@@ -1268,8 +1383,9 @@ function constructFromEvents(events, options) {
|
|
|
1268
1383
|
const event = state.events[state.eventIndex++];
|
|
1269
1384
|
state.position = eventPosition$1(event);
|
|
1270
1385
|
switch (event.type) {
|
|
1271
|
-
case
|
|
1386
|
+
case EVENT_ID.DOCUMENT:
|
|
1272
1387
|
state.anchors = /* @__PURE__ */ new Map();
|
|
1388
|
+
state.nodeTags = /* @__PURE__ */ new Map();
|
|
1273
1389
|
state.aliasCount = 0;
|
|
1274
1390
|
state.tagHandlers = /* @__PURE__ */ Object.create(null);
|
|
1275
1391
|
for (const directive of event.directives) if (directive.kind === "tag") state.tagHandlers[directive.handle] = directive.prefix;
|
|
@@ -1280,47 +1396,49 @@ function constructFromEvents(events, options) {
|
|
|
1280
1396
|
hasValue: false
|
|
1281
1397
|
});
|
|
1282
1398
|
break;
|
|
1283
|
-
case
|
|
1399
|
+
case EVENT_ID.SCALAR: {
|
|
1284
1400
|
const { value, tag } = constructScalar(state, event);
|
|
1285
1401
|
storeAnchor(state, event, value, tag, true);
|
|
1286
1402
|
addValue(state, value, tag);
|
|
1287
1403
|
break;
|
|
1288
1404
|
}
|
|
1289
|
-
case
|
|
1290
|
-
const
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
const
|
|
1294
|
-
const
|
|
1405
|
+
case EVENT_ID.SEQUENCE: {
|
|
1406
|
+
const tagName = collectionTagName(state, event, "tag:yaml.org,2002:seq");
|
|
1407
|
+
const tag = state.schema.lookupSequenceTag(tagName);
|
|
1408
|
+
if (!tag) throwError$1(state, `unknown sequence tag !<${tagName}>`);
|
|
1409
|
+
const value = tag.create(tagName);
|
|
1410
|
+
const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult);
|
|
1295
1411
|
state.frames.push({
|
|
1296
1412
|
kind: "sequence",
|
|
1297
1413
|
position: state.position,
|
|
1298
1414
|
value,
|
|
1299
|
-
tag
|
|
1415
|
+
tag,
|
|
1300
1416
|
anchor,
|
|
1301
|
-
index: 0
|
|
1302
|
-
merge
|
|
1417
|
+
index: 0
|
|
1303
1418
|
});
|
|
1304
1419
|
break;
|
|
1305
1420
|
}
|
|
1306
|
-
case
|
|
1307
|
-
const
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1421
|
+
case EVENT_ID.MAPPING: {
|
|
1422
|
+
const tagName = collectionTagName(state, event, "tag:yaml.org,2002:map");
|
|
1423
|
+
const tag = state.schema.lookupMappingTag(tagName);
|
|
1424
|
+
if (!tag) throwError$1(state, `unknown mapping tag !<${tagName}>`);
|
|
1425
|
+
const value = tag.create(tagName);
|
|
1426
|
+
const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult);
|
|
1310
1427
|
state.frames.push({
|
|
1311
1428
|
kind: "mapping",
|
|
1312
1429
|
position: state.position,
|
|
1313
1430
|
value,
|
|
1314
|
-
tag
|
|
1431
|
+
tag,
|
|
1315
1432
|
anchor,
|
|
1316
1433
|
key: void 0,
|
|
1317
1434
|
keyPosition: state.position,
|
|
1318
1435
|
hasKey: false,
|
|
1436
|
+
keyIsMerge: false,
|
|
1319
1437
|
overridable: null
|
|
1320
1438
|
});
|
|
1321
1439
|
break;
|
|
1322
1440
|
}
|
|
1323
|
-
case
|
|
1441
|
+
case EVENT_ID.ALIAS: {
|
|
1324
1442
|
if (state.maxAliases !== -1 && ++state.aliasCount > state.maxAliases) throwError$1(state, `aliases exceeded maxAliases (${state.maxAliases})`);
|
|
1325
1443
|
const name = state.source.slice(event.anchorStart, event.anchorEnd);
|
|
1326
1444
|
const anchor = state.anchors.get(name);
|
|
@@ -1329,8 +1447,12 @@ function constructFromEvents(events, options) {
|
|
|
1329
1447
|
addValue(state, anchor.value, anchor.tag);
|
|
1330
1448
|
break;
|
|
1331
1449
|
}
|
|
1332
|
-
case
|
|
1450
|
+
case EVENT_ID.POP: {
|
|
1333
1451
|
const frame = state.frames.pop();
|
|
1452
|
+
if (frame.kind === "mapping" && frame.hasKey) {
|
|
1453
|
+
state.position = frame.keyPosition;
|
|
1454
|
+
throwError$1(state, "incomplete mapping pair in event stream");
|
|
1455
|
+
}
|
|
1334
1456
|
if (frame.kind === "document") state.documents.push(frame.value);
|
|
1335
1457
|
else {
|
|
1336
1458
|
const value = frame.tag.carrierIsResult ? frame.value : finalizeCollection(state, frame.position, frame.tag, frame.value);
|
|
@@ -1366,7 +1488,7 @@ var DEFAULT_PARSER_OPTIONS = {
|
|
|
1366
1488
|
};
|
|
1367
1489
|
function addDocumentEvent(state, explicitStart, explicitEnd) {
|
|
1368
1490
|
state.events.push({
|
|
1369
|
-
type:
|
|
1491
|
+
type: EVENT_ID.DOCUMENT,
|
|
1370
1492
|
explicitStart,
|
|
1371
1493
|
explicitEnd,
|
|
1372
1494
|
directives: state.directives
|
|
@@ -1374,7 +1496,7 @@ function addDocumentEvent(state, explicitStart, explicitEnd) {
|
|
|
1374
1496
|
}
|
|
1375
1497
|
function addSequenceEvent(state, start2, anchorStart, anchorEnd, tagStart, tagEnd, style) {
|
|
1376
1498
|
state.events.push({
|
|
1377
|
-
type:
|
|
1499
|
+
type: EVENT_ID.SEQUENCE,
|
|
1378
1500
|
start: start2,
|
|
1379
1501
|
anchorStart,
|
|
1380
1502
|
anchorEnd,
|
|
@@ -1385,7 +1507,7 @@ function addSequenceEvent(state, start2, anchorStart, anchorEnd, tagStart, tagEn
|
|
|
1385
1507
|
}
|
|
1386
1508
|
function addMappingEvent(state, start2, anchorStart, anchorEnd, tagStart, tagEnd, style) {
|
|
1387
1509
|
state.events.push({
|
|
1388
|
-
type:
|
|
1510
|
+
type: EVENT_ID.MAPPING,
|
|
1389
1511
|
start: start2,
|
|
1390
1512
|
anchorStart,
|
|
1391
1513
|
anchorEnd,
|
|
@@ -1394,9 +1516,20 @@ function addMappingEvent(state, start2, anchorStart, anchorEnd, tagStart, tagEnd
|
|
|
1394
1516
|
style
|
|
1395
1517
|
});
|
|
1396
1518
|
}
|
|
1397
|
-
function
|
|
1519
|
+
function insertFlowPairMappingEvent(state, snapshot) {
|
|
1520
|
+
state.events.splice(snapshot.eventsLength, 0, {
|
|
1521
|
+
type: EVENT_ID.MAPPING,
|
|
1522
|
+
start: snapshot.position,
|
|
1523
|
+
anchorStart: NO_RANGE$1,
|
|
1524
|
+
anchorEnd: NO_RANGE$1,
|
|
1525
|
+
tagStart: NO_RANGE$1,
|
|
1526
|
+
tagEnd: NO_RANGE$1,
|
|
1527
|
+
style: COLLECTION_STYLE.FLOW
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tagStart, tagEnd, style, chomping = CHOMPING_MODE.CLIP, indent = -1, fast = false) {
|
|
1398
1531
|
state.events.push({
|
|
1399
|
-
type:
|
|
1532
|
+
type: EVENT_ID.SCALAR,
|
|
1400
1533
|
valueStart,
|
|
1401
1534
|
valueEnd,
|
|
1402
1535
|
anchorStart,
|
|
@@ -1411,16 +1544,16 @@ function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tag
|
|
|
1411
1544
|
}
|
|
1412
1545
|
function addAliasEvent(state, anchorStart, anchorEnd) {
|
|
1413
1546
|
state.events.push({
|
|
1414
|
-
type:
|
|
1547
|
+
type: EVENT_ID.ALIAS,
|
|
1415
1548
|
anchorStart,
|
|
1416
1549
|
anchorEnd
|
|
1417
1550
|
});
|
|
1418
1551
|
}
|
|
1419
1552
|
function addPopEvent(state) {
|
|
1420
|
-
state.events.push({ type:
|
|
1553
|
+
state.events.push({ type: EVENT_ID.POP });
|
|
1421
1554
|
}
|
|
1422
1555
|
function addEmptyScalarEvent(state) {
|
|
1423
|
-
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1,
|
|
1556
|
+
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, SCALAR_STYLE.PLAIN);
|
|
1424
1557
|
}
|
|
1425
1558
|
function emptyProperties() {
|
|
1426
1559
|
return {
|
|
@@ -1449,7 +1582,7 @@ function restoreState(state, snapshot) {
|
|
|
1449
1582
|
state.events.length = snapshot.eventsLength;
|
|
1450
1583
|
}
|
|
1451
1584
|
function throwError(state, message) {
|
|
1452
|
-
|
|
1585
|
+
YAMLException.throwAt(state.input.slice(0, state.length), state.position, message, state.filename);
|
|
1453
1586
|
}
|
|
1454
1587
|
function isEol(c) {
|
|
1455
1588
|
return c === 10 || c === 13;
|
|
@@ -1617,7 +1750,7 @@ function readSingleQuotedScalar(state, nodeIndent, props) {
|
|
|
1617
1750
|
}
|
|
1618
1751
|
const end = state.position;
|
|
1619
1752
|
state.position++;
|
|
1620
|
-
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1753
|
+
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.SINGLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple);
|
|
1621
1754
|
return true;
|
|
1622
1755
|
}
|
|
1623
1756
|
if (isEol(ch)) {
|
|
@@ -1639,7 +1772,7 @@ function readDoubleQuotedScalar(state, nodeIndent, props) {
|
|
|
1639
1772
|
if (ch === 34) {
|
|
1640
1773
|
const end = state.position;
|
|
1641
1774
|
state.position++;
|
|
1642
|
-
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1775
|
+
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.DOUBLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple);
|
|
1643
1776
|
return true;
|
|
1644
1777
|
}
|
|
1645
1778
|
if (ch === 92) {
|
|
@@ -1667,18 +1800,18 @@ function readDoubleQuotedScalar(state, nodeIndent, props) {
|
|
|
1667
1800
|
}
|
|
1668
1801
|
function readBlockScalar(state, parentIndent, props) {
|
|
1669
1802
|
const ch = state.input.charCodeAt(state.position);
|
|
1670
|
-
let chomping =
|
|
1803
|
+
let chomping = CHOMPING_MODE.CLIP;
|
|
1671
1804
|
let indent = -1;
|
|
1672
1805
|
let detectedIndent = false;
|
|
1673
1806
|
if (ch !== 124 && ch !== 62) return false;
|
|
1674
|
-
const style = ch === 124 ?
|
|
1807
|
+
const style = ch === 124 ? SCALAR_STYLE.LITERAL_BLOCK : SCALAR_STYLE.FOLDED_BLOCK;
|
|
1675
1808
|
state.position++;
|
|
1676
1809
|
while (state.input.charCodeAt(state.position) !== 0) {
|
|
1677
1810
|
const current = state.input.charCodeAt(state.position);
|
|
1678
1811
|
const digit = fromDecimalCode(current);
|
|
1679
1812
|
if (current === 43 || current === 45) {
|
|
1680
|
-
if (chomping !==
|
|
1681
|
-
chomping = current === 43 ?
|
|
1813
|
+
if (chomping !== CHOMPING_MODE.CLIP) throwError(state, "repeat of a chomping mode identifier");
|
|
1814
|
+
chomping = current === 43 ? CHOMPING_MODE.KEEP : CHOMPING_MODE.STRIP;
|
|
1682
1815
|
state.position++;
|
|
1683
1816
|
} else if (digit >= 0) {
|
|
1684
1817
|
if (digit === 0) throwError(state, "bad explicit indentation width of a block scalar; it cannot be less than one");
|
|
@@ -1793,7 +1926,7 @@ function readPlainScalar(state, nodeIndent, nodeContext, props) {
|
|
|
1793
1926
|
}
|
|
1794
1927
|
if (end === start2) return false;
|
|
1795
1928
|
checkPrintable(state, start2, end);
|
|
1796
|
-
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1929
|
+
addScalarEvent(state, start2, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.PLAIN, CHOMPING_MODE.CLIP, -1, !multiline);
|
|
1797
1930
|
return true;
|
|
1798
1931
|
}
|
|
1799
1932
|
function skipFlowSeparationSpace(state, nodeIndent) {
|
|
@@ -1808,8 +1941,8 @@ function readFlowCollection(state, nodeIndent, props) {
|
|
|
1808
1941
|
let readNext = true;
|
|
1809
1942
|
if (ch !== 91 && ch !== 123) return false;
|
|
1810
1943
|
const terminator = isMapping ? 125 : 93;
|
|
1811
|
-
if (isMapping) addMappingEvent(state, start2, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1812
|
-
else addSequenceEvent(state, start2, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1944
|
+
if (isMapping) addMappingEvent(state, start2, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW);
|
|
1945
|
+
else addSequenceEvent(state, start2, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW);
|
|
1813
1946
|
state.position++;
|
|
1814
1947
|
while (state.input.charCodeAt(state.position) !== 0) {
|
|
1815
1948
|
skipFlowSeparationSpace(state, nodeIndent);
|
|
@@ -1837,12 +1970,8 @@ function readFlowCollection(state, nodeIndent, props) {
|
|
|
1837
1970
|
state.position++;
|
|
1838
1971
|
skipFlowSeparationSpace(state, nodeIndent);
|
|
1839
1972
|
if (!isMapping) {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
if (!parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)) addEmptyScalarEvent(state);
|
|
1843
|
-
skipFlowSeparationSpace(state, nodeIndent);
|
|
1844
|
-
state.position++;
|
|
1845
|
-
skipFlowSeparationSpace(state, nodeIndent);
|
|
1973
|
+
insertFlowPairMappingEvent(state, entryStart);
|
|
1974
|
+
if (!keyWasRead) addEmptyScalarEvent(state);
|
|
1846
1975
|
} else if (!keyWasRead) addEmptyScalarEvent(state);
|
|
1847
1976
|
if (!parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)) addEmptyScalarEvent(state);
|
|
1848
1977
|
skipFlowSeparationSpace(state, nodeIndent);
|
|
@@ -1852,9 +1981,8 @@ function readFlowCollection(state, nodeIndent, props) {
|
|
|
1852
1981
|
addEmptyScalarEvent(state);
|
|
1853
1982
|
} else if (isMapping) addEmptyScalarEvent(state);
|
|
1854
1983
|
else if (isPair) {
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
|
|
1984
|
+
insertFlowPairMappingEvent(state, entryStart);
|
|
1985
|
+
if (!keyWasRead) addEmptyScalarEvent(state);
|
|
1858
1986
|
addEmptyScalarEvent(state);
|
|
1859
1987
|
addPopEvent(state);
|
|
1860
1988
|
}
|
|
@@ -1868,7 +1996,7 @@ function readFlowCollection(state, nodeIndent, props) {
|
|
|
1868
1996
|
}
|
|
1869
1997
|
function readBlockSequence(state, nodeIndent, props) {
|
|
1870
1998
|
if (state.firstTabInLine !== -1 || state.input.charCodeAt(state.position) !== 45 || !isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) return false;
|
|
1871
|
-
addSequenceEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
1999
|
+
addSequenceEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
|
|
1872
2000
|
while (state.input.charCodeAt(state.position) === 45 && isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {
|
|
1873
2001
|
if (state.firstTabInLine !== -1) {
|
|
1874
2002
|
state.position = state.firstTabInLine;
|
|
@@ -1904,7 +2032,7 @@ function readBlockMapping(state, nodeIndent, flowIndent, props) {
|
|
|
1904
2032
|
const entryLine = state.line;
|
|
1905
2033
|
if ((ch === 63 || ch === 58) && isWsOrEolOrEnd(following)) {
|
|
1906
2034
|
if (!mappingOpened) {
|
|
1907
|
-
addMappingEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
2035
|
+
addMappingEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
|
|
1908
2036
|
mappingOpened = true;
|
|
1909
2037
|
}
|
|
1910
2038
|
if (ch === 63) {
|
|
@@ -1934,7 +2062,7 @@ function readBlockMapping(state, nodeIndent, flowIndent, props) {
|
|
|
1934
2062
|
if (!isWsOrEolOrEnd(ch)) throwError(state, "a whitespace character is expected after the key-value separator within a block mapping");
|
|
1935
2063
|
if (!mappingOpened) {
|
|
1936
2064
|
restoreState(state, beforeKey);
|
|
1937
|
-
addMappingEvent(state, beforeKey.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
2065
|
+
addMappingEvent(state, beforeKey.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
|
|
1938
2066
|
mappingOpened = true;
|
|
1939
2067
|
parseNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true);
|
|
1940
2068
|
ch = state.input.charCodeAt(state.position);
|
|
@@ -1995,10 +2123,6 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
|
|
|
1995
2123
|
else if (state.lineIndent === parentIndent) indentStatus = 0;
|
|
1996
2124
|
else indentStatus = -1;
|
|
1997
2125
|
}
|
|
1998
|
-
if (state.position === state.lineStart && testDocumentSeparator(state)) {
|
|
1999
|
-
state.depth--;
|
|
2000
|
-
return false;
|
|
2001
|
-
}
|
|
2002
2126
|
if (indentStatus === 1) while (true) {
|
|
2003
2127
|
const ch = state.input.charCodeAt(state.position);
|
|
2004
2128
|
const propertyState = snapshotState(state);
|
|
@@ -2006,7 +2130,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
|
|
|
2006
2130
|
if (atNewLine && allowBlockStyles && (props.tagStart !== NO_RANGE$1 || props.anchorStart !== NO_RANGE$1) && (ch === 33 || ch === 38)) {
|
|
2007
2131
|
const fallbackState = snapshotState(state);
|
|
2008
2132
|
const flowIndent = parentIndent + 1;
|
|
2009
|
-
if (readBlockMapping(state, state.position - state.lineStart, flowIndent, props) && state.events[fallbackState.eventsLength]?.type ===
|
|
2133
|
+
if (readBlockMapping(state, state.position - state.lineStart, flowIndent, props) && state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) {
|
|
2010
2134
|
state.depth--;
|
|
2011
2135
|
return true;
|
|
2012
2136
|
}
|
|
@@ -2034,7 +2158,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
|
|
|
2034
2158
|
const fallbackState = snapshotState(state);
|
|
2035
2159
|
const propertyIndent = propertyStart.position - propertyStart.lineStart;
|
|
2036
2160
|
restoreState(state, propertyStart);
|
|
2037
|
-
if (readBlockMapping(state, propertyIndent, flowIndent, emptyProperties()) && state.events[fallbackState.eventsLength]?.type ===
|
|
2161
|
+
if (readBlockMapping(state, propertyIndent, flowIndent, emptyProperties()) && state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) hasContent = true;
|
|
2038
2162
|
else restoreState(state, fallbackState);
|
|
2039
2163
|
}
|
|
2040
2164
|
if (!hasContent && (allowBlockScalars && readBlockScalar(state, flowIndent, props) || readSingleQuotedScalar(state, flowIndent, props) || readDoubleQuotedScalar(state, flowIndent, props) || readAlias(state, props) || readPlainScalar(state, flowIndent, nodeContext, props))) hasContent = true;
|
|
@@ -2043,7 +2167,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
|
|
|
2043
2167
|
}
|
|
2044
2168
|
allowBlockScalars = allowBlockScalars && !hasContent;
|
|
2045
2169
|
if (!hasContent && (props.anchorStart !== NO_RANGE$1 || props.tagStart !== NO_RANGE$1 || allowBlockScalars)) {
|
|
2046
|
-
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd,
|
|
2170
|
+
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.PLAIN);
|
|
2047
2171
|
hasContent = true;
|
|
2048
2172
|
}
|
|
2049
2173
|
state.depth--;
|
|
@@ -2128,7 +2252,7 @@ function readDocument(state) {
|
|
|
2128
2252
|
}
|
|
2129
2253
|
}
|
|
2130
2254
|
const documentEvent = state.events[documentEventIndex];
|
|
2131
|
-
if (documentEvent?.type ===
|
|
2255
|
+
if (documentEvent?.type === EVENT_ID.DOCUMENT) documentEvent.explicitEnd = explicitEnd;
|
|
2132
2256
|
addPopEvent(state);
|
|
2133
2257
|
if (!explicitEnd && state.position < state.length && !(state.position === state.lineStart && testDocumentSeparator(state))) throwError(state, "end of the stream or a document separator is expected");
|
|
2134
2258
|
}
|
|
@@ -2150,7 +2274,7 @@ function parseEvents(input, options) {
|
|
|
2150
2274
|
events: []
|
|
2151
2275
|
};
|
|
2152
2276
|
const nullpos = input.indexOf("\0");
|
|
2153
|
-
if (nullpos !== -1)
|
|
2277
|
+
if (nullpos !== -1) YAMLException.throwAt(input, nullpos, "null byte is not allowed in input", state.filename);
|
|
2154
2278
|
if (state.input.charCodeAt(state.position) === 65279) state.position++;
|
|
2155
2279
|
while (state.position < state.length) {
|
|
2156
2280
|
skipSeparationSpace(state, true);
|
|
@@ -2186,6 +2310,7 @@ function load(input, options) {
|
|
|
2186
2310
|
throw new YAMLException("expected a single document in the stream, but found more");
|
|
2187
2311
|
}
|
|
2188
2312
|
var Style = class {
|
|
2313
|
+
/** Whether to print the node's tag explicitly. */
|
|
2189
2314
|
tagged = false;
|
|
2190
2315
|
flow = false;
|
|
2191
2316
|
singleQuoted = false;
|
|
@@ -2221,9 +2346,9 @@ function buildRepresentTypes(schema) {
|
|
|
2221
2346
|
function matchTag(state, object) {
|
|
2222
2347
|
for (let index = 0, length = state.representTypes.length; index < length; index += 1) {
|
|
2223
2348
|
const { tag, implicitTag } = state.representTypes[index];
|
|
2224
|
-
if (tag.identify
|
|
2349
|
+
if (tag.identify(object)) {
|
|
2225
2350
|
let tagName;
|
|
2226
|
-
if (tag.matchByTagPrefix
|
|
2351
|
+
if (tag.matchByTagPrefix) tagName = tag.representTagName(object);
|
|
2227
2352
|
else tagName = tag.tagName;
|
|
2228
2353
|
return {
|
|
2229
2354
|
tag,
|
|
@@ -2423,8 +2548,7 @@ function createPresenterState(options) {
|
|
|
2423
2548
|
};
|
|
2424
2549
|
return {
|
|
2425
2550
|
...opts,
|
|
2426
|
-
defaultScalarTagName: opts.schema.defaultScalarTag.tagName
|
|
2427
|
-
implicitResolvers: opts.schema.implicitScalarTags
|
|
2551
|
+
defaultScalarTagName: opts.schema.defaultScalarTag.tagName
|
|
2428
2552
|
};
|
|
2429
2553
|
}
|
|
2430
2554
|
function encodeNonPrintable(character) {
|
|
@@ -2465,13 +2589,6 @@ function scalarLayout(state, level) {
|
|
|
2465
2589
|
lineWidth: state.lineWidth === -1 ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent)
|
|
2466
2590
|
};
|
|
2467
2591
|
}
|
|
2468
|
-
function resolveImplicitTag(state, str) {
|
|
2469
|
-
for (let index = 0, length = state.implicitResolvers.length; index < length; index += 1) {
|
|
2470
|
-
const tagDefinition = state.implicitResolvers[index];
|
|
2471
|
-
if (tagDefinition.resolve(str, false, tagDefinition.tagName) !== NOT_RESOLVED) return tagDefinition.tagName;
|
|
2472
|
-
}
|
|
2473
|
-
return state.defaultScalarTagName;
|
|
2474
|
-
}
|
|
2475
2592
|
function isWhitespace(c) {
|
|
2476
2593
|
return c === CHAR_SPACE || c === CHAR_TAB;
|
|
2477
2594
|
}
|
|
@@ -2491,7 +2608,7 @@ function isNsCharOrWhitespace(c) {
|
|
|
2491
2608
|
function isPlainSafe(c, prev, inblock) {
|
|
2492
2609
|
const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c);
|
|
2493
2610
|
const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c);
|
|
2494
|
-
return (inblock ? cIsNsCharOrWhitespace : cIsNsCharOrWhitespace && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET) && c !== CHAR_SHARP && !(prev === CHAR_COLON && !cIsNsChar) || isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP || prev === CHAR_COLON && cIsNsChar;
|
|
2611
|
+
return (inblock ? cIsNsCharOrWhitespace : cIsNsCharOrWhitespace && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET) && c !== CHAR_SHARP && !(prev === CHAR_COLON && !cIsNsChar) || isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP || prev === CHAR_COLON && cIsNsChar && (inblock || c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET);
|
|
2495
2612
|
}
|
|
2496
2613
|
function isPlainSafeFirst(c) {
|
|
2497
2614
|
return isPrintable(c) && c !== CHAR_BOM && !isWhitespace(c) && c !== CHAR_MINUS && c !== CHAR_QUESTION && c !== CHAR_COLON && c !== CHAR_COMMA && c !== CHAR_LEFT_SQUARE_BRACKET && c !== CHAR_RIGHT_SQUARE_BRACKET && c !== CHAR_LEFT_CURLY_BRACKET && c !== CHAR_RIGHT_CURLY_BRACKET && c !== CHAR_SHARP && c !== CHAR_AMPERSAND && c !== CHAR_ASTERISK && c !== CHAR_EXCLAMATION && c !== CHAR_VERTICAL_LINE && c !== CHAR_EQUALS && c !== CHAR_GREATER_THAN && c !== CHAR_SINGLE_QUOTE && c !== CHAR_DOUBLE_QUOTE && c !== CHAR_PERCENT && c !== CHAR_COMMERCIAL_AT && c !== CHAR_GRAVE_ACCENT;
|
|
@@ -2547,14 +2664,14 @@ function chooseScalarStyle(state, string, layout, singleLineOnly, forceQuote, in
|
|
|
2547
2664
|
if (char === CHAR_LINE_FEED) {
|
|
2548
2665
|
hasLineBreak = true;
|
|
2549
2666
|
if (shouldTrackWidth) {
|
|
2550
|
-
hasFoldableLine = hasFoldableLine || i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1]
|
|
2667
|
+
hasFoldableLine = hasFoldableLine || i - previousLineBreak - 1 > lineWidth && !isMoreIndented(string[previousLineBreak + 1]);
|
|
2551
2668
|
previousLineBreak = i;
|
|
2552
2669
|
}
|
|
2553
2670
|
} else if (!isPrintable(char)) return STYLE_DOUBLE;
|
|
2554
2671
|
plain = plain && isPlainSafe(char, prevChar, inblock);
|
|
2555
2672
|
prevChar = char;
|
|
2556
2673
|
}
|
|
2557
|
-
hasFoldableLine = hasFoldableLine || shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1]
|
|
2674
|
+
hasFoldableLine = hasFoldableLine || shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && !isMoreIndented(string[previousLineBreak + 1]);
|
|
2558
2675
|
}
|
|
2559
2676
|
if (!hasLineBreak && !hasFoldableLine) {
|
|
2560
2677
|
if (plain && !forceQuote) return STYLE_PLAIN;
|
|
@@ -2588,11 +2705,11 @@ function resolveScalarStyle(state, node, layout, iskey, inblock) {
|
|
|
2588
2705
|
}
|
|
2589
2706
|
const string = node.value;
|
|
2590
2707
|
if (string.length === 0) {
|
|
2591
|
-
if (node.style.tagged ||
|
|
2708
|
+
if (node.style.tagged || state.schema.resolveImplicitScalarTag(string).tag.tagName === node.tag) return STYLE_PLAIN;
|
|
2592
2709
|
return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE;
|
|
2593
2710
|
}
|
|
2594
2711
|
const style = chooseScalarStyle(state, string, layout, singleLineOnly, state.forceQuotes && !iskey, inblock);
|
|
2595
|
-
if (style === STYLE_PLAIN && !node.style.tagged &&
|
|
2712
|
+
if (style === STYLE_PLAIN && !node.style.tagged && state.schema.resolveImplicitScalarTag(string).tag.tagName !== node.tag) return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE;
|
|
2596
2713
|
return style;
|
|
2597
2714
|
}
|
|
2598
2715
|
function blockHeader(string, indentPerLevel) {
|
|
@@ -2619,27 +2736,30 @@ function encodeFlowBreaks(string, indent) {
|
|
|
2619
2736
|
function dropEndingNewline(string) {
|
|
2620
2737
|
return string[string.length - 1] === "\n" ? string.slice(0, -1) : string;
|
|
2621
2738
|
}
|
|
2739
|
+
function isMoreIndented(char) {
|
|
2740
|
+
return char === " " || char === " ";
|
|
2741
|
+
}
|
|
2622
2742
|
function foldBlockScalar(string, width) {
|
|
2623
2743
|
const lineRe = /(\n+)([^\n]*)/g;
|
|
2624
2744
|
let nextLF = string.indexOf("\n");
|
|
2625
2745
|
if (nextLF === -1) nextLF = string.length;
|
|
2626
2746
|
lineRe.lastIndex = nextLF;
|
|
2627
2747
|
let result = foldLine(string.slice(0, nextLF), width);
|
|
2628
|
-
let prevMoreIndented = string[0] === "\n" || string[0]
|
|
2748
|
+
let prevMoreIndented = string[0] === "\n" || isMoreIndented(string[0]);
|
|
2629
2749
|
let moreIndented;
|
|
2630
2750
|
let match;
|
|
2631
2751
|
while (match = lineRe.exec(string)) {
|
|
2632
2752
|
const prefix = match[1];
|
|
2633
2753
|
const line = match[2];
|
|
2634
|
-
moreIndented = line
|
|
2754
|
+
moreIndented = line !== "" && isMoreIndented(line[0]);
|
|
2635
2755
|
result += prefix + (!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") + foldLine(line, width);
|
|
2636
2756
|
prevMoreIndented = moreIndented;
|
|
2637
2757
|
}
|
|
2638
2758
|
return result;
|
|
2639
2759
|
}
|
|
2640
2760
|
function foldLine(line, width) {
|
|
2641
|
-
if (line === "" || line[0]
|
|
2642
|
-
const breakRe = / [^ ]/g;
|
|
2761
|
+
if (line === "" || isMoreIndented(line[0])) return line;
|
|
2762
|
+
const breakRe = / [^ \t]/g;
|
|
2643
2763
|
let match;
|
|
2644
2764
|
let start2 = 0;
|
|
2645
2765
|
let end;
|
|
@@ -2873,22 +2993,9 @@ function present(documents, options) {
|
|
|
2873
2993
|
}
|
|
2874
2994
|
return result;
|
|
2875
2995
|
}
|
|
2876
|
-
var DEFAULT_DUMP_SCHEMA = YAML11_SCHEMA.withTags({
|
|
2877
|
-
...intYaml11Tag,
|
|
2878
|
-
resolve: (source, isExplicit, tagName) => {
|
|
2879
|
-
const result = intYaml11Tag.resolve(source, isExplicit, tagName);
|
|
2880
|
-
return result === NOT_RESOLVED ? intCoreTag.resolve(source, isExplicit, tagName) : result;
|
|
2881
|
-
}
|
|
2882
|
-
}, {
|
|
2883
|
-
...floatYaml11Tag,
|
|
2884
|
-
resolve: (source, isExplicit, tagName) => {
|
|
2885
|
-
const result = floatYaml11Tag.resolve(source, isExplicit, tagName);
|
|
2886
|
-
return result === NOT_RESOLVED ? floatCoreTag.resolve(source, isExplicit, tagName) : result;
|
|
2887
|
-
}
|
|
2888
|
-
});
|
|
2889
2996
|
var DEFAULT_DUMP_OPTIONS = {
|
|
2890
2997
|
...DEFAULT_PRESENTER_OPTIONS,
|
|
2891
|
-
schema:
|
|
2998
|
+
schema: DUMP_SCHEMA,
|
|
2892
2999
|
skipInvalid: false,
|
|
2893
3000
|
noRefs: false,
|
|
2894
3001
|
flowLevel: -1,
|
|
@@ -2915,9 +3022,26 @@ function dump(input, options = {}) {
|
|
|
2915
3022
|
schema: opts.schema
|
|
2916
3023
|
});
|
|
2917
3024
|
}
|
|
3025
|
+
var EVENT_DOCUMENT = EVENT_ID.DOCUMENT;
|
|
3026
|
+
var EVENT_SEQUENCE = EVENT_ID.SEQUENCE;
|
|
3027
|
+
var EVENT_MAPPING = EVENT_ID.MAPPING;
|
|
3028
|
+
var EVENT_SCALAR = EVENT_ID.SCALAR;
|
|
3029
|
+
var EVENT_ALIAS = EVENT_ID.ALIAS;
|
|
3030
|
+
var EVENT_POP = EVENT_ID.POP;
|
|
3031
|
+
var SCALAR_STYLE_PLAIN = SCALAR_STYLE.PLAIN;
|
|
3032
|
+
var SCALAR_STYLE_SINGLE_QUOTED = SCALAR_STYLE.SINGLE_QUOTED;
|
|
3033
|
+
var SCALAR_STYLE_DOUBLE_QUOTED = SCALAR_STYLE.DOUBLE_QUOTED;
|
|
3034
|
+
var SCALAR_STYLE_LITERAL_BLOCK = SCALAR_STYLE.LITERAL_BLOCK;
|
|
3035
|
+
var SCALAR_STYLE_FOLDED_BLOCK = SCALAR_STYLE.FOLDED_BLOCK;
|
|
3036
|
+
var COLLECTION_STYLE_BLOCK = COLLECTION_STYLE.BLOCK;
|
|
3037
|
+
var COLLECTION_STYLE_FLOW = COLLECTION_STYLE.FLOW;
|
|
3038
|
+
var CHOMPING_CLIP = CHOMPING_MODE.CLIP;
|
|
3039
|
+
var CHOMPING_STRIP = CHOMPING_MODE.STRIP;
|
|
3040
|
+
var CHOMPING_KEEP = CHOMPING_MODE.KEEP;
|
|
2918
3041
|
|
|
2919
3042
|
// src/constants.ts
|
|
2920
|
-
var AVAILABLE_MIRRORS = ["gh-proxy.org", "
|
|
3043
|
+
var AVAILABLE_MIRRORS = ["v6.gh-proxy.org", "gh-proxy.org", "hk.gh-proxy.org", "cdn.gh-proxy.org"];
|
|
3044
|
+
var DEFAULT_MIRROR = "https://v6.gh-proxy.org/";
|
|
2921
3045
|
var UI_URLS = {
|
|
2922
3046
|
zash: "https://board.zash.run.place",
|
|
2923
3047
|
dash: "https://metacubex.github.io/metacubexd",
|
|
@@ -2974,6 +3098,7 @@ var DEFAULT_TEST_URL = "http://www.gstatic.com/generate_204";
|
|
|
2974
3098
|
var DEFAULT_CLEAN_ROUNDS = 2;
|
|
2975
3099
|
var AUTO_CLEAN_THRESHOLD = 100;
|
|
2976
3100
|
var AUTO_CLEAN_THRESHOLD_GITHUB = 50;
|
|
3101
|
+
var AUTO_CLEAN_COOLDOWN_HOURS = 12;
|
|
2977
3102
|
|
|
2978
3103
|
// src/overwrite.ts
|
|
2979
3104
|
import fs3 from "fs";
|
|
@@ -3025,8 +3150,9 @@ function ensureDirs() {
|
|
|
3025
3150
|
}
|
|
3026
3151
|
}
|
|
3027
3152
|
}
|
|
3153
|
+
var atomicWriteSeq = 0;
|
|
3028
3154
|
function atomicWriteFileSync(filePath, content, options) {
|
|
3029
|
-
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
3155
|
+
const tmp = `${filePath}.${process.pid}.${atomicWriteSeq++}.tmp`;
|
|
3030
3156
|
try {
|
|
3031
3157
|
fs.writeFileSync(tmp, content, options);
|
|
3032
3158
|
fs.renameSync(tmp, filePath);
|
|
@@ -3097,6 +3223,7 @@ function maskUrl(url) {
|
|
|
3097
3223
|
}
|
|
3098
3224
|
if (parsed.username) parsed.username = "***";
|
|
3099
3225
|
if (parsed.password) parsed.password = "***";
|
|
3226
|
+
parsed.pathname = parsed.pathname.split("/").map((seg) => seg.length >= 16 ? `${seg.slice(0, 4)}***${seg.slice(-4)}` : seg).join("/");
|
|
3100
3227
|
return parsed.toString();
|
|
3101
3228
|
} catch {
|
|
3102
3229
|
if (url.length > 30) {
|
|
@@ -3112,6 +3239,12 @@ function readSubscriptionCache() {
|
|
|
3112
3239
|
const content = fs2.readFileSync(PATHS.subscriptionsCacheFile, "utf8");
|
|
3113
3240
|
return JSON.parse(content);
|
|
3114
3241
|
} catch {
|
|
3242
|
+
try {
|
|
3243
|
+
fs2.copyFileSync(PATHS.subscriptionsCacheFile, `${PATHS.subscriptionsCacheFile}.bak`);
|
|
3244
|
+
console.warn(`\u8B66\u544A: \u8BA2\u9605\u7F13\u5B58\u683C\u5F0F\u635F\u574F\uFF0C\u5DF2\u5907\u4EFD\u5230 ${PATHS.subscriptionsCacheFile}.bak`);
|
|
3245
|
+
} catch {
|
|
3246
|
+
console.warn("\u8B66\u544A: \u8BA2\u9605\u7F13\u5B58\u683C\u5F0F\u635F\u574F\uFF0C\u5DF2\u5FFD\u7565");
|
|
3247
|
+
}
|
|
3115
3248
|
return {};
|
|
3116
3249
|
}
|
|
3117
3250
|
}
|
|
@@ -3214,6 +3347,7 @@ function parseOverrideKey(key) {
|
|
|
3214
3347
|
let forceOverwrite = false;
|
|
3215
3348
|
let arrayPrepend = false;
|
|
3216
3349
|
let arrayAppend = false;
|
|
3350
|
+
let arrayMergeByName = false;
|
|
3217
3351
|
const lastChar = key[key.length - 1];
|
|
3218
3352
|
const openAngleCount = (key.match(/</g) || []).length;
|
|
3219
3353
|
const closeAngleCount = (key.match(/>/g) || []).length;
|
|
@@ -3234,6 +3368,9 @@ function parseOverrideKey(key) {
|
|
|
3234
3368
|
} else {
|
|
3235
3369
|
actualKey = unwrapped;
|
|
3236
3370
|
}
|
|
3371
|
+
} else if (actualKey.startsWith("~")) {
|
|
3372
|
+
arrayMergeByName = true;
|
|
3373
|
+
actualKey = actualKey.slice(1);
|
|
3237
3374
|
} else {
|
|
3238
3375
|
if (actualKey.startsWith("+")) {
|
|
3239
3376
|
arrayPrepend = true;
|
|
@@ -3244,7 +3381,7 @@ function parseOverrideKey(key) {
|
|
|
3244
3381
|
actualKey = actualKey.slice(0, -1);
|
|
3245
3382
|
}
|
|
3246
3383
|
}
|
|
3247
|
-
return { key: actualKey, forceOverwrite, arrayPrepend, arrayAppend };
|
|
3384
|
+
return { key: actualKey, forceOverwrite, arrayPrepend, arrayAppend, arrayMergeByName };
|
|
3248
3385
|
}
|
|
3249
3386
|
function deepMergeWithOverrides(target, override) {
|
|
3250
3387
|
let t = target;
|
|
@@ -3262,8 +3399,24 @@ function deepMergeWithOverrides(target, override) {
|
|
|
3262
3399
|
}
|
|
3263
3400
|
const result = { ...t };
|
|
3264
3401
|
for (const [rawKey, value] of Object.entries(override)) {
|
|
3265
|
-
const { key, forceOverwrite, arrayPrepend, arrayAppend } = parseOverrideKey(rawKey);
|
|
3402
|
+
const { key, forceOverwrite, arrayPrepend, arrayAppend, arrayMergeByName } = parseOverrideKey(rawKey);
|
|
3266
3403
|
const existingValue = result[key];
|
|
3404
|
+
if (arrayMergeByName) {
|
|
3405
|
+
const existingArr = Array.isArray(existingValue) ? existingValue : [];
|
|
3406
|
+
const overrideArr = Array.isArray(value) ? value : [value];
|
|
3407
|
+
const merged = [...existingArr];
|
|
3408
|
+
for (const item of overrideArr) {
|
|
3409
|
+
const name = item && typeof item === "object" && !Array.isArray(item) ? item.name : void 0;
|
|
3410
|
+
const idx = name != null ? merged.findIndex((e) => e && typeof e === "object" && e.name === name) : -1;
|
|
3411
|
+
if (idx >= 0) {
|
|
3412
|
+
merged[idx] = deepMergeWithOverrides(merged[idx], item);
|
|
3413
|
+
} else {
|
|
3414
|
+
merged.push(item);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
result[key] = merged;
|
|
3418
|
+
continue;
|
|
3419
|
+
}
|
|
3267
3420
|
if (arrayPrepend || arrayAppend) {
|
|
3268
3421
|
const existingArr = Array.isArray(existingValue) ? existingValue : [];
|
|
3269
3422
|
const overrideArr = Array.isArray(value) ? value : [value];
|
|
@@ -3296,6 +3449,69 @@ function setOverwriteEnabled(enabled) {
|
|
|
3296
3449
|
function isOverwriteFilename(filename) {
|
|
3297
3450
|
return filename === "overwrite.yaml" || /^overwrite\..+\.ya?ml$/.test(filename);
|
|
3298
3451
|
}
|
|
3452
|
+
var MATCH_KEYS = /* @__PURE__ */ new Set(["subscription", "url-domain"]);
|
|
3453
|
+
function normalizeMatch(raw, fileName) {
|
|
3454
|
+
if (raw == null) return void 0;
|
|
3455
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
3456
|
+
console.warn(`\u8B66\u544A: \u8986\u5199\u6587\u4EF6 "${fileName}" \u7684 match \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565\u4F5C\u7528\u57DF\u9650\u5B9A`);
|
|
3457
|
+
return void 0;
|
|
3458
|
+
}
|
|
3459
|
+
const result = {};
|
|
3460
|
+
let hasValid = false;
|
|
3461
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
3462
|
+
if (!MATCH_KEYS.has(key)) {
|
|
3463
|
+
console.warn(`\u8B66\u544A: \u8986\u5199\u6587\u4EF6 "${fileName}" \u7684 match \u542B\u672A\u77E5\u952E "${key}"\uFF0C\u5DF2\u5FFD\u7565`);
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
const arr = (Array.isArray(value) ? value : [value]).filter((v) => typeof v === "string" && v.length > 0);
|
|
3467
|
+
if (arr.length === 0) continue;
|
|
3468
|
+
result[key] = arr;
|
|
3469
|
+
hasValid = true;
|
|
3470
|
+
}
|
|
3471
|
+
return hasValid ? result : void 0;
|
|
3472
|
+
}
|
|
3473
|
+
function summarizeMatch(match) {
|
|
3474
|
+
if (!match) return void 0;
|
|
3475
|
+
const parts = [];
|
|
3476
|
+
for (const [key, value] of Object.entries(match)) {
|
|
3477
|
+
const vals = Array.isArray(value) ? value : [value];
|
|
3478
|
+
parts.push(`${key}=${vals.join("/")}`);
|
|
3479
|
+
}
|
|
3480
|
+
return parts.length > 0 ? parts.join(", ") : void 0;
|
|
3481
|
+
}
|
|
3482
|
+
function splitUrlsLocal(url) {
|
|
3483
|
+
return url.split(",").map((u) => u.trim()).filter(Boolean);
|
|
3484
|
+
}
|
|
3485
|
+
function hostMatchesDomain(host, domain) {
|
|
3486
|
+
const h = host.toLowerCase();
|
|
3487
|
+
const d = domain.toLowerCase();
|
|
3488
|
+
return h === d || h.endsWith(`.${d}`);
|
|
3489
|
+
}
|
|
3490
|
+
function matchesScope(match, scope) {
|
|
3491
|
+
if (!match) return true;
|
|
3492
|
+
if (match.subscription) {
|
|
3493
|
+
const names = Array.isArray(match.subscription) ? match.subscription : [match.subscription];
|
|
3494
|
+
if (!scope?.subName || !names.includes(scope.subName)) return false;
|
|
3495
|
+
}
|
|
3496
|
+
if (match["url-domain"]) {
|
|
3497
|
+
const domains = Array.isArray(match["url-domain"]) ? match["url-domain"] : [match["url-domain"]];
|
|
3498
|
+
if (!scope?.subUrl) return false;
|
|
3499
|
+
const hosts = [];
|
|
3500
|
+
for (const u of splitUrlsLocal(scope.subUrl)) {
|
|
3501
|
+
try {
|
|
3502
|
+
hosts.push(new URL(u).hostname);
|
|
3503
|
+
} catch {
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
if (hosts.length === 0) return false;
|
|
3507
|
+
const ok = domains.some((d) => hosts.some((h) => hostMatchesDomain(h, d)));
|
|
3508
|
+
if (!ok) return false;
|
|
3509
|
+
}
|
|
3510
|
+
return true;
|
|
3511
|
+
}
|
|
3512
|
+
function filterOverwriteFilesByScope(files, scope) {
|
|
3513
|
+
return files.filter((f) => matchesScope(f.match, scope));
|
|
3514
|
+
}
|
|
3299
3515
|
function loadOverwriteFile() {
|
|
3300
3516
|
const dir = USER_DATA_DIR;
|
|
3301
3517
|
if (!fs3.existsSync(dir)) return [];
|
|
@@ -3309,9 +3525,12 @@ function loadOverwriteFile() {
|
|
|
3309
3525
|
const filePath = path3.join(dir, file);
|
|
3310
3526
|
try {
|
|
3311
3527
|
const content = fs3.readFileSync(filePath, "utf8");
|
|
3312
|
-
const parsed = load(content);
|
|
3313
|
-
if (parsed && typeof parsed === "object") {
|
|
3314
|
-
|
|
3528
|
+
const parsed = load(content, { maxAliases: 200 });
|
|
3529
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3530
|
+
const { match, ...config } = parsed;
|
|
3531
|
+
results.push({ name: file, path: filePath, config, match: normalizeMatch(match, file) });
|
|
3532
|
+
} else if (parsed !== null) {
|
|
3533
|
+
console.warn(`\u8B66\u544A: \u8986\u5199\u6587\u4EF6 "${file}" \u9876\u5C42\u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
3315
3534
|
}
|
|
3316
3535
|
} catch (e) {
|
|
3317
3536
|
console.warn(`\u8B66\u544A: \u8986\u5199\u6587\u4EF6 "${file}" \u89E3\u6790\u5931\u8D25: ${e.message}`);
|
|
@@ -3320,9 +3539,9 @@ function loadOverwriteFile() {
|
|
|
3320
3539
|
return results;
|
|
3321
3540
|
}
|
|
3322
3541
|
function applyOverwrite(baseConfig, preloadedFiles) {
|
|
3323
|
-
if (!isOverwriteEnabled()) return baseConfig;
|
|
3542
|
+
if (!isOverwriteEnabled()) return { ...baseConfig };
|
|
3324
3543
|
const overwriteFiles = preloadedFiles || loadOverwriteFile();
|
|
3325
|
-
if (overwriteFiles.length === 0) return baseConfig;
|
|
3544
|
+
if (overwriteFiles.length === 0) return { ...baseConfig };
|
|
3326
3545
|
let result = { ...baseConfig };
|
|
3327
3546
|
for (const file of overwriteFiles) {
|
|
3328
3547
|
result = deepMergeWithOverrides(result, file.config);
|
|
@@ -3338,7 +3557,8 @@ function listOverwriteFile() {
|
|
|
3338
3557
|
files: files.map((f) => ({
|
|
3339
3558
|
name: f.name,
|
|
3340
3559
|
path: f.path,
|
|
3341
|
-
keys: Object.keys(f.config || {})
|
|
3560
|
+
keys: Object.keys(f.config || {}),
|
|
3561
|
+
scope: summarizeMatch(f.match)
|
|
3342
3562
|
}))
|
|
3343
3563
|
};
|
|
3344
3564
|
}
|
|
@@ -3349,6 +3569,7 @@ import { createRequire } from "module";
|
|
|
3349
3569
|
var require2 = createRequire(import.meta.url);
|
|
3350
3570
|
var pkg = require2("../package.json");
|
|
3351
3571
|
var VERSION = pkg.version;
|
|
3572
|
+
var MAX_RESPONSE_BYTES = 50 * 1024 * 1024;
|
|
3352
3573
|
var sleepBuf = new Int32Array(new SharedArrayBuffer(4));
|
|
3353
3574
|
var NO_COLOR = process.env.NO_COLOR !== void 0 || !process.stdout.isTTY;
|
|
3354
3575
|
function colorize(code, str) {
|
|
@@ -3408,6 +3629,7 @@ function formatBytes(bytes) {
|
|
|
3408
3629
|
}
|
|
3409
3630
|
function formatTimestamp(ts) {
|
|
3410
3631
|
if (ts === void 0 || ts === null) return "\u672A\u77E5";
|
|
3632
|
+
if (ts === 0) return "\u6C38\u4E45";
|
|
3411
3633
|
try {
|
|
3412
3634
|
return new Date(ts * 1e3).toLocaleString("zh-CN");
|
|
3413
3635
|
} catch {
|
|
@@ -3429,7 +3651,7 @@ function formatDate(dateOrIso) {
|
|
|
3429
3651
|
}
|
|
3430
3652
|
}
|
|
3431
3653
|
function hasFlag(args, short, long) {
|
|
3432
|
-
return !!args && (args.includes(short) || args.includes(long));
|
|
3654
|
+
return !!args && (args.includes(short) || long !== void 0 && args.includes(long));
|
|
3433
3655
|
}
|
|
3434
3656
|
function parseIntArg(args, short, long, defaultValue) {
|
|
3435
3657
|
if (!args) return defaultValue;
|
|
@@ -3439,11 +3661,29 @@ function parseIntArg(args, short, long, defaultValue) {
|
|
|
3439
3661
|
const val = parseInt(args[i + 1], 10);
|
|
3440
3662
|
return Number.isNaN(val) ? defaultValue : val;
|
|
3441
3663
|
}
|
|
3664
|
+
} else if (args[i].startsWith(`${long}=`)) {
|
|
3665
|
+
const val = parseInt(args[i].slice(long.length + 1), 10);
|
|
3666
|
+
if (!Number.isNaN(val)) return val;
|
|
3442
3667
|
}
|
|
3443
3668
|
}
|
|
3444
3669
|
return defaultValue;
|
|
3445
3670
|
}
|
|
3446
3671
|
var VALUE_FLAGS = /* @__PURE__ */ new Set(["-t", "--timeout", "-j", "--concurrency", "-r", "--rounds", "-n", "--lines", "-u", "--update-timeout"]);
|
|
3672
|
+
function extractStartOptions(args) {
|
|
3673
|
+
if (!args) return [];
|
|
3674
|
+
const BOOL_FLAGS = /* @__PURE__ */ new Set(["-s", "--no-update", "--no-clean"]);
|
|
3675
|
+
const out = [];
|
|
3676
|
+
for (let i = 0; i < args.length; i++) {
|
|
3677
|
+
const a = args[i];
|
|
3678
|
+
if (VALUE_FLAGS.has(a)) {
|
|
3679
|
+
out.push(a);
|
|
3680
|
+
if (i + 1 < args.length) out.push(args[++i]);
|
|
3681
|
+
} else if (BOOL_FLAGS.has(a) || /^--(timeout|concurrency|rounds|update-timeout)=/.test(a)) {
|
|
3682
|
+
out.push(a);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
return out;
|
|
3686
|
+
}
|
|
3447
3687
|
function getNonFlagArg(args, startIdx, valueFlags = VALUE_FLAGS) {
|
|
3448
3688
|
if (!args) return null;
|
|
3449
3689
|
for (let i = startIdx; i < args.length; i++) {
|
|
@@ -3465,6 +3705,15 @@ function isProcessRunning(pid) {
|
|
|
3465
3705
|
return false;
|
|
3466
3706
|
}
|
|
3467
3707
|
}
|
|
3708
|
+
function isProcessCommandMatching(pid, needle) {
|
|
3709
|
+
if (!pid) return false;
|
|
3710
|
+
try {
|
|
3711
|
+
const result = spawnSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 5e3 });
|
|
3712
|
+
return (result.stdout || "").includes(needle);
|
|
3713
|
+
} catch {
|
|
3714
|
+
return false;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3468
3717
|
function isProcessRoot(pid) {
|
|
3469
3718
|
if (!pid) return false;
|
|
3470
3719
|
try {
|
|
@@ -3475,7 +3724,8 @@ function isProcessRoot(pid) {
|
|
|
3475
3724
|
}
|
|
3476
3725
|
}
|
|
3477
3726
|
function createHttpClient(options = {}) {
|
|
3478
|
-
const { timeout = 6e4 } = options;
|
|
3727
|
+
const { timeout = 6e4, secret } = options;
|
|
3728
|
+
const authHeaders = secret ? { Authorization: `Bearer ${secret}` } : {};
|
|
3479
3729
|
return {
|
|
3480
3730
|
async get(url, config) {
|
|
3481
3731
|
const controller = new AbortController();
|
|
@@ -3484,7 +3734,7 @@ function createHttpClient(options = {}) {
|
|
|
3484
3734
|
try {
|
|
3485
3735
|
const response = await fetch(url, {
|
|
3486
3736
|
signal,
|
|
3487
|
-
headers: { "User-Agent": `mihomo-cli/${VERSION}
|
|
3737
|
+
headers: { "User-Agent": `mihomo-cli/${VERSION}`, ...authHeaders }
|
|
3488
3738
|
});
|
|
3489
3739
|
if (!response.ok) {
|
|
3490
3740
|
const error = new Error(`HTTP ${response.status}`);
|
|
@@ -3495,7 +3745,12 @@ function createHttpClient(options = {}) {
|
|
|
3495
3745
|
}
|
|
3496
3746
|
throw error;
|
|
3497
3747
|
}
|
|
3498
|
-
const
|
|
3748
|
+
const declaredLen = Number(response.headers.get("content-length"));
|
|
3749
|
+
if (Number.isFinite(declaredLen) && declaredLen > MAX_RESPONSE_BYTES) {
|
|
3750
|
+
throw new Error(`\u54CD\u5E94\u4F53\u8FC7\u5927\uFF08${formatBytes(declaredLen)}\uFF0C\u4E0A\u9650 ${formatBytes(MAX_RESPONSE_BYTES)}\uFF09`);
|
|
3751
|
+
}
|
|
3752
|
+
const text = await readBodyWithLimit(response, controller);
|
|
3753
|
+
const data = config?.responseType === "json" ? JSON.parse(text) : text;
|
|
3499
3754
|
return { data, headers: response.headers, status: response.status };
|
|
3500
3755
|
} finally {
|
|
3501
3756
|
clearTimeout(timer);
|
|
@@ -3503,6 +3758,29 @@ function createHttpClient(options = {}) {
|
|
|
3503
3758
|
}
|
|
3504
3759
|
};
|
|
3505
3760
|
}
|
|
3761
|
+
async function readBodyWithLimit(response, controller) {
|
|
3762
|
+
if (!response.body) return response.text();
|
|
3763
|
+
const reader = response.body.getReader();
|
|
3764
|
+
const chunks = [];
|
|
3765
|
+
let total = 0;
|
|
3766
|
+
try {
|
|
3767
|
+
while (true) {
|
|
3768
|
+
const { done, value } = await reader.read();
|
|
3769
|
+
if (done) break;
|
|
3770
|
+
if (value) {
|
|
3771
|
+
total += value.byteLength;
|
|
3772
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
3773
|
+
controller.abort();
|
|
3774
|
+
throw new Error(`\u54CD\u5E94\u4F53\u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08${formatBytes(MAX_RESPONSE_BYTES)}\uFF09`);
|
|
3775
|
+
}
|
|
3776
|
+
chunks.push(value);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
} finally {
|
|
3780
|
+
reader.releaseLock();
|
|
3781
|
+
}
|
|
3782
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3783
|
+
}
|
|
3506
3784
|
function normalizeMirrorUrl(val) {
|
|
3507
3785
|
if (!val) return null;
|
|
3508
3786
|
if (val === "direct" || val === "no" || val === "none") return null;
|
|
@@ -3522,19 +3800,23 @@ function parseMirrorArg(args) {
|
|
|
3522
3800
|
if (args.includes("--no-mirror") || args.includes("--direct")) {
|
|
3523
3801
|
return { mirror: null, isOverride: true, type: "download" };
|
|
3524
3802
|
}
|
|
3803
|
+
const mirrorAllEq = args.find((a) => a.startsWith("--mirror-all="));
|
|
3525
3804
|
const mirrorAllIdx = args.indexOf("--mirror-all");
|
|
3526
|
-
if (mirrorAllIdx >= 0) {
|
|
3527
|
-
const
|
|
3805
|
+
if (mirrorAllIdx >= 0 || mirrorAllEq) {
|
|
3806
|
+
const inline = mirrorAllEq?.slice("--mirror-all=".length);
|
|
3807
|
+
const nextArg = inline ?? args[mirrorAllIdx + 1];
|
|
3528
3808
|
if (!nextArg || nextArg.startsWith("-")) {
|
|
3529
|
-
return { mirror:
|
|
3809
|
+
return { mirror: DEFAULT_MIRROR, isOverride: true, type: "all" };
|
|
3530
3810
|
}
|
|
3531
3811
|
return { mirror: normalizeMirrorUrl(nextArg), isOverride: true, type: "all" };
|
|
3532
3812
|
}
|
|
3813
|
+
const mirrorEq = args.find((a) => a.startsWith("--mirror="));
|
|
3533
3814
|
const mirrorIdx = args.indexOf("--mirror");
|
|
3534
|
-
if (mirrorIdx >= 0) {
|
|
3535
|
-
const
|
|
3815
|
+
if (mirrorIdx >= 0 || mirrorEq) {
|
|
3816
|
+
const inline = mirrorEq?.slice("--mirror=".length);
|
|
3817
|
+
const nextArg = inline ?? args[mirrorIdx + 1];
|
|
3536
3818
|
if (!nextArg || nextArg.startsWith("-")) {
|
|
3537
|
-
return { mirror:
|
|
3819
|
+
return { mirror: DEFAULT_MIRROR, isOverride: true, type: "download" };
|
|
3538
3820
|
}
|
|
3539
3821
|
return { mirror: normalizeMirrorUrl(nextArg), isOverride: true, type: "download" };
|
|
3540
3822
|
}
|
|
@@ -3551,12 +3833,16 @@ function isProxyValid(proxy) {
|
|
|
3551
3833
|
}
|
|
3552
3834
|
|
|
3553
3835
|
// src/config.ts
|
|
3836
|
+
var SAFE_YAML_LOAD_OPTIONS = { maxAliases: 200 };
|
|
3837
|
+
function loadYamlSafe(content) {
|
|
3838
|
+
return load(content, SAFE_YAML_LOAD_OPTIONS);
|
|
3839
|
+
}
|
|
3554
3840
|
function parseYamlOrJson(content, errorMsg) {
|
|
3555
3841
|
if (!content?.trim()) {
|
|
3556
3842
|
throw new Error(`${errorMsg || "\u5185\u5BB9"}\u4E3A\u7A7A`);
|
|
3557
3843
|
}
|
|
3558
3844
|
try {
|
|
3559
|
-
const result =
|
|
3845
|
+
const result = loadYamlSafe(content);
|
|
3560
3846
|
if (result != null && typeof result === "object" && !Array.isArray(result)) return result;
|
|
3561
3847
|
} catch {
|
|
3562
3848
|
}
|
|
@@ -3567,16 +3853,17 @@ function parseYamlOrJson(content, errorMsg) {
|
|
|
3567
3853
|
}
|
|
3568
3854
|
}
|
|
3569
3855
|
function dumpYaml(obj) {
|
|
3570
|
-
return dump(obj, { indent: 2, lineWidth: -1
|
|
3856
|
+
return dump(obj, { indent: 2, lineWidth: -1 });
|
|
3571
3857
|
}
|
|
3572
3858
|
function collectOverwriteProxyNames(overwriteFiles) {
|
|
3573
3859
|
const names = [];
|
|
3574
3860
|
for (const file of overwriteFiles) {
|
|
3575
3861
|
for (const [key, value] of Object.entries(file.config)) {
|
|
3576
|
-
if ((key === "+proxies" || key === "proxies+") && Array.isArray(value)) {
|
|
3862
|
+
if ((key === "+proxies" || key === "proxies+" || key === "~proxies") && Array.isArray(value)) {
|
|
3577
3863
|
for (const proxy of value) {
|
|
3578
3864
|
if (proxy && typeof proxy === "object" && "name" in proxy) {
|
|
3579
|
-
|
|
3865
|
+
const name = proxy.name;
|
|
3866
|
+
if (typeof name === "string" && name.length > 0) names.push(name);
|
|
3580
3867
|
}
|
|
3581
3868
|
}
|
|
3582
3869
|
}
|
|
@@ -3614,6 +3901,7 @@ function deduplicateByName(items) {
|
|
|
3614
3901
|
});
|
|
3615
3902
|
return { result, names, duplicates };
|
|
3616
3903
|
}
|
|
3904
|
+
var NON_TARGET_RULE_TYPES = /* @__PURE__ */ new Set(["SUB-RULE"]);
|
|
3617
3905
|
function getRuleTarget(rule) {
|
|
3618
3906
|
const parts = rule.split(",");
|
|
3619
3907
|
if (parts.length < 2) return "";
|
|
@@ -3667,6 +3955,8 @@ function validateConfig(config) {
|
|
|
3667
3955
|
if (rules.length > 0) {
|
|
3668
3956
|
const removedRules = [];
|
|
3669
3957
|
config.rules = rules.filter((rule) => {
|
|
3958
|
+
const ruleType = rule.split(",")[0]?.trim().toUpperCase();
|
|
3959
|
+
if (NON_TARGET_RULE_TYPES.has(ruleType)) return true;
|
|
3670
3960
|
const target = getRuleTarget(rule);
|
|
3671
3961
|
if (!target || validNames.has(target)) return true;
|
|
3672
3962
|
removedRules.push(rule);
|
|
@@ -3678,13 +3968,14 @@ function validateConfig(config) {
|
|
|
3678
3968
|
}
|
|
3679
3969
|
return warnings;
|
|
3680
3970
|
}
|
|
3681
|
-
function buildConfig(subRawContent, mode) {
|
|
3971
|
+
function buildConfig(subRawContent, mode, scope) {
|
|
3682
3972
|
const subscriptionConfig = parseYamlOrJson(subRawContent, "\u8BA2\u9605\u5185\u5BB9");
|
|
3683
3973
|
if (!subscriptionConfig) {
|
|
3684
3974
|
throw new Error("\u8BA2\u9605\u5185\u5BB9\u4E3A\u7A7A");
|
|
3685
3975
|
}
|
|
3686
3976
|
const overwriteEnabled = isOverwriteEnabled();
|
|
3687
|
-
const
|
|
3977
|
+
const allFiles = overwriteEnabled ? loadOverwriteFile() : [];
|
|
3978
|
+
const overwriteFiles = filterOverwriteFilesByScope(allFiles, scope);
|
|
3688
3979
|
const withOverwrites = applyOverwrite(subscriptionConfig, overwriteFiles);
|
|
3689
3980
|
if (overwriteFiles.length > 0) {
|
|
3690
3981
|
excludeOverwriteProxiesFromIncludeAll(withOverwrites, overwriteFiles);
|
|
@@ -3695,7 +3986,6 @@ function buildConfig(subRawContent, mode) {
|
|
|
3695
3986
|
systemConfig[key] = value;
|
|
3696
3987
|
}
|
|
3697
3988
|
}
|
|
3698
|
-
systemConfig["allow-lan"] = false;
|
|
3699
3989
|
systemConfig["external-controller"] = BASE_CONFIG["external-controller"];
|
|
3700
3990
|
systemConfig["mixed-port"] = BASE_CONFIG["mixed-port"];
|
|
3701
3991
|
delete withOverwrites["mixed-port"];
|
|
@@ -3704,6 +3994,11 @@ function buildConfig(subRawContent, mode) {
|
|
|
3704
3994
|
delete withOverwrites["external-ui"];
|
|
3705
3995
|
delete withOverwrites["external-ui-name"];
|
|
3706
3996
|
delete withOverwrites["external-ui-url"];
|
|
3997
|
+
delete withOverwrites.secret;
|
|
3998
|
+
const controllerSecret = readSettings().controller_secret;
|
|
3999
|
+
if (controllerSecret) {
|
|
4000
|
+
systemConfig.secret = controllerSecret;
|
|
4001
|
+
}
|
|
3707
4002
|
if (mode === "tun") {
|
|
3708
4003
|
systemConfig.tun = TUN_CONFIG.tun;
|
|
3709
4004
|
const subDns = withOverwrites.dns || {};
|
|
@@ -3759,7 +4054,7 @@ function getConfigInfo() {
|
|
|
3759
4054
|
if (!hasConfig()) return null;
|
|
3760
4055
|
try {
|
|
3761
4056
|
const content = fs4.readFileSync(PATHS.configFile, "utf8");
|
|
3762
|
-
const cfg =
|
|
4057
|
+
const cfg = loadYamlSafe(content);
|
|
3763
4058
|
if (!cfg) return null;
|
|
3764
4059
|
const proxies = cfg.proxies;
|
|
3765
4060
|
const proxyGroups = cfg["proxy-groups"];
|
|
@@ -3892,6 +4187,33 @@ import path5 from "path";
|
|
|
3892
4187
|
import { spawn, spawnSync as spawnSync3 } from "child_process";
|
|
3893
4188
|
import fs5 from "fs";
|
|
3894
4189
|
import path4 from "path";
|
|
4190
|
+
|
|
4191
|
+
// src/lifecycle.ts
|
|
4192
|
+
var cleanupFns = /* @__PURE__ */ new Set();
|
|
4193
|
+
var silentSigint = false;
|
|
4194
|
+
function setSilentSigint(value) {
|
|
4195
|
+
silentSigint = value;
|
|
4196
|
+
}
|
|
4197
|
+
function isSilentSigint() {
|
|
4198
|
+
return silentSigint;
|
|
4199
|
+
}
|
|
4200
|
+
function registerCleanup(fn) {
|
|
4201
|
+
cleanupFns.add(fn);
|
|
4202
|
+
return () => {
|
|
4203
|
+
cleanupFns.delete(fn);
|
|
4204
|
+
};
|
|
4205
|
+
}
|
|
4206
|
+
function runCleanup() {
|
|
4207
|
+
for (const fn of cleanupFns) {
|
|
4208
|
+
try {
|
|
4209
|
+
fn();
|
|
4210
|
+
} catch {
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
cleanupFns.clear();
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
// src/process.ts
|
|
3895
4217
|
var PROCESS_WAIT_ATTEMPTS = 50;
|
|
3896
4218
|
var PROCESS_WAIT_INTERVAL = 100;
|
|
3897
4219
|
var STARTUP_WAIT_MS = 800;
|
|
@@ -3950,6 +4272,9 @@ function checkStaleState() {
|
|
|
3950
4272
|
needsSudo: hasRootProcess || hasRootPidFile
|
|
3951
4273
|
};
|
|
3952
4274
|
}
|
|
4275
|
+
function hasRootResidue() {
|
|
4276
|
+
return checkStaleState().needsSudo;
|
|
4277
|
+
}
|
|
3953
4278
|
function savePid(pid) {
|
|
3954
4279
|
ensureDirs();
|
|
3955
4280
|
fs5.writeFileSync(PATHS.pidFile, pid.toString(), { mode: 384 });
|
|
@@ -3968,23 +4293,10 @@ function clearPid() {
|
|
|
3968
4293
|
}
|
|
3969
4294
|
}
|
|
3970
4295
|
}
|
|
3971
|
-
function killProcess(pid
|
|
4296
|
+
function killProcess(pid) {
|
|
3972
4297
|
try {
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
if (result.status === 0) {
|
|
3976
|
-
return true;
|
|
3977
|
-
}
|
|
3978
|
-
try {
|
|
3979
|
-
process.kill(pid, "SIGKILL");
|
|
3980
|
-
return true;
|
|
3981
|
-
} catch {
|
|
3982
|
-
return false;
|
|
3983
|
-
}
|
|
3984
|
-
} else {
|
|
3985
|
-
process.kill(pid, "SIGKILL");
|
|
3986
|
-
return true;
|
|
3987
|
-
}
|
|
4298
|
+
process.kill(pid, "SIGKILL");
|
|
4299
|
+
return true;
|
|
3988
4300
|
} catch {
|
|
3989
4301
|
return false;
|
|
3990
4302
|
}
|
|
@@ -4030,7 +4342,7 @@ function cleanupAll(forceSudo = false) {
|
|
|
4030
4342
|
killedCount = pids.length;
|
|
4031
4343
|
} else {
|
|
4032
4344
|
for (const pid of pids) {
|
|
4033
|
-
if (killProcess(pid
|
|
4345
|
+
if (killProcess(pid)) {
|
|
4034
4346
|
killedCount++;
|
|
4035
4347
|
} else {
|
|
4036
4348
|
failedPids.push(pid);
|
|
@@ -4082,12 +4394,13 @@ for i in 1 2 3 4 5; do
|
|
|
4082
4394
|
fi
|
|
4083
4395
|
done
|
|
4084
4396
|
|
|
4085
|
-
# \u5931\u8D25\uFF0C\u663E\u793A\u65E5\u5FD7
|
|
4397
|
+
# \u5931\u8D25\uFF0C\u663E\u793A\u65E5\u5FD7\uFF08\u9000\u51FA\u7801 2\uFF1A\u907F\u5F00 sudo \u7684 1=\u9274\u6743\u5931\u8D25/\u53D6\u6D88\uFF0C\u4F9B\u8C03\u7528\u65B9\u533A\u5206\uFF09
|
|
4398
|
+
rm -f "\${PID_FILE}" 2>/dev/null || true
|
|
4086
4399
|
echo "TUN \u542F\u52A8\u5931\u8D25"
|
|
4087
4400
|
echo ""
|
|
4088
4401
|
echo "--- \u65E5\u5FD7 ---"
|
|
4089
4402
|
tail -25 "\${LOG_FILE}" 2>/dev/null
|
|
4090
|
-
exit
|
|
4403
|
+
exit 2
|
|
4091
4404
|
`;
|
|
4092
4405
|
const scriptPath = path4.join(DIRS.runtime, "launch-tun.sh");
|
|
4093
4406
|
fs5.writeFileSync(scriptPath, scriptContent, { mode: 448 });
|
|
@@ -4145,7 +4458,7 @@ async function startMixedMode(staleState) {
|
|
|
4145
4458
|
if (staleState.needsCleanup) {
|
|
4146
4459
|
if (staleState.needsSudo) {
|
|
4147
4460
|
console.log("\n\u53D1\u73B0\u9700\u8981 root \u6743\u9650\u6E05\u7406\u7684\u6B8B\u7559\u8FDB\u7A0B/\u6587\u4EF6");
|
|
4148
|
-
console.log(
|
|
4461
|
+
console.log(`\u8BF7\u5148\u624B\u52A8\u6E05\u7406: sudo pkill -9 mihomo && sudo rm -f ${PATHS.pidFile}`);
|
|
4149
4462
|
console.log("\u6216\u8005\u5207\u6362\u5230 TUN \u6A21\u5F0F\uFF0C\u542F\u52A8\u65F6\u4F1A\u81EA\u52A8\u6E05\u7406");
|
|
4150
4463
|
throw new Error("\u5B58\u5728\u9700\u8981 root \u6743\u9650\u6E05\u7406\u7684\u6B8B\u7559");
|
|
4151
4464
|
}
|
|
@@ -4176,9 +4489,15 @@ async function startMixedMode(staleState) {
|
|
|
4176
4489
|
detached: true,
|
|
4177
4490
|
stdio: ["ignore", logFd, logFd]
|
|
4178
4491
|
});
|
|
4492
|
+
child.on("error", () => {
|
|
4493
|
+
});
|
|
4179
4494
|
fs5.closeSync(logFd);
|
|
4180
4495
|
child.unref();
|
|
4181
4496
|
const pid = child.pid;
|
|
4497
|
+
if (!pid) {
|
|
4498
|
+
clearPid();
|
|
4499
|
+
throw new Error("\u542F\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u521B\u5EFA\u5185\u6838\u8FDB\u7A0B\uFF08\u5185\u6838\u4E8C\u8FDB\u5236\u53EF\u80FD\u4E0D\u53EF\u6267\u884C\uFF09");
|
|
4500
|
+
}
|
|
4182
4501
|
savePid(pid);
|
|
4183
4502
|
await new Promise((resolve) => setTimeout(resolve, STARTUP_WAIT_MS));
|
|
4184
4503
|
if (!isRunning()) {
|
|
@@ -4219,6 +4538,9 @@ async function startTunMode(staleState) {
|
|
|
4219
4538
|
if (e.status === 1) {
|
|
4220
4539
|
throw new Error("\u5BC6\u7801\u9519\u8BEF\u6216\u53D6\u6D88");
|
|
4221
4540
|
}
|
|
4541
|
+
if (e.status === 2) {
|
|
4542
|
+
throw new Error("TUN \u542F\u52A8\u5931\u8D25\uFF08\u8BE6\u89C1\u4E0A\u65B9\u65E5\u5FD7\uFF09");
|
|
4543
|
+
}
|
|
4222
4544
|
throw new Error(e.message);
|
|
4223
4545
|
}
|
|
4224
4546
|
try {
|
|
@@ -4351,7 +4673,7 @@ function getLogPathByName(name) {
|
|
|
4351
4673
|
}
|
|
4352
4674
|
function openUrl(url) {
|
|
4353
4675
|
try {
|
|
4354
|
-
const child = spawn("open", [url], { stdio: "ignore", detached: true });
|
|
4676
|
+
const child = spawn("open", ["--", url], { stdio: "ignore", detached: true });
|
|
4355
4677
|
child.unref();
|
|
4356
4678
|
child.on("error", () => {
|
|
4357
4679
|
});
|
|
@@ -4383,6 +4705,7 @@ function viewLogWithTail(logPath, options) {
|
|
|
4383
4705
|
tailArgs.push("-n", lines.toString());
|
|
4384
4706
|
tailArgs.push(logPath);
|
|
4385
4707
|
const tail = spawn("tail", tailArgs, { stdio: "inherit" });
|
|
4708
|
+
if (follow) setSilentSigint(true);
|
|
4386
4709
|
tail.on("close", () => process.exit(0));
|
|
4387
4710
|
tail.on("error", (e) => {
|
|
4388
4711
|
console.error(`\u65E0\u6CD5\u8BFB\u53D6\u65E5\u5FD7: ${e.message}`);
|
|
@@ -4538,10 +4861,13 @@ function disableDaemon() {
|
|
|
4538
4861
|
async function tryHotReload() {
|
|
4539
4862
|
const controller = new AbortController();
|
|
4540
4863
|
const timer = setTimeout(() => controller.abort(), HOT_RELOAD_TIMEOUT_MS);
|
|
4864
|
+
const secret = readSettings().controller_secret;
|
|
4865
|
+
const headers = { "Content-Type": "application/json" };
|
|
4866
|
+
if (secret) headers.Authorization = `Bearer ${secret}`;
|
|
4541
4867
|
try {
|
|
4542
4868
|
const res = await fetch(`${CONTROLLER_BASE_URL}/configs?force=true`, {
|
|
4543
4869
|
method: "PUT",
|
|
4544
|
-
headers
|
|
4870
|
+
headers,
|
|
4545
4871
|
body: "{}",
|
|
4546
4872
|
signal: controller.signal
|
|
4547
4873
|
});
|
|
@@ -4581,7 +4907,11 @@ async function restartDaemon() {
|
|
|
4581
4907
|
|
|
4582
4908
|
// src/subscription.ts
|
|
4583
4909
|
function isGithubUrl(url) {
|
|
4584
|
-
|
|
4910
|
+
const githubRe = /github\.com|raw\.githubusercontent\.com/i;
|
|
4911
|
+
if (isMultiUrl(url)) {
|
|
4912
|
+
return splitUrls(url).every((u) => githubRe.test(u));
|
|
4913
|
+
}
|
|
4914
|
+
return githubRe.test(url);
|
|
4585
4915
|
}
|
|
4586
4916
|
function getDefaultUpdateInterval(url) {
|
|
4587
4917
|
return isGithubUrl(url) ? DEFAULT_UPDATE_INTERVAL_HOURS_GITHUB : DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
@@ -4593,6 +4923,14 @@ var HTTP_CLIENT = createHttpClient({ timeout: 6e4 });
|
|
|
4593
4923
|
function isMultiUrl(url) {
|
|
4594
4924
|
return url.includes(",");
|
|
4595
4925
|
}
|
|
4926
|
+
function isValidHttpUrl(url) {
|
|
4927
|
+
try {
|
|
4928
|
+
const u = new URL(url.trim());
|
|
4929
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
4930
|
+
} catch {
|
|
4931
|
+
return false;
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4596
4934
|
function splitUrls(url) {
|
|
4597
4935
|
return url.split(",").map((u) => u.trim()).filter(Boolean);
|
|
4598
4936
|
}
|
|
@@ -4708,7 +5046,7 @@ function pickSingleSubscription(subs, pattern) {
|
|
|
4708
5046
|
for (const s of subs) console.log(` ${s.name}`);
|
|
4709
5047
|
process.exit(1);
|
|
4710
5048
|
}
|
|
4711
|
-
async function downloadSubscription(url, subName = "default", signal) {
|
|
5049
|
+
async function downloadSubscription(url, subName = "default", signal, persist = true) {
|
|
4712
5050
|
let response;
|
|
4713
5051
|
try {
|
|
4714
5052
|
response = await HTTP_CLIENT.get(url, { responseType: "text", signal });
|
|
@@ -4729,9 +5067,13 @@ async function downloadSubscription(url, subName = "default", signal) {
|
|
|
4729
5067
|
}
|
|
4730
5068
|
const parsed = parseYamlOrJson(content, "\u8BA2\u9605\u5185\u5BB9");
|
|
4731
5069
|
if (!parsed) throw new Error("\u8BA2\u9605\u5185\u5BB9\u4E3A\u7A7A");
|
|
4732
|
-
|
|
5070
|
+
if (persist) {
|
|
5071
|
+
saveSubscriptionRawConfig(subName, content);
|
|
5072
|
+
}
|
|
4733
5073
|
const meta = extractSubscriptionMeta(response.headers);
|
|
4734
|
-
|
|
5074
|
+
if (persist) {
|
|
5075
|
+
saveSubscriptionMeta(subName, meta);
|
|
5076
|
+
}
|
|
4735
5077
|
const proxies = parsed.proxies;
|
|
4736
5078
|
const proxyGroups = parsed["proxy-groups"];
|
|
4737
5079
|
return {
|
|
@@ -4743,13 +5085,16 @@ async function downloadSubscription(url, subName = "default", signal) {
|
|
|
4743
5085
|
username: meta.username
|
|
4744
5086
|
};
|
|
4745
5087
|
}
|
|
4746
|
-
async function downloadMergedSubscription(urls, subName, signal) {
|
|
5088
|
+
async function downloadMergedSubscription(urls, subName, signal, persist = true) {
|
|
5089
|
+
const internal = new AbortController();
|
|
5090
|
+
const combinedSignal = signal ? AbortSignal.any([signal, internal.signal]) : internal.signal;
|
|
4747
5091
|
const responses = await Promise.all(
|
|
4748
5092
|
urls.map(async (url, index) => {
|
|
4749
5093
|
try {
|
|
4750
|
-
const response = await HTTP_CLIENT.get(url, { responseType: "text", signal });
|
|
5094
|
+
const response = await HTTP_CLIENT.get(url, { responseType: "text", signal: combinedSignal });
|
|
4751
5095
|
return { url, index, response, error: null };
|
|
4752
5096
|
} catch (e) {
|
|
5097
|
+
internal.abort();
|
|
4753
5098
|
return { url, index, response: null, error: e };
|
|
4754
5099
|
}
|
|
4755
5100
|
})
|
|
@@ -4780,9 +5125,13 @@ async function downloadMergedSubscription(urls, subName, signal) {
|
|
|
4780
5125
|
}
|
|
4781
5126
|
base.proxies = baseProxies;
|
|
4782
5127
|
const mergedContent = dumpYaml(base);
|
|
4783
|
-
|
|
5128
|
+
if (persist) {
|
|
5129
|
+
saveSubscriptionRawConfig(subName, mergedContent);
|
|
5130
|
+
}
|
|
4784
5131
|
const meta = extractSubscriptionMeta(responses[0].response?.headers);
|
|
4785
|
-
|
|
5132
|
+
if (persist) {
|
|
5133
|
+
saveSubscriptionMeta(subName, meta);
|
|
5134
|
+
}
|
|
4786
5135
|
const proxyGroups = base["proxy-groups"];
|
|
4787
5136
|
return {
|
|
4788
5137
|
proxies: baseProxies.length,
|
|
@@ -4798,7 +5147,8 @@ function prepareConfigForStart(mode, subName = "default") {
|
|
|
4798
5147
|
if (!rawContent) {
|
|
4799
5148
|
throw new Error(`\u672A\u627E\u5230\u8BA2\u9605\u914D\u7F6E "${subName}"\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605`);
|
|
4800
5149
|
}
|
|
4801
|
-
const
|
|
5150
|
+
const subUrl = getSubscriptions().find((s) => s.name === subName)?.url;
|
|
5151
|
+
const buildResult = buildConfig(rawContent, mode, { subName, subUrl });
|
|
4802
5152
|
if (buildResult.warnings.length > 0) {
|
|
4803
5153
|
for (const warning of buildResult.warnings) {
|
|
4804
5154
|
console.log(`${colors.yellow("\u81EA\u52A8\u4FEE\u590D:")} ${warning}`);
|
|
@@ -4857,16 +5207,23 @@ async function autoUpdateStaleSubscription(options = {}) {
|
|
|
4857
5207
|
}
|
|
4858
5208
|
const timeoutMs = options.timeout ?? DEFAULT_AUTO_UPDATE_TIMEOUT;
|
|
4859
5209
|
const controller = new AbortController();
|
|
4860
|
-
|
|
5210
|
+
const results = [];
|
|
5211
|
+
const updatePromise = Promise.all(
|
|
5212
|
+
staleSubs.map(
|
|
5213
|
+
(sub) => tryUpdateOne(sub, controller.signal).then((r) => {
|
|
5214
|
+
results.push(r);
|
|
5215
|
+
return r;
|
|
5216
|
+
})
|
|
5217
|
+
)
|
|
5218
|
+
);
|
|
4861
5219
|
try {
|
|
4862
|
-
|
|
5220
|
+
await withTimeout(updatePromise, timeoutMs);
|
|
4863
5221
|
} catch (e) {
|
|
4864
|
-
if (e instanceof TimeoutError)
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
}
|
|
4869
|
-
throw e;
|
|
5222
|
+
if (!(e instanceof TimeoutError)) throw e;
|
|
5223
|
+
controller.abort();
|
|
5224
|
+
await updatePromise.catch(() => {
|
|
5225
|
+
});
|
|
5226
|
+
console.log(colors.yellow(`\u81EA\u52A8\u66F4\u65B0\u8D85\u65F6 (${timeoutMs / 1e3}s)\uFF0C\u5DF2\u5B8C\u6210\u7684\u66F4\u65B0\u751F\u6548\uFF0C\u5176\u4F59\u4F7F\u7528\u7F13\u5B58\u914D\u7F6E`));
|
|
4870
5227
|
}
|
|
4871
5228
|
let updatedCount = 0;
|
|
4872
5229
|
for (const r of results) {
|
|
@@ -4908,7 +5265,8 @@ async function testSubscriptionProxies(subName, options = {}) {
|
|
|
4908
5265
|
if (proxies.length === 0) {
|
|
4909
5266
|
return { total: 0, alive: 0, dead: 0, results: [] };
|
|
4910
5267
|
}
|
|
4911
|
-
const
|
|
5268
|
+
const secret = apiBase === CONTROLLER_BASE_URL ? readSettings().controller_secret : void 0;
|
|
5269
|
+
const client = createHttpClient({ timeout: timeout + 3e3, secret });
|
|
4912
5270
|
const results = new Array(proxies.length);
|
|
4913
5271
|
let completedCount = 0;
|
|
4914
5272
|
let nextIndex = 0;
|
|
@@ -4949,6 +5307,19 @@ function normalizeProxyNamesBeforeSave(parsed) {
|
|
|
4949
5307
|
group.proxies = group.proxies.map((name) => renameMap.get(name) || name);
|
|
4950
5308
|
}
|
|
4951
5309
|
}
|
|
5310
|
+
const rules = parsed.raw.rules;
|
|
5311
|
+
if (Array.isArray(rules)) {
|
|
5312
|
+
parsed.raw.rules = rules.map((rule) => {
|
|
5313
|
+
if (typeof rule !== "string") return rule;
|
|
5314
|
+
const parts = rule.split(",");
|
|
5315
|
+
if (parts.length < 2) return rule;
|
|
5316
|
+
const targetIdx = parts[parts.length - 1].trim().toLowerCase() === "no-resolve" && parts.length >= 3 ? parts.length - 2 : parts.length - 1;
|
|
5317
|
+
const target = parts[targetIdx].trim();
|
|
5318
|
+
const renamed = renameMap.get(target);
|
|
5319
|
+
if (renamed) parts[targetIdx] = renamed;
|
|
5320
|
+
return parts.join(",");
|
|
5321
|
+
});
|
|
5322
|
+
}
|
|
4952
5323
|
return renameMap.size;
|
|
4953
5324
|
}
|
|
4954
5325
|
function cleanDeadProxies(parsed, deadNames) {
|
|
@@ -4978,11 +5349,14 @@ function cleanDeadProxies(parsed, deadNames) {
|
|
|
4978
5349
|
group.proxies = group.proxies.filter((name) => !removedGroupNames.has(name));
|
|
4979
5350
|
}
|
|
4980
5351
|
}
|
|
5352
|
+
}
|
|
5353
|
+
const removedTargets = /* @__PURE__ */ new Set([...removedGroupNames, ...deadNames]);
|
|
5354
|
+
if (removedTargets.size > 0) {
|
|
4981
5355
|
const rules = parsed.raw.rules;
|
|
4982
5356
|
if (Array.isArray(rules)) {
|
|
4983
5357
|
parsed.raw.rules = rules.filter((rule) => {
|
|
4984
5358
|
if (typeof rule !== "string") return true;
|
|
4985
|
-
return !
|
|
5359
|
+
return !removedTargets.has(getRuleTarget(rule));
|
|
4986
5360
|
});
|
|
4987
5361
|
}
|
|
4988
5362
|
}
|
|
@@ -5344,7 +5718,7 @@ async function downloadKernel(progressCallback, mirror, releaseInfo) {
|
|
|
5344
5718
|
\u5E73\u53F0: ${platform}, \u67B6\u6784: ${arch}${hint}`);
|
|
5345
5719
|
}
|
|
5346
5720
|
const downloadUrl = withMirror(asset.browser_download_url, mirror);
|
|
5347
|
-
const tempPath = path6.join(DIRS.kernel, asset.name);
|
|
5721
|
+
const tempPath = path6.join(DIRS.kernel, path6.basename(asset.name));
|
|
5348
5722
|
const sizeMB = (asset.size / 1024 / 1024).toFixed(2);
|
|
5349
5723
|
if (mirror && progressCallback) {
|
|
5350
5724
|
progressCallback("\u63D0\u793A: \u7ECF\u7B2C\u4E09\u65B9\u955C\u50CF\u4E2D\u8F6C\u4E0B\u8F7D\uFF0C\u65E0\u6CD5\u9A8C\u8BC1\u6765\u6E90\u5B8C\u6574\u6027\uFF0C\u5EFA\u8BAE\u76F4\u8FDE\u6216\u81EA\u884C\u6821\u9A8C\u4EA7\u7269");
|
|
@@ -5357,6 +5731,12 @@ async function downloadKernel(progressCallback, mirror, releaseInfo) {
|
|
|
5357
5731
|
["-L", "--progress-bar", "--connect-timeout", "30", "--max-time", String(Math.floor(KERNEL_DOWNLOAD_TIMEOUT / 1e3)), "-o", tempPath, downloadUrl],
|
|
5358
5732
|
{ stdio: "inherit" }
|
|
5359
5733
|
);
|
|
5734
|
+
if (curlResult.error) {
|
|
5735
|
+
if (curlResult.error.code === "ENOENT") {
|
|
5736
|
+
throw new Error("\u672A\u627E\u5230 curl \u547D\u4EE4\uFF0C\u8BF7\u5148\u5B89\u88C5 curl \u540E\u91CD\u8BD5");
|
|
5737
|
+
}
|
|
5738
|
+
throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${curlResult.error.message}`);
|
|
5739
|
+
}
|
|
5360
5740
|
if (curlResult.status !== 0) {
|
|
5361
5741
|
try {
|
|
5362
5742
|
fs7.unlinkSync(tempPath);
|
|
@@ -5374,6 +5754,15 @@ async function downloadKernel(progressCallback, mirror, releaseInfo) {
|
|
|
5374
5754
|
let extractedBinary = null;
|
|
5375
5755
|
try {
|
|
5376
5756
|
if (tempPath.endsWith(".tar.gz") || tempPath.endsWith(".tgz")) {
|
|
5757
|
+
const listResult = spawnSync5("tar", ["-tzf", tempPath], { encoding: "utf8", timeout: 6e4 });
|
|
5758
|
+
if (listResult.error) throw listResult.error;
|
|
5759
|
+
if (listResult.status !== 0) throw new Error(`tar \u5217\u8868\u9000\u51FA\u7801 ${listResult.status}`);
|
|
5760
|
+
const entries = (listResult.stdout || "").split("\n").filter(Boolean);
|
|
5761
|
+
for (const entry of entries) {
|
|
5762
|
+
if (entry.startsWith("/") || entry.split("/").includes("..")) {
|
|
5763
|
+
throw new Error(`\u5F52\u6863\u542B\u975E\u6CD5\u8DEF\u5F84\u6761\u76EE: ${entry}`);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5377
5766
|
const tarResult = spawnSync5("tar", ["-xzf", tempPath, "-C", extractPath], { stdio: ["ignore", "ignore", "inherit"], timeout: 6e4 });
|
|
5378
5767
|
if (tarResult.error) throw tarResult.error;
|
|
5379
5768
|
if (tarResult.status !== 0) throw new Error(`tar \u9000\u51FA\u7801 ${tarResult.status}`);
|
|
@@ -5568,7 +5957,7 @@ function cmdLogs(args) {
|
|
|
5568
5957
|
}
|
|
5569
5958
|
|
|
5570
5959
|
// src/commands/overwrite.ts
|
|
5571
|
-
import
|
|
5960
|
+
import path7 from "path";
|
|
5572
5961
|
|
|
5573
5962
|
// src/runtime.ts
|
|
5574
5963
|
function getRuntimeMode() {
|
|
@@ -5596,256 +5985,30 @@ async function launchOrRestart(mode) {
|
|
|
5596
5985
|
return result.pid;
|
|
5597
5986
|
}
|
|
5598
5987
|
|
|
5599
|
-
// src/
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
const
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
}
|
|
5613
|
-
const statusText = running ? colors.green("\u25CF \u8FD0\u884C\u4E2D") : colors.yellow("\u4E0D\u5728\u8FD0\u884C");
|
|
5614
|
-
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}${modeLabel}`);
|
|
5615
|
-
console.log(`${colors.gray("\u5185\u6838: ")}${status.kernelVersion || "\u672A\u5B89\u88C5"}`);
|
|
5616
|
-
if (pid) {
|
|
5617
|
-
console.log(`${colors.gray("PID: ")}${pid}`);
|
|
5618
|
-
if (!daemonManaged && status.processInfo) {
|
|
5619
|
-
console.log(`${colors.gray("\u5185\u5B58: ")}${status.processInfo.memory}`);
|
|
5620
|
-
}
|
|
5621
|
-
}
|
|
5622
|
-
if (info) {
|
|
5623
|
-
if (info.mixedPort) {
|
|
5624
|
-
console.log(`${colors.gray("\u7AEF\u53E3: ")}${info.mixedPort}`);
|
|
5625
|
-
} else {
|
|
5626
|
-
const ports = [];
|
|
5627
|
-
if (info.httpPort) ports.push(`HTTP:${info.httpPort}`);
|
|
5628
|
-
if (info.socksPort) ports.push(`SOCKS:${info.socksPort}`);
|
|
5629
|
-
console.log(`${colors.gray("\u7AEF\u53E3: ")}${ports.length > 0 ? ports.join(", ") : "\u672A\u77E5"}`);
|
|
5630
|
-
}
|
|
5631
|
-
}
|
|
5632
|
-
if (activeSub) {
|
|
5633
|
-
let subLine = `${colors.gray("\u8BA2\u9605: ")}${activeSub.name}`;
|
|
5634
|
-
if (info) {
|
|
5635
|
-
subLine += ` (${formatProxySummary(info)})`;
|
|
5636
|
-
}
|
|
5637
|
-
console.log(subLine);
|
|
5638
|
-
} else {
|
|
5639
|
-
console.log(`${colors.gray("\u8BA2\u9605: ")}\u672A\u914D\u7F6E`);
|
|
5640
|
-
}
|
|
5641
|
-
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
5642
|
-
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "").replace(/\.ya?ml$/, "") || "\u4E3B\u6587\u4EF6").join(", ");
|
|
5643
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (${names})`);
|
|
5644
|
-
} else if (overwriteEnabled) {
|
|
5645
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (\u65E0\u6587\u4EF6)`);
|
|
5646
|
-
} else {
|
|
5647
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.yellow("\u5DF2\u7981\u7528")}`);
|
|
5648
|
-
}
|
|
5649
|
-
if (isDaemonEnabled()) {
|
|
5650
|
-
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${colors.green("\u5DF2\u542F\u7528")} ${colors.gray("(\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F)")}`);
|
|
5651
|
-
}
|
|
5652
|
-
console.log("");
|
|
5653
|
-
}
|
|
5654
|
-
|
|
5655
|
-
// src/commands/stop.ts
|
|
5656
|
-
function handleStopResult(result) {
|
|
5657
|
-
if (result.remaining && result.remaining.length > 0) {
|
|
5658
|
-
console.error(`${colors.red("\u90E8\u5206\u8FDB\u7A0B\u672A\u7EC8\u6B62:")} ${result.remaining.join(", ")}`);
|
|
5659
|
-
console.error("\u8BF7\u624B\u52A8\u8FD0\u884C: sudo pkill -9 mihomo");
|
|
5660
|
-
process.exit(1);
|
|
5661
|
-
}
|
|
5662
|
-
}
|
|
5663
|
-
async function cmdStop() {
|
|
5664
|
-
if (isDaemonEnabled()) {
|
|
5665
|
-
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u4EE3\u7406\u7531 launchd \u6258\u7BA1"));
|
|
5666
|
-
console.log("\u76F4\u63A5\u505C\u6B62\u4F1A\u88AB\u81EA\u52A8\u91CD\u65B0\u62C9\u8D77\uFF0C\u8BF7\u7528: mihomo daemon off");
|
|
5667
|
-
return;
|
|
5668
|
-
}
|
|
5669
|
-
const pids = getMihomoPids();
|
|
5670
|
-
if (pids.length === 0) {
|
|
5671
|
-
console.log(colors.yellow("\u4E0D\u5728\u8FD0\u884C"));
|
|
5672
|
-
return;
|
|
5673
|
-
}
|
|
5674
|
-
console.log(`\u505C\u6B62 ${pids.length} \u4E2A\u8FDB\u7A0B...`);
|
|
5675
|
-
handleStopResult(stop());
|
|
5676
|
-
console.log(colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B"));
|
|
5677
|
-
}
|
|
5678
|
-
|
|
5679
|
-
// src/test-instance.ts
|
|
5680
|
-
import { spawn as spawn2 } from "child_process";
|
|
5681
|
-
import fs8 from "fs";
|
|
5682
|
-
import path7 from "path";
|
|
5683
|
-
|
|
5684
|
-
// src/lifecycle.ts
|
|
5685
|
-
var cleanupFns = /* @__PURE__ */ new Set();
|
|
5686
|
-
function registerCleanup(fn) {
|
|
5687
|
-
cleanupFns.add(fn);
|
|
5688
|
-
return () => {
|
|
5689
|
-
cleanupFns.delete(fn);
|
|
5690
|
-
};
|
|
5691
|
-
}
|
|
5692
|
-
function runCleanup() {
|
|
5693
|
-
for (const fn of cleanupFns) {
|
|
5694
|
-
try {
|
|
5695
|
-
fn();
|
|
5696
|
-
} catch {
|
|
5697
|
-
}
|
|
5698
|
-
}
|
|
5699
|
-
cleanupFns.clear();
|
|
5700
|
-
}
|
|
5701
|
-
|
|
5702
|
-
// src/test-instance.ts
|
|
5703
|
-
var TEST_DIR = path7.join(USER_DATA_DIR, "test");
|
|
5704
|
-
var TEST_DIRS = {
|
|
5705
|
-
data: path7.join(TEST_DIR, "data"),
|
|
5706
|
-
runtime: path7.join(TEST_DIR, "runtime")
|
|
5707
|
-
};
|
|
5708
|
-
var TEST_PATHS = {
|
|
5709
|
-
configFile: path7.join(TEST_DIRS.runtime, "config.yaml"),
|
|
5710
|
-
pidFile: path7.join(TEST_DIRS.runtime, "pid"),
|
|
5711
|
-
logFile: path7.join(TEST_DIR, "test.log")
|
|
5712
|
-
};
|
|
5713
|
-
var TEST_API = `http://${TEST_CONFIG["external-controller"]}`;
|
|
5714
|
-
function ensureTestDirs() {
|
|
5715
|
-
for (const dir of Object.values(TEST_DIRS)) {
|
|
5716
|
-
fs8.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5717
|
-
}
|
|
5718
|
-
}
|
|
5719
|
-
function cleanupTestDir() {
|
|
5720
|
-
rmrf(TEST_DIR);
|
|
5721
|
-
}
|
|
5722
|
-
function buildTestConfig(subName) {
|
|
5723
|
-
ensureTestDirs();
|
|
5724
|
-
const rawContent = readSubscriptionRawConfig(subName);
|
|
5725
|
-
if (!rawContent) {
|
|
5726
|
-
throw new Error(`\u672A\u627E\u5230\u8BA2\u9605\u914D\u7F6E "${subName}"`);
|
|
5727
|
-
}
|
|
5728
|
-
const parsed = parseYamlOrJson(rawContent, "\u8BA2\u9605\u5185\u5BB9");
|
|
5729
|
-
const proxies = (parsed.proxies || []).filter(isProxyValid);
|
|
5730
|
-
if (proxies.length === 0) {
|
|
5731
|
-
throw new Error(`\u8BA2\u9605 "${subName}" \u6CA1\u6709\u6709\u6548\u8282\u70B9`);
|
|
5732
|
-
}
|
|
5733
|
-
const nameCount = /* @__PURE__ */ new Map();
|
|
5734
|
-
for (const proxy of proxies) {
|
|
5735
|
-
const count = (nameCount.get(proxy.name) || 0) + 1;
|
|
5736
|
-
nameCount.set(proxy.name, count);
|
|
5737
|
-
if (count > 1) {
|
|
5738
|
-
proxy.name = `${proxy.name} #${count}`;
|
|
5739
|
-
}
|
|
5740
|
-
}
|
|
5741
|
-
const config = {
|
|
5742
|
-
...TEST_CONFIG,
|
|
5743
|
-
proxies,
|
|
5744
|
-
"proxy-groups": [
|
|
5745
|
-
{
|
|
5746
|
-
name: "PROXY",
|
|
5747
|
-
type: "select",
|
|
5748
|
-
proxies: proxies.map((p) => p.name)
|
|
5749
|
-
}
|
|
5750
|
-
],
|
|
5751
|
-
rules: ["MATCH,PROXY"]
|
|
5752
|
-
};
|
|
5753
|
-
const content = dumpYaml(config);
|
|
5754
|
-
fs8.writeFileSync(TEST_PATHS.configFile, content, { mode: 384 });
|
|
5755
|
-
}
|
|
5756
|
-
async function startTestInstance() {
|
|
5757
|
-
const binary = PATHS.mihomoBinary;
|
|
5758
|
-
if (!fs8.existsSync(binary)) throw new Error("\u672A\u627E\u5230 mihomo \u5185\u6838");
|
|
5759
|
-
stopTestInstance();
|
|
5760
|
-
const logFd = fs8.openSync(TEST_PATHS.logFile, "a");
|
|
5761
|
-
const child = spawn2(binary, ["-d", TEST_DIRS.data, "-f", TEST_PATHS.configFile], {
|
|
5762
|
-
detached: true,
|
|
5763
|
-
stdio: ["ignore", logFd, logFd]
|
|
5764
|
-
});
|
|
5765
|
-
fs8.closeSync(logFd);
|
|
5766
|
-
child.unref();
|
|
5767
|
-
const pid = child.pid;
|
|
5768
|
-
fs8.writeFileSync(TEST_PATHS.pidFile, pid.toString(), { mode: 384 });
|
|
5769
|
-
const client = createHttpClient({ timeout: 2e3 });
|
|
5770
|
-
let ready = false;
|
|
5771
|
-
for (let i = 0; i < 60; i++) {
|
|
5772
|
-
if (!isProcessRunning(pid)) break;
|
|
5773
|
-
try {
|
|
5774
|
-
await client.get(`${TEST_API}/version`);
|
|
5775
|
-
ready = true;
|
|
5776
|
-
break;
|
|
5777
|
-
} catch {
|
|
5778
|
-
await sleep(500);
|
|
5779
|
-
}
|
|
5780
|
-
}
|
|
5781
|
-
if (!isProcessRunning(pid)) {
|
|
5782
|
-
let errorDetail = "";
|
|
5783
|
-
try {
|
|
5784
|
-
errorDetail = fs8.readFileSync(TEST_PATHS.logFile, "utf8").slice(-1e3);
|
|
5785
|
-
} catch {
|
|
5786
|
-
}
|
|
5787
|
-
throw new Error(`\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u5931\u8D25${errorDetail ? `
|
|
5788
|
-
${errorDetail}` : ""}`);
|
|
5789
|
-
}
|
|
5790
|
-
if (!ready) {
|
|
5791
|
-
throw new Error("\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u8D85\u65F6\uFF0CAPI \u672A\u54CD\u5E94");
|
|
5792
|
-
}
|
|
5793
|
-
}
|
|
5794
|
-
function stopTestInstance() {
|
|
5795
|
-
let pid;
|
|
5796
|
-
try {
|
|
5797
|
-
pid = parseInt(fs8.readFileSync(TEST_PATHS.pidFile, "utf8").trim(), 10);
|
|
5798
|
-
} catch {
|
|
5799
|
-
return;
|
|
5800
|
-
}
|
|
5801
|
-
if (pid > 0 && isProcessRunning(pid)) {
|
|
5802
|
-
process.kill(pid, "SIGKILL");
|
|
5803
|
-
for (let i = 0; i < 20; i++) {
|
|
5804
|
-
if (!isProcessRunning(pid)) break;
|
|
5805
|
-
sleepSync(100);
|
|
5806
|
-
}
|
|
5807
|
-
}
|
|
5808
|
-
try {
|
|
5809
|
-
fs8.unlinkSync(TEST_PATHS.pidFile);
|
|
5810
|
-
} catch {
|
|
5811
|
-
}
|
|
5812
|
-
}
|
|
5813
|
-
async function withTestInstance(subName, fn) {
|
|
5814
|
-
cleanupTestDir();
|
|
5815
|
-
buildTestConfig(subName);
|
|
5816
|
-
const unregister = registerCleanup(() => {
|
|
5817
|
-
stopTestInstance();
|
|
5818
|
-
cleanupTestDir();
|
|
5819
|
-
});
|
|
5820
|
-
try {
|
|
5821
|
-
await startTestInstance();
|
|
5822
|
-
return await fn(TEST_API);
|
|
5823
|
-
} finally {
|
|
5824
|
-
unregister();
|
|
5825
|
-
stopTestInstance();
|
|
5826
|
-
cleanupTestDir();
|
|
5827
|
-
}
|
|
5828
|
-
}
|
|
5829
|
-
|
|
5830
|
-
// src/commands/subscription.ts
|
|
5831
|
-
var IS_TTY = process.stdout.isTTY === true;
|
|
5832
|
-
var BAR_WIDTH = 20;
|
|
5833
|
-
function createProgressPrinter(totalRounds = 1) {
|
|
5834
|
-
let alive = 0;
|
|
5835
|
-
let dead = 0;
|
|
5836
|
-
const resultMap = /* @__PURE__ */ new Map();
|
|
5837
|
-
function render(done, total) {
|
|
5838
|
-
if (!IS_TTY) return;
|
|
5839
|
-
const pct = Math.round(done / total * 100);
|
|
5840
|
-
const filled = Math.round(done / total * BAR_WIDTH);
|
|
5841
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
|
|
5842
|
-
process.stdout.write(`\r${bar} ${done}/${total} (${pct}%) | ${colors.green(`\u2713${alive}`)} ${colors.red(`\u2717${dead}`)}`);
|
|
5988
|
+
// src/progress.ts
|
|
5989
|
+
var IS_TTY = process.stdout.isTTY === true;
|
|
5990
|
+
var BAR_WIDTH = 20;
|
|
5991
|
+
function createProgressPrinter(totalRounds = 1) {
|
|
5992
|
+
let alive = 0;
|
|
5993
|
+
let dead = 0;
|
|
5994
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
5995
|
+
function render(done, total) {
|
|
5996
|
+
if (!IS_TTY) return;
|
|
5997
|
+
const pct = Math.round(done / total * 100);
|
|
5998
|
+
const filled = Math.round(done / total * BAR_WIDTH);
|
|
5999
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
|
|
6000
|
+
process.stdout.write(`\r${bar} ${done}/${total} (${pct}%) | ${colors.green(`\u2713${alive}`)} ${colors.red(`\u2717${dead}`)}`);
|
|
5843
6001
|
}
|
|
5844
6002
|
return {
|
|
5845
6003
|
onResult(result, index, total, round = 1) {
|
|
5846
6004
|
if (resultMap.size === 0 && totalRounds > 1) {
|
|
5847
6005
|
console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
|
|
5848
6006
|
}
|
|
6007
|
+
const prev = resultMap.get(result.name);
|
|
6008
|
+
if (prev) {
|
|
6009
|
+
if (prev.result.delay !== null) alive--;
|
|
6010
|
+
else dead--;
|
|
6011
|
+
}
|
|
5849
6012
|
if (result.delay !== null) alive++;
|
|
5850
6013
|
else dead++;
|
|
5851
6014
|
resultMap.set(result.name, { result, round });
|
|
@@ -5864,6 +6027,7 @@ function createProgressPrinter(totalRounds = 1) {
|
|
|
5864
6027
|
process.stdout.write("\n");
|
|
5865
6028
|
}
|
|
5866
6029
|
console.log("");
|
|
6030
|
+
if (!IS_TTY) return;
|
|
5867
6031
|
const entries = [...resultMap.values()];
|
|
5868
6032
|
entries.sort((a, b) => a.result.name.localeCompare(b.result.name));
|
|
5869
6033
|
const total = entries.length;
|
|
@@ -5892,687 +6056,935 @@ function formatCleanSummary(result) {
|
|
|
5892
6056
|
function formatTestSummary(summary) {
|
|
5893
6057
|
return `\u7ED3\u679C: ${colors.green(`${summary.alive} \u5B58\u6D3B`)} / ${colors.red(`${summary.dead} \u5931\u8D25`)} / ${summary.total} \u603B\u8BA1`;
|
|
5894
6058
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
const
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
6059
|
+
|
|
6060
|
+
// src/commands/status.ts
|
|
6061
|
+
function printStatus() {
|
|
6062
|
+
const status = getStatus();
|
|
6063
|
+
const state = getRunningState();
|
|
6064
|
+
const info = getConfigInfo();
|
|
6065
|
+
const overwriteEnabled = isOverwriteEnabled();
|
|
6066
|
+
const overwriteFiles = listOverwriteFile().files;
|
|
6067
|
+
const activeSub = getActiveSubscription();
|
|
6068
|
+
const { running, pid, daemon: daemonManaged } = state;
|
|
6069
|
+
console.log("");
|
|
6070
|
+
let modeLabel = "";
|
|
6071
|
+
if (info) {
|
|
6072
|
+
modeLabel = colors.cyan(info.tun ? " (TUN)" : " (Mixed)");
|
|
5905
6073
|
}
|
|
5906
|
-
const
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
} else {
|
|
5914
|
-
const activeSub = getActiveSubscription();
|
|
5915
|
-
if (!activeSub) {
|
|
5916
|
-
console.error("\u9519\u8BEF: \u6CA1\u6709\u6D3B\u8DC3\u8BA2\u9605\uFF0C\u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
5917
|
-
process.exit(1);
|
|
6074
|
+
const statusText = running ? colors.green("\u25CF \u8FD0\u884C\u4E2D") : colors.yellow("\u4E0D\u5728\u8FD0\u884C");
|
|
6075
|
+
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}${modeLabel}`);
|
|
6076
|
+
console.log(`${colors.gray("\u5185\u6838: ")}${status.kernelVersion || "\u672A\u5B89\u88C5"}`);
|
|
6077
|
+
if (pid) {
|
|
6078
|
+
console.log(`${colors.gray("PID: ")}${pid}`);
|
|
6079
|
+
if (!daemonManaged && status.processInfo) {
|
|
6080
|
+
console.log(`${colors.gray("\u5185\u5B58: ")}${status.processInfo.memory}`);
|
|
5918
6081
|
}
|
|
5919
|
-
target = activeSub;
|
|
5920
|
-
}
|
|
5921
|
-
return { target, timeout, concurrency };
|
|
5922
|
-
}
|
|
5923
|
-
function printRestartHintIfRunning() {
|
|
5924
|
-
if (getRunningState().running) {
|
|
5925
|
-
console.log(colors.yellow("\u63D0\u793A: \u8FD0\u884C\u4E2D\u7684\u5B9E\u4F8B\u4ECD\u4F7F\u7528\u65E7\u914D\u7F6E\uFF0C\u6267\u884C mihomo start \u4F7F\u66F4\u65B0\u751F\u6548"));
|
|
5926
|
-
console.log("");
|
|
5927
6082
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
if (
|
|
6083
|
+
if (info) {
|
|
6084
|
+
if (info.tun) {
|
|
6085
|
+
const extra = info.mixedPort ? `\uFF0C\u53E6\u76D1\u542C ${info.mixedPort}` : "";
|
|
6086
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}TUN \u63A5\u7BA1${extra}`);
|
|
6087
|
+
} else if (info.mixedPort) {
|
|
6088
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}${info.mixedPort}`);
|
|
6089
|
+
} else {
|
|
6090
|
+
const ports = [];
|
|
6091
|
+
if (info.httpPort) ports.push(`HTTP:${info.httpPort}`);
|
|
6092
|
+
if (info.socksPort) ports.push(`SOCKS:${info.socksPort}`);
|
|
6093
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}${ports.length > 0 ? ports.join(", ") : "\u672A\u77E5"}`);
|
|
6094
|
+
}
|
|
5933
6095
|
}
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
console.log(
|
|
5940
|
-
|
|
6096
|
+
if (activeSub) {
|
|
6097
|
+
let subLine = `${colors.gray("\u8BA2\u9605: ")}${activeSub.name}`;
|
|
6098
|
+
if (info) {
|
|
6099
|
+
subLine += ` (${formatProxySummary(info)})`;
|
|
6100
|
+
}
|
|
6101
|
+
console.log(subLine);
|
|
6102
|
+
} else {
|
|
6103
|
+
console.log(`${colors.gray("\u8BA2\u9605: ")}\u672A\u914D\u7F6E`);
|
|
6104
|
+
}
|
|
6105
|
+
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
6106
|
+
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "").replace(/\.ya?ml$/, "") || "\u4E3B\u6587\u4EF6").join(", ");
|
|
6107
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (${names})`);
|
|
6108
|
+
} else if (overwriteEnabled) {
|
|
6109
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (\u65E0\u6587\u4EF6)`);
|
|
6110
|
+
} else {
|
|
6111
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.yellow("\u5DF2\u7981\u7528")}`);
|
|
6112
|
+
}
|
|
6113
|
+
if (isDaemonEnabled()) {
|
|
6114
|
+
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${colors.green("\u5DF2\u542F\u7528")} ${colors.gray("(\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F)")}`);
|
|
5941
6115
|
}
|
|
5942
|
-
const activeSub = getActiveSubscription();
|
|
5943
|
-
console.log(colors.cyan("\u8BA2\u9605\u5217\u8868:"));
|
|
5944
|
-
subs.forEach((s, i) => {
|
|
5945
|
-
const time = formatDate(s.updated_at);
|
|
5946
|
-
const defaultMark = activeSub && s.name === activeSub.name ? colors.green(" [\u4F7F\u7528\u4E2D]") : "";
|
|
5947
|
-
const mergeBadge = isMultiUrl(s.url) ? colors.cyan(` [\u5408\u5E76 ${splitUrls(s.url).length} \u6E90]`) : "";
|
|
5948
|
-
const interval = resolveUpdateInterval(s.url, s.update_interval);
|
|
5949
|
-
console.log(` ${i + 1}. ${s.name}${defaultMark}${mergeBadge}`);
|
|
5950
|
-
console.log(` ${colors.gray("\u66F4\u65B0: ")}${time} (\u95F4\u9694: ${interval}h)`);
|
|
5951
|
-
if (s.username) {
|
|
5952
|
-
console.log(` ${colors.gray("\u7528\u6237: ")}${s.username}`);
|
|
5953
|
-
}
|
|
5954
|
-
if (s.download !== void 0 || s.total !== void 0) {
|
|
5955
|
-
const used = (s.upload || 0) + (s.download || 0);
|
|
5956
|
-
const usedStr = formatBytes(used);
|
|
5957
|
-
const totalStr = formatBytes(s.total);
|
|
5958
|
-
let percentStr = "";
|
|
5959
|
-
if (s.total && s.total > 0) {
|
|
5960
|
-
const percent = Math.min(used / s.total * 100, 100);
|
|
5961
|
-
percentStr = ` (${percent.toFixed(1)}%)`;
|
|
5962
|
-
}
|
|
5963
|
-
console.log(` ${colors.gray("\u6D41\u91CF: ")}${usedStr} / ${totalStr}${percentStr}`);
|
|
5964
|
-
}
|
|
5965
|
-
if (s.expire !== void 0) {
|
|
5966
|
-
console.log(` ${colors.gray("\u5230\u671F: ")}${formatTimestamp(s.expire)}`);
|
|
5967
|
-
}
|
|
5968
|
-
if (s.web_page_url) {
|
|
5969
|
-
console.log(` ${colors.gray("\u9875\u9762: ")}${s.web_page_url}`);
|
|
5970
|
-
}
|
|
5971
|
-
});
|
|
5972
|
-
console.log("");
|
|
5973
|
-
console.log("\u5207\u6362\u8BA2\u9605: mihomo sub use <name>");
|
|
5974
|
-
console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
|
|
5975
|
-
console.log("\u66F4\u65B0\u8BA2\u9605: mihomo sub update [name]");
|
|
5976
|
-
console.log("\u5220\u9664\u8BA2\u9605: mihomo sub remove <name>");
|
|
5977
|
-
console.log("\u6D4B\u8BD5\u8282\u70B9: mihomo sub test [name]");
|
|
5978
|
-
console.log("\u6E05\u7406\u8282\u70B9: mihomo sub clean [name]");
|
|
5979
|
-
console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
|
|
5980
6116
|
console.log("");
|
|
5981
6117
|
}
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
6118
|
+
|
|
6119
|
+
// src/commands/stop.ts
|
|
6120
|
+
function handleStopResult(result) {
|
|
6121
|
+
if (result.remaining && result.remaining.length > 0) {
|
|
6122
|
+
console.error(`${colors.red("\u90E8\u5206\u8FDB\u7A0B\u672A\u7EC8\u6B62:")} ${result.remaining.join(", ")}`);
|
|
6123
|
+
console.error("\u8BF7\u624B\u52A8\u8FD0\u884C: sudo pkill -9 mihomo");
|
|
6124
|
+
process.exit(1);
|
|
6125
|
+
}
|
|
6126
|
+
}
|
|
6127
|
+
async function cmdStop() {
|
|
6128
|
+
if (isDaemonEnabled()) {
|
|
6129
|
+
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u4EE3\u7406\u7531 launchd \u6258\u7BA1"));
|
|
6130
|
+
console.log("\u76F4\u63A5\u505C\u6B62\u4F1A\u88AB\u81EA\u52A8\u91CD\u65B0\u62C9\u8D77\uFF0C\u8BF7\u7528: mihomo daemon off");
|
|
5986
6131
|
return;
|
|
5987
6132
|
}
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
6133
|
+
const pids = getMihomoPids();
|
|
6134
|
+
if (pids.length === 0) {
|
|
6135
|
+
console.log(colors.yellow("\u4E0D\u5728\u8FD0\u884C"));
|
|
6136
|
+
return;
|
|
6137
|
+
}
|
|
6138
|
+
console.log(`\u505C\u6B62 ${pids.length} \u4E2A\u8FDB\u7A0B...`);
|
|
6139
|
+
handleStopResult(stop());
|
|
6140
|
+
console.log(colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B"));
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
// src/commands/start.ts
|
|
6144
|
+
async function cmdStart(args) {
|
|
6145
|
+
if (!hasKernel()) {
|
|
6146
|
+
console.error('\u9519\u8BEF: \u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
6147
|
+
process.exit(1);
|
|
6148
|
+
}
|
|
6149
|
+
const targetMode = args[1] === "tun" ? "tun" : "mixed";
|
|
6150
|
+
const daemonEnabled = isDaemonEnabled();
|
|
6151
|
+
if (targetMode === "tun" && daemonEnabled) {
|
|
6152
|
+
console.error(`${colors.red("\u9519\u8BEF:")} \u4FDD\u6D3B\u5DF2\u542F\u7528\uFF08\u4EC5\u652F\u6301 Mixed \u6A21\u5F0F\uFF09\uFF0C\u65E0\u6CD5\u542F\u52A8 TUN`);
|
|
6153
|
+
console.error("\u8BF7\u5148\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
6154
|
+
process.exit(1);
|
|
6155
|
+
}
|
|
6156
|
+
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
6157
|
+
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
6158
|
+
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
6159
|
+
const skipUpdate = hasFlag(args, "-s", "--no-update");
|
|
6160
|
+
const skipClean = hasFlag(args, "--no-clean");
|
|
6161
|
+
const updateTimeout = parseIntArg(args, "-u", "--update-timeout", DEFAULT_AUTO_UPDATE_TIMEOUT);
|
|
6162
|
+
const sub = getActiveSubscription();
|
|
6163
|
+
if (!sub) {
|
|
6164
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
6165
|
+
process.exit(1);
|
|
6166
|
+
}
|
|
6167
|
+
if (!skipUpdate) {
|
|
6168
|
+
await autoUpdateStaleSubscription({ timeout: updateTimeout });
|
|
6169
|
+
}
|
|
6170
|
+
if (!daemonEnabled) {
|
|
6171
|
+
if (hasRootResidue()) {
|
|
6172
|
+
console.error(`${colors.red("\u9519\u8BEF:")} \u5B58\u5728\u9700\u8981 root \u6743\u9650\u6E05\u7406\u7684\u6B8B\u7559\u8FDB\u7A0B/\u6587\u4EF6`);
|
|
6173
|
+
console.error(`\u8BF7\u5148\u624B\u52A8\u6E05\u7406: sudo pkill -9 mihomo && sudo rm -f ${PATHS.pidFile}`);
|
|
6174
|
+
console.error("\u6216\u5207\u6362\u5230 TUN \u6A21\u5F0F\u542F\u52A8\uFF08\u81EA\u52A8\u6E05\u7406\uFF09: mihomo start tun");
|
|
5993
6175
|
process.exit(1);
|
|
5994
6176
|
}
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6177
|
+
const status = getStatus();
|
|
6178
|
+
const hasProcess = status.running || status.allProcesses.length > 0;
|
|
6179
|
+
if (hasProcess) {
|
|
6180
|
+
const count = status.allProcesses.length > 0 ? status.allProcesses.length : 1;
|
|
6181
|
+
console.log(`\u505C\u6B62 ${count} \u4E2A\u8FDB\u7A0B...`);
|
|
6182
|
+
}
|
|
6183
|
+
handleStopResult(stop());
|
|
6184
|
+
if (hasProcess) {
|
|
6185
|
+
console.log(`${colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B")}
|
|
6186
|
+
`);
|
|
6187
|
+
}
|
|
6188
|
+
}
|
|
6189
|
+
let configInfo;
|
|
6190
|
+
try {
|
|
6191
|
+
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
6192
|
+
} catch (e) {
|
|
6193
|
+
console.error(`${colors.red("\u914D\u7F6E\u9519\u8BEF:")} ${e.message}`);
|
|
6194
|
+
process.exit(1);
|
|
6195
|
+
}
|
|
6196
|
+
const modeLabel = targetMode === "tun" ? "TUN" : "Mixed";
|
|
6197
|
+
console.log([colors.cyan(modeLabel), sub.name, formatProxySummary(configInfo)].join(" \xB7 "));
|
|
6198
|
+
try {
|
|
6199
|
+
const pid = await launchOrRestart(targetMode);
|
|
6200
|
+
const label = daemonEnabled ? "\u5DF2\u542F\u52A8 (\u4FDD\u6D3B)" : "\u5DF2\u542F\u52A8";
|
|
6201
|
+
console.log(`${colors.green(label)}${pid ? ` (PID ${pid})` : ""}`);
|
|
6202
|
+
} catch (e) {
|
|
6203
|
+
const msg = e.message;
|
|
6204
|
+
const lines = msg.split("\n");
|
|
6205
|
+
console.error(`${colors.red("\u542F\u52A8\u5931\u8D25:")} ${lines[0]}`);
|
|
6206
|
+
if (lines.length > 1) {
|
|
6207
|
+
for (const line of lines.slice(1)) console.error(line);
|
|
6208
|
+
}
|
|
6209
|
+
process.exit(1);
|
|
6210
|
+
}
|
|
6211
|
+
const cleanThreshold = isGithubUrl(sub.url) ? AUTO_CLEAN_THRESHOLD_GITHUB : AUTO_CLEAN_THRESHOLD;
|
|
6212
|
+
if (!skipClean && configInfo.proxies > cleanThreshold) {
|
|
6213
|
+
const cache = readSubscriptionCache();
|
|
6214
|
+
const lastCleanAt = cache[sub.name]?.last_auto_clean_at;
|
|
6215
|
+
const withinCooldown = !!lastCleanAt && Date.now() - new Date(lastCleanAt).getTime() < AUTO_CLEAN_COOLDOWN_HOURS * 60 * 60 * 1e3;
|
|
6216
|
+
if (!withinCooldown) {
|
|
6217
|
+
console.log("");
|
|
6218
|
+
console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${cleanThreshold}\uFF0C\u81EA\u52A8\u6E05\u7406\uFF08${AUTO_CLEAN_COOLDOWN_HOURS}h \u5185\u4EC5\u4E00\u6B21\uFF0C--no-clean \u8DF3\u8FC7\uFF09...`);
|
|
6219
|
+
console.log("");
|
|
6220
|
+
await sleep(1e3);
|
|
6221
|
+
const progress = createProgressPrinter(rounds);
|
|
6222
|
+
const cleanResult = await autoCleanSubscription(sub.name, {
|
|
6223
|
+
timeout,
|
|
6224
|
+
concurrency,
|
|
6225
|
+
rounds,
|
|
6226
|
+
onResult: progress.onResult,
|
|
6227
|
+
onRetryRound: progress.onRetryRound
|
|
6228
|
+
});
|
|
6229
|
+
progress.finish();
|
|
6230
|
+
console.log(formatTestSummary(cleanResult.summary));
|
|
6231
|
+
if (cleanResult.skipped) {
|
|
6232
|
+
console.log(colors.yellow("\u5B58\u6D3B\u8282\u70B9\u4E0D\u8DB3 1%\uFF0C\u8DF3\u8FC7\u6E05\u7406\u3002\u8BF7\u68C0\u67E5\u539F\u59CB\u8BA2\u9605\u662F\u5426\u6709\u6548"));
|
|
6233
|
+
} else if (cleanResult.removedProxies > 0) {
|
|
6234
|
+
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
6235
|
+
console.log("");
|
|
6236
|
+
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|
|
6237
|
+
if (!daemonEnabled) handleStopResult(stop());
|
|
6238
|
+
try {
|
|
6239
|
+
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
6240
|
+
const pid = await launchOrRestart(targetMode);
|
|
6241
|
+
console.log(`${colors.green("\u5DF2\u91CD\u542F")}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
6242
|
+
} catch (e) {
|
|
6243
|
+
console.error(`${colors.red("\u91CD\u542F\u5931\u8D25:")} ${e.message.split("\n")[0]}`);
|
|
6000
6244
|
process.exit(1);
|
|
6001
6245
|
}
|
|
6002
6246
|
}
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6247
|
+
saveSubscriptionCache(sub.name, { last_auto_clean_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
printStatus();
|
|
6251
|
+
}
|
|
6252
|
+
|
|
6253
|
+
// src/commands/overwrite.ts
|
|
6254
|
+
function printOverwriteList() {
|
|
6255
|
+
const info = listOverwriteFile();
|
|
6256
|
+
const statusText = info.enabled ? colors.green("\u5DF2\u542F\u7528") : colors.yellow("\u5DF2\u7981\u7528");
|
|
6257
|
+
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}`);
|
|
6258
|
+
console.log(`${colors.gray("\u4F4D\u7F6E: ")}${info.dir}`);
|
|
6259
|
+
console.log("");
|
|
6260
|
+
if (info.files.length === 0) {
|
|
6261
|
+
console.log("\u6682\u65E0\u8986\u5199\u6587\u4EF6");
|
|
6262
|
+
console.log("");
|
|
6263
|
+
console.log(`\u7528\u6CD5\u793A\u4F8B: \u521B\u5EFA\u6587\u4EF6 ${path7.join(info.dir, "overwrite.yaml")}`);
|
|
6264
|
+
console.log(` \u6216 ${path7.join(info.dir, "overwrite.dns.yaml")}`);
|
|
6265
|
+
console.log("");
|
|
6266
|
+
} else {
|
|
6267
|
+
console.log(`${colors.cyan("\u8986\u5199\u6587\u4EF6")} (${info.files.length} \u4E2A\uFF0C\u6309\u987A\u5E8F\u52A0\u8F7D):`);
|
|
6268
|
+
console.log("");
|
|
6269
|
+
info.files.forEach((f, i) => {
|
|
6270
|
+
const num = i < 10 ? ` ${i}` : `${i}`;
|
|
6271
|
+
console.log(` ${num}. ${f.name}`);
|
|
6272
|
+
if (f.scope) {
|
|
6273
|
+
console.log(` ${colors.gray("\u4F5C\u7528\u57DF: ")}${f.scope}`);
|
|
6017
6274
|
}
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
addSubscription(url, name);
|
|
6021
|
-
setDefaultSubscription(name);
|
|
6022
|
-
const info = await downloadSubscription(url, name);
|
|
6023
|
-
const repoUrl = githubRepoUrl(url);
|
|
6024
|
-
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
6025
|
-
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
6026
|
-
} catch (e) {
|
|
6027
|
-
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
6028
|
-
process.exit(1);
|
|
6275
|
+
if (f.keys.length > 0) {
|
|
6276
|
+
console.log(` ${colors.gray("\u5B57\u6BB5: ")}${f.keys.join(", ")}`);
|
|
6029
6277
|
}
|
|
6030
|
-
}
|
|
6278
|
+
});
|
|
6031
6279
|
console.log("");
|
|
6032
|
-
await printSubscriptionList();
|
|
6033
|
-
return;
|
|
6034
6280
|
}
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
for (const r of results) {
|
|
6047
|
-
if (r.success) ok++;
|
|
6048
|
-
printUpdateResult(r);
|
|
6049
|
-
}
|
|
6050
|
-
if (ok === 0) process.exit(1);
|
|
6281
|
+
console.log("\u542F\u7528\u8986\u5199: mihomo ow on");
|
|
6282
|
+
console.log("\u7981\u7528\u8986\u5199: mihomo ow off");
|
|
6283
|
+
console.log("");
|
|
6284
|
+
}
|
|
6285
|
+
async function cmdOverwrite(args) {
|
|
6286
|
+
const action = args?.[1];
|
|
6287
|
+
const currentMode = getRuntimeMode();
|
|
6288
|
+
const restartNeeded = isRestartNeededOnChange();
|
|
6289
|
+
if (action === "on" || action === "enable") {
|
|
6290
|
+
if (isOverwriteEnabled()) {
|
|
6291
|
+
console.log("\u8986\u5199\u914D\u7F6E\u5DF2\u662F\u542F\u7528\u72B6\u6001");
|
|
6051
6292
|
console.log("");
|
|
6052
|
-
|
|
6053
|
-
await printSubscriptionList();
|
|
6293
|
+
printOverwriteList();
|
|
6054
6294
|
return;
|
|
6055
6295
|
}
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
process.exit(1);
|
|
6296
|
+
setOverwriteEnabled(true);
|
|
6297
|
+
console.log("\u5DF2\u542F\u7528\u8986\u5199\u914D\u7F6E");
|
|
6298
|
+
if (restartNeeded) {
|
|
6299
|
+
console.log("");
|
|
6300
|
+
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
6301
|
+
return;
|
|
6063
6302
|
}
|
|
6064
|
-
console.log(`\u5DF2\u66F4\u65B0 (${formatProxySummary(result)})`);
|
|
6065
6303
|
console.log("");
|
|
6066
|
-
|
|
6067
|
-
await printSubscriptionList();
|
|
6304
|
+
printOverwriteList();
|
|
6068
6305
|
return;
|
|
6069
6306
|
}
|
|
6070
|
-
if (action === "
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
if (!name) {
|
|
6074
|
-
console.error("\u9519\u8BEF: \u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
6075
|
-
if (subs.length > 0) {
|
|
6076
|
-
console.log("\n\u53EF\u7528\u8BA2\u9605:");
|
|
6077
|
-
for (const s of subs) console.log(` ${s.name}`);
|
|
6078
|
-
}
|
|
6079
|
-
process.exit(1);
|
|
6080
|
-
}
|
|
6081
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
6082
|
-
const target = pickSingleSubscription(matches, name);
|
|
6083
|
-
const currentDefault = getActiveSubscription();
|
|
6084
|
-
const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
|
|
6085
|
-
if (isAlreadyDefault) {
|
|
6086
|
-
console.log(`"${target.name}" \u5DF2\u662F\u5F53\u524D\u4F7F\u7528\u7684\u8BA2\u9605`);
|
|
6307
|
+
if (action === "off" || action === "disable") {
|
|
6308
|
+
if (!isOverwriteEnabled()) {
|
|
6309
|
+
console.log("\u8986\u5199\u914D\u7F6E\u5DF2\u662F\u7981\u7528\u72B6\u6001");
|
|
6087
6310
|
console.log("");
|
|
6088
|
-
|
|
6311
|
+
printOverwriteList();
|
|
6089
6312
|
return;
|
|
6090
6313
|
}
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
const success = setDefaultSubscription(target.name);
|
|
6094
|
-
if (success) {
|
|
6095
|
-
console.log(`\u5DF2\u5207\u6362\u5230 "${target.name}"`);
|
|
6096
|
-
} else {
|
|
6097
|
-
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u8BA2\u9605 "${name}"`);
|
|
6098
|
-
process.exit(1);
|
|
6099
|
-
}
|
|
6314
|
+
setOverwriteEnabled(false);
|
|
6315
|
+
console.log("\u5DF2\u7981\u7528\u8986\u5199\u914D\u7F6E");
|
|
6100
6316
|
if (restartNeeded) {
|
|
6101
6317
|
console.log("");
|
|
6102
|
-
await cmdStart(["start", currentMode]);
|
|
6318
|
+
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
6103
6319
|
return;
|
|
6104
6320
|
}
|
|
6105
6321
|
console.log("");
|
|
6106
|
-
|
|
6322
|
+
printOverwriteList();
|
|
6107
6323
|
return;
|
|
6108
6324
|
}
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6325
|
+
console.log("");
|
|
6326
|
+
printOverwriteList();
|
|
6327
|
+
}
|
|
6328
|
+
|
|
6329
|
+
// src/commands/reset.ts
|
|
6330
|
+
import fs8 from "fs";
|
|
6331
|
+
import readline from "readline";
|
|
6332
|
+
var RESET_TARGETS = [
|
|
6333
|
+
{
|
|
6334
|
+
id: "subs",
|
|
6335
|
+
aliases: ["sub", "subs", "subscription", "subscriptions"],
|
|
6336
|
+
label: "\u8BA2\u9605",
|
|
6337
|
+
paths: () => [DIRS.subscriptions],
|
|
6338
|
+
needsStop: true,
|
|
6339
|
+
// 同步清空 settings 里的订阅列表:只删缓存文件会留下"列表存在但无配置"的半重置状态
|
|
6340
|
+
// (start 会报"未找到订阅配置")。active_subscription 一并清除
|
|
6341
|
+
onAfter: () => writeSettings({ subscriptions: void 0, active_subscription: void 0 })
|
|
6342
|
+
},
|
|
6343
|
+
{
|
|
6344
|
+
id: "logs",
|
|
6345
|
+
aliases: ["log", "logs"],
|
|
6346
|
+
label: "\u65E5\u5FD7",
|
|
6347
|
+
paths: () => [DIRS.logs],
|
|
6348
|
+
needsStop: false
|
|
6349
|
+
},
|
|
6350
|
+
{
|
|
6351
|
+
id: "data",
|
|
6352
|
+
aliases: ["data"],
|
|
6353
|
+
label: "\u8FD0\u884C\u6570\u636E",
|
|
6354
|
+
paths: () => [DIRS.data],
|
|
6355
|
+
needsStop: true
|
|
6356
|
+
},
|
|
6357
|
+
{
|
|
6358
|
+
id: "runtime",
|
|
6359
|
+
aliases: ["runtime"],
|
|
6360
|
+
label: "\u8FD0\u884C\u65F6",
|
|
6361
|
+
paths: () => [DIRS.runtime],
|
|
6362
|
+
needsStop: true
|
|
6363
|
+
},
|
|
6364
|
+
{
|
|
6365
|
+
id: "settings",
|
|
6366
|
+
aliases: ["setting", "settings", "config"],
|
|
6367
|
+
label: "\u8BBE\u7F6E",
|
|
6368
|
+
paths: () => [PATHS.settingsFile],
|
|
6369
|
+
needsStop: false
|
|
6370
|
+
},
|
|
6371
|
+
{
|
|
6372
|
+
id: "kernel",
|
|
6373
|
+
aliases: ["kernel", "core"],
|
|
6374
|
+
label: "\u5185\u6838",
|
|
6375
|
+
paths: () => [DIRS.kernel],
|
|
6376
|
+
needsStop: false,
|
|
6377
|
+
onAfter: () => clearKernelVersionCache(),
|
|
6378
|
+
checkEmpty: () => !hasKernel(),
|
|
6379
|
+
emptyMsg: "\u5185\u6838\u672A\u5B89\u88C5\uFF0C\u65E0\u9700\u5220\u9664",
|
|
6380
|
+
warnIfRunning: true
|
|
6381
|
+
},
|
|
6382
|
+
{
|
|
6383
|
+
id: "overwrites",
|
|
6384
|
+
aliases: ["overwrite", "overwrites", "ow"],
|
|
6385
|
+
label: "\u8986\u5199",
|
|
6386
|
+
paths: () => {
|
|
6387
|
+
const dir = USER_DATA_DIR;
|
|
6388
|
+
if (!fs8.existsSync(dir)) return [];
|
|
6389
|
+
return fs8.readdirSync(dir).filter(isOverwriteFilename).map((f) => `${dir}/${f}`);
|
|
6390
|
+
},
|
|
6391
|
+
needsStop: false
|
|
6392
|
+
},
|
|
6393
|
+
{
|
|
6394
|
+
id: "daemon",
|
|
6395
|
+
aliases: ["daemon"],
|
|
6396
|
+
label: "\u4FDD\u6D3B",
|
|
6397
|
+
// 卸载由确认后的 disablesDaemon 段统一处理(需 sudo,受取消保护);
|
|
6398
|
+
// 此处 paths 返回空(plist 在系统目录,用户态删不掉,且不应提前删破坏卸载),
|
|
6399
|
+
// onAfter 因幂等守卫(plist 已删)成为 no-op,仅作单独 reset 未走前段时的兜底。
|
|
6400
|
+
paths: () => [],
|
|
6401
|
+
needsStop: false,
|
|
6402
|
+
onAfter: () => disableDaemon(),
|
|
6403
|
+
checkEmpty: () => !isDaemonEnabled(),
|
|
6404
|
+
emptyMsg: "\u4FDD\u6D3B\u672A\u542F\u7528\uFF0C\u65E0\u9700\u5220\u9664"
|
|
6405
|
+
}
|
|
6406
|
+
];
|
|
6407
|
+
function resolveResetTargets(names) {
|
|
6408
|
+
const matched = [];
|
|
6409
|
+
const unmatched = [];
|
|
6410
|
+
for (const name of names) {
|
|
6411
|
+
const t = RESET_TARGETS.find((t2) => t2.aliases.includes(name.toLowerCase()));
|
|
6412
|
+
if (t) {
|
|
6413
|
+
if (!matched.find((m) => m.id === t.id)) matched.push(t);
|
|
6120
6414
|
} else {
|
|
6121
|
-
|
|
6122
|
-
}
|
|
6123
|
-
const cached = subs.find((s) => s.name === target.name);
|
|
6124
|
-
let webPageUrl = cached?.web_page_url;
|
|
6125
|
-
if (!webPageUrl) {
|
|
6126
|
-
console.log("\u8BA2\u9605\u4FE1\u606F\u4E2D\u7F3A\u5C11\u9875\u9762\u5730\u5740\uFF0C\u6B63\u5728\u66F4\u65B0\u8BA2\u9605...");
|
|
6127
|
-
try {
|
|
6128
|
-
await downloadSubscription(target.url, target.name);
|
|
6129
|
-
const cache = readSubscriptionCache();
|
|
6130
|
-
if (cache[target.name]?.web_page_url) {
|
|
6131
|
-
webPageUrl = cache[target.name].web_page_url;
|
|
6132
|
-
} else {
|
|
6133
|
-
console.error("\u9519\u8BEF: \u8BE5\u8BA2\u9605\u6CA1\u6709\u63D0\u4F9B\u9875\u9762\u5730\u5740");
|
|
6134
|
-
process.exit(1);
|
|
6135
|
-
}
|
|
6136
|
-
} catch (e) {
|
|
6137
|
-
console.error(`\u66F4\u65B0\u5931\u8D25: ${e.message}`);
|
|
6138
|
-
process.exit(1);
|
|
6139
|
-
}
|
|
6140
|
-
}
|
|
6141
|
-
console.log(`\u6253\u5F00\u8BA2\u9605\u9875\u9762: ${webPageUrl}`);
|
|
6142
|
-
const opened = openUrl(webPageUrl);
|
|
6143
|
-
if (!opened) {
|
|
6144
|
-
console.log("\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684\u5730\u5740");
|
|
6415
|
+
unmatched.push(name);
|
|
6145
6416
|
}
|
|
6146
|
-
return;
|
|
6147
6417
|
}
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6418
|
+
return { matched, unmatched };
|
|
6419
|
+
}
|
|
6420
|
+
async function confirmPrompt(question) {
|
|
6421
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
6422
|
+
const answer = await new Promise((resolve) => {
|
|
6423
|
+
rl.question(`${question} (y/N) `, (a) => {
|
|
6424
|
+
rl.close();
|
|
6425
|
+
resolve(a);
|
|
6426
|
+
});
|
|
6427
|
+
});
|
|
6428
|
+
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
6429
|
+
}
|
|
6430
|
+
async function cmdReset(args) {
|
|
6431
|
+
const flags = (args || []).filter((a) => a.startsWith("-"));
|
|
6432
|
+
const names = (args || []).slice(1).filter((a) => !a.startsWith("-"));
|
|
6433
|
+
const KNOWN_FLAGS = /* @__PURE__ */ new Set(["--full", "--yes", "-y"]);
|
|
6434
|
+
const unknownFlags = flags.filter((f) => !KNOWN_FLAGS.has(f));
|
|
6435
|
+
if (unknownFlags.length > 0) {
|
|
6436
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684\u9009\u9879: ${unknownFlags.join(", ")}`);
|
|
6166
6437
|
console.log("");
|
|
6167
|
-
|
|
6168
|
-
|
|
6438
|
+
console.log("\u53EF\u7528\u9009\u9879: --full\uFF08\u5220\u5168\u90E8\uFF09, -y/--yes\uFF08\u8DF3\u8FC7\u786E\u8BA4\uFF09");
|
|
6439
|
+
process.exit(1);
|
|
6169
6440
|
}
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
const
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
timeout,
|
|
6180
|
-
concurrency,
|
|
6181
|
-
rounds,
|
|
6182
|
-
apiBase,
|
|
6183
|
-
onResult: progress.onResult,
|
|
6184
|
-
onRetryRound: progress.onRetryRound
|
|
6185
|
-
});
|
|
6186
|
-
});
|
|
6187
|
-
progress.finish();
|
|
6188
|
-
console.log(formatTestSummary(result.summary));
|
|
6189
|
-
if (result.skipped) {
|
|
6441
|
+
const fullReset = flags.includes("--full");
|
|
6442
|
+
const skipConfirm = flags.includes("--yes") || flags.includes("-y");
|
|
6443
|
+
let targets;
|
|
6444
|
+
if (fullReset) {
|
|
6445
|
+
targets = RESET_TARGETS;
|
|
6446
|
+
} else if (names.length > 0) {
|
|
6447
|
+
const { matched, unmatched } = resolveResetTargets(names);
|
|
6448
|
+
if (unmatched.length > 0) {
|
|
6449
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684\u91CD\u7F6E\u76EE\u6807: ${unmatched.join(", ")}`);
|
|
6190
6450
|
console.log("");
|
|
6191
|
-
console.log(
|
|
6192
|
-
|
|
6193
|
-
console.log(
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6451
|
+
console.log(`\u53EF\u7528\u76EE\u6807: ${RESET_TARGETS.map((t) => t.aliases[0]).join(", ")}`);
|
|
6452
|
+
console.log("");
|
|
6453
|
+
console.log("\u793A\u4F8B:");
|
|
6454
|
+
console.log(" mihomo reset sub log # \u5220\u9664\u8BA2\u9605\u548C\u65E5\u5FD7");
|
|
6455
|
+
console.log(" mihomo reset kernel # \u53EA\u5220\u5185\u6838");
|
|
6456
|
+
console.log(" mihomo reset --full # \u5220\u9664\u5168\u90E8");
|
|
6457
|
+
console.log(" mihomo reset # \u5220\u9664\u5168\u90E8\uFF08\u4FDD\u7559\u8BBE\u7F6E\u3001\u5185\u6838\u3001\u8986\u5199\uFF09");
|
|
6458
|
+
process.exit(1);
|
|
6459
|
+
}
|
|
6460
|
+
targets = matched;
|
|
6461
|
+
} else {
|
|
6462
|
+
targets = RESET_TARGETS.filter((t) => !["settings", "kernel", "overwrites", "daemon"].includes(t.id));
|
|
6463
|
+
}
|
|
6464
|
+
for (const t of targets) {
|
|
6465
|
+
if (t.checkEmpty?.()) {
|
|
6466
|
+
if (targets.length === 1) {
|
|
6467
|
+
console.log(t.emptyMsg);
|
|
6468
|
+
return;
|
|
6197
6469
|
}
|
|
6198
6470
|
}
|
|
6199
|
-
return;
|
|
6200
6471
|
}
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
timeout,
|
|
6210
|
-
concurrency,
|
|
6211
|
-
apiBase,
|
|
6212
|
-
onResult: progress.onResult
|
|
6213
|
-
});
|
|
6214
|
-
});
|
|
6215
|
-
progress.finish();
|
|
6216
|
-
console.log(formatTestSummary(summary));
|
|
6217
|
-
return;
|
|
6472
|
+
const needsStop = targets.some((t) => t.needsStop);
|
|
6473
|
+
const warnRunning = targets.some((t) => t.warnIfRunning);
|
|
6474
|
+
const kernelTargeted = targets.some((t) => t.id === "kernel");
|
|
6475
|
+
const daemonTargeted = targets.some((t) => t.id === "daemon");
|
|
6476
|
+
const disablesDaemon = needsStop || kernelTargeted || daemonTargeted;
|
|
6477
|
+
const pids = needsStop || warnRunning ? getMihomoPids() : [];
|
|
6478
|
+
if (warnRunning && pids.length > 0) {
|
|
6479
|
+
console.log(colors.yellow(`\u8B66\u544A: mihomo \u6B63\u5728\u8FD0\u884C (PID ${pids.join(", ")})\uFF0C\u5220\u9664\u5185\u6838\u540E\u5C06\u65E0\u6CD5\u91CD\u65B0\u542F\u52A8`));
|
|
6218
6480
|
}
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
process.exit(1);
|
|
6222
|
-
}
|
|
6223
|
-
|
|
6224
|
-
// src/commands/start.ts
|
|
6225
|
-
async function cmdStart(args) {
|
|
6226
|
-
if (!hasKernel()) {
|
|
6227
|
-
console.error('\u9519\u8BEF: \u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
6228
|
-
process.exit(1);
|
|
6229
|
-
}
|
|
6230
|
-
const targetMode = args[1] === "tun" ? "tun" : "mixed";
|
|
6231
|
-
const daemonEnabled = isDaemonEnabled();
|
|
6232
|
-
if (targetMode === "tun" && daemonEnabled) {
|
|
6233
|
-
console.error(`${colors.red("\u9519\u8BEF:")} \u4FDD\u6D3B\u5DF2\u542F\u7528\uFF08\u4EC5\u652F\u6301 Mixed \u6A21\u5F0F\uFF09\uFF0C\u65E0\u6CD5\u542F\u52A8 TUN`);
|
|
6234
|
-
console.error("\u8BF7\u5148\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
6235
|
-
process.exit(1);
|
|
6481
|
+
if (disablesDaemon && isDaemonEnabled()) {
|
|
6482
|
+
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u91CD\u7F6E\u5C06\u4E00\u5E76\u5173\u95ED\u4FDD\u6D3B\uFF08\u79FB\u9664\u5F00\u673A\u81EA\u542F\uFF09"));
|
|
6236
6483
|
}
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
const updateTimeout = parseIntArg(args, "-u", "--update-timeout", DEFAULT_AUTO_UPDATE_TIMEOUT);
|
|
6242
|
-
const sub = getActiveSubscription();
|
|
6243
|
-
if (!sub) {
|
|
6244
|
-
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
6245
|
-
process.exit(1);
|
|
6484
|
+
console.log(`\u5C06\u5220\u9664: ${targets.map((t) => t.label).join("\u3001")}`);
|
|
6485
|
+
if (!skipConfirm && !await confirmPrompt("\u786E\u8BA4?")) {
|
|
6486
|
+
console.log("\u5DF2\u53D6\u6D88");
|
|
6487
|
+
return;
|
|
6246
6488
|
}
|
|
6247
|
-
if (
|
|
6248
|
-
|
|
6489
|
+
if (disablesDaemon && isDaemonEnabled()) {
|
|
6490
|
+
try {
|
|
6491
|
+
disableDaemon();
|
|
6492
|
+
} catch (e) {
|
|
6493
|
+
console.error(`${colors.red("\u4FDD\u6D3B\u5173\u95ED\u5DF2\u53D6\u6D88\uFF0C\u91CD\u7F6E\u4E2D\u6B62:")} ${e.message.split("\n")[0]}`);
|
|
6494
|
+
return;
|
|
6495
|
+
}
|
|
6249
6496
|
}
|
|
6250
|
-
if (
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6497
|
+
if (needsStop && getMihomoPids().length > 0) {
|
|
6498
|
+
console.log("\u505C\u6B62\u8FDB\u7A0B...");
|
|
6499
|
+
cleanupAll();
|
|
6500
|
+
for (let i = 0; i < PROCESS_WAIT_ATTEMPTS; i++) {
|
|
6501
|
+
if (getMihomoPids().length === 0) break;
|
|
6502
|
+
await new Promise((r) => setTimeout(r, PROCESS_WAIT_INTERVAL));
|
|
6256
6503
|
}
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6504
|
+
}
|
|
6505
|
+
for (const t of targets) {
|
|
6506
|
+
for (const p of t.paths()) {
|
|
6507
|
+
if (fs8.existsSync(p)) {
|
|
6508
|
+
try {
|
|
6509
|
+
rmrf(p);
|
|
6510
|
+
} catch (e) {
|
|
6511
|
+
console.warn(` \u8B66\u544A: \u65E0\u6CD5\u5220\u9664 ${p}: ${e.message}`);
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6261
6514
|
}
|
|
6515
|
+
t.onAfter?.();
|
|
6262
6516
|
}
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
} catch (e) {
|
|
6267
|
-
console.error(`${colors.red("\u914D\u7F6E\u9519\u8BEF:")} ${e.message}`);
|
|
6268
|
-
process.exit(1);
|
|
6517
|
+
ensureDirs();
|
|
6518
|
+
if (targets.some((t) => t.id === "settings")) {
|
|
6519
|
+
invalidateSettingsCache();
|
|
6269
6520
|
}
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6521
|
+
console.log(colors.green(`\u5DF2\u91CD\u7F6E: ${targets.map((t) => t.label).join("\u3001")}`));
|
|
6522
|
+
}
|
|
6523
|
+
|
|
6524
|
+
// src/test-instance.ts
|
|
6525
|
+
import { spawn as spawn2 } from "child_process";
|
|
6526
|
+
import fs9 from "fs";
|
|
6527
|
+
import path8 from "path";
|
|
6528
|
+
var TEST_DIR = path8.join(USER_DATA_DIR, "test");
|
|
6529
|
+
var TEST_DIRS = {
|
|
6530
|
+
data: path8.join(TEST_DIR, "data"),
|
|
6531
|
+
runtime: path8.join(TEST_DIR, "runtime")
|
|
6532
|
+
};
|
|
6533
|
+
var TEST_PATHS = {
|
|
6534
|
+
configFile: path8.join(TEST_DIRS.runtime, "config.yaml"),
|
|
6535
|
+
pidFile: path8.join(TEST_DIRS.runtime, "pid"),
|
|
6536
|
+
logFile: path8.join(TEST_DIR, "test.log")
|
|
6537
|
+
};
|
|
6538
|
+
var TEST_API = `http://${TEST_CONFIG["external-controller"]}`;
|
|
6539
|
+
function ensureTestDirs() {
|
|
6540
|
+
for (const dir of Object.values(TEST_DIRS)) {
|
|
6541
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
function cleanupTestDir() {
|
|
6545
|
+
rmrf(TEST_DIR);
|
|
6546
|
+
}
|
|
6547
|
+
function buildTestConfig(subName) {
|
|
6548
|
+
ensureTestDirs();
|
|
6549
|
+
const rawContent = readSubscriptionRawConfig(subName);
|
|
6550
|
+
if (!rawContent) {
|
|
6551
|
+
throw new Error(`\u672A\u627E\u5230\u8BA2\u9605\u914D\u7F6E "${subName}"`);
|
|
6552
|
+
}
|
|
6553
|
+
const parsed = parseYamlOrJson(rawContent, "\u8BA2\u9605\u5185\u5BB9");
|
|
6554
|
+
const proxies = (parsed.proxies || []).filter(isProxyValid);
|
|
6555
|
+
if (proxies.length === 0) {
|
|
6556
|
+
throw new Error(`\u8BA2\u9605 "${subName}" \u6CA1\u6709\u6709\u6548\u8282\u70B9`);
|
|
6557
|
+
}
|
|
6558
|
+
const nameCount = /* @__PURE__ */ new Map();
|
|
6559
|
+
for (const proxy of proxies) {
|
|
6560
|
+
const count = (nameCount.get(proxy.name) || 0) + 1;
|
|
6561
|
+
nameCount.set(proxy.name, count);
|
|
6562
|
+
if (count > 1) {
|
|
6563
|
+
proxy.name = `${proxy.name} #${count}`;
|
|
6282
6564
|
}
|
|
6283
|
-
process.exit(1);
|
|
6284
6565
|
}
|
|
6285
|
-
const
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
timeout,
|
|
6294
|
-
concurrency,
|
|
6295
|
-
rounds,
|
|
6296
|
-
onResult: progress.onResult,
|
|
6297
|
-
onRetryRound: progress.onRetryRound
|
|
6298
|
-
});
|
|
6299
|
-
progress.finish();
|
|
6300
|
-
console.log(formatTestSummary(cleanResult.summary));
|
|
6301
|
-
if (cleanResult.skipped) {
|
|
6302
|
-
console.log(colors.yellow("\u5B58\u6D3B\u8282\u70B9\u4E0D\u8DB3 1%\uFF0C\u8DF3\u8FC7\u6E05\u7406\u3002\u8BF7\u68C0\u67E5\u539F\u59CB\u8BA2\u9605\u662F\u5426\u6709\u6548"));
|
|
6303
|
-
} else if (cleanResult.removedProxies > 0) {
|
|
6304
|
-
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
6305
|
-
console.log("");
|
|
6306
|
-
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|
|
6307
|
-
if (!daemonEnabled) handleStopResult(stop());
|
|
6308
|
-
try {
|
|
6309
|
-
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
6310
|
-
const pid = await launchOrRestart(targetMode);
|
|
6311
|
-
console.log(`${colors.green("\u5DF2\u91CD\u542F")}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
6312
|
-
} catch (e) {
|
|
6313
|
-
console.error(`${colors.red("\u91CD\u542F\u5931\u8D25:")} ${e.message.split("\n")[0]}`);
|
|
6314
|
-
process.exit(1);
|
|
6566
|
+
const config = {
|
|
6567
|
+
...TEST_CONFIG,
|
|
6568
|
+
proxies,
|
|
6569
|
+
"proxy-groups": [
|
|
6570
|
+
{
|
|
6571
|
+
name: "PROXY",
|
|
6572
|
+
type: "select",
|
|
6573
|
+
proxies: proxies.map((p) => p.name)
|
|
6315
6574
|
}
|
|
6575
|
+
],
|
|
6576
|
+
rules: ["MATCH,PROXY"]
|
|
6577
|
+
};
|
|
6578
|
+
const content = dumpYaml(config);
|
|
6579
|
+
fs9.writeFileSync(TEST_PATHS.configFile, content, { mode: 384 });
|
|
6580
|
+
}
|
|
6581
|
+
async function startTestInstance() {
|
|
6582
|
+
const binary = PATHS.mihomoBinary;
|
|
6583
|
+
if (!fs9.existsSync(binary)) throw new Error('\u672A\u627E\u5230 mihomo \u5185\u6838\uFF0C\u8BF7\u5148\u8FD0\u884C "mihomo kernel" \u4E0B\u8F7D');
|
|
6584
|
+
stopTestInstance();
|
|
6585
|
+
const logFd = fs9.openSync(TEST_PATHS.logFile, "a");
|
|
6586
|
+
const child = spawn2(binary, ["-d", TEST_DIRS.data, "-f", TEST_PATHS.configFile], {
|
|
6587
|
+
detached: true,
|
|
6588
|
+
stdio: ["ignore", logFd, logFd]
|
|
6589
|
+
});
|
|
6590
|
+
child.on("error", () => {
|
|
6591
|
+
});
|
|
6592
|
+
fs9.closeSync(logFd);
|
|
6593
|
+
child.unref();
|
|
6594
|
+
const pid = child.pid;
|
|
6595
|
+
if (!pid) throw new Error("\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u521B\u5EFA\u8FDB\u7A0B\uFF08\u5185\u6838\u4E8C\u8FDB\u5236\u53EF\u80FD\u4E0D\u53EF\u6267\u884C\uFF09");
|
|
6596
|
+
fs9.writeFileSync(TEST_PATHS.pidFile, pid.toString(), { mode: 384 });
|
|
6597
|
+
const client = createHttpClient({ timeout: 2e3 });
|
|
6598
|
+
let ready = false;
|
|
6599
|
+
for (let i = 0; i < 60; i++) {
|
|
6600
|
+
if (!isProcessRunning(pid)) break;
|
|
6601
|
+
try {
|
|
6602
|
+
await client.get(`${TEST_API}/version`);
|
|
6603
|
+
ready = true;
|
|
6604
|
+
break;
|
|
6605
|
+
} catch {
|
|
6606
|
+
await sleep(500);
|
|
6316
6607
|
}
|
|
6317
6608
|
}
|
|
6318
|
-
|
|
6609
|
+
if (!isProcessRunning(pid)) {
|
|
6610
|
+
let errorDetail = "";
|
|
6611
|
+
try {
|
|
6612
|
+
errorDetail = fs9.readFileSync(TEST_PATHS.logFile, "utf8").slice(-1e3);
|
|
6613
|
+
} catch {
|
|
6614
|
+
}
|
|
6615
|
+
throw new Error(`\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u5931\u8D25${errorDetail ? `
|
|
6616
|
+
${errorDetail}` : ""}`);
|
|
6617
|
+
}
|
|
6618
|
+
if (!ready) {
|
|
6619
|
+
throw new Error("\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u8D85\u65F6\uFF0CAPI \u672A\u54CD\u5E94");
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
function stopTestInstance() {
|
|
6623
|
+
let pid;
|
|
6624
|
+
try {
|
|
6625
|
+
pid = parseInt(fs9.readFileSync(TEST_PATHS.pidFile, "utf8").trim(), 10);
|
|
6626
|
+
} catch {
|
|
6627
|
+
return;
|
|
6628
|
+
}
|
|
6629
|
+
if (pid > 0 && isProcessRunning(pid) && isProcessCommandMatching(pid, TEST_PATHS.configFile)) {
|
|
6630
|
+
process.kill(pid, "SIGKILL");
|
|
6631
|
+
for (let i = 0; i < 20; i++) {
|
|
6632
|
+
if (!isProcessRunning(pid)) break;
|
|
6633
|
+
sleepSync(100);
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
try {
|
|
6637
|
+
fs9.unlinkSync(TEST_PATHS.pidFile);
|
|
6638
|
+
} catch {
|
|
6639
|
+
}
|
|
6640
|
+
}
|
|
6641
|
+
async function withTestInstance(subName, fn) {
|
|
6642
|
+
cleanupTestDir();
|
|
6643
|
+
buildTestConfig(subName);
|
|
6644
|
+
const unregister = registerCleanup(() => {
|
|
6645
|
+
stopTestInstance();
|
|
6646
|
+
cleanupTestDir();
|
|
6647
|
+
});
|
|
6648
|
+
try {
|
|
6649
|
+
await startTestInstance();
|
|
6650
|
+
return await fn(TEST_API);
|
|
6651
|
+
} finally {
|
|
6652
|
+
unregister();
|
|
6653
|
+
stopTestInstance();
|
|
6654
|
+
cleanupTestDir();
|
|
6655
|
+
}
|
|
6319
6656
|
}
|
|
6320
6657
|
|
|
6321
|
-
// src/commands/
|
|
6322
|
-
function
|
|
6323
|
-
const
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
console.
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6658
|
+
// src/commands/subscription.ts
|
|
6659
|
+
function githubRepoUrl(rawUrl) {
|
|
6660
|
+
const match = rawUrl.match(/raw\.githubusercontent\.com\/([^/]+\/[^/]+)/);
|
|
6661
|
+
if (match) return `https://github.com/${match[1]}`;
|
|
6662
|
+
return null;
|
|
6663
|
+
}
|
|
6664
|
+
function resolveTestTarget(args) {
|
|
6665
|
+
const subs = getSubscriptions();
|
|
6666
|
+
if (subs.length === 0) {
|
|
6667
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605");
|
|
6668
|
+
process.exit(1);
|
|
6669
|
+
}
|
|
6670
|
+
const nameArg = getNonFlagArg(args, 2);
|
|
6671
|
+
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
6672
|
+
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
6673
|
+
let target;
|
|
6674
|
+
if (nameArg) {
|
|
6675
|
+
const matches = findSubscriptionFuzzy(subs, nameArg);
|
|
6676
|
+
target = pickSingleSubscription(matches, nameArg);
|
|
6334
6677
|
} else {
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6678
|
+
const activeSub = getActiveSubscription();
|
|
6679
|
+
if (!activeSub) {
|
|
6680
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u6D3B\u8DC3\u8BA2\u9605\uFF0C\u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
6681
|
+
process.exit(1);
|
|
6682
|
+
}
|
|
6683
|
+
target = activeSub;
|
|
6684
|
+
}
|
|
6685
|
+
return { target, timeout, concurrency };
|
|
6686
|
+
}
|
|
6687
|
+
function printRestartHintIfRunning() {
|
|
6688
|
+
if (getRunningState().running) {
|
|
6689
|
+
console.log(colors.yellow("\u63D0\u793A: \u8FD0\u884C\u4E2D\u7684\u5B9E\u4F8B\u4ECD\u4F7F\u7528\u65E7\u914D\u7F6E\uFF0C\u6267\u884C mihomo start \u4F7F\u66F4\u65B0\u751F\u6548"));
|
|
6344
6690
|
console.log("");
|
|
6345
6691
|
}
|
|
6346
|
-
console.log("\u542F\u7528\u8986\u5199: mihomo ow on");
|
|
6347
|
-
console.log("\u7981\u7528\u8986\u5199: mihomo ow off");
|
|
6348
|
-
console.log("");
|
|
6349
6692
|
}
|
|
6350
|
-
|
|
6351
|
-
const
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
console.log("\u8986\u5199\u914D\u7F6E\u5DF2\u662F\u542F\u7528\u72B6\u6001");
|
|
6357
|
-
console.log("");
|
|
6358
|
-
printOverwriteList();
|
|
6359
|
-
return;
|
|
6360
|
-
}
|
|
6361
|
-
setOverwriteEnabled(true);
|
|
6362
|
-
console.log("\u5DF2\u542F\u7528\u8986\u5199\u914D\u7F6E");
|
|
6363
|
-
if (restartNeeded) {
|
|
6364
|
-
console.log("");
|
|
6365
|
-
await cmdStart(["start", currentMode]);
|
|
6366
|
-
return;
|
|
6367
|
-
}
|
|
6693
|
+
function printSubscriptionList() {
|
|
6694
|
+
const subs = getSubscriptionsWithCache();
|
|
6695
|
+
if (subs.length === 0) {
|
|
6696
|
+
console.log("\u6CA1\u6709\u8BA2\u9605");
|
|
6697
|
+
console.log("");
|
|
6698
|
+
console.log("\u6DFB\u52A0\u8BA2\u9605: mihomo sub add <url> [name]");
|
|
6368
6699
|
console.log("");
|
|
6369
|
-
printOverwriteList();
|
|
6370
6700
|
return;
|
|
6371
6701
|
}
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6702
|
+
const activeSub = getActiveSubscription();
|
|
6703
|
+
console.log(colors.cyan("\u8BA2\u9605\u5217\u8868:"));
|
|
6704
|
+
subs.forEach((s, i) => {
|
|
6705
|
+
const time = formatDate(s.updated_at);
|
|
6706
|
+
const defaultMark = activeSub && s.name === activeSub.name ? colors.green(" [\u4F7F\u7528\u4E2D]") : "";
|
|
6707
|
+
const mergeBadge = isMultiUrl(s.url) ? colors.cyan(` [\u5408\u5E76 ${splitUrls(s.url).length} \u6E90]`) : "";
|
|
6708
|
+
const interval = resolveUpdateInterval(s.url, s.update_interval);
|
|
6709
|
+
console.log(` ${i + 1}. ${s.name}${defaultMark}${mergeBadge}`);
|
|
6710
|
+
console.log(` ${colors.gray("\u66F4\u65B0: ")}${time} (\u95F4\u9694: ${interval}h)`);
|
|
6711
|
+
if (s.username) {
|
|
6712
|
+
console.log(` ${colors.gray("\u7528\u6237: ")}${s.username}`);
|
|
6378
6713
|
}
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6714
|
+
if (s.download !== void 0 || s.total !== void 0) {
|
|
6715
|
+
const used = (s.upload || 0) + (s.download || 0);
|
|
6716
|
+
const usedStr = formatBytes(used);
|
|
6717
|
+
const totalStr = formatBytes(s.total);
|
|
6718
|
+
let percentStr = "";
|
|
6719
|
+
if (s.total && s.total > 0) {
|
|
6720
|
+
const percent = Math.min(used / s.total * 100, 100);
|
|
6721
|
+
percentStr = ` (${percent.toFixed(1)}%)`;
|
|
6722
|
+
}
|
|
6723
|
+
console.log(` ${colors.gray("\u6D41\u91CF: ")}${usedStr} / ${totalStr}${percentStr}`);
|
|
6385
6724
|
}
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6725
|
+
if (s.expire !== void 0) {
|
|
6726
|
+
console.log(` ${colors.gray("\u5230\u671F: ")}${formatTimestamp(s.expire)}`);
|
|
6727
|
+
}
|
|
6728
|
+
if (s.web_page_url) {
|
|
6729
|
+
console.log(` ${colors.gray("\u9875\u9762: ")}${s.web_page_url}`);
|
|
6730
|
+
}
|
|
6731
|
+
});
|
|
6732
|
+
console.log("");
|
|
6733
|
+
console.log("\u5207\u6362\u8BA2\u9605: mihomo sub use <name>");
|
|
6734
|
+
console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
|
|
6735
|
+
console.log("\u66F4\u65B0\u8BA2\u9605: mihomo sub update [name]");
|
|
6736
|
+
console.log("\u5220\u9664\u8BA2\u9605: mihomo sub remove <name>");
|
|
6737
|
+
console.log("\u6D4B\u8BD5\u8282\u70B9: mihomo sub test [name]");
|
|
6738
|
+
console.log("\u6E05\u7406\u8282\u70B9: mihomo sub clean [name]");
|
|
6739
|
+
console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
|
|
6390
6740
|
console.log("");
|
|
6391
|
-
printOverwriteList();
|
|
6392
6741
|
}
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
{
|
|
6399
|
-
id: "subs",
|
|
6400
|
-
aliases: ["sub", "subs", "subscription", "subscriptions"],
|
|
6401
|
-
label: "\u8BA2\u9605",
|
|
6402
|
-
paths: () => [DIRS.subscriptions],
|
|
6403
|
-
needsStop: true
|
|
6404
|
-
},
|
|
6405
|
-
{
|
|
6406
|
-
id: "logs",
|
|
6407
|
-
aliases: ["log", "logs"],
|
|
6408
|
-
label: "\u65E5\u5FD7",
|
|
6409
|
-
paths: () => [DIRS.logs],
|
|
6410
|
-
needsStop: false
|
|
6411
|
-
},
|
|
6412
|
-
{
|
|
6413
|
-
id: "data",
|
|
6414
|
-
aliases: ["data"],
|
|
6415
|
-
label: "\u8FD0\u884C\u6570\u636E",
|
|
6416
|
-
paths: () => [DIRS.data],
|
|
6417
|
-
needsStop: true
|
|
6418
|
-
},
|
|
6419
|
-
{
|
|
6420
|
-
id: "runtime",
|
|
6421
|
-
aliases: ["runtime"],
|
|
6422
|
-
label: "\u8FD0\u884C\u65F6",
|
|
6423
|
-
paths: () => [DIRS.runtime],
|
|
6424
|
-
needsStop: true
|
|
6425
|
-
},
|
|
6426
|
-
{
|
|
6427
|
-
id: "settings",
|
|
6428
|
-
aliases: ["setting", "settings", "config"],
|
|
6429
|
-
label: "\u8BBE\u7F6E",
|
|
6430
|
-
paths: () => [PATHS.settingsFile],
|
|
6431
|
-
needsStop: false
|
|
6432
|
-
},
|
|
6433
|
-
{
|
|
6434
|
-
id: "kernel",
|
|
6435
|
-
aliases: ["kernel", "core"],
|
|
6436
|
-
label: "\u5185\u6838",
|
|
6437
|
-
paths: () => [DIRS.kernel],
|
|
6438
|
-
needsStop: false,
|
|
6439
|
-
onAfter: () => clearKernelVersionCache(),
|
|
6440
|
-
checkEmpty: () => !hasKernel(),
|
|
6441
|
-
emptyMsg: "\u5185\u6838\u672A\u5B89\u88C5\uFF0C\u65E0\u9700\u5220\u9664",
|
|
6442
|
-
warnIfRunning: true
|
|
6443
|
-
},
|
|
6444
|
-
{
|
|
6445
|
-
id: "overwrites",
|
|
6446
|
-
aliases: ["overwrite", "overwrites", "ow"],
|
|
6447
|
-
label: "\u8986\u5199",
|
|
6448
|
-
paths: () => {
|
|
6449
|
-
const dir = USER_DATA_DIR;
|
|
6450
|
-
if (!fs9.existsSync(dir)) return [];
|
|
6451
|
-
return fs9.readdirSync(dir).filter(isOverwriteFilename).map((f) => `${dir}/${f}`);
|
|
6452
|
-
},
|
|
6453
|
-
needsStop: false
|
|
6454
|
-
},
|
|
6455
|
-
{
|
|
6456
|
-
id: "daemon",
|
|
6457
|
-
aliases: ["daemon"],
|
|
6458
|
-
label: "\u4FDD\u6D3B",
|
|
6459
|
-
// 卸载由确认后的 disablesDaemon 段统一处理(需 sudo,受取消保护);
|
|
6460
|
-
// 此处 paths 返回空(plist 在系统目录,用户态删不掉,且不应提前删破坏卸载),
|
|
6461
|
-
// onAfter 因幂等守卫(plist 已删)成为 no-op,仅作单独 reset 未走前段时的兜底。
|
|
6462
|
-
paths: () => [],
|
|
6463
|
-
needsStop: false,
|
|
6464
|
-
onAfter: () => disableDaemon(),
|
|
6465
|
-
checkEmpty: () => !isDaemonEnabled(),
|
|
6466
|
-
emptyMsg: "\u4FDD\u6D3B\u672A\u542F\u7528\uFF0C\u65E0\u9700\u5220\u9664"
|
|
6742
|
+
async function cmdSubscription(args) {
|
|
6743
|
+
const action = args[1];
|
|
6744
|
+
if (!action || action === "list") {
|
|
6745
|
+
printSubscriptionList();
|
|
6746
|
+
return;
|
|
6467
6747
|
}
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6748
|
+
if (action === "add") {
|
|
6749
|
+
const url = args[2]?.trim();
|
|
6750
|
+
const name = args[3] || "default";
|
|
6751
|
+
if (!url) {
|
|
6752
|
+
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6753
|
+
process.exit(1);
|
|
6754
|
+
}
|
|
6755
|
+
if (isMultiUrl(url)) {
|
|
6756
|
+
const urls = splitUrls(url);
|
|
6757
|
+
if (urls.length === 0) {
|
|
6758
|
+
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6759
|
+
process.exit(1);
|
|
6760
|
+
}
|
|
6761
|
+
for (const u of urls) {
|
|
6762
|
+
if (!isValidHttpUrl(u)) {
|
|
6763
|
+
console.error(`\u9519\u8BEF: \u65E0\u6548\u7684 URL: ${u}`);
|
|
6764
|
+
process.exit(1);
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
const normalizedUrl = urls.join(",");
|
|
6768
|
+
console.log(`\u6DFB\u52A0\u5408\u5E76\u8BA2\u9605: ${name} (${urls.length} \u4E2A\u6E90)`);
|
|
6769
|
+
try {
|
|
6770
|
+
addSubscription(normalizedUrl, name);
|
|
6771
|
+
setDefaultSubscription(name);
|
|
6772
|
+
const info = await downloadMergedSubscription(urls, name);
|
|
6773
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)}, \u5408\u5E76 ${urls.length} \u6E90)`);
|
|
6774
|
+
} catch (e) {
|
|
6775
|
+
removeSubscription(name);
|
|
6776
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
6777
|
+
process.exit(1);
|
|
6778
|
+
}
|
|
6476
6779
|
} else {
|
|
6477
|
-
|
|
6780
|
+
if (!isValidHttpUrl(url)) {
|
|
6781
|
+
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL\uFF08\u9700\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF09");
|
|
6782
|
+
process.exit(1);
|
|
6783
|
+
}
|
|
6784
|
+
console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
|
|
6785
|
+
try {
|
|
6786
|
+
addSubscription(url, name);
|
|
6787
|
+
setDefaultSubscription(name);
|
|
6788
|
+
const info = await downloadSubscription(url, name);
|
|
6789
|
+
const repoUrl = githubRepoUrl(url);
|
|
6790
|
+
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
6791
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
6792
|
+
} catch (e) {
|
|
6793
|
+
removeSubscription(name);
|
|
6794
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
6795
|
+
process.exit(1);
|
|
6796
|
+
}
|
|
6478
6797
|
}
|
|
6798
|
+
console.log("");
|
|
6799
|
+
printSubscriptionList();
|
|
6800
|
+
return;
|
|
6479
6801
|
}
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
const skipConfirm = flags.includes("--yes") || flags.includes("-y");
|
|
6497
|
-
let targets;
|
|
6498
|
-
if (fullReset) {
|
|
6499
|
-
targets = RESET_TARGETS;
|
|
6500
|
-
} else if (names.length > 0) {
|
|
6501
|
-
const { matched, unmatched } = resolveResetTargets(names);
|
|
6502
|
-
if (unmatched.length > 0) {
|
|
6503
|
-
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684\u91CD\u7F6E\u76EE\u6807: ${unmatched.join(", ")}`);
|
|
6504
|
-
console.log("");
|
|
6505
|
-
console.log(`\u53EF\u7528\u76EE\u6807: ${RESET_TARGETS.map((t) => t.aliases[0]).join(", ")}`);
|
|
6802
|
+
if (action === "update") {
|
|
6803
|
+
const name = args[2];
|
|
6804
|
+
const subs = getSubscriptions();
|
|
6805
|
+
if (subs.length === 0) {
|
|
6806
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605");
|
|
6807
|
+
process.exit(1);
|
|
6808
|
+
}
|
|
6809
|
+
if (!name) {
|
|
6810
|
+
console.log(`\u66F4\u65B0\u6240\u6709 ${subs.length} \u4E2A\u8BA2\u9605...`);
|
|
6811
|
+
const results = await Promise.all(subs.map((sub) => tryUpdateOne(sub)));
|
|
6812
|
+
let ok = 0;
|
|
6813
|
+
for (const r of results) {
|
|
6814
|
+
if (r.success) ok++;
|
|
6815
|
+
printUpdateResult(r);
|
|
6816
|
+
}
|
|
6817
|
+
if (ok === 0) process.exit(1);
|
|
6506
6818
|
console.log("");
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6819
|
+
printRestartHintIfRunning();
|
|
6820
|
+
printSubscriptionList();
|
|
6821
|
+
return;
|
|
6822
|
+
}
|
|
6823
|
+
const matches = findSubscriptionFuzzy(subs, name);
|
|
6824
|
+
const target = pickSingleSubscription(matches, name);
|
|
6825
|
+
console.log(`\u66F4\u65B0\u8BA2\u9605: ${target.name}`);
|
|
6826
|
+
const result = await tryUpdateOne(target);
|
|
6827
|
+
if (!result.success) {
|
|
6828
|
+
console.error(`\u66F4\u65B0\u5931\u8D25: ${(result.error || "").split("\n")[0]}`);
|
|
6512
6829
|
process.exit(1);
|
|
6513
6830
|
}
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6831
|
+
console.log(`\u5DF2\u66F4\u65B0 (${formatProxySummary(result)})`);
|
|
6832
|
+
console.log("");
|
|
6833
|
+
printRestartHintIfRunning();
|
|
6834
|
+
printSubscriptionList();
|
|
6835
|
+
return;
|
|
6517
6836
|
}
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6837
|
+
if (action === "use") {
|
|
6838
|
+
const name = args[2];
|
|
6839
|
+
const subs = getSubscriptions();
|
|
6840
|
+
if (!name) {
|
|
6841
|
+
console.error("\u9519\u8BEF: \u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
6842
|
+
if (subs.length > 0) {
|
|
6843
|
+
console.log("\n\u53EF\u7528\u8BA2\u9605:");
|
|
6844
|
+
for (const s of subs) console.log(` ${s.name}`);
|
|
6523
6845
|
}
|
|
6846
|
+
process.exit(1);
|
|
6524
6847
|
}
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6848
|
+
const matches = findSubscriptionFuzzy(subs, name);
|
|
6849
|
+
const target = pickSingleSubscription(matches, name);
|
|
6850
|
+
const currentDefault = getActiveSubscription();
|
|
6851
|
+
const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
|
|
6852
|
+
if (isAlreadyDefault) {
|
|
6853
|
+
console.log(`"${target.name}" \u5DF2\u662F\u5F53\u524D\u4F7F\u7528\u7684\u8BA2\u9605`);
|
|
6854
|
+
console.log("");
|
|
6855
|
+
printSubscriptionList();
|
|
6856
|
+
return;
|
|
6857
|
+
}
|
|
6858
|
+
const currentMode = getRuntimeMode();
|
|
6859
|
+
const restartNeeded = isRestartNeededOnChange();
|
|
6860
|
+
const success = setDefaultSubscription(target.name);
|
|
6861
|
+
if (success) {
|
|
6862
|
+
console.log(`\u5DF2\u5207\u6362\u5230 "${target.name}"`);
|
|
6863
|
+
} else {
|
|
6864
|
+
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u8BA2\u9605 "${name}"`);
|
|
6865
|
+
process.exit(1);
|
|
6866
|
+
}
|
|
6867
|
+
if (restartNeeded) {
|
|
6868
|
+
console.log("");
|
|
6869
|
+
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
6870
|
+
return;
|
|
6871
|
+
}
|
|
6872
|
+
console.log("");
|
|
6873
|
+
printSubscriptionList();
|
|
6541
6874
|
return;
|
|
6542
6875
|
}
|
|
6543
|
-
if (
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
console.error(
|
|
6548
|
-
|
|
6876
|
+
if (action === "web" || action === "open") {
|
|
6877
|
+
const name = args[2];
|
|
6878
|
+
const subs = getSubscriptionsWithCache();
|
|
6879
|
+
if (subs.length === 0) {
|
|
6880
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605");
|
|
6881
|
+
process.exit(1);
|
|
6882
|
+
}
|
|
6883
|
+
let target;
|
|
6884
|
+
if (name) {
|
|
6885
|
+
const matches = findSubscriptionFuzzy(subs, name);
|
|
6886
|
+
target = pickSingleSubscription(matches, name);
|
|
6887
|
+
} else {
|
|
6888
|
+
target = getActiveSubscription() || subs[0];
|
|
6889
|
+
}
|
|
6890
|
+
const cached = subs.find((s) => s.name === target.name);
|
|
6891
|
+
let webPageUrl = cached?.web_page_url;
|
|
6892
|
+
if (!webPageUrl) {
|
|
6893
|
+
console.log("\u8BA2\u9605\u4FE1\u606F\u4E2D\u7F3A\u5C11\u9875\u9762\u5730\u5740\uFF0C\u6B63\u5728\u67E5\u8BE2\u8BA2\u9605...");
|
|
6894
|
+
try {
|
|
6895
|
+
const info = isMultiUrl(target.url) ? await downloadMergedSubscription(splitUrls(target.url), target.name, void 0, false) : await downloadSubscription(target.url, target.name, void 0, false);
|
|
6896
|
+
if (info.webPageUrl) {
|
|
6897
|
+
webPageUrl = info.webPageUrl;
|
|
6898
|
+
} else {
|
|
6899
|
+
console.error("\u9519\u8BEF: \u8BE5\u8BA2\u9605\u6CA1\u6709\u63D0\u4F9B\u9875\u9762\u5730\u5740");
|
|
6900
|
+
process.exit(1);
|
|
6901
|
+
}
|
|
6902
|
+
} catch (e) {
|
|
6903
|
+
console.error(`\u67E5\u8BE2\u5931\u8D25: ${e.message}`);
|
|
6904
|
+
process.exit(1);
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
console.log(`\u6253\u5F00\u8BA2\u9605\u9875\u9762: ${webPageUrl}`);
|
|
6908
|
+
const opened = openUrl(webPageUrl);
|
|
6909
|
+
if (!opened) {
|
|
6910
|
+
console.log("\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684\u5730\u5740");
|
|
6549
6911
|
}
|
|
6912
|
+
return;
|
|
6550
6913
|
}
|
|
6551
|
-
if (
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6914
|
+
if (action === "remove" || action === "rm" || action === "delete") {
|
|
6915
|
+
const name = args[2];
|
|
6916
|
+
const subs = getSubscriptions();
|
|
6917
|
+
if (!name) {
|
|
6918
|
+
console.error("\u9519\u8BEF: \u8BF7\u6307\u5B9A\u8981\u5220\u9664\u7684\u8BA2\u9605\u540D\u79F0");
|
|
6919
|
+
if (subs.length > 0) {
|
|
6920
|
+
console.log("\n\u53EF\u7528\u8BA2\u9605:");
|
|
6921
|
+
for (const s of subs) console.log(` ${s.name}`);
|
|
6922
|
+
}
|
|
6923
|
+
process.exit(1);
|
|
6924
|
+
}
|
|
6925
|
+
const matches = findSubscriptionFuzzy(subs, name);
|
|
6926
|
+
const target = pickSingleSubscription(matches, name);
|
|
6927
|
+
const switchedTo = removeSubscription(target.name);
|
|
6928
|
+
console.log(`\u5DF2\u5220\u9664\u8BA2\u9605 "${target.name}"`);
|
|
6929
|
+
if (switchedTo) {
|
|
6930
|
+
console.log(`\u5DF2\u81EA\u52A8\u5207\u6362\u5230 "${switchedTo}"`);
|
|
6557
6931
|
}
|
|
6932
|
+
console.log("");
|
|
6933
|
+
printSubscriptionList();
|
|
6934
|
+
return;
|
|
6558
6935
|
}
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6936
|
+
if (action === "clean") {
|
|
6937
|
+
const { target, timeout, concurrency } = resolveTestTarget(args);
|
|
6938
|
+
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
6939
|
+
console.log(`\u6E05\u7406\u8BA2\u9605 "${target.name}"...`);
|
|
6940
|
+
console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
|
|
6941
|
+
console.log("");
|
|
6942
|
+
const progress = createProgressPrinter(rounds);
|
|
6943
|
+
const result = await withTestInstance(target.name, async (apiBase) => {
|
|
6944
|
+
return autoCleanSubscription(target.name, {
|
|
6945
|
+
timeout,
|
|
6946
|
+
concurrency,
|
|
6947
|
+
rounds,
|
|
6948
|
+
apiBase,
|
|
6949
|
+
onResult: progress.onResult,
|
|
6950
|
+
onRetryRound: progress.onRetryRound
|
|
6951
|
+
});
|
|
6952
|
+
});
|
|
6953
|
+
progress.finish();
|
|
6954
|
+
console.log(formatTestSummary(result.summary));
|
|
6955
|
+
if (result.skipped) {
|
|
6956
|
+
console.log("");
|
|
6957
|
+
console.log(colors.yellow("\u5B58\u6D3B\u8282\u70B9\u4E0D\u8DB3 1%\uFF0C\u8DF3\u8FC7\u6E05\u7406\u3002\u8BF7\u68C0\u67E5\u539F\u59CB\u8BA2\u9605\u662F\u5426\u6709\u6548"));
|
|
6958
|
+
} else if (result.removedProxies > 0) {
|
|
6959
|
+
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(result)}`);
|
|
6960
|
+
if (getRunningState().running) {
|
|
6961
|
+
console.log("");
|
|
6962
|
+
console.log("\u63D0\u793A: \u9700\u8981\u91CD\u542F mihomo \u4F7F\u66F4\u6539\u751F\u6548 (mihomo start)");
|
|
6567
6963
|
}
|
|
6568
6964
|
}
|
|
6569
|
-
|
|
6965
|
+
return;
|
|
6570
6966
|
}
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6967
|
+
if (action === "test") {
|
|
6968
|
+
const { target, timeout, concurrency } = resolveTestTarget(args);
|
|
6969
|
+
console.log(`\u6D4B\u8BD5\u8BA2\u9605 "${target.name}" \u7684\u8282\u70B9\u8FDE\u901A\u6027...`);
|
|
6970
|
+
console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
|
|
6971
|
+
console.log("");
|
|
6972
|
+
const progress = createProgressPrinter();
|
|
6973
|
+
const summary = await withTestInstance(target.name, async (apiBase) => {
|
|
6974
|
+
return testSubscriptionProxies(target.name, {
|
|
6975
|
+
timeout,
|
|
6976
|
+
concurrency,
|
|
6977
|
+
apiBase,
|
|
6978
|
+
onResult: progress.onResult
|
|
6979
|
+
});
|
|
6980
|
+
});
|
|
6981
|
+
progress.finish();
|
|
6982
|
+
console.log(formatTestSummary(summary));
|
|
6983
|
+
return;
|
|
6574
6984
|
}
|
|
6575
|
-
console.
|
|
6985
|
+
console.error("\u9519\u8BEF: \u672A\u77E5\u7684\u8BA2\u9605\u547D\u4EE4");
|
|
6986
|
+
console.log("\u7528\u6CD5: mihomo sub [list|use|add|update|remove|web|test|clean]");
|
|
6987
|
+
process.exit(1);
|
|
6576
6988
|
}
|
|
6577
6989
|
|
|
6578
6990
|
// src/commands/test.ts
|
|
@@ -6640,7 +7052,14 @@ async function cmdClean(args) {
|
|
|
6640
7052
|
const mode = getRuntimeMode();
|
|
6641
7053
|
const daemonManaged = isDaemonEnabled();
|
|
6642
7054
|
try {
|
|
6643
|
-
if (!daemonManaged)
|
|
7055
|
+
if (!daemonManaged) {
|
|
7056
|
+
if (hasRootResidue()) {
|
|
7057
|
+
console.error(`${colors.red("\u9519\u8BEF:")} \u4E3B\u5B9E\u4F8B\u4EE5 root \u8FD0\u884C\uFF08TUN\uFF09\uFF0C\u505C\u6B62\u5B83\u9700\u8981 sudo`);
|
|
7058
|
+
console.error("\u8BF7\u6539\u7528 mihomo sub clean\uFF08\u9694\u79BB\u5B9E\u4F8B\u6D4B\u901F\uFF0C\u65E0\u9700\u505C\u6B62\u4E3B\u5B9E\u4F8B\uFF09");
|
|
7059
|
+
process.exit(1);
|
|
7060
|
+
}
|
|
7061
|
+
handleStopResult(stop());
|
|
7062
|
+
}
|
|
6644
7063
|
const configInfo = prepareConfigForStart(mode, activeSub.name);
|
|
6645
7064
|
const pid = await launchOrRestart(mode);
|
|
6646
7065
|
const label = daemonManaged ? "\u5DF2\u91CD\u542F (\u4FDD\u6D3B)" : "\u5DF2\u91CD\u542F";
|
|
@@ -6663,6 +7082,10 @@ function cmdUI(args) {
|
|
|
6663
7082
|
const url = UI_URLS[uiName];
|
|
6664
7083
|
console.log(`\u6253\u5F00 Web UI: ${uiName}`);
|
|
6665
7084
|
console.log(`\u5730\u5740: ${url}`);
|
|
7085
|
+
const secret = readSettings().controller_secret;
|
|
7086
|
+
if (secret) {
|
|
7087
|
+
console.log("\u5DF2\u914D\u7F6E\u8BBF\u95EE\u5BC6\u94A5\uFF08UI \u8FDE\u63A5 127.0.0.1:9090 \u65F6\u9700\u8F93\u5165\uFF0C\u5BC6\u94A5\u89C1 settings.json\uFF09");
|
|
7088
|
+
}
|
|
6666
7089
|
const success = openUrl(url);
|
|
6667
7090
|
if (!success) {
|
|
6668
7091
|
console.log("\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684\u5730\u5740");
|
|
@@ -6684,11 +7107,16 @@ async function cmdUpdate() {
|
|
|
6684
7107
|
if (code === 0) {
|
|
6685
7108
|
resolve();
|
|
6686
7109
|
} else {
|
|
7110
|
+
console.error("\u66F4\u65B0\u5931\u8D25\u3002\u82E5\u4E3A\u6743\u9650\u95EE\u9898\uFF08EACCES\uFF09\uFF0C\u53EF\u5C1D\u8BD5: sudo npm install -g mihomo-cli");
|
|
6687
7111
|
process.exit(code || 1);
|
|
6688
7112
|
}
|
|
6689
7113
|
});
|
|
6690
7114
|
npm.on("error", (e) => {
|
|
6691
|
-
|
|
7115
|
+
if (e.message.includes("EACCES") || e.message.includes("permission")) {
|
|
7116
|
+
console.error("\u6743\u9650\u4E0D\u8DB3\uFF0C\u53EF\u5C1D\u8BD5: sudo npm install -g mihomo-cli");
|
|
7117
|
+
} else {
|
|
7118
|
+
console.error(`\u6267\u884C\u5931\u8D25: ${e.message}`);
|
|
7119
|
+
}
|
|
6692
7120
|
process.exit(1);
|
|
6693
7121
|
});
|
|
6694
7122
|
});
|
|
@@ -6716,7 +7144,7 @@ var COMMANDS = [
|
|
|
6716
7144
|
aliases: ["up"],
|
|
6717
7145
|
handler: cmdStart,
|
|
6718
7146
|
group: "control",
|
|
6719
|
-
usage: ["start [tun|mixed] [-s] [-u ms] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)", " [-r N] [-t ms] [-j N]"]
|
|
7147
|
+
usage: ["start [tun|mixed] [-s] [-u ms] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)", " [-r N] [-t ms] [-j N] [--no-clean]"]
|
|
6720
7148
|
},
|
|
6721
7149
|
{
|
|
6722
7150
|
name: "tun",
|
|
@@ -6775,8 +7203,8 @@ var COMMANDS = [
|
|
|
6775
7203
|
"subscription update [name] \u66F4\u65B0\u8BA2\u9605\uFF08\u65E0\u53C2\u66F4\u65B0\u6240\u6709\uFF09",
|
|
6776
7204
|
"subscription remove <name> \u5220\u9664\u8BA2\u9605",
|
|
6777
7205
|
"subscription web [name] \u6253\u5F00\u8BA2\u9605\u9875\u9762",
|
|
6778
|
-
"subscription test [name] \u6D4B\u8BD5\u8282\u70B9\
|
|
6779
|
-
"subscription clean [name] \u6D4B\u901F\
|
|
7206
|
+
"subscription test [name] \u6D4B\u8BD5\u8282\u70B9\uFF08\u72EC\u7ACB\u5B9E\u4F8B\uFF0C\u65E0\u9700\u8FD0\u884C\uFF09",
|
|
7207
|
+
"subscription clean [name] \u6D4B\u901F\u6E05\u7406\uFF08\u72EC\u7ACB\u5B9E\u4F8B\uFF0C\u4E0D\u52A8\u4E3B\u5B9E\u4F8B\uFF09"
|
|
6780
7208
|
]
|
|
6781
7209
|
},
|
|
6782
7210
|
{
|
|
@@ -6792,14 +7220,14 @@ var COMMANDS = [
|
|
|
6792
7220
|
aliases: [],
|
|
6793
7221
|
handler: cmdTest,
|
|
6794
7222
|
group: "subscription",
|
|
6795
|
-
usage: ["test [-t ms] [-j N] \
|
|
7223
|
+
usage: ["test [-t ms] [-j N] \u6D4B\u8BD5\u5F53\u524D\u8282\u70B9\uFF08\u7ECF\u8FD0\u884C\u4E2D\u7684\u4E3B\u5B9E\u4F8B\uFF09"]
|
|
6796
7224
|
},
|
|
6797
7225
|
{
|
|
6798
7226
|
name: "clean",
|
|
6799
7227
|
aliases: [],
|
|
6800
7228
|
handler: cmdClean,
|
|
6801
7229
|
group: "subscription",
|
|
6802
|
-
usage: ["clean [-t ms] [-j N] [-r N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\
|
|
7230
|
+
usage: ["clean [-t ms] [-j N] [-r N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\u91CD\u542F\uFF08\u7ECF\u4E3B\u5B9E\u4F8B\uFF09"]
|
|
6803
7231
|
},
|
|
6804
7232
|
// === 配置 ===
|
|
6805
7233
|
{
|
|
@@ -6867,7 +7295,7 @@ var COMMANDS = [
|
|
|
6867
7295
|
aliases: [],
|
|
6868
7296
|
handler: cmdReset,
|
|
6869
7297
|
group: "system",
|
|
6870
|
-
usage: ["reset [\u76EE\u6807...] [--full] \u91CD\u7F6E: \u7559\u7A7A\u4FDD\u7559\u8BBE\u7F6E/\u5185\u6838/\u8986\u5199, \u6307\u5B9A\u76EE\u6807\u5220\u5BF9\u5E94\u9879, --full \u5220\u5168\u90E8"]
|
|
7298
|
+
usage: ["reset [\u76EE\u6807...] [--full] [-y] \u91CD\u7F6E: \u7559\u7A7A\u4FDD\u7559\u8BBE\u7F6E/\u5185\u6838/\u8986\u5199, \u6307\u5B9A\u76EE\u6807\u5220\u5BF9\u5E94\u9879, --full \u5220\u5168\u90E8, -y \u8DF3\u8FC7\u786E\u8BA4"]
|
|
6871
7299
|
},
|
|
6872
7300
|
// === meta(不在分组清单展示,help 末尾单列) ===
|
|
6873
7301
|
{
|
|
@@ -6903,7 +7331,9 @@ function findCommand(token) {
|
|
|
6903
7331
|
|
|
6904
7332
|
// src/index.ts
|
|
6905
7333
|
process.on("SIGINT", () => {
|
|
6906
|
-
|
|
7334
|
+
if (!isSilentSigint()) {
|
|
7335
|
+
console.log("\n\u6B63\u5728\u9000\u51FA...");
|
|
7336
|
+
}
|
|
6907
7337
|
runCleanup();
|
|
6908
7338
|
process.exit(130);
|
|
6909
7339
|
});
|
|
@@ -6961,5 +7391,5 @@ main().catch((e) => {
|
|
|
6961
7391
|
/*! Bundled license information:
|
|
6962
7392
|
|
|
6963
7393
|
js-yaml/dist/js-yaml.mjs:
|
|
6964
|
-
(*! js-yaml 5.
|
|
7394
|
+
(*! js-yaml 5.3.0 https://github.com/nodeca/js-yaml @license MIT *)
|
|
6965
7395
|
*/
|