@ladderkitlab/hub-kb 0.1.0 → 0.3.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/README.md +0 -2
- package/dist/main.js +690 -412
- package/package.json +1 -1
- package/snapshot/index.json +1 -1
- package/snapshot/projection.json +1 -1
- package/snapshot/release-manifest.json +9 -8
- package/snapshot/t2s-min.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
|
-
import
|
|
5
|
-
import path3 from "node:path";
|
|
4
|
+
import path2 from "node:path";
|
|
6
5
|
import { createHash as createHash2 } from "node:crypto";
|
|
7
6
|
import { fileURLToPath } from "node:url";
|
|
8
7
|
|
|
@@ -41,141 +40,10 @@ function tokenize(n, cfg) {
|
|
|
41
40
|
}
|
|
42
41
|
return [...out];
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const k = cfg.tokenizer.cjk_ngram;
|
|
49
|
-
for (const run2 of cjkRuns(n)) {
|
|
50
|
-
const mark = new Array(run2.length).fill(false);
|
|
51
|
-
for (const w of QUESTION_WORDS) for (let i = run2.indexOf(w); i >= 0; i = run2.indexOf(w, i + 1)) mark.fill(true, i, i + w.length);
|
|
52
|
-
for (let i = 0; i + k <= run2.length; i++) if (mark.slice(i, i + k).some(Boolean)) out.add(run2.slice(i, i + k));
|
|
53
|
-
if (run2.length < k && mark.some(Boolean)) out.add(run2);
|
|
54
|
-
}
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
function dimMatchAny(d, vs, cfg) {
|
|
58
|
-
if (!vs?.some(Boolean)) return cfg.filters.applicability_context_missing === "drop" && d !== "any" ? null : 1;
|
|
59
|
-
if (d === "any") return 1;
|
|
60
|
-
if (d === "unknown") return cfg.filters.unknown_applicability_multiplier;
|
|
61
|
-
return vs.some((v) => v && d.includes(v)) ? 1 : null;
|
|
62
|
-
}
|
|
63
|
-
var dimMatch = (d, v, cfg) => dimMatchAny(d, v === void 0 ? void 0 : [v], cfg);
|
|
64
|
-
function intersect(sets) {
|
|
65
|
-
const common = sets.length ? sets[0].filter((v) => sets.every((s) => s.includes(v))) : [];
|
|
66
|
-
return common.length ? common : void 0;
|
|
67
|
-
}
|
|
68
|
-
var MAX_SEARCH_RESULTS = 5;
|
|
69
|
-
function osMentions(n, terms, groups) {
|
|
70
|
-
const os = /* @__PURE__ */ new Set();
|
|
71
|
-
const mentions = [];
|
|
72
|
-
const byWord = /* @__PURE__ */ new Map();
|
|
73
|
-
for (const [id, words] of Object.entries(terms ?? {})) for (const w of words) byWord.set(w, [id]);
|
|
74
|
-
for (const g of Object.values(groups ?? {})) for (const w of g.aliases) byWord.set(w, g.members);
|
|
75
|
-
if (byWord.size === 0) return { text: n, os, mentions };
|
|
76
|
-
const alt = [...byWord.keys()].sort((a, b) => b.length - a.length).join("|");
|
|
77
|
-
const text = n.replace(new RegExp(`(?<![a-z0-9_])(${alt})(?:\\s*\\d+)?(?![a-z0-9_])`, "g"), (m, w) => {
|
|
78
|
-
const set = byWord.get(w);
|
|
79
|
-
mentions.push(set);
|
|
80
|
-
if (set.length === 1) os.add(set[0]);
|
|
81
|
-
return /^[a-z0-9 ]+$/.test(w) ? " " : m === w ? w : `${w} `;
|
|
82
|
-
});
|
|
83
|
-
return { text, os, mentions };
|
|
84
|
-
}
|
|
85
|
-
var MAX_GAP = 12;
|
|
86
|
-
var MIN_FIXED = 3;
|
|
87
|
-
var wordSeq = (n) => n.match(/[a-z0-9_]+|[一-鿿]+/g) ?? [];
|
|
88
|
-
var segAt = (q, seg, i) => seg.every((w, j) => q[i + j] === w);
|
|
89
|
-
function matchSegments(q, segs, from, first) {
|
|
90
|
-
if (segs.length === 0) return true;
|
|
91
|
-
const [seg, ...rest2] = segs;
|
|
92
|
-
const last = first ? q.length - seg.length : Math.min(q.length - seg.length, from + MAX_GAP);
|
|
93
|
-
for (let i = from; i <= last; i++) if (segAt(q, seg, i) && matchSegments(q, rest2, i + seg.length, false)) return true;
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
var fixedWords = (p) => p.segments.reduce((a, s) => a + s.length, 0);
|
|
97
|
-
function errorLineHits(patterns, n) {
|
|
98
|
-
const q = wordSeq(n);
|
|
99
|
-
return (patterns ?? []).filter((p) => fixedWords(p) >= MIN_FIXED && matchSegments(q, p.segments, 0, true)).sort((a, b) => fixedWords(b) - fixedWords(a));
|
|
100
|
-
}
|
|
101
|
-
function explicitContext(ctx) {
|
|
102
|
-
return { client: ctx.client?.trim() ? ctx.client : void 0, os: ctx.os?.trim() ? ctx.os : void 0 };
|
|
103
|
-
}
|
|
104
|
-
function search(index, query, ctx, t2s) {
|
|
105
|
-
const cfg = index.config;
|
|
106
|
-
const normalized = normalize(query, cfg, t2s);
|
|
107
|
-
const mentioned = osMentions(normalized, index.os_terms, index.os_groups);
|
|
108
|
-
const clients = osMentions(mentioned.text, index.client_terms);
|
|
109
|
-
const q = tokenize(clients.text, cfg);
|
|
110
|
-
if (q.length === 0) return { results: [], abstained: true };
|
|
111
|
-
const { os: ctxOs, client: ctxClient } = explicitContext(ctx);
|
|
112
|
-
const os = ctxOs !== void 0 ? [ctxOs] : intersect(mentioned.mentions);
|
|
113
|
-
const client = ctxClient ?? (clients.os.size === 1 ? [...clients.os][0] : void 0);
|
|
114
|
-
const hits = errorLineHits(index.error_patterns, normalized).filter((h) => dimMatch(h.client ?? "any", client, cfg) !== null && dimMatchAny(h.os ?? "any", os, cfg) !== null);
|
|
115
|
-
if (hits.length) {
|
|
116
|
-
const byId = new Map(index.docs.map((d) => [d.id, d]));
|
|
117
|
-
const lines = [];
|
|
118
|
-
const add = (id) => {
|
|
119
|
-
const d = byId.get(id);
|
|
120
|
-
if (!d || lines.some((r) => r.id === id)) return;
|
|
121
|
-
const mc = dimMatch(d.client, client, cfg), mo = dimMatchAny(d.os, os, cfg);
|
|
122
|
-
if (mc === null || mo === null) return;
|
|
123
|
-
lines.push({ id, type: d.type, score: (cfg.weights.error_messages ?? 0) * mc * mo * (d.stale ? cfg.freshness.stale_multiplier : 1) });
|
|
124
|
-
};
|
|
125
|
-
for (const h of hits) {
|
|
126
|
-
if (h.maps_to !== "unmapped") add(h.maps_to);
|
|
127
|
-
add(h.owner);
|
|
128
|
-
}
|
|
129
|
-
if (lines.length) {
|
|
130
|
-
const results2 = lines.slice(0, Math.min(cfg.max_results, MAX_SEARCH_RESULTS));
|
|
131
|
-
const abstained2 = results2[0].score < cfg.abstain.min_score;
|
|
132
|
-
return { results: abstained2 ? [] : results2, abstained: abstained2 };
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const latin = q.filter((t) => /^[a-z0-9_]+$/.test(t));
|
|
136
|
-
const question = questionTokens(clients.text, cfg);
|
|
137
|
-
const candidates = index.docs.flatMap((d) => {
|
|
138
|
-
const mc = dimMatch(d.client, client, cfg), mo = dimMatchAny(d.os, os, cfg);
|
|
139
|
-
return mc === null || mo === null ? [] : [{ d, mc, mo, tokens: new Set(Object.entries(d.fields).filter(([f]) => cfg.weights[f]).flatMap(([, toks]) => toks)) }];
|
|
140
|
-
});
|
|
141
|
-
const n = candidates.length;
|
|
142
|
-
const weigh = (has) => new Map(q.map((t) => {
|
|
143
|
-
const df = candidates.filter((c) => c.tokens.has(t)).length;
|
|
144
|
-
const shared = candidates.filter((c) => has(c, t)).length;
|
|
145
|
-
return [t, question.has(t) || n > 1 && shared === n ? 0 : df === 0 ? UNKNOWN_TOKEN_WEIGHT : 1];
|
|
146
|
-
}));
|
|
147
|
-
let weight = weigh((c, t) => c.tokens.has(t));
|
|
148
|
-
if (q.every((t) => weight.get(t) === 0 || !candidates.some((c) => c.tokens.has(t)))) {
|
|
149
|
-
const strong = new Map(candidates.map((c) => [c, new Set(Object.entries(c.d.fields).filter(([f]) => f !== "body" && cfg.weights[f]).flatMap(([, toks]) => toks))]));
|
|
150
|
-
weight = weigh((c, t) => strong.get(c).has(t));
|
|
151
|
-
}
|
|
152
|
-
const total = q.reduce((a, t) => a + weight.get(t), 0);
|
|
153
|
-
if (total === 0) return { results: [], abstained: true };
|
|
154
|
-
const scored = [];
|
|
155
|
-
for (const { d, mc, mo } of candidates) {
|
|
156
|
-
const matched = /* @__PURE__ */ new Set();
|
|
157
|
-
let s = 0;
|
|
158
|
-
for (const [field2, toks] of Object.entries(d.fields)) {
|
|
159
|
-
const w = cfg.weights[field2] ?? 0;
|
|
160
|
-
if (!w) continue;
|
|
161
|
-
const set = new Set(toks);
|
|
162
|
-
let hit = 0;
|
|
163
|
-
for (const t of q) if (set.has(t)) {
|
|
164
|
-
hit++;
|
|
165
|
-
matched.add(t);
|
|
166
|
-
}
|
|
167
|
-
s += w * (hit / q.length);
|
|
168
|
-
}
|
|
169
|
-
if ([...matched].reduce((a, t) => a + weight.get(t), 0) / total < cfg.abstain.min_token_coverage) continue;
|
|
170
|
-
if (latin.some((t) => !matched.has(t))) continue;
|
|
171
|
-
s *= mc * mo;
|
|
172
|
-
if (d.stale) s *= cfg.freshness.stale_multiplier;
|
|
173
|
-
if (s > 0) scored.push({ id: d.id, type: d.type, score: s, lv: d.last_verified });
|
|
174
|
-
}
|
|
175
|
-
scored.sort((a, b) => b.score - a.score || (b.lv > a.lv ? 1 : b.lv < a.lv ? -1 : 0) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
176
|
-
const results = scored.slice(0, Math.min(cfg.max_results, MAX_SEARCH_RESULTS)).map(({ id, type, score }) => ({ id, type, score }));
|
|
177
|
-
const abstained = results.length === 0 || results[0].score < cfg.abstain.min_score;
|
|
178
|
-
return { results: abstained ? [] : results, abstained };
|
|
43
|
+
function freshnessAt(e, now) {
|
|
44
|
+
if (!e.fresh_windows) return e.freshness;
|
|
45
|
+
const t = now.getTime();
|
|
46
|
+
return e.fresh_windows.some(([from, until]) => (from === null || t >= Date.parse(from)) && t < Date.parse(until)) ? "current" : "stale";
|
|
179
47
|
}
|
|
180
48
|
|
|
181
49
|
// ../redact/context.ts
|
|
@@ -185,6 +53,11 @@ var MAX_DEPTH = 64;
|
|
|
185
53
|
var BLOCK_PREFIX = /[\s,,。::]/;
|
|
186
54
|
var isKeyChar = (c) => c !== void 0 && /[A-Za-z0-9_-]/.test(c);
|
|
187
55
|
var isNewline = (c) => c === "\n" || c === "\r";
|
|
56
|
+
var JSON_ESC = { '"': '"', "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: " " };
|
|
57
|
+
function decodeJsonKey(raw) {
|
|
58
|
+
if (!raw.includes("\\")) return raw;
|
|
59
|
+
return raw.replace(/\\(u[0-9A-Fa-f]{4}|.)/g, (m, e) => e.length === 5 ? String.fromCharCode(parseInt(e.slice(1), 16)) : JSON_ESC[e] ?? m);
|
|
60
|
+
}
|
|
188
61
|
function configRegions(s) {
|
|
189
62
|
const t = s.text;
|
|
190
63
|
const n = s.n;
|
|
@@ -226,7 +99,17 @@ function configRegions(s) {
|
|
|
226
99
|
}
|
|
227
100
|
s.budget.tick(j - i);
|
|
228
101
|
const closed = j < n && t[j] === c;
|
|
229
|
-
|
|
102
|
+
const keyText = c === '"' ? decodeJsonKey(t.slice(i + 1, j)) : t.slice(i + 1, j);
|
|
103
|
+
lastKey = depth > 0 ? { value: keyText } : null;
|
|
104
|
+
if (depth === 0 && closed && (i === 0 || BLOCK_PREFIX.test(t[i - 1])) && ROOTS.has(keyText.toLowerCase())) {
|
|
105
|
+
let k = j + 1;
|
|
106
|
+
while (k < n && /\s/.test(t[k])) k++;
|
|
107
|
+
s.budget.tick(k - j);
|
|
108
|
+
if (t[k] === ":") {
|
|
109
|
+
out.push({ index: i === 0 ? 0 : i - 1, end: n });
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
230
113
|
i = closed ? j + 1 : j;
|
|
231
114
|
continue;
|
|
232
115
|
}
|
|
@@ -341,6 +224,10 @@ var RULE_LABELS = {
|
|
|
341
224
|
};
|
|
342
225
|
var ATOMS = [...new Set([...Object.values(RULE_LABELS), CREDENTIAL_MASK].map((s) => s.trim()))].sort((a, b) => b.length - a.length);
|
|
343
226
|
var FIRST = new Set(ATOMS.map((a) => a[0]));
|
|
227
|
+
function placeholderEndingAt(text, end) {
|
|
228
|
+
for (const a of ATOMS) if (end >= a.length && text.startsWith(a, end - a.length)) return a.length;
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
344
231
|
function placeholderAt(text, i) {
|
|
345
232
|
if (i >= text.length || !FIRST.has(text[i])) return 0;
|
|
346
233
|
for (const a of ATOMS) if (text.startsWith(a, i)) return a.length;
|
|
@@ -348,8 +235,8 @@ function placeholderAt(text, i) {
|
|
|
348
235
|
}
|
|
349
236
|
|
|
350
237
|
// ../redact/value.ts
|
|
351
|
-
function newScan(text, limit) {
|
|
352
|
-
return { text, n: text.length, budget: new Budget(text.length, limit), values: /* @__PURE__ */ new Map(), unfinished: text.length };
|
|
238
|
+
function newScan(text, limit, raw = text) {
|
|
239
|
+
return { text, raw, n: text.length, budget: new Budget(text.length, limit), values: /* @__PURE__ */ new Map(), unfinished: text.length };
|
|
353
240
|
}
|
|
354
241
|
var IP_LITERAL_MAX = 64;
|
|
355
242
|
var isHexCode = (c) => c >= 48 && c <= 57 || c >= 97 && c <= 102 || c >= 65 && c <= 70;
|
|
@@ -477,6 +364,16 @@ function isContent(s, raw) {
|
|
|
477
364
|
}
|
|
478
365
|
return decoded.trim() !== "";
|
|
479
366
|
}
|
|
367
|
+
function hasRawValueContent(s, start, end) {
|
|
368
|
+
let raw = "";
|
|
369
|
+
for (let k = start; k < end; ) {
|
|
370
|
+
s.budget.tick();
|
|
371
|
+
const a = placeholderAt(s.text, k);
|
|
372
|
+
if (a > 0 && k + a <= end) k += a;
|
|
373
|
+
else raw += s.text[k++];
|
|
374
|
+
}
|
|
375
|
+
return s.budget.exhausted || isContent(s, raw);
|
|
376
|
+
}
|
|
480
377
|
function interpretUrlValue(s, start) {
|
|
481
378
|
const cached = s.values.get(start);
|
|
482
379
|
if (cached) {
|
|
@@ -539,14 +436,7 @@ function interpretUrlValue(s, start) {
|
|
|
539
436
|
} else if (segments === 0) {
|
|
540
437
|
record = { state: "none", judgeEnd: start, displayEnd: start, next: start };
|
|
541
438
|
} else if (content) {
|
|
542
|
-
|
|
543
|
-
let k = lastEnd;
|
|
544
|
-
while (k < n && isInlineWs(t[k])) k++;
|
|
545
|
-
s.budget.tick(k - lastEnd);
|
|
546
|
-
const d = t[k];
|
|
547
|
-
if (k < n && (placeholderAt(t, k) > 0 || isPrintableAscii(t.charCodeAt(k)) && d !== "&" && d !== "#" && d !== ";" && d !== ")")) wide = true;
|
|
548
|
-
} else if (!wide && continuationStarts(t, n, lastEnd)) wide = true;
|
|
549
|
-
const displayEnd = wide ? restEnd(s, lastEnd) : lastEnd;
|
|
439
|
+
const displayEnd = contentDisplayEnd(s, lastEnd, wide);
|
|
550
440
|
record = { state: "content", judgeEnd: lastEnd, displayEnd, next: displayEnd };
|
|
551
441
|
} else {
|
|
552
442
|
record = { state: placeholder ? "placeholder" : "blank", judgeEnd: lastEnd, displayEnd: lastEnd, next: lastEnd };
|
|
@@ -554,6 +444,18 @@ function interpretUrlValue(s, start) {
|
|
|
554
444
|
s.values.set(start, record);
|
|
555
445
|
return record;
|
|
556
446
|
}
|
|
447
|
+
function contentDisplayEnd(s, judgeEnd, wide = false) {
|
|
448
|
+
const t = s.text;
|
|
449
|
+
const n = s.n;
|
|
450
|
+
if (!wide && judgeEnd < n && isInlineWs(t[judgeEnd])) {
|
|
451
|
+
let k = judgeEnd;
|
|
452
|
+
while (k < n && isInlineWs(t[k])) k++;
|
|
453
|
+
s.budget.tick(k - judgeEnd);
|
|
454
|
+
const d = t[k];
|
|
455
|
+
if (k < n && (placeholderAt(t, k) > 0 || isPrintableAscii(t.charCodeAt(k)) && d !== "&" && d !== "#" && d !== ";" && d !== ")")) wide = true;
|
|
456
|
+
} else if (!wide && continuationStarts(t, n, judgeEnd)) wide = true;
|
|
457
|
+
return wide ? restEnd(s, judgeEnd) : judgeEnd;
|
|
458
|
+
}
|
|
557
459
|
var hasCredentialValue = (v) => v.state === "content" || v.state === "uncertain";
|
|
558
460
|
|
|
559
461
|
// ../redact/named-credential.ts
|
|
@@ -562,9 +464,11 @@ var PROSE_CREDENTIAL_NAMES = ["token", "secret", "password", "pwd", "session_id"
|
|
|
562
464
|
var PROSE_KEY_NAME = "key";
|
|
563
465
|
var PROSE_KEY_MIN_LENGTH = 8;
|
|
564
466
|
var AUTH_SCHEME_NAME = "bearer";
|
|
467
|
+
var AUTH_HEADER_NAME = "authorization";
|
|
565
468
|
var PROXY_URI_SCHEMES = ["ss", "ssr", "vmess", "vless", "trojan", "hysteria", "hysteria2", "hy2", "tuic", "anytls"];
|
|
469
|
+
var SOCKS_URI_SCHEMES = ["socks", "socks4", "socks4a", "socks5", "socks5h"];
|
|
566
470
|
var SCHEMES_LONGEST_FIRST = [...PROXY_URI_SCHEMES].sort((a, b) => b.length - a.length);
|
|
567
|
-
var PROSE_NAMES_LONGEST_FIRST = [...PROSE_CREDENTIAL_NAMES, PROSE_KEY_NAME, AUTH_SCHEME_NAME].sort((a, b) => b.length - a.length);
|
|
471
|
+
var PROSE_NAMES_LONGEST_FIRST = [...PROSE_CREDENTIAL_NAMES, PROSE_KEY_NAME, AUTH_SCHEME_NAME, AUTH_HEADER_NAME].sort((a, b) => b.length - a.length);
|
|
568
472
|
var PROSE_NAME_FIRST = new Set(PROSE_NAMES_LONGEST_FIRST.map((n) => n[0]));
|
|
569
473
|
var isAsciiName = (name) => name.charCodeAt(0) < 128;
|
|
570
474
|
var SEPARATORS = "=:\uFF1A";
|
|
@@ -572,6 +476,18 @@ var isAlnumCode = (x) => x >= 48 && x <= 57 || x >= 65 && x <= 90 || x >= 97 &&
|
|
|
572
476
|
var isAlnumAt = (t, i) => i >= 0 && i < t.length && isAlnumCode(t.charCodeAt(i));
|
|
573
477
|
var isConnector = (c) => c === "_" || c === "-" || c === ".";
|
|
574
478
|
var isTokenCharAt = (t, i) => i < t.length && (isAlnumCode(t.charCodeAt(i)) || t[i] === "_" || t[i] === "-");
|
|
479
|
+
var isB64TokenCharAt = (t, i) => isTokenCharAt(t, i) || i < t.length && ".~+/".includes(t[i]);
|
|
480
|
+
var isUpperCode = (x) => x >= 65 && x <= 90;
|
|
481
|
+
var isLowerOrDigitCode = (x) => x >= 97 && x <= 122 || x >= 48 && x <= 57;
|
|
482
|
+
var camelBoundaryAt = (t, i) => i > 0 && isUpperCode(t.charCodeAt(i)) && isLowerOrDigitCode(t.charCodeAt(i - 1));
|
|
483
|
+
var B64_LONG_MIN = 40;
|
|
484
|
+
function b64LongEnd(s, k) {
|
|
485
|
+
const t = s.text;
|
|
486
|
+
let j = k;
|
|
487
|
+
while (j < s.n && (isAlnumAt(t, j) || t[j] === "+" || t[j] === "/")) j++;
|
|
488
|
+
s.budget.tick(j - k + 1);
|
|
489
|
+
return j - k >= B64_LONG_MIN ? j : -1;
|
|
490
|
+
}
|
|
575
491
|
function compoundNameStart(text, nameStart, s) {
|
|
576
492
|
let start = nameStart;
|
|
577
493
|
for (; ; ) {
|
|
@@ -605,6 +521,9 @@ function urlNameClass(rawName) {
|
|
|
605
521
|
if (m) return m[1] === PROSE_KEY_NAME ? "key" : "prose";
|
|
606
522
|
return URL_NAME_SET.has(name) ? "url" : null;
|
|
607
523
|
}
|
|
524
|
+
function isUrlCredentialName(rawName) {
|
|
525
|
+
return urlNameClass(rawName) !== null;
|
|
526
|
+
}
|
|
608
527
|
function keyThresholdEnd(s, from) {
|
|
609
528
|
const t = s.text;
|
|
610
529
|
let k = from;
|
|
@@ -613,7 +532,8 @@ function keyThresholdEnd(s, from) {
|
|
|
613
532
|
let c = 0;
|
|
614
533
|
while (c < PROSE_KEY_MIN_LENGTH && isTokenCharAt(t, k + c)) c++;
|
|
615
534
|
s.budget.tick(k - from + c + 1);
|
|
616
|
-
|
|
535
|
+
if (c >= PROSE_KEY_MIN_LENGTH) return k + PROSE_KEY_MIN_LENGTH;
|
|
536
|
+
return b64LongEnd(s, k) < 0 ? -1 : k + PROSE_KEY_MIN_LENGTH;
|
|
617
537
|
}
|
|
618
538
|
function proseNameAt(text, i) {
|
|
619
539
|
if (!PROSE_NAME_FIRST.has(text[i].toLowerCase())) return null;
|
|
@@ -652,7 +572,8 @@ function proseNamedHits(s) {
|
|
|
652
572
|
break;
|
|
653
573
|
}
|
|
654
574
|
const name = proseNameAt(t, i);
|
|
655
|
-
|
|
575
|
+
const camel = name !== null && isAsciiName(name) && isAlnumAt(t, i - 1) && camelBoundaryAt(t, i);
|
|
576
|
+
if (!name || isAsciiName(name) && isAlnumAt(t, i - 1) && !camel) {
|
|
656
577
|
i++;
|
|
657
578
|
continue;
|
|
658
579
|
}
|
|
@@ -661,7 +582,7 @@ function proseNamedHits(s) {
|
|
|
661
582
|
let k = nameEnd;
|
|
662
583
|
while (k < n && isInlineWs(t[k])) k++;
|
|
663
584
|
let c = 0;
|
|
664
|
-
while (c < PROSE_KEY_MIN_LENGTH &&
|
|
585
|
+
while (c < PROSE_KEY_MIN_LENGTH && isB64TokenCharAt(t, k + c)) c++;
|
|
665
586
|
s.budget.tick(k - nameEnd + c + 1);
|
|
666
587
|
if (k === nameEnd || c < PROSE_KEY_MIN_LENGTH) {
|
|
667
588
|
i++;
|
|
@@ -700,9 +621,43 @@ function proseNamedHits(s) {
|
|
|
700
621
|
i++;
|
|
701
622
|
continue;
|
|
702
623
|
}
|
|
703
|
-
|
|
624
|
+
let first = k;
|
|
625
|
+
if (name === AUTH_HEADER_NAME) {
|
|
626
|
+
let w = k;
|
|
627
|
+
while (w < n && /[A-Za-z]/.test(t[w])) w++;
|
|
628
|
+
let v = w;
|
|
629
|
+
while (v < n && isInlineWs(t[v])) v++;
|
|
630
|
+
s.budget.tick(v - k + 1);
|
|
631
|
+
if (w > k && v > w) {
|
|
632
|
+
const v0 = v;
|
|
633
|
+
for (; ; ) {
|
|
634
|
+
const a = placeholderAt(t, v);
|
|
635
|
+
if (a > 0) {
|
|
636
|
+
v += a;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (v < n && isInlineWs(t[v])) {
|
|
640
|
+
v++;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
s.budget.tick(v - v0 + 1);
|
|
646
|
+
if (v >= n || !isPrintableAscii(t.charCodeAt(v)) || isWs(t[v])) {
|
|
647
|
+
i++;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
first = v;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
headEnd = first + 1;
|
|
704
654
|
}
|
|
705
|
-
|
|
655
|
+
let runStart = i;
|
|
656
|
+
if (camel) {
|
|
657
|
+
while (runStart > 0 && isAlnumAt(t, runStart - 1)) runStart--;
|
|
658
|
+
s.budget.tick(i - runStart + 1);
|
|
659
|
+
}
|
|
660
|
+
const start = compoundNameStart(t, runStart, s);
|
|
706
661
|
let urlEnd = -1;
|
|
707
662
|
if (t[j] === "=" && j === nameEnd && inUrlParamName(s, start)) {
|
|
708
663
|
const v = interpretUrlValue(s, sepEnd);
|
|
@@ -748,6 +703,44 @@ function urlCredentialSpans(s) {
|
|
|
748
703
|
break;
|
|
749
704
|
}
|
|
750
705
|
const c = t[i];
|
|
706
|
+
if (c === "%" && i + 2 < n) {
|
|
707
|
+
const head = t.slice(i, i + 3).toUpperCase();
|
|
708
|
+
if (head === "%3F" || head === "%26" || head === "%23") {
|
|
709
|
+
let j = i + 3;
|
|
710
|
+
while (j < n) {
|
|
711
|
+
if (PARAM_NAME_CHAR(t, j)) {
|
|
712
|
+
j++;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (t[j] === "%" && isHex(t[j + 1]) && isHex(t[j + 2]) && !["%3D", "%26", "%3F", "%23"].includes(t.slice(j, j + 3).toUpperCase())) {
|
|
716
|
+
j += 3;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
s.budget.tick(j - i + 1);
|
|
722
|
+
if (j > i + 3 && t.slice(j, j + 3).toUpperCase() === "%3D") {
|
|
723
|
+
const cls2 = urlNameClass(t.slice(i + 3, j).replace(/%25/gi, "%"));
|
|
724
|
+
if (!cls2) {
|
|
725
|
+
i = j;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
const vStart = j + 3;
|
|
729
|
+
let k = vStart;
|
|
730
|
+
while (k < n && t[k] !== "&" && t[k] !== "#" && !isWs(t[k]) && !(t[k] === "%" && t.slice(k, k + 3).toUpperCase() === "%26")) k++;
|
|
731
|
+
s.budget.tick(k - vStart + 1);
|
|
732
|
+
if (k > vStart && hasRawValueContent(s, vStart, k)) {
|
|
733
|
+
out.push({ index: cls2 === "url" ? vStart : i + 3, end: k });
|
|
734
|
+
i = k;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
i = k > vStart ? k : j;
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
i++;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
751
744
|
if (c !== "?" && c !== "&" && c !== "#" && c !== ";") {
|
|
752
745
|
i++;
|
|
753
746
|
continue;
|
|
@@ -781,110 +774,312 @@ function proxySchemeStart(t, colon) {
|
|
|
781
774
|
return -1;
|
|
782
775
|
}
|
|
783
776
|
|
|
784
|
-
// ../redact/
|
|
785
|
-
var
|
|
786
|
-
var
|
|
787
|
-
var
|
|
788
|
-
var isHexCode2 = (x) => x >= 48 && x <= 57 || x >= 65 && x <= 70 || x >= 97 && x <= 102;
|
|
789
|
-
function stopPositions(s) {
|
|
790
|
-
return /* @__PURE__ */ new Set([...proseNamedHits(s).map((h) => h.index), ...inviteSpans(s).map((h) => h.index)]);
|
|
791
|
-
}
|
|
792
|
-
var LINK_END = (c) => isWs(c) || c === ")" || c === "]" || c === ">" || c === '"' || c === "'";
|
|
777
|
+
// ../redact/link.ts
|
|
778
|
+
var AMP = 1;
|
|
779
|
+
var HASH = 2;
|
|
780
|
+
var SEMI = 4;
|
|
793
781
|
function httpHeadAt(t, i) {
|
|
794
782
|
if (t[i] !== "h" && t[i] !== "H") return 0;
|
|
795
783
|
const head = t.slice(i, i + 8).toLowerCase();
|
|
796
784
|
return head.startsWith("https://") ? 8 : head.startsWith("http://") ? 7 : 0;
|
|
797
785
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
786
|
+
var ENCODED_HEAD = /https?(?::|%(?:25)?3a)(?:\/|%(?:25)?2f)(?:\/|%(?:25)?2f)/iy;
|
|
787
|
+
function encodedHeadAt(t, i) {
|
|
788
|
+
if (t[i] !== "h" && t[i] !== "H") return 0;
|
|
789
|
+
ENCODED_HEAD.lastIndex = i;
|
|
790
|
+
if (!ENCODED_HEAD.test(t)) return 0;
|
|
791
|
+
const len = ENCODED_HEAD.lastIndex - i;
|
|
792
|
+
return t.slice(i, i + len).includes("%") ? len : 0;
|
|
793
|
+
}
|
|
794
|
+
var SOCKS_HEADS = [...SOCKS_URI_SCHEMES].sort((x, y) => y.length - x.length).map((x) => `${x}://`);
|
|
795
|
+
function socksHeadAt(t, i) {
|
|
796
|
+
if (t[i] !== "s" && t[i] !== "S") return 0;
|
|
797
|
+
const head = t.slice(i, i + 10).toLowerCase();
|
|
798
|
+
for (const p of SOCKS_HEADS) if (head.startsWith(p)) return p.length;
|
|
799
|
+
return 0;
|
|
800
|
+
}
|
|
801
|
+
function hasSchemeSeparator(text) {
|
|
802
|
+
return text.includes("://");
|
|
806
803
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
804
|
+
var ENCODED_UNIT = { "3A": ":", "2F": "/", "3F": "?", "23": "#", "40": "@", "5B": "[", "5D": "]", "28": "(", "29": ")" };
|
|
805
|
+
var ONCE = 1;
|
|
806
|
+
var TWICE = 2;
|
|
807
|
+
function headDepth(head) {
|
|
808
|
+
return (/%(?:3a|2f)/i.test(head) ? ONCE : 0) | (/%25(?:3a|2f)/i.test(head) ? TWICE : 0);
|
|
809
|
+
}
|
|
810
|
+
function unitAt(t, j, depth) {
|
|
811
|
+
if (depth && t[j] === "%") {
|
|
812
|
+
const hex = t.slice(j + 1, j + 3).toUpperCase();
|
|
813
|
+
if (depth & ONCE && ENCODED_UNIT[hex]) return { ch: ENCODED_UNIT[hex], len: 3, encodedChar: true };
|
|
814
|
+
if (depth & TWICE && hex === "25") {
|
|
815
|
+
const h2 = t.slice(j + 3, j + 5).toUpperCase();
|
|
816
|
+
if (ENCODED_UNIT[h2]) return { ch: ENCODED_UNIT[h2], len: 5, encodedChar: true };
|
|
817
|
+
}
|
|
812
818
|
}
|
|
813
|
-
return {
|
|
819
|
+
return { ch: t[j], len: 1, encodedChar: false };
|
|
814
820
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
821
|
+
function valueContextAt(s, start) {
|
|
822
|
+
const t = s.text;
|
|
823
|
+
if (start === 0 || t[start - 1] !== "=") return false;
|
|
824
|
+
let p = start - 1;
|
|
825
|
+
while (p > 0 && /[A-Za-z0-9_.%-]/.test(t[p - 1])) p--;
|
|
826
|
+
s.budget.tick(start - p + 1);
|
|
827
|
+
if (p === 0 || !"?&#;".includes(t[p - 1])) return false;
|
|
828
|
+
return paramNameEnd(t, p) === start - 1 && isUrlCredentialName(t.slice(p, start - 1));
|
|
829
|
+
}
|
|
830
|
+
function headAt(s, i, inherited) {
|
|
831
|
+
const t = s.text;
|
|
832
|
+
let kind = null;
|
|
833
|
+
let start = i;
|
|
834
|
+
let contentStart = 0;
|
|
835
|
+
const hl = httpHeadAt(t, i);
|
|
836
|
+
if (hl) {
|
|
837
|
+
kind = "http";
|
|
838
|
+
contentStart = i + hl;
|
|
839
|
+
} else {
|
|
840
|
+
const el = encodedHeadAt(t, i);
|
|
841
|
+
if (el) {
|
|
842
|
+
kind = "encoded";
|
|
843
|
+
contentStart = i + el;
|
|
844
|
+
} else if (t[i] === ":" && t[i + 1] === "/" && t[i + 2] === "/") {
|
|
845
|
+
const ps = proxySchemeStart(t, i);
|
|
846
|
+
s.budget.tick(9);
|
|
847
|
+
if (ps >= 0) {
|
|
848
|
+
kind = "proxy";
|
|
849
|
+
start = ps;
|
|
850
|
+
contentStart = i + 3;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (!kind) return null;
|
|
855
|
+
const extra = inherited | (valueContextAt(s, start) ? AMP | HASH | SEMI : 0) | (kind === "encoded" ? AMP | HASH : 0);
|
|
856
|
+
const displayable = kind === "proxy" || placeholderAt(t, contentStart) === 0;
|
|
857
|
+
const depth = kind === "encoded" ? headDepth(t.slice(i, contentStart)) : 0;
|
|
858
|
+
return { start, kind, contentStart, extra, userinfo: null, scanEnd: contentStart, displayEnd: contentStart, displayable, depth };
|
|
859
|
+
}
|
|
860
|
+
var isNonAscii = (s, j) => s.raw.charCodeAt(j) > 127;
|
|
861
|
+
var isExtraEnd = (c, extra) => (extra & AMP) !== 0 && c === "&" || (extra & HASH) !== 0 && c === "#" || (extra & SEMI) !== 0 && c === ";";
|
|
862
|
+
var KI_WRAP = (c) => c === ")" || c === "]" || c === ">" || c === '"' || c === "'";
|
|
863
|
+
function locateUserinfo(s, from, encoded) {
|
|
864
|
+
const t = s.text;
|
|
865
|
+
const n = s.n;
|
|
866
|
+
let j = from;
|
|
867
|
+
let at = null;
|
|
868
|
+
let afterAtom = false;
|
|
869
|
+
while (j < n && !isWs(t[j])) {
|
|
870
|
+
const a = placeholderAt(t, j);
|
|
871
|
+
if (a) {
|
|
872
|
+
afterAtom = true;
|
|
873
|
+
j += a;
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (afterAtom && isNonAscii(s, j)) break;
|
|
877
|
+
if (encoded && (t[j] === "&" || t[j] === "#")) break;
|
|
878
|
+
const u = unitAt(t, j, encoded);
|
|
879
|
+
if (u.ch === "/" || u.ch === "?" || u.ch === "#") break;
|
|
880
|
+
if (u.ch === "@") at = [j, j + u.len];
|
|
881
|
+
j += u.len;
|
|
882
|
+
}
|
|
883
|
+
s.budget.tick(j - from + 1);
|
|
884
|
+
return at;
|
|
819
885
|
}
|
|
820
|
-
function
|
|
886
|
+
function linkTable(s) {
|
|
887
|
+
if (s.links) return s.links;
|
|
821
888
|
const t = s.text;
|
|
822
889
|
const n = s.n;
|
|
823
890
|
const out = [];
|
|
824
|
-
const
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
891
|
+
const backoff = /* @__PURE__ */ new Map();
|
|
892
|
+
const open = (rec) => {
|
|
893
|
+
out.push(rec);
|
|
894
|
+
const encoded = rec.depth;
|
|
895
|
+
const at = locateUserinfo(s, rec.contentStart, encoded);
|
|
896
|
+
if (at) rec.userinfo = { index: rec.contentStart, end: at[0] };
|
|
897
|
+
return { rec, encoded, kiT62: !at && rec.kind === "proxy", j: at ? at[1] : rec.contentStart, paren: 0, bracket: 0 };
|
|
898
|
+
};
|
|
899
|
+
const close = (f) => {
|
|
900
|
+
const rec = f.rec;
|
|
901
|
+
rec.scanEnd = f.j;
|
|
902
|
+
if (f.kiT62) {
|
|
903
|
+
let b = backoff.get(f.j);
|
|
904
|
+
if (b === void 0) {
|
|
905
|
+
b = f.j;
|
|
906
|
+
while (b > 0 && KI_WRAP(t[b - 1]) && placeholderEndingAt(t, b) === 0) b--;
|
|
907
|
+
s.budget.tick(f.j - b + 1);
|
|
908
|
+
backoff.set(f.j, b);
|
|
909
|
+
}
|
|
910
|
+
rec.displayEnd = Math.max(b, rec.contentStart);
|
|
911
|
+
} else rec.displayEnd = f.j;
|
|
830
912
|
};
|
|
831
|
-
const limitAt = valueLimits(s, creds);
|
|
832
913
|
let i = 0;
|
|
833
914
|
while (i < n) {
|
|
834
915
|
if (!s.budget.tick()) {
|
|
835
916
|
s.unfinished = Math.min(s.unfinished, i);
|
|
836
917
|
break;
|
|
837
918
|
}
|
|
838
|
-
const
|
|
839
|
-
if (!
|
|
919
|
+
const top = headAt(s, i, 0);
|
|
920
|
+
if (!top) {
|
|
840
921
|
i++;
|
|
841
922
|
continue;
|
|
842
923
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
let lastAt = -1;
|
|
851
|
-
let k = i + len;
|
|
852
|
-
for (; k < end && !stops.has(k) && !AUTHORITY_END.has(t[k]); k++) {
|
|
853
|
-
if (t[k] === "[") {
|
|
854
|
-
const b = bracketSpan(t, k, end);
|
|
855
|
-
if (!b.len) {
|
|
856
|
-
s.budget.tick(b.scanned);
|
|
857
|
-
break;
|
|
858
|
-
}
|
|
859
|
-
k += b.len - 1;
|
|
860
|
-
continue;
|
|
924
|
+
const stack = [open(top)];
|
|
925
|
+
while (stack.length) {
|
|
926
|
+
const f = stack[stack.length - 1];
|
|
927
|
+
const j = f.j;
|
|
928
|
+
if (!s.budget.tick()) {
|
|
929
|
+
s.unfinished = Math.min(s.unfinished, j);
|
|
930
|
+
f.j = n;
|
|
861
931
|
}
|
|
862
|
-
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
s.budget.tick(k - (i + len));
|
|
866
|
-
const ipl = lastAt >= 0 ? 0 : ipLiteralAt(s, j);
|
|
867
|
-
if (ipl && j + ipl <= end && !spanHasStop(stops, j, j + ipl)) j += ipl;
|
|
868
|
-
let inAuthority = true;
|
|
869
|
-
while (j < end && !LINK_END(t[j]) && !stops.has(j)) {
|
|
870
|
-
if (AUTHORITY_END.has(t[j])) inAuthority = false;
|
|
871
|
-
if (t[j] === "[") {
|
|
872
|
-
const host = inAuthority && j - 1 === lastAt ? ipLiteralAt(s, j) : 0;
|
|
873
|
-
if (!host || j + host > end || spanHasStop(stops, j, j + host)) break;
|
|
874
|
-
j += host;
|
|
932
|
+
const atom = j < n ? placeholderAt(t, j) : 0;
|
|
933
|
+
if (atom && f.rec.kind === "proxy") {
|
|
934
|
+
f.j = j + atom;
|
|
875
935
|
continue;
|
|
876
936
|
}
|
|
877
|
-
j
|
|
937
|
+
let ended = j >= n || isWs(t[j]) || isNonAscii(s, j) || isExtraEnd(t[j], f.rec.extra);
|
|
938
|
+
if (!ended && !f.kiT62) {
|
|
939
|
+
const c = t[j];
|
|
940
|
+
if (c === '"' || c === ">" || !f.encoded && c === "'") ended = true;
|
|
941
|
+
else if (atom) ended = true;
|
|
942
|
+
}
|
|
943
|
+
if (!ended) {
|
|
944
|
+
const child = headAt(s, j, f.rec.extra);
|
|
945
|
+
if (child && child.start >= f.rec.contentStart) {
|
|
946
|
+
stack.push(open(child));
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const u = unitAt(t, j, f.encoded);
|
|
950
|
+
if (u.ch === "(") f.paren++;
|
|
951
|
+
else if (u.ch === "[") f.bracket++;
|
|
952
|
+
else if (u.ch === ")" || u.ch === "]") {
|
|
953
|
+
const isParen = u.ch === ")";
|
|
954
|
+
const count = isParen ? f.paren : f.bracket;
|
|
955
|
+
if (count > 0) {
|
|
956
|
+
if (isParen) f.paren--;
|
|
957
|
+
else f.bracket--;
|
|
958
|
+
} else if (!u.encodedChar && !f.kiT62) ended = true;
|
|
959
|
+
}
|
|
960
|
+
if (!ended) {
|
|
961
|
+
f.j = j + u.len;
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
f.j = Math.min(f.j, n);
|
|
966
|
+
close(f);
|
|
967
|
+
stack.pop();
|
|
968
|
+
const parent = stack[stack.length - 1];
|
|
969
|
+
if (parent) {
|
|
970
|
+
parent.j = Math.max(parent.j, f.rec.scanEnd);
|
|
971
|
+
parent.paren += f.paren;
|
|
972
|
+
parent.bracket += f.bracket;
|
|
973
|
+
}
|
|
878
974
|
}
|
|
879
|
-
|
|
880
|
-
if (j > i + len) {
|
|
881
|
-
out.push({ index: i, end: j });
|
|
882
|
-
i = j;
|
|
883
|
-
} else i++;
|
|
975
|
+
i = Math.max(top.scanEnd, i + 1);
|
|
884
976
|
}
|
|
977
|
+
out.sort((a, b) => a.start - b.start);
|
|
978
|
+
s.links = out;
|
|
885
979
|
return out;
|
|
886
980
|
}
|
|
981
|
+
function linkStartingAt(s, pos) {
|
|
982
|
+
const links = s.links;
|
|
983
|
+
if (!links || links.length === 0) return null;
|
|
984
|
+
let lo = 0, hi = links.length - 1;
|
|
985
|
+
while (lo <= hi) {
|
|
986
|
+
const mid = lo + hi >> 1;
|
|
987
|
+
if (links[mid].start < pos) lo = mid + 1;
|
|
988
|
+
else hi = mid - 1;
|
|
989
|
+
}
|
|
990
|
+
s.budget.tick(Math.ceil(Math.log2(links.length + 1)) + 1);
|
|
991
|
+
const r = links[lo];
|
|
992
|
+
return r && r.start === pos && r.displayEnd > r.contentStart ? r : null;
|
|
993
|
+
}
|
|
994
|
+
function userinfoHasContent(s, from, end, encoded) {
|
|
995
|
+
const t = s.text;
|
|
996
|
+
s.budget.tick(end - from);
|
|
997
|
+
for (let k = from; k < end; ) {
|
|
998
|
+
const a = placeholderAt(t, k);
|
|
999
|
+
if (a > 0 && k + a <= end) {
|
|
1000
|
+
k += a;
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
const u = unitAt(t, k, encoded);
|
|
1004
|
+
if (u.ch !== ":") return true;
|
|
1005
|
+
k += u.len;
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// ../redact/index.ts
|
|
1011
|
+
var isAlnumCode2 = (x) => x >= 48 && x <= 57 || x >= 65 && x <= 90 || x >= 97 && x <= 122;
|
|
1012
|
+
var isWordCode = (x) => isAlnumCode2(x) || x === 95;
|
|
1013
|
+
var isTokenCode = (x) => isWordCode(x) || x === 45;
|
|
1014
|
+
var isHexCode2 = (x) => x >= 48 && x <= 57 || x >= 65 && x <= 70 || x >= 97 && x <= 102;
|
|
1015
|
+
function stopPositions(s) {
|
|
1016
|
+
return /* @__PURE__ */ new Set([...proseNamedHits(s).map((h) => h.index), ...inviteSpans(s).map((h) => h.index)]);
|
|
1017
|
+
}
|
|
1018
|
+
var linkedCreds = /* @__PURE__ */ new WeakMap();
|
|
1019
|
+
var isSeparatorEnd = (s, pos) => pos < s.n && (s.text[pos] === "&" || s.text[pos] === "#" || s.text[pos] === ";" || CJK_STOPS.includes(s.text[pos]));
|
|
1020
|
+
function credSpans(s) {
|
|
1021
|
+
const hit = linkedCreds.get(s);
|
|
1022
|
+
if (hit) return hit;
|
|
1023
|
+
const creds = urlCredentialSpans(s).map((sp) => ({ index: sp.index, end: sp.end }));
|
|
1024
|
+
const recs = [];
|
|
1025
|
+
for (const r of s.links) recs.push({ start: r.start, end: r.displayEnd, userinfo: r.userinfo, socks: false });
|
|
1026
|
+
for (const k of socksScan(s)) recs.push({ start: k.start, end: k.authEnd, userinfo: k.userinfo, socks: true });
|
|
1027
|
+
recs.sort((a, b) => a.start - b.start);
|
|
1028
|
+
let ci = 0;
|
|
1029
|
+
for (const r of recs) {
|
|
1030
|
+
s.budget.tick();
|
|
1031
|
+
while (ci < creds.length && creds[ci].end <= r.start) ci++;
|
|
1032
|
+
const c = creds[ci];
|
|
1033
|
+
if (!c || c.index > r.start || r.end <= c.end) continue;
|
|
1034
|
+
const u = r.userinfo;
|
|
1035
|
+
const crossesUserinfo = u !== null && u.index <= c.end && c.end < u.end;
|
|
1036
|
+
if (crossesUserinfo || !r.socks && !isSeparatorEnd(s, c.end)) c.end = contentDisplayEnd(s, r.end);
|
|
1037
|
+
}
|
|
1038
|
+
const out = mergeSpans(creds, false);
|
|
1039
|
+
linkedCreds.set(s, out);
|
|
1040
|
+
return out;
|
|
1041
|
+
}
|
|
1042
|
+
function valueLimits(s) {
|
|
1043
|
+
const creds = credSpans(s);
|
|
1044
|
+
let k = 0;
|
|
1045
|
+
return (pos) => {
|
|
1046
|
+
while (k < creds.length && creds[k].end <= pos) k++;
|
|
1047
|
+
return k < creds.length && creds[k].index <= pos ? creds[k].end : -1;
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function mergeSpans(spans, touching) {
|
|
1051
|
+
const out = [];
|
|
1052
|
+
for (const sp of [...spans].sort((a, b) => a.index - b.index || b.end - a.end)) {
|
|
1053
|
+
const last = out[out.length - 1];
|
|
1054
|
+
if (last && (sp.index < last.end || touching && sp.index === last.end)) last.end = Math.max(last.end, sp.end);
|
|
1055
|
+
else if (sp.end > sp.index) out.push({ index: sp.index, end: sp.end });
|
|
1056
|
+
}
|
|
1057
|
+
return out;
|
|
1058
|
+
}
|
|
1059
|
+
function urlSpans(s) {
|
|
1060
|
+
const limitAt = valueLimits(s);
|
|
1061
|
+
const out = [];
|
|
1062
|
+
for (const r of s.links) {
|
|
1063
|
+
if (r.kind === "proxy" || !r.displayable || r.displayEnd <= r.contentStart) continue;
|
|
1064
|
+
s.budget.tick();
|
|
1065
|
+
const lim = limitAt(r.start);
|
|
1066
|
+
out.push({ index: r.start, end: lim >= 0 ? Math.min(r.displayEnd, lim) : r.displayEnd });
|
|
1067
|
+
}
|
|
1068
|
+
return mergeSpans(out, true);
|
|
1069
|
+
}
|
|
887
1070
|
function userinfoSpans(s) {
|
|
1071
|
+
const out = [];
|
|
1072
|
+
for (const r of s.links) {
|
|
1073
|
+
const u = r.userinfo;
|
|
1074
|
+
if (u && u.end > u.index && userinfoHasContent(s, u.index, u.end, r.depth)) out.push({ index: u.index, end: u.end });
|
|
1075
|
+
}
|
|
1076
|
+
for (const k of socksScan(s)) if (k.userinfo && userinfoHasContent(s, k.userinfo.index, k.userinfo.end, 0)) out.push({ ...k.userinfo });
|
|
1077
|
+
return mergeSpans(out, false);
|
|
1078
|
+
}
|
|
1079
|
+
var socksScans = /* @__PURE__ */ new WeakMap();
|
|
1080
|
+
function socksScan(s) {
|
|
1081
|
+
const hit = socksScans.get(s);
|
|
1082
|
+
if (hit) return hit;
|
|
888
1083
|
const t = s.text;
|
|
889
1084
|
const n = s.n;
|
|
890
1085
|
const out = [];
|
|
@@ -894,7 +1089,7 @@ function userinfoSpans(s) {
|
|
|
894
1089
|
s.unfinished = Math.min(s.unfinished, i);
|
|
895
1090
|
break;
|
|
896
1091
|
}
|
|
897
|
-
const len =
|
|
1092
|
+
const len = socksHeadAt(t, i);
|
|
898
1093
|
if (!len) {
|
|
899
1094
|
i++;
|
|
900
1095
|
continue;
|
|
@@ -902,31 +1097,20 @@ function userinfoSpans(s) {
|
|
|
902
1097
|
const from = i + len;
|
|
903
1098
|
let j = from;
|
|
904
1099
|
let at = -1;
|
|
905
|
-
|
|
1100
|
+
const lead = placeholderAt(t, j);
|
|
1101
|
+
if (lead) j += lead;
|
|
1102
|
+
while (j < n && !isWs(t[j]) && t[j] !== "?" && t[j] !== "#" && placeholderAt(t, j) === 0 && !(lead && s.raw.charCodeAt(j) > 127)) {
|
|
906
1103
|
if (t[j] === "@") at = j;
|
|
907
1104
|
j++;
|
|
908
1105
|
}
|
|
909
1106
|
s.budget.tick(j - i);
|
|
910
|
-
|
|
911
|
-
i = from;
|
|
1107
|
+
out.push({ start: i, userinfo: at > from ? { index: from, end: at } : null, authEnd: j });
|
|
1108
|
+
i = j > from ? j : from;
|
|
912
1109
|
}
|
|
1110
|
+
socksScans.set(s, out);
|
|
913
1111
|
return out;
|
|
914
1112
|
}
|
|
915
|
-
|
|
916
|
-
const t = s.text;
|
|
917
|
-
s.budget.tick(end - from);
|
|
918
|
-
for (let k = from; k < end; ) {
|
|
919
|
-
const a = placeholderAt(t, k);
|
|
920
|
-
if (a > 0 && k + a <= end) {
|
|
921
|
-
k += a;
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
if (t[k] !== ":") return true;
|
|
925
|
-
k++;
|
|
926
|
-
}
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
|
-
var SUB_END = (c) => isWs(c) || c === ")" || c === "]" || c === ">" || c === '"' || c === "'";
|
|
1113
|
+
var KEYWORD_VALUE_END = (c) => isWs(c) || c === ")" || c === "]" || c === ">" || c === '"' || c === "'";
|
|
930
1114
|
function subscriptionKeywordEnd(t, i) {
|
|
931
1115
|
if (i > 0 && isWordCode(t.charCodeAt(i - 1))) return -1;
|
|
932
1116
|
const c = t[i];
|
|
@@ -948,84 +1132,58 @@ function subscriptionSpans(s) {
|
|
|
948
1132
|
const t = s.text;
|
|
949
1133
|
const n = s.n;
|
|
950
1134
|
const cands = [];
|
|
1135
|
+
const proxies = [];
|
|
1136
|
+
const proxyLimit = valueLimits(s);
|
|
1137
|
+
for (const r of s.links) {
|
|
1138
|
+
if (r.kind !== "proxy" || r.displayEnd <= r.contentStart) continue;
|
|
1139
|
+
s.budget.tick();
|
|
1140
|
+
const lim = proxyLimit(r.start);
|
|
1141
|
+
proxies.push({ index: r.start, end: lim >= 0 ? Math.min(r.displayEnd, lim) : r.displayEnd });
|
|
1142
|
+
}
|
|
1143
|
+
for (const sp of proxies) cands.push(sp);
|
|
951
1144
|
const stops = stopPositions(s);
|
|
952
1145
|
const limitAt = valueLimits(s);
|
|
953
1146
|
const endAt = (pos) => {
|
|
954
1147
|
const l = limitAt(pos);
|
|
955
1148
|
return l >= 0 ? l : n;
|
|
956
1149
|
};
|
|
957
|
-
|
|
958
|
-
let j = from;
|
|
959
|
-
while (j < end && !SUB_END(t[j]) && !stops.has(j)) j++;
|
|
960
|
-
s.budget.tick(j - from + 1);
|
|
961
|
-
return j;
|
|
962
|
-
};
|
|
963
|
-
const extendNoStops = (from, end) => {
|
|
964
|
-
let j = from;
|
|
965
|
-
while (j < end && !SUB_END(t[j]) && !CJK_STOPS.includes(t[j])) j++;
|
|
966
|
-
s.budget.tick(j - from + 1);
|
|
967
|
-
return j;
|
|
968
|
-
};
|
|
969
|
-
const authorityAt = (from, end) => {
|
|
970
|
-
let j = from, at = -1;
|
|
971
|
-
while (j < end && !isWs(t[j]) && t[j] !== "/" && t[j] !== "?" && t[j] !== "#") {
|
|
972
|
-
if (t[j] === "@") at = j;
|
|
973
|
-
j++;
|
|
974
|
-
}
|
|
975
|
-
s.budget.tick(j - from + 1);
|
|
976
|
-
return at;
|
|
977
|
-
};
|
|
978
|
-
const extendNoAt = (from, end) => {
|
|
979
|
-
let j = from;
|
|
980
|
-
while (j < end && !isWs(t[j]) && !CJK_STOPS.includes(t[j])) j++;
|
|
981
|
-
const scanned = j - from;
|
|
982
|
-
while (j > from && SUB_END(t[j - 1])) j--;
|
|
983
|
-
s.budget.tick(scanned * 2 + 1);
|
|
984
|
-
return j;
|
|
985
|
-
};
|
|
986
|
-
const proxyExtend = (from, end) => {
|
|
987
|
-
const at = authorityAt(from, end);
|
|
988
|
-
return at < 0 ? extendNoAt(from, end) : extendNoStops(at + 1, end);
|
|
989
|
-
};
|
|
1150
|
+
let p = 0;
|
|
990
1151
|
let i = 0;
|
|
991
1152
|
while (i < n) {
|
|
992
1153
|
if (!s.budget.tick()) {
|
|
993
1154
|
s.unfinished = Math.min(s.unfinished, i);
|
|
994
1155
|
break;
|
|
995
1156
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
if (start >= 0) {
|
|
1000
|
-
const j = proxyExtend(i + 3, endAt(i));
|
|
1001
|
-
if (j > i + 3) {
|
|
1002
|
-
cands.push({ index: start, end: j });
|
|
1003
|
-
i = j;
|
|
1004
|
-
continue;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
i++;
|
|
1157
|
+
while (p < proxies.length && proxies[p].end <= i) p++;
|
|
1158
|
+
if (p < proxies.length && proxies[p].index <= i) {
|
|
1159
|
+
i = proxies[p].end;
|
|
1008
1160
|
continue;
|
|
1009
1161
|
}
|
|
1010
1162
|
const kw = subscriptionKeywordEnd(t, i);
|
|
1011
|
-
if (kw
|
|
1012
|
-
|
|
1013
|
-
cands.push({ index: i, end: j });
|
|
1014
|
-
i = j;
|
|
1163
|
+
if (kw < 0) {
|
|
1164
|
+
i++;
|
|
1015
1165
|
continue;
|
|
1016
1166
|
}
|
|
1017
|
-
i
|
|
1167
|
+
const end = endAt(i);
|
|
1168
|
+
let q = kw;
|
|
1169
|
+
while (q < end && isInlineWs(t[q])) q++;
|
|
1170
|
+
if (q < end && (t[q] === "=" || t[q] === ":" || t[q] === "\uFF1A")) {
|
|
1171
|
+
q++;
|
|
1172
|
+
while (q < end && isInlineWs(t[q])) q++;
|
|
1173
|
+
}
|
|
1174
|
+
s.budget.tick(q - kw + 1);
|
|
1175
|
+
const link = linkStartingAt(s, q);
|
|
1176
|
+
let j;
|
|
1177
|
+
if (link) j = Math.min(link.displayEnd, end);
|
|
1178
|
+
else {
|
|
1179
|
+
j = kw;
|
|
1180
|
+
while (j < end && !KEYWORD_VALUE_END(t[j]) && !stops.has(j)) j++;
|
|
1181
|
+
s.budget.tick(j - kw + 1);
|
|
1182
|
+
}
|
|
1183
|
+
cands.push({ index: i, end: j });
|
|
1184
|
+
i = Math.max(j, i + 1);
|
|
1018
1185
|
}
|
|
1019
|
-
return
|
|
1020
|
-
}
|
|
1021
|
-
function disjoint(spans) {
|
|
1022
|
-
const out = [];
|
|
1023
|
-
let last = -1;
|
|
1024
|
-
for (const sp of [...spans].sort((a, b) => a.index - b.index)) if (sp.index >= last && sp.end > sp.index) {
|
|
1025
|
-
out.push(sp);
|
|
1026
|
-
last = sp.end;
|
|
1027
|
-
}
|
|
1028
|
-
return out;
|
|
1186
|
+
return mergeSpans(cands, false);
|
|
1029
1187
|
}
|
|
1030
1188
|
var REGISTERED_ERROR_CODE = /^(?:INSTALL_FAILED|INSTALL_PARSE_FAILED|ERR)(?:_[A-Z]{2,})+$/;
|
|
1031
1189
|
function tokenSpans(s) {
|
|
@@ -1157,23 +1315,35 @@ function inviteSpans(s) {
|
|
|
1157
1315
|
s.invites = out;
|
|
1158
1316
|
return out;
|
|
1159
1317
|
}
|
|
1318
|
+
function emailSpans(s) {
|
|
1319
|
+
const seps = /* @__PURE__ */ new Set();
|
|
1320
|
+
for (const r of s.links) if (r.userinfo) seps.add(r.userinfo.end);
|
|
1321
|
+
for (const k of socksScan(s)) if (k.userinfo) seps.add(k.userinfo.end);
|
|
1322
|
+
if (seps.size === 0) return EMAIL(s);
|
|
1323
|
+
return EMAIL(s).filter((sp) => !seps.has(s.text.indexOf("@", sp.index)));
|
|
1324
|
+
}
|
|
1160
1325
|
var RULES = [
|
|
1161
1326
|
["subscription", subscriptionSpans],
|
|
1162
1327
|
["url", urlSpans],
|
|
1163
|
-
["email",
|
|
1328
|
+
["email", emailSpans],
|
|
1164
1329
|
["uuid", UUID],
|
|
1165
1330
|
["ipv6", IPV6],
|
|
1166
1331
|
["ipv4", IPV4],
|
|
1167
1332
|
["token", tokenSpans],
|
|
1168
|
-
["urlcred",
|
|
1333
|
+
["urlcred", credSpans],
|
|
1169
1334
|
["userinfo", userinfoSpans],
|
|
1170
1335
|
["invite", inviteSpans],
|
|
1171
1336
|
["config", configRegions]
|
|
1172
1337
|
];
|
|
1173
1338
|
var RULE_NAMES = RULES.map(([name]) => name);
|
|
1174
1339
|
var TOKEN_ORDER = RULE_NAMES.indexOf("token");
|
|
1340
|
+
function foldFullwidth(text) {
|
|
1341
|
+
if (!/[\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF1D]/.test(text)) return text;
|
|
1342
|
+
return text.replace(/[\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF1D]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 65248));
|
|
1343
|
+
}
|
|
1175
1344
|
function sensitiveMatches(text, opts = {}) {
|
|
1176
|
-
const s = newScan(text, opts.budget);
|
|
1345
|
+
const s = newScan(foldFullwidth(text), opts.budget, text);
|
|
1346
|
+
linkTable(s);
|
|
1177
1347
|
const out = [];
|
|
1178
1348
|
let exhaustedAt = -1;
|
|
1179
1349
|
for (let order = 0; order < RULES.length; order++) {
|
|
@@ -1229,7 +1399,7 @@ function redactSensitive(text, mask, opts = {}) {
|
|
|
1229
1399
|
var SECRET_RULES = /* @__PURE__ */ new Set(["token", "urlcred", "userinfo", "subscription", "ipv4", "ipv6", "invite", "config", "uuid", "email"]);
|
|
1230
1400
|
var SHORT_WORD_KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)+$/;
|
|
1231
1401
|
function isKnownFalsePositive(m) {
|
|
1232
|
-
if (m.rule === "subscription") return /^subscri/i.test(m.text) && !m.text
|
|
1402
|
+
if (m.rule === "subscription") return /^subscri/i.test(m.text) && !hasSchemeSeparator(m.text);
|
|
1233
1403
|
if (m.rule === "token") return SHORT_WORD_KEBAB.test(m.text) && m.text.split("-").every((p) => p.length <= 16);
|
|
1234
1404
|
return false;
|
|
1235
1405
|
}
|
|
@@ -1252,45 +1422,190 @@ var USAGE_RULES = [
|
|
|
1252
1422
|
"",
|
|
1253
1423
|
"1. \u8C03 `list_entries`\uFF0C`query` \u7528\u7528\u6237\u539F\u8BDD\uFF1B\u539F\u8BDD\u8D85\u8FC7 200 \u5B57\u65F6\uFF0C\u622A\u53D6\u6700\u80FD\u4EE3\u8868\u95EE\u9898\u7684\u4E00\u6BB5\u2014\u2014\u6709\u62A5\u9519\u539F\u6587\u5C31\u53D6\u62A5\u9519\u539F\u6587\u2014\u2014\u4E0D\u6539\u5199\u63AA\u8F9E\u3002\u8C03\u7528\u672C skill \u65F6\u4F20\u5165\u7684\u53C2\u6570\u662F\u4F60\u81EA\u5DF1\u7684\u8F6C\u8FF0\uFF0C\u4E0D\u80FD\u4EE3\u66FF\u7528\u6237\u539F\u8BDD\u3002\u7528\u6237\u9644\u4E86\u622A\u56FE\u3001\u4F60\u770B\u5F97\u5230\u65F6\uFF0C\u628A\u56FE\u91CC\u7684\u62A5\u9519\u539F\u6587\u9010\u5B57\u3001\u8FDE\u540C\u5BA2\u6237\u7AEF\u540D\u79F0\u63A5\u5728\u539F\u8BDD\u540E\u9762\u4E00\u8D77\u4F5C\u4E3A `query`\uFF1B\u8D85\u8FC7 200 \u5B57\u65F6\u4F18\u5148\u4FDD\u7559\u62A5\u9519\u539F\u6587\u3002",
|
|
1254
1424
|
`2. \u5148\u5224\u65AD\u95EE\u9898\u662F\u5426\u5C5E\u4E8E\u7B2C 4 \u6B65\u300C\u76F8\u90BB\u4F46\u4E0D\u505A\u300D\u7684\u90A3\u51E0\u7C7B\u2014\u2014\u770B\u95EE\u9898\u8981\u7684\u7B54\u6848\u5728\u54EA\u4E00\u4FA7\uFF0C\u4E0D\u770B\u95EE\u9898\u91CC\u63D0\u5230\u4E86\u4EC0\u4E48\uFF1A\u8981\u7684\u662F\u5BA2\u6237\u7AEF\u600E\u4E48\u7528\u3001\u600E\u4E48\u8BBE\u7F6E\u3001\u600E\u4E48\u6392\u9519\uFF0C\u6216\u5BA2\u6237\u7AEF\u600E\u4E48\u53D6\u7528\u3001\u663E\u793A\u670D\u52A1\u65B9\u7ED9\u7684\u8BA2\u9605\u3001\u6D41\u91CF\u4FE1\u606F\u4E0E\u8282\u70B9\u51ED\u636E\uFF0C\u662F\u77E5\u8BC6\u5E93\u7684\u4E8B\uFF0C\u8D77\u56E0\u5728\u670D\u52A1\u5546\u6216\u670D\u52A1\u7AEF\u4E5F\u4E00\u6837\uFF1B\u8981\u7684\u662F\u670D\u52A1\u5546\uFF08\u542B\u5BBD\u5E26\u8FD0\u8425\u5546\uFF09\u81EA\u5DF1\u7684\u89C4\u5219\u6216\u51B3\u5B9A\uFF08\u5982\u5957\u9910\u3001\u8BA1\u8D39\u3001\u8D26\u53F7\u4E0E\u552E\u540E\uFF09\uFF0C\u6216\u53EA\u95EE\u5B83\u7684\u7EBF\u8DEF\u3001\u7F51\u7EDC\u662F\u4E0D\u662F\u51FA\u4E86\u6545\u969C\uFF0C\u6216\u670D\u52A1\u7AEF\u4E0E\u4E2D\u8F6C\u7EBF\u8DEF\u65B9\u6848\u672C\u8EAB\uFF08\u600E\u4E48\u642D\u5EFA\u3001\u6548\u679C\u4E0E\u53D6\u820D\u3001\u9009\u54EA\u79CD\uFF09\uFF0C\u5C31\u7B97\u76F8\u90BB\uFF0C\u95EE\u6CD5\u518D\u6280\u672F\u3001\u63D0\u5230\u7684\u5BA2\u6237\u7AEF\u6216\u73B0\u8C61\u518D\u5177\u4F53\u4E5F\u4E00\u6837\uFF1B\u5E26\u7740\u5BA2\u6237\u7AEF\u91CC\u7684\u4F7F\u7528\u5F02\u5E38\u95EE\u662F\u4E0D\u662F\u670D\u52A1\u5546\u6216\u7F51\u7EDC\u90A3\u8FB9\u51FA\u4E86\u95EE\u9898\u7684\uFF0C\u8981\u7684\u662F\u6392\u9519\uFF0C\u5F52\u77E5\u8BC6\u5E93\u3002\u662F\u5C31\u4E0D\u6311\u6761\u76EE\u3001\u4E0D\u8BFB\u6B63\u6587\uFF0C\u76F4\u63A5\u56DE\u300C${OUT_OF_SCOPE_REPLY}\u300D\uFF0C\u9664\u8FD9\u4E00\u53E5\u5916\u4E0D\u8F93\u51FA\u4EFB\u4F55\u6587\u5B57\uFF08\u7B2C 1 \u6B65\u7684 \`list_entries\` \u7167\u8C03\uFF09\u3002\u95EE\u9898\u542B\u7CCA\u2014\u2014\u4E0D\u8FFD\u95EE\u5C31\u4E0D\u77E5\u9053\u5177\u4F53\u95EE\u7684\u662F\u4EC0\u4E48\uFF0C\u4F8B\u5982\u53EA\u8BF4\u300C\u7528\u4E0D\u4E86\u4E86\u300D\uFF0C\u6216\u8BF4\u4E86\u300C\u6709\u4E00\u5904\u7A7A\u767D\u300D\u5374\u6CA1\u8BF4\u662F\u54EA\u4E00\u5904\uFF0C\u6216\u53EA\u8BF4\u62A5\u9519\u53CD\u590D\u5F39\u51FA\u5374\u6CA1\u7ED9\u51FA\u539F\u6587\u2014\u2014\u65F6\uFF0C\u4E0D\u6311\u6761\u76EE\u3001\u4E0D\u8BFB\u6B63\u6587\uFF0C\u53EA\u7528\u4E00\u53E5\u8BDD\u8FFD\u95EE\u7528\u7684\u662F\u4EC0\u4E48\u5BA2\u6237\u7AEF\u3001\u9047\u5230\u4E86\u4EC0\u4E48\u73B0\u8C61\u3001\u662F\u54EA\u4E00\u5904\u3002\u7528\u6237\u53EA\u8BF4\u9644\u4E86\u622A\u56FE\u3001\u6CA1\u5199\u51FA\u73B0\u8C61\u6216\u62A5\u9519\u539F\u6587\uFF0C\u800C\u4F60\u770B\u4E0D\u5230\u56FE\u65F6\uFF0C\u4E5F\u6309\u542B\u7CCA\u5904\u7406\uFF1A\u4E0D\u6311\u6761\u76EE\u3001\u4E0D\u8BFB\u6B63\u6587\uFF0C\u53EA\u7528\u4E00\u53E5\u8BDD\u8BF7\u7528\u6237\u628A\u56FE\u91CC\u7684\u62A5\u9519\u6587\u5B57\u590D\u5236\u8FC7\u6765\uFF0C\u4E0D\u95EE\u522B\u7684\uFF1B\u95EE\u9898\u672C\u8EAB\u5DF2\u5199\u6E05\u73B0\u8C61\u6216\u62A5\u9519\u7684\uFF0C\u770B\u4E0D\u5230\u56FE\u4E5F\u7167\u5E38\u5F80\u4E0B\u8D70\uFF0C\u4E0D\u7B97\u542B\u7CCA\u3002\u770B\u5F97\u5230\u622A\u56FE\u65F6\uFF0C\u6309\u56FE\u91CC\u7684\u73B0\u8C61\u5904\u7406\uFF0C\u4E0D\u7B97\u542B\u7CCA\uFF1B\u770B\u5F97\u51FA\u4E0E\u4EE3\u7406\u5BA2\u6237\u7AEF\u6709\u5173\u65F6\u7167\u5E38\u5148\u8C03 \`list_entries\`\uFF0C\u770B\u4E0D\u51FA\u65F6\u76F4\u63A5\u8FFD\u95EE\u3002\u5426\u5219\u4ECE\u76EE\u5F55\u6311 1\u20133 \u6761\u53EF\u80FD\u76F8\u5173\u7684\u6761\u76EE\uFF1B\u4E00\u6761\u90FD\u4E0D\u6CBE\u8FB9\u5C31\u8DF3\u5230\u7B2C 4 \u6B65\u3002`,
|
|
1255
|
-
"3. \u7528 `get_entry` \u9010\u6761\u8BFB\u6B63\u6587\uFF1B\u8FD4\u56DE\u300C\u6761\u76EE\u4E0D\u5B58\u5728\u300D\u65F6\uFF0C\u91CD\u65B0\u8C03\u4E00\u6B21 `list_entries` \u53D6\u5F53\u524D\u76EE\u5F55\uFF0C\u4ECD\u6CA1\u6709\u5C31\u6309\u7B2C 4 \u6B65\u300C\u6CA1\u6709\u6761\u76EE\u80FD\u56DE\u7B54\u300D\u5904\u7406\u3002",
|
|
1256
|
-
"4. \u56DE\u7B54\uFF08\u56DE\u7B54\u6B63\u6587\u4E0D\u5199\u6761\u76EE id\uFF0C\u4F9D\u636E\u653E\u5728\u672B\u5C3E\u7684\u6761\u76EE\u9875\u9762\u94FE\u63A5\u91CC\uFF1B\u5F15\u7528\u6761\u76EE\u65F6\uFF0C\u4F5C\u4E3A\u4F9D\u636E\u7684\u53EA\u80FD\u662F\u8BFB\u8FC7\u6B63\u6587\u7684\u6761\u76EE\uFF1B\u6761\u76EE\u6B63\u6587\u91CC\u63D0\u5230\u7684\u5176\u4ED6\u6761\u76EE\u53EF\u4EE5\u70B9\u540D\u4F9B\u7528\u6237\u5EF6\u4F38\u9605\u8BFB\uFF0C\u4F46\u4E0D\u5F53\u4F5C\u4F9D\u636E\uFF1B\u76EE\u5F55\u91CC\u770B\u5230\u4F46\u6CA1\u8BFB\u8FC7\u6B63\u6587\u7684\u6761\u76EE\u4E0D\u70B9\u540D\u3002\u8BFB\u8FC7\u6B63\u6587\u540E\u8FD8\u9700\u8981\u8FFD\u95EE\u7684\uFF0C\u4E5F\u5148\u6309\u4E0B\u9762\u7684\u4E09\u79CD\u56DE\u7B54\u4E4B\u4E00\u4F5C\u7B54\uFF0C\u518D\u8FFD\u95EE\uFF09\u3002\u56DE\u7B54\u8981\u77ED\uFF0C\u7EC6\u8282\u7B49\u7528\u6237\u8FFD\u95EE\u518D\u7ED9\u3002\u7B2C\u4E00\u6B21\u56DE\u7B54\u8981\u80FD\u4E00\u773C\u770B\u5B8C\uFF1A\u4E0D\u8D85\u8FC7 8 \u884C\u3001300 \u5B57\uFF08\u672B\u5C3E\u7684\u6761\u76EE\u94FE\u63A5\u4E0D\u7B97\uFF09\uFF0C\u6BCF\u884C\u53EA\u5199\u4E00\u53E5\u8BDD\uFF1B\u653E\u4E0D\u4E0B\u7684\u7559\u7ED9\u8FFD\u95EE\u3002\u5199\u6CD5\uFF1A\u7528\u81EA\u7136\u7684\u51E0\u6BB5\u8BDD\uFF0C\u4E0D\u52A0\u300C\u7ED3\u8BBA\u300D\u300C\u786E\u8BA4\u300D\u300C\u4F9D\u636E\u300D\u8FD9\u7C7B\u5C0F\u6807\u9898\u6216\u6807\u7B7E\uFF0C\u4E0D\u7528\u8868\u683C\u3001\u5D4C\u5957\u5217\u8868\u548C\u7C97\u4F53\uFF1B\u4E00\u53E5\u53EA\u8BF4\u4E00\u4EF6\u4E8B\uFF1B\u7528\u7948\u4F7F\u53E5\uFF1B\u540C\u4E00\u4E2A\u4E1C\u897F\u59CB\u7EC8\u7528\u540C\u4E00\u4E2A\u8BCD\uFF1B\u4E0D\u7528\u300C\u5927\u6982\u300D\u300C\u4E00\u4E9B\u300D\u8FD9\u7C7B\u542B\u7CCA\u7684\u8BCD\uFF1B\u6309\u94AE\u540D\u3001\u83DC\u5355\u540D\u3001\u62A5\u9519\u539F\u6587\u548C\u547D\u4EE4\u7167\u539F\u6837\u5199\uFF1B\u4E0D\u4E3B\u52A8\u8BB2\u539F\u7406\uFF0C\u7528\u6237\u95EE\u4E3A\u4EC0\u4E48\u518D\u8BB2\u3002\u7528\u4E86\u6761\u76EE\u5185\u5BB9\u7684\u56DE\u7B54\uFF08\u80FD\u5B8C\u6574\u56DE\u7B54\u3001\u53EA\u80FD\u90E8\u5206\u56DE\u7B54\uFF09\uFF0C\
|
|
1425
|
+
"3. \u7528 `get_entry` \u9010\u6761\u8BFB\u6B63\u6587\uFF1B\u8FD4\u56DE\u91CC\u6CE8\u660E\u6761\u76EE\u5206\u4E86\u51E0\u6BB5\u65F6\uFF0C\u6309\u6BB5\u6807\u9898\u7528 `part` \u53C2\u6570\u518D\u8BFB\u4E0E\u95EE\u9898\u6709\u5173\u7684\u6BB5\u3002\u8FD4\u56DE\u300C\u6761\u76EE\u4E0D\u5B58\u5728\u300D\u65F6\uFF0C\u91CD\u65B0\u8C03\u4E00\u6B21 `list_entries` \u53D6\u5F53\u524D\u76EE\u5F55\uFF0C\u4ECD\u6CA1\u6709\u5C31\u6309\u7B2C 4 \u6B65\u300C\u6CA1\u6709\u6761\u76EE\u80FD\u56DE\u7B54\u300D\u5904\u7406\u3002",
|
|
1426
|
+
"4. \u56DE\u7B54\uFF08\u56DE\u7B54\u6B63\u6587\u4E0D\u5199\u6761\u76EE id\uFF0C\u4F9D\u636E\u653E\u5728\u672B\u5C3E\u7684\u6761\u76EE\u9875\u9762\u94FE\u63A5\u91CC\uFF1B\u5F15\u7528\u6761\u76EE\u65F6\uFF0C\u4F5C\u4E3A\u4F9D\u636E\u7684\u53EA\u80FD\u662F\u8BFB\u8FC7\u6B63\u6587\u7684\u6761\u76EE\uFF1B\u6761\u76EE\u6B63\u6587\u91CC\u63D0\u5230\u7684\u5176\u4ED6\u6761\u76EE\u53EF\u4EE5\u70B9\u540D\u4F9B\u7528\u6237\u5EF6\u4F38\u9605\u8BFB\uFF0C\u4F46\u4E0D\u5F53\u4F5C\u4F9D\u636E\uFF1B\u76EE\u5F55\u91CC\u770B\u5230\u4F46\u6CA1\u8BFB\u8FC7\u6B63\u6587\u7684\u6761\u76EE\u4E0D\u70B9\u540D\u3002\u8BFB\u8FC7\u6B63\u6587\u540E\u8FD8\u9700\u8981\u8FFD\u95EE\u7684\uFF0C\u4E5F\u5148\u6309\u4E0B\u9762\u7684\u4E09\u79CD\u56DE\u7B54\u4E4B\u4E00\u4F5C\u7B54\uFF0C\u518D\u8FFD\u95EE\uFF09\u3002\u56DE\u7B54\u8981\u77ED\uFF0C\u7EC6\u8282\u7B49\u7528\u6237\u8FFD\u95EE\u518D\u7ED9\u3002\u7B2C\u4E00\u6B21\u56DE\u7B54\u8981\u80FD\u4E00\u773C\u770B\u5B8C\uFF1A\u4E0D\u8D85\u8FC7 8 \u884C\u3001300 \u5B57\uFF08\u672B\u5C3E\u7684\u6761\u76EE\u94FE\u63A5\u4E0D\u7B97\uFF09\uFF0C\u6BCF\u884C\u53EA\u5199\u4E00\u53E5\u8BDD\uFF1B\u653E\u4E0D\u4E0B\u7684\u7559\u7ED9\u8FFD\u95EE\u3002\u5199\u6CD5\uFF1A\u7528\u81EA\u7136\u7684\u51E0\u6BB5\u8BDD\uFF0C\u4E0D\u52A0\u300C\u7ED3\u8BBA\u300D\u300C\u786E\u8BA4\u300D\u300C\u4F9D\u636E\u300D\u8FD9\u7C7B\u5C0F\u6807\u9898\u6216\u6807\u7B7E\uFF0C\u4E0D\u7528\u8868\u683C\u3001\u5D4C\u5957\u5217\u8868\u548C\u7C97\u4F53\uFF1B\u4E00\u53E5\u53EA\u8BF4\u4E00\u4EF6\u4E8B\uFF1B\u7528\u7948\u4F7F\u53E5\uFF1B\u540C\u4E00\u4E2A\u4E1C\u897F\u59CB\u7EC8\u7528\u540C\u4E00\u4E2A\u8BCD\uFF1B\u4E0D\u7528\u300C\u5927\u6982\u300D\u300C\u4E00\u4E9B\u300D\u8FD9\u7C7B\u542B\u7CCA\u7684\u8BCD\uFF1B\u6309\u94AE\u540D\u3001\u83DC\u5355\u540D\u3001\u62A5\u9519\u539F\u6587\u548C\u547D\u4EE4\u7167\u539F\u6837\u5199\uFF1B\u4E0D\u4E3B\u52A8\u8BB2\u539F\u7406\uFF0C\u7528\u6237\u95EE\u4E3A\u4EC0\u4E48\u518D\u8BB2\u3002\u7528\u4E86\u6761\u76EE\u5185\u5BB9\u7684\u56DE\u7B54\uFF08\u80FD\u5B8C\u6574\u56DE\u7B54\u3001\u53EA\u80FD\u90E8\u5206\u56DE\u7B54\uFF09\uFF0C`get_entry` \u8FD4\u56DE\u7684\u6B63\u6587\u7B2C\u4E00\u884C\u5199\u7740\u300C\u6761\u76EE\u9875\u9762\uFF1A\u300D\u5730\u5740\u65F6\uFF0C\u5FC5\u987B\u5728\u672B\u5C3E\u5355\u72EC\u4E00\u884C\u7ED9\u51FA\u7528\u5230\u7684\u6761\u76EE\u9875\u9762\u94FE\u63A5\uFF0C\u7167\u6284\u90A3\u4E2A\u5730\u5740\uFF1B\u6CA1\u6709\u8FD9\u4E00\u884C\u800C\u672C skill \u7684\u300C\u56DE\u9000\u300D\u4E00\u8282\u7ED9\u4E86\u4E3B\u7AD9\u5730\u5740\u65F6\uFF0C\u6309 `<\u4E3B\u7AD9>/<type>/<id>/` \u62FC\u51FA\uFF0C`<type>` \u53D6\u76EE\u5F55\u91CC\u7684\u6761\u76EE\u7C7B\u578B\uFF1B\u4E24\u6837\u90FD\u6CA1\u6709\u5C31\u4E0D\u7ED9\u3002\u7528\u6237\u8FFD\u95EE\u7EC6\u8282\u65F6\uFF0C\u6309\u6761\u76EE\u7ED9\u51FA\u5B8C\u6574\u6B65\u9AA4\uFF0C\u4E0D\u53D7\u4E0B\u9762\u7684\u957F\u5EA6\u9650\u5236\uFF1A",
|
|
1257
1427
|
" - \u80FD\u5B8C\u6574\u56DE\u7B54\uFF1A\u8981\u52A8\u624B\u64CD\u4F5C\u7684\u95EE\u9898\uFF0C\u5148\u7528\u4E00\u53E5\u8BDD\u8BF4\u662F\u4EC0\u4E48\u60C5\u51B5\u3001\u8BE5\u505A\u4EC0\u4E48\uFF0C\u518D\u5217\u6700\u591A 3 \u6B65\uFF0C\u6BCF\u6B65\u4E00\u4E2A\u52A8\u4F5C\uFF0C\u7136\u540E\u7528\u4E00\u53E5\u8BDD\u8BF4\u505A\u5B8C\u540E\u770B\u5230\u4EC0\u4E48\u5C31\u8BF4\u660E\u597D\u4E86\uFF08\u53D6\u81EA\u6761\u76EE\u7684\u300C\u9A8C\u8BC1\u300D\uFF09\uFF1B\u95EE\u300C\u80FD\u4E0D\u80FD\u300D\u300C\u662F\u4E0D\u662F\u300D\u300C\u662F\u4EC0\u4E48\u300D\u7684\u95EE\u9898\uFF0C\u76F4\u63A5\u7528 2\u20133 \u53E5\u8BDD\u56DE\u7B54\uFF0C\u4E0D\u5217\u6B65\u9AA4\uFF0C\u4E5F\u4E0D\u5199\u600E\u4E48\u786E\u8BA4\u3002\u6761\u76EE\u6709\u591A\u4E2A\u5206\u652F\u65F6\uFF0C\u6309\u7528\u6237\u8BF4\u7684\u73B0\u8C61\u6311\u4E00\u652F\uFF1B\u7528\u6237\u6CA1\u8BF4\u73B0\u8C61\uFF0C\u6311\u6700\u5E38\u89C1\u7684\u4E00\u652F\uFF1B\u5176\u4F59\u5206\u652F\u4E0D\u5C55\u5F00\uFF0C\u53EA\u5728\u6700\u540E\u4E00\u884C\u70B9\u540D 2\u20133 \u79CD\u522B\u7684\u60C5\u51B5\u8BA9\u7528\u6237\u9009\uFF08\u4F8B\u5982\u300C\u5982\u679C\u4F60\u770B\u5230\u7684\u662F A \u6216 B\uFF0C\u544A\u8BC9\u6211\u662F\u54EA\u4E2A\u300D\uFF09\uFF1B\u6CA1\u6709\u522B\u7684\u5206\u652F\u65F6\uFF0C\u6700\u540E\u4E00\u53E5\u544A\u8BC9\u7528\u6237\u8FD8\u80FD\u600E\u4E48\u7EE7\u7EED\u3002\u4E0D\u5199\u7528\u6237\u6CA1\u95EE\u7684\u63D0\u9192\u548C\u6CE8\u610F\u4E8B\u9879\uFF1B",
|
|
1258
1428
|
` - \u53EA\u80FD\u90E8\u5206\u56DE\u7B54\uFF1A\u6761\u76EE\u5BF9\u8FD9\u4E2A\u95EE\u9898\u6709\u5B9E\u8D28\u5E2E\u52A9\uFF08\u7B54\u6848\u6216\u6392\u67E5\u6B65\u9AA4\u7684\u4E00\u90E8\u5206\uFF09\u3001\u4F46\u7B54\u4E0D\u5168\u2014\u2014\u56DE\u7B54\u91CC\u7528\u4E86\u6761\u76EE\u91CC\u7684\u6392\u67E5\u6B65\u9AA4\uFF0C\u5C31\u6309\u8FD9\u4E00\u79CD\u4F5C\u7B54\uFF1B\u6761\u76EE\u8986\u76D6\u7684\u90E8\u5206\u6309\u4E0A\u9762\u7684\u77ED\u7B54\u5199\uFF0C\u5E76\u4EE5\u56FA\u5B9A\u524D\u7F00\u300C${PARTIAL_PREFIX}\u300D\u7528\u4E00\u53E5\u8BDD\u5199\u51FA\u7F3A\u7684\u90E8\u5206\uFF08\u524D\u7F00\u5355\u72EC\u8D77\u4E00\u884C\u5199\u5728\u884C\u9996\uFF0C\u4E0D\u5199\u6210\u6807\u9898\uFF09\uFF1B\u7F3A\u7684\u90E8\u5206\u8981\u7528\u81EA\u5DF1\u7684\u77E5\u8BC6\u8865\uFF0C\u5C31\u5728\u524D\u7F00\u540E\u5148\u5199\u56FA\u5B9A\u6807\u6CE8\u300C${NOT_KB_LABEL}\u300D\u518D\u8865\uFF0C\u81EA\u5DF1\u8865\u7684\u53EA\u5199 1\u20132 \u53E5\uFF1B`,
|
|
1259
1429
|
` - \u6CA1\u6709\u6761\u76EE\u80FD\u56DE\u7B54\uFF08\u4E3B\u9898\u76F8\u8FD1\u3001\u5374\u7B54\u4E0D\u4E86\u7528\u6237\u5177\u4F53\u95EE\u9898\u7684\u6761\u76EE\u4E5F\u7B97\u6CA1\u6709\uFF09\uFF1A\u56DE\u7B54\u4EE5\u56FA\u5B9A\u6807\u6CE8\u300C${NOT_KB_LABEL}\u300D\u5F00\u5934\u2014\u2014\u8981\u81EA\u7B54\uFF0C\u5C31\u5728\u6807\u6CE8\u540E\u7528\u81EA\u5DF1\u7684\u77E5\u8BC6\u56DE\u7B54\uFF0C\u53EA\u5199 1\u20132 \u53E5\uFF0C\u5E76\u4E0E\u6761\u76EE\u5185\u5BB9\u5206\u5F00\uFF1B\u4E0D\u81EA\u7B54\uFF0C\u5C31\u5728\u6807\u6CE8\u540E\u8BF4\u660E\u77E5\u8BC6\u5E93\u6CA1\u6709\u8986\u76D6\uFF1B`,
|
|
1260
1430
|
` - \u4E0E\u77E5\u8BC6\u5E93\u76F8\u90BB\u4F46\u523B\u610F\u4E0D\u505A\u2014\u2014${ADJACENT_SCOPE.join("\uFF1B")}\u2014\u2014\u4E0D\u4F5C\u7B54\uFF0C\u4E5F\u4E0D\u7528\u81EA\u5DF1\u7684\u77E5\u8BC6\u8865\uFF0C\u53EA\u56DE\u7B54\u300C${OUT_OF_SCOPE_REPLY}\u300D\u3002\u8FD9\u6761\u4F18\u5148\u4E8E\u524D\u9762\u4E09\u79CD\u56DE\u7B54\uFF1A\u5373\u4F7F\u76EE\u5F55\u91CC\u6709\u770B\u8D77\u6765\u76F8\u5173\u7684\u6761\u76EE\uFF0C\u4E5F\u4E0D\u6309\u6761\u76EE\u4F5C\u7B54\u3001\u4E0D\u90E8\u5206\u4F5C\u7B54\u3001\u4E0D\u81EA\u7B54\uFF1B\u76F8\u90BB\u95EE\u9898\u4E0D\u8D70\u300C\u6CA1\u6709\u6761\u76EE\u80FD\u56DE\u7B54\u300D\u90A3\u6761\u8DEF\uFF0C\u5E26\u4E0A\u300C${NOT_KB_LABEL}\u300D\u6807\u6CE8\u81EA\u7B54\u540C\u6837\u4E0D\u884C\u3002`,
|
|
1261
1431
|
"5. \u4E0D\u56DE\u663E\u8BA2\u9605\u94FE\u63A5\u3001\u5BC6\u94A5\u3001\u8282\u70B9 IP\uFF0C\u622A\u56FE\u91CC\u7684\u4E5F\u4E00\u6837\uFF1B\u53EA\u94FE\u63A5\u6761\u76EE\u91CC\u5217\u51FA\u7684\u5B98\u65B9\u6765\u6E90\u548C\u672C\u77E5\u8BC6\u5E93\u4E3B\u7AD9\u4E0A\u7684\u6761\u76EE\u9875\u9762\uFF1B\u6807\u4E3A\u300C\u4FE1\u606F\u8BF4\u660E\u300D\u7684\u6761\u76EE\u4E0D\u7ED9\u64CD\u4F5C\u6B65\u9AA4\u3002"
|
|
1262
1432
|
].join("\n");
|
|
1263
|
-
var SKILL_DESCRIPTION = `\
|
|
1433
|
+
var SKILL_DESCRIPTION = `\u4EE3\u7406\u5BA2\u6237\u7AEF\u77E5\u8BC6\u5E93\u3002\u7528\u6237\u95EE Clash Verge Rev\u3001Clash Party\u3001FlClash\u3001v2rayN\u3001v2rayNG\u3001Clash Meta for Android\u3001sing-box\u3001Karing\u3001Shadowrocket \u7B49\u4EE3\u7406\u5BA2\u6237\u7AEF\u6216 mihomo\u3001Xray \u5185\u6838\u7684\u95EE\u9898\u65F6\u4F7F\u7528\uFF1A\u8BA2\u9605\u3001\u8282\u70B9\u3001\u534F\u8BAE\u3001\u5206\u6D41\u89C4\u5219\u3001TUN\uFF08\u865A\u62DF\u7F51\u5361\uFF09\u3001\u7CFB\u7EDF\u4EE3\u7406\u662F\u4EC0\u4E48\uFF0C\u5BA2\u6237\u7AEF\u4E0E\u5185\u6838\u7684\u7248\u672C\u3001\u66F4\u65B0\u3001\u4E0B\u8F7D\uFF08\u542B\u9884\u53D1\u5E03\u4E0E autobuild\uFF09\u600E\u4E48\u9009\uFF0C\u60F3\u5B8C\u6210\u5BFC\u5165\u8BA2\u9605\u3001\u5207\u6362\u8282\u70B9\u7B49\u64CD\u4F5C\uFF0C\u6216\u9047\u5230\u8282\u70B9\u4E3A\u7A7A\u3001\u8BA2\u9605\u66F4\u65B0\u5931\u8D25\u3001\u8FDE\u4E0A\u4F46\u6253\u4E0D\u5F00\u7F51\u9875\u7B49\u95EE\u9898\uFF08\u9644\u622A\u56FE\u6C42\u52A9\u4E5F\u7B97\uFF09\uFF1B\u7528\u6237\u95EE${ADJACENT_SCOPE.join("\u3001")}\u65F6\u540C\u6837\u4F7F\u7528\uFF08\u6309\u89C4\u5219\u53EA\u56DE\u4E00\u53E5\u56FA\u5B9A\u8BDD\uFF09\u3002\u53EA\u8BFB\uFF0C\u4E0D\u8BFB\u53D6\u7528\u6237\u673A\u5668\u72B6\u6001\uFF0C\u4E0D\u5199\u5165\u3002`;
|
|
1264
1434
|
|
|
1265
1435
|
// ../runtime-core/src/mcp.ts
|
|
1436
|
+
function siteOriginOf(manifest) {
|
|
1437
|
+
const v = typeof manifest === "object" && manifest !== null ? manifest.site_origin : void 0;
|
|
1438
|
+
if (typeof v !== "string" || !/^https:\/\/[A-Za-z0-9.-]+(:[0-9]+)?$/.test(v)) return void 0;
|
|
1439
|
+
try {
|
|
1440
|
+
if (new URL(v).port === "0") return void 0;
|
|
1441
|
+
} catch {
|
|
1442
|
+
return void 0;
|
|
1443
|
+
}
|
|
1444
|
+
return v;
|
|
1445
|
+
}
|
|
1266
1446
|
var ID = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
1267
1447
|
var MAX_BYTES = 131072;
|
|
1268
1448
|
var MAX_ID_UNITS = 256;
|
|
1269
1449
|
function channelOf(v, cfg) {
|
|
1270
1450
|
return typeof v === "string" && (cfg.channels ?? []).includes(v) ? v : null;
|
|
1271
1451
|
}
|
|
1452
|
+
var LIST_ENTRIES_BUDGET = 38e3;
|
|
1272
1453
|
var RESULT_SIZE_CHARS = 131072;
|
|
1273
|
-
var LIST_ENTRIES_DESCRIPTION = `\u4ECE\u8FD9\u91CC\u5F00\u59CB\uFF1A\u8FD4\u56DE\u77E5\u8BC6\u5E93\u7684\u6761\u76EE\u76EE\u5F55\uFF08\u6BCF\u884C\u4E00\u6761\uFF1Aid | \u7C7B\u578B | \u6807\u9898
|
|
1454
|
+
var LIST_ENTRIES_DESCRIPTION = `\u4ECE\u8FD9\u91CC\u5F00\u59CB\uFF1A\u8FD4\u56DE\u77E5\u8BC6\u5E93\u7684\u6761\u76EE\u76EE\u5F55\uFF08\u6BCF\u884C\u4E00\u6761\uFF1Aid | \u7C7B\u578B | \u6807\u9898\uFF0C\u4E0E query \u76F8\u5173\u7684\u6761\u76EE\u540E\u9762\u8FD8\u5217\u51FA\u522B\u540D\uFF09\uFF0C\u518D\u7528 get_entry \u8BFB\u6B63\u6587\u3002query \u586B\u7528\u6237\u539F\u8BDD\uFF081\u2013200 \u5B57\uFF09\u3002
|
|
1274
1455
|
|
|
1275
1456
|
${USAGE_RULES}`;
|
|
1276
1457
|
var TOOLS = [
|
|
1277
|
-
{ name: "search", description: "\u6309\u7528\u6237\u539F\u8BDD\u68C0\u7D22\u4EE3\u7406\u5BA2\u6237\u7AEF\u77E5\u8BC6\u5E93\u6761\u76EE\u3002\u53EF\u9009 client\u3001os \u7F29\u5C0F\u8303\u56F4\u3002", inputSchema: { type: "object", properties: { query: { type: "string", minLength: 1, maxLength: 200 }, client: { type: "string" }, os: { type: "string" }, channel: { type: "string", maxLength: 32 } }, required: ["query"], additionalProperties: false } },
|
|
1278
1458
|
{ name: "list_entries", description: LIST_ENTRIES_DESCRIPTION, inputSchema: { type: "object", properties: { query: { type: "string", minLength: 1, maxLength: 200 } }, required: ["query"], additionalProperties: false } },
|
|
1279
|
-
{ name: "get_entry", description: "\u6309\u6761\u76EE id \u53D6\u56DE\
|
|
1459
|
+
{ name: "get_entry", description: "\u6309\u6761\u76EE id \u53D6\u56DE\u5185\u5BB9\uFF08Markdown\uFF09\u3002\u957F\u6761\u76EE\u5206\u6BB5\u8FD4\u56DE\uFF1A\u6B63\u6587\u524D\u9762\u7684\u8BF4\u660E\u5199\u660E\u5171\u51E0\u6BB5\u548C\u5404\u6BB5\u6807\u9898\uFF0C\u7528 part \u53C2\u6570\u8BFB\u5176\u4F59\u5404\u6BB5\u3002", inputSchema: { type: "object", properties: { id: { type: "string" }, part: { type: "integer", minimum: 1 } }, required: ["id"], additionalProperties: false } }
|
|
1280
1460
|
];
|
|
1281
1461
|
var MCP_TOOLS = ["list_entries", "get_entry"].map((n) => ({ ...TOOLS.find((t) => t.name === n), name: n, _meta: { "anthropic/maxResultSizeChars": RESULT_SIZE_CHARS } }));
|
|
1282
|
-
function
|
|
1462
|
+
function aliasOrder(ids, index, query, t2s) {
|
|
1463
|
+
const score = /* @__PURE__ */ new Map();
|
|
1464
|
+
if (index.docs.length > 0) {
|
|
1465
|
+
const qt = [...new Set(tokens(query, index.config, t2s))];
|
|
1466
|
+
const n = index.docs.length;
|
|
1467
|
+
const df = /* @__PURE__ */ new Map();
|
|
1468
|
+
for (const d of index.docs) for (const t of new Set(Object.values(d.fields).flat())) df.set(t, (df.get(t) ?? 0) + 1);
|
|
1469
|
+
for (const d of index.docs) {
|
|
1470
|
+
let s = 0;
|
|
1471
|
+
for (const [f, toks] of Object.entries(d.fields)) {
|
|
1472
|
+
const w = index.config.weights[f] ?? 0;
|
|
1473
|
+
if (!w) continue;
|
|
1474
|
+
const set = new Set(toks);
|
|
1475
|
+
for (const t of qt) if (set.has(t)) s += w * Math.log(1 + n / (df.get(t) ?? 1));
|
|
1476
|
+
}
|
|
1477
|
+
score.set(d.id, s);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return [...ids].sort((a, b) => (score.get(b) ?? 0) - (score.get(a) ?? 0) || (a < b ? -1 : a > b ? 1 : 0));
|
|
1481
|
+
}
|
|
1482
|
+
function catalogOf(projection, index, query, t2s) {
|
|
1283
1483
|
const ids = Object.keys(projection.entries).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1284
|
-
|
|
1484
|
+
const entries = ids.map((id) => {
|
|
1285
1485
|
const e = projection.entries[id];
|
|
1286
|
-
return { id: e.id, type: e.type, title: e.title
|
|
1287
|
-
})
|
|
1486
|
+
return { id: e.id, type: e.type, title: e.title };
|
|
1487
|
+
});
|
|
1488
|
+
const byId = new Map(entries.map((e) => [e.id, e]));
|
|
1489
|
+
const base = { content_version: projection.content_version, entries };
|
|
1490
|
+
let size = bytes(catalogText(base)) + bytes(JSON.stringify(base));
|
|
1491
|
+
for (const id of aliasOrder(ids, index, query, t2s)) {
|
|
1492
|
+
const e = byId.get(id);
|
|
1493
|
+
const aliases = projection.entries[id]?.aliases ?? [];
|
|
1494
|
+
if (!e || aliases.length === 0) continue;
|
|
1495
|
+
const extra = aliases.reduce((a, x) => a + bytes(" | " + field(x)), 0) + bytes(`,"aliases":${JSON.stringify(aliases)}`);
|
|
1496
|
+
if (size + extra > LIST_ENTRIES_BUDGET) break;
|
|
1497
|
+
e.aliases = [...aliases];
|
|
1498
|
+
size += extra;
|
|
1499
|
+
}
|
|
1500
|
+
return { content_version: projection.content_version, entries };
|
|
1288
1501
|
}
|
|
1289
1502
|
var field = (s) => s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|\r|\n/g, " ");
|
|
1290
1503
|
function catalogText(c) {
|
|
1291
|
-
return [`content_version: ${c.content_version}`, ...c.entries.map((e) => [e.id, e.type, e.title, ...e.aliases].map(field).join(" | "))].join("\n");
|
|
1504
|
+
return [`content_version: ${c.content_version}`, ...c.entries.map((e) => [e.id, e.type, e.title, ...e.aliases ?? []].map(field).join(" | "))].join("\n");
|
|
1292
1505
|
}
|
|
1293
1506
|
var bytes = (s) => new TextEncoder().encode(s).length;
|
|
1507
|
+
var PART_BYTES = 17e3;
|
|
1508
|
+
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
|
|
1509
|
+
var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*\r?\n?$/;
|
|
1510
|
+
function linesWithFence(s, start = null) {
|
|
1511
|
+
const lines = [];
|
|
1512
|
+
let fence = start;
|
|
1513
|
+
for (const line of s.match(/[^\n]*\n|[^\n]+$/g) ?? []) {
|
|
1514
|
+
lines.push({ line, inFence: fence !== null });
|
|
1515
|
+
if (fence === null) {
|
|
1516
|
+
const m = FENCE_OPEN.exec(line);
|
|
1517
|
+
if (m) fence = { ch: m[1][0], len: m[1].length };
|
|
1518
|
+
} else {
|
|
1519
|
+
const m = FENCE_CLOSE.exec(line);
|
|
1520
|
+
if (m && m[1][0] === fence.ch && m[1].length >= fence.len) fence = null;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return { lines, end: fence };
|
|
1524
|
+
}
|
|
1525
|
+
function groupLines(s, isStart) {
|
|
1526
|
+
const ls = linesWithFence(s).lines;
|
|
1527
|
+
const out = [];
|
|
1528
|
+
ls.forEach((l, i) => {
|
|
1529
|
+
if (i === 0 || isStart(ls, i)) out.push(l.line);
|
|
1530
|
+
else out[out.length - 1] += l.line;
|
|
1531
|
+
});
|
|
1532
|
+
return out;
|
|
1533
|
+
}
|
|
1534
|
+
var cost = (s) => bytes(JSON.stringify(s)) - 2;
|
|
1535
|
+
function codePointChunks(s, limit) {
|
|
1536
|
+
const out = [];
|
|
1537
|
+
let cur = "";
|
|
1538
|
+
let n = 0;
|
|
1539
|
+
for (const cp of s) {
|
|
1540
|
+
const b = cost(cp);
|
|
1541
|
+
if (n + b > limit && cur) {
|
|
1542
|
+
out.push(cur);
|
|
1543
|
+
cur = "";
|
|
1544
|
+
n = 0;
|
|
1545
|
+
}
|
|
1546
|
+
cur += cp;
|
|
1547
|
+
n += b;
|
|
1548
|
+
}
|
|
1549
|
+
if (cur) out.push(cur);
|
|
1550
|
+
return out;
|
|
1551
|
+
}
|
|
1552
|
+
function pack(units, limit, split) {
|
|
1553
|
+
const out = [];
|
|
1554
|
+
let cur = "";
|
|
1555
|
+
let n = 0;
|
|
1556
|
+
const add = (u) => {
|
|
1557
|
+
const b = cost(u);
|
|
1558
|
+
if (n + b <= limit) {
|
|
1559
|
+
cur += u;
|
|
1560
|
+
n += b;
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (cur) out.push(cur);
|
|
1564
|
+
cur = u;
|
|
1565
|
+
n = b;
|
|
1566
|
+
};
|
|
1567
|
+
for (const u of units) {
|
|
1568
|
+
if (cost(u) <= limit) add(u);
|
|
1569
|
+
else for (const piece of split(u)) add(piece);
|
|
1570
|
+
}
|
|
1571
|
+
if (cur || out.length === 0) out.push(cur);
|
|
1572
|
+
return out;
|
|
1573
|
+
}
|
|
1574
|
+
function entryParts(markdown, limit) {
|
|
1575
|
+
const byLine = (u) => pack(groupLines(u, () => true), limit, (x) => codePointChunks(x, limit));
|
|
1576
|
+
const byParagraph = (u) => pack(groupLines(u, (ls, i) => !ls[i - 1].inFence && ls[i - 1].line.trim() === ""), limit, byLine);
|
|
1577
|
+
return pack(groupLines(markdown, (ls, i) => !ls[i].inFence && ls[i].line.startsWith("## ")), limit, byParagraph);
|
|
1578
|
+
}
|
|
1579
|
+
function partHeadings(segs) {
|
|
1580
|
+
const clip = (line) => {
|
|
1581
|
+
const cps = [...line];
|
|
1582
|
+
return cps.length > 40 ? cps.slice(0, 40).join("") + "\u2026" : line;
|
|
1583
|
+
};
|
|
1584
|
+
const { lines } = linesWithFence(segs.join(""));
|
|
1585
|
+
const starts = [];
|
|
1586
|
+
let off = 0;
|
|
1587
|
+
for (const l of lines) {
|
|
1588
|
+
starts.push(off);
|
|
1589
|
+
off += l.line.length;
|
|
1590
|
+
}
|
|
1591
|
+
let lastH2 = "";
|
|
1592
|
+
let segStart = 0;
|
|
1593
|
+
let i = 0;
|
|
1594
|
+
return segs.map((seg) => {
|
|
1595
|
+
const segEnd = segStart + seg.length;
|
|
1596
|
+
while (i < lines.length && starts[i] + lines[i].line.length <= segStart) i++;
|
|
1597
|
+
let heading;
|
|
1598
|
+
if (i < lines.length && starts[i] === segStart) {
|
|
1599
|
+
let k = i;
|
|
1600
|
+
while (k < lines.length && starts[k] < segEnd && lines[k].line.trim() === "") k++;
|
|
1601
|
+
if (k < lines.length && starts[k] < segEnd && !lines[k].inFence && /^#{1,2} /.test(lines[k].line)) heading = lines[k].line.trim();
|
|
1602
|
+
}
|
|
1603
|
+
const label = heading ?? (lastH2 ? `${lastH2}\uFF08\u7EED\uFF09` : seg.split("\n").find((l) => l.trim() !== "")?.trim() ?? "");
|
|
1604
|
+
for (let k = i; k < lines.length && starts[k] < segEnd; k++) if (starts[k] >= segStart && !lines[k].inFence && lines[k].line.startsWith("## ")) lastH2 = lines[k].line.trim();
|
|
1605
|
+
segStart = segEnd;
|
|
1606
|
+
return clip(label);
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1294
1609
|
function vocabulary(index) {
|
|
1295
1610
|
const v = /* @__PURE__ */ new Set();
|
|
1296
1611
|
for (const d of index.docs) for (const toks of Object.values(d.fields)) for (const t of toks) v.add(t);
|
|
@@ -1328,47 +1643,38 @@ async function handleToolCall(name, args, deps, measure = (r) => bytes(JSON.stri
|
|
|
1328
1643
|
if (!tool) return { ok: false, code: -32602, message: "unknown tool" };
|
|
1329
1644
|
const props = tool.inputSchema.properties;
|
|
1330
1645
|
for (const k of Object.keys(a)) if (!(k in props)) return { ok: false, code: -32602, message: "unexpected argument" };
|
|
1646
|
+
const typeOk = (v, p) => p.type === "integer" ? Number.isInteger(v) && (p.minimum === void 0 || v >= p.minimum) : typeof v === p.type;
|
|
1331
1647
|
for (const [k, v] of Object.entries(a)) {
|
|
1332
1648
|
const p = props[k];
|
|
1333
|
-
if (v !== void 0 && (
|
|
1649
|
+
if (v !== void 0 && (!typeOk(v, p) || p.maxLength !== void 0 && v.length > p.maxLength)) return { ok: false, code: -32602, message: `argument ${k} must be ${p.type}${p.maxLength ? ` \u2264${p.maxLength}` : ""}${p.minimum !== void 0 ? ` \u2265${p.minimum}` : ""}` };
|
|
1334
1650
|
}
|
|
1335
1651
|
for (const k of tool.inputSchema.required) if (a[k] === void 0) return { ok: false, code: -32602, message: `missing argument ${k}` };
|
|
1336
|
-
if (name === "search") {
|
|
1337
|
-
const query = typeof a.query === "string" ? a.query : "";
|
|
1338
|
-
if (query.length < 1 || query.length > 200) return { ok: false, code: -32602, message: "query must be 1..200 chars" };
|
|
1339
|
-
const ctx = { client: typeof a.client === "string" ? a.client : void 0, os: typeof a.os === "string" ? a.os : void 0 };
|
|
1340
|
-
const cleanQuery = queryText(query, deps.redact);
|
|
1341
|
-
const r = search(deps.index, cleanQuery, ctx, deps.t2s);
|
|
1342
|
-
const results = r.results.map((x) => ({ id: x.id, type: x.type, title: deps.projection.entries[x.id]?.title ?? x.id, score: Number(x.score.toFixed(4)) }));
|
|
1343
|
-
const top = results[0] ? deps.projection.entries[results[0].id] : void 0;
|
|
1344
|
-
await logQuery(deps, cleanQuery, { tool: "search", intent: top?.intents[0] ?? null, top_id: results[0]?.id ?? null, top_type: results[0]?.type ?? null, abstained: r.abstained ? 1 : 0, channel: channelOf(a.channel, deps.index.config) });
|
|
1345
|
-
return { ok: true, result: { results, abstained: r.abstained, content_version: deps.index.content_version, sponsored: null } };
|
|
1346
|
-
}
|
|
1347
1652
|
if (name === "list_entries") {
|
|
1348
1653
|
const query = typeof a.query === "string" ? a.query : "";
|
|
1349
1654
|
if (query.length < 1 || query.length > 200) return { ok: false, code: -32602, message: "query must be 1..200 chars" };
|
|
1350
1655
|
await logQuery(deps, queryText(query, deps.redact), { tool: "list_entries", intent: null, top_id: null, top_type: null, abstained: 0, channel: channelOf(deps.channel, deps.index.config) });
|
|
1351
|
-
return { ok: true, result: catalogOf(deps.projection) };
|
|
1656
|
+
return { ok: true, result: catalogOf(deps.projection, deps.index, query, deps.t2s) };
|
|
1352
1657
|
}
|
|
1353
1658
|
if (name === "get_entry") {
|
|
1354
1659
|
const id = typeof a.id === "string" ? a.id : "";
|
|
1355
1660
|
if (!ID.test(id)) return { ok: false, code: -32602, message: "bad id" };
|
|
1356
1661
|
const e = Object.hasOwn(deps.projection.entries, id) ? deps.projection.entries[id] : void 0;
|
|
1357
1662
|
if (!e) return { ok: false, code: -32004, message: "entry not found" };
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1663
|
+
const segs = entryParts(e.markdown, PART_BYTES);
|
|
1664
|
+
const part = typeof a.part === "number" ? a.part : 1;
|
|
1665
|
+
if (part > segs.length) return { ok: false, code: -32602, message: "part out of range" };
|
|
1666
|
+
const headings = partHeadings(segs);
|
|
1667
|
+
const others = headings.map((h, i) => `${i + 1}\u300C${h}\u300D`).filter((_, i) => i + 1 !== part).join("\u3001");
|
|
1668
|
+
const url = deps.siteOrigin ? `${deps.siteOrigin}/${e.type}/${e.id}/` : void 0;
|
|
1669
|
+
const freshness = freshnessAt(e, deps.now?.() ?? /* @__PURE__ */ new Date());
|
|
1670
|
+
const staleNote = freshness === "stale" ? `\uFF08\u8FD9\u6761\u5185\u5BB9\u6700\u540E\u9A8C\u8BC1\u4E8E ${e.last_verified_at}\uFF0C\u5DF2\u8D85\u8FC7\u6709\u6548\u671F\uFF0C\u53EF\u80FD\u5DF2\u8FC7\u671F\u3002\uFF09` : "";
|
|
1671
|
+
const lead = [url ? `\u6761\u76EE\u9875\u9762\uFF1A${url}` : "", staleNote, segs.length === 1 ? "" : `\uFF08\u672C\u6761\u76EE\u5206 ${segs.length} \u6BB5\uFF0C\u8FD9\u662F\u7B2C ${part} \u6BB5\u3002\u5176\u4F59\u5404\u6BB5\uFF1A${others}\uFF1B\u7528 get_entry \u7684 part \u53C2\u6570\u8BFB\u3002\uFF09`].filter(Boolean).join("\n");
|
|
1672
|
+
const md0 = lead ? `${lead}
|
|
1673
|
+
|
|
1674
|
+
${segs[part - 1]}` : segs[part - 1];
|
|
1675
|
+
const make = (md, truncated) => ({ id: e.id, type: e.type, title: e.title, ...url ? { url } : {}, markdown: md, truncated, part, parts: segs.length, part_headings: headings, last_verified_at: e.last_verified_at, freshness, content_version: deps.projection.content_version, sponsored: null });
|
|
1676
|
+
const result = make(md0, false);
|
|
1677
|
+
if (measure(result) > MAX_BYTES) return { ok: false, code: -32603, message: "entry too large" };
|
|
1372
1678
|
return { ok: true, result };
|
|
1373
1679
|
}
|
|
1374
1680
|
return { ok: false, code: -32602, message: "unknown tool" };
|
|
@@ -1391,7 +1697,7 @@ function metaVersion(m) {
|
|
|
1391
1697
|
return m.params._meta[PV];
|
|
1392
1698
|
}
|
|
1393
1699
|
function acceptedId(rawId) {
|
|
1394
|
-
if (rawId !== void 0 && rawId !== null && typeof rawId !== "number" && (typeof rawId !== "string" || rawId.length > MAX_ID_UNITS || credentialRules(rawId).
|
|
1700
|
+
if (rawId !== void 0 && rawId !== null && typeof rawId !== "number" && (typeof rawId !== "string" || rawId.length > MAX_ID_UNITS || credentialRules(rawId).some((r) => r !== "uuid"))) return false;
|
|
1395
1701
|
return rawId;
|
|
1396
1702
|
}
|
|
1397
1703
|
function modernMetaError(meta) {
|
|
@@ -1486,12 +1792,14 @@ async function loadDepsFromDir(dir, extra) {
|
|
|
1486
1792
|
if (!want || want.bytes !== buf.length || want.sha256 !== createHash("sha256").update(buf).digest("hex")) throw new Error(`${f} does not match release-manifest.json in ${dir}`);
|
|
1487
1793
|
return JSON.parse(buf.toString("utf8"));
|
|
1488
1794
|
}));
|
|
1489
|
-
|
|
1795
|
+
const siteOrigin = siteOriginOf(manifest);
|
|
1796
|
+
return { index, projection, t2s: makeTableT2s(table), salt: extra.salt, sha256: extra.sha256, redact: redactSensitive, ...siteOrigin ? { siteOrigin } : {} };
|
|
1490
1797
|
}
|
|
1491
1798
|
var TOO_LARGE = Symbol("request too large");
|
|
1799
|
+
var BAD_UTF8 = Symbol("invalid utf-8");
|
|
1492
1800
|
async function* boundedLines(input, maxBytes = MAX_JSON_BODY_BYTES) {
|
|
1493
1801
|
const enc = new TextEncoder();
|
|
1494
|
-
const dec = new TextDecoder();
|
|
1802
|
+
const dec = new TextDecoder("utf-8", { fatal: true });
|
|
1495
1803
|
let parts = [];
|
|
1496
1804
|
let size = 0;
|
|
1497
1805
|
let skipping = false;
|
|
@@ -1504,7 +1812,12 @@ async function* boundedLines(input, maxBytes = MAX_JSON_BODY_BYTES) {
|
|
|
1504
1812
|
}
|
|
1505
1813
|
parts = [];
|
|
1506
1814
|
size = 0;
|
|
1507
|
-
|
|
1815
|
+
let line;
|
|
1816
|
+
try {
|
|
1817
|
+
line = dec.decode(buf);
|
|
1818
|
+
} catch {
|
|
1819
|
+
return BAD_UTF8;
|
|
1820
|
+
}
|
|
1508
1821
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
1509
1822
|
};
|
|
1510
1823
|
for await (const raw of input) {
|
|
@@ -1541,13 +1854,18 @@ async function createStdioServer(deps, io) {
|
|
|
1541
1854
|
await io.write(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32600, message: "request too large" } }));
|
|
1542
1855
|
continue;
|
|
1543
1856
|
}
|
|
1857
|
+
const parseError = JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
|
|
1858
|
+
if (line === BAD_UTF8) {
|
|
1859
|
+
await io.write(parseError);
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1544
1862
|
const trimmed = line.trim();
|
|
1545
1863
|
if (!trimmed) continue;
|
|
1546
1864
|
let msg;
|
|
1547
1865
|
try {
|
|
1548
1866
|
msg = JSON.parse(trimmed);
|
|
1549
1867
|
} catch {
|
|
1550
|
-
await io.write(
|
|
1868
|
+
await io.write(parseError);
|
|
1551
1869
|
continue;
|
|
1552
1870
|
}
|
|
1553
1871
|
const r = await handleJsonRpc(msg, deps);
|
|
@@ -1555,48 +1873,23 @@ async function createStdioServer(deps, io) {
|
|
|
1555
1873
|
}
|
|
1556
1874
|
}
|
|
1557
1875
|
|
|
1558
|
-
// src/static.ts
|
|
1559
|
-
import http from "node:http";
|
|
1560
|
-
import fs2 from "node:fs/promises";
|
|
1561
|
-
import path2 from "node:path";
|
|
1562
|
-
var TYPES = { ".html": "text/html; charset=utf-8", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".svg": "image/svg+xml", ".png": "image/png", ".webp": "image/webp", ".mp4": "video/mp4", ".webm": "video/webm", ".txt": "text/plain; charset=utf-8" };
|
|
1563
|
-
function createStaticServer(root) {
|
|
1564
|
-
return http.createServer(async (req, res) => {
|
|
1565
|
-
let p;
|
|
1566
|
-
try {
|
|
1567
|
-
p = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
1568
|
-
} catch {
|
|
1569
|
-
res.writeHead(400);
|
|
1570
|
-
res.end("bad request");
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
if (p.endsWith("/")) p += "index.html";
|
|
1574
|
-
const candidate = path2.resolve(root, `.${p}`);
|
|
1575
|
-
if (candidate !== root && !candidate.startsWith(root + path2.sep)) {
|
|
1576
|
-
res.writeHead(403);
|
|
1577
|
-
res.end();
|
|
1578
|
-
return;
|
|
1579
|
-
}
|
|
1580
|
-
const file = await fs2.realpath(candidate).catch(() => null);
|
|
1581
|
-
if (!file || file !== root && !file.startsWith(root + path2.sep)) {
|
|
1582
|
-
res.writeHead(404);
|
|
1583
|
-
res.end("not found");
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
try {
|
|
1587
|
-
const data = await fs2.readFile(file);
|
|
1588
|
-
res.writeHead(200, { "content-type": TYPES[path2.extname(file)] ?? "application/octet-stream" });
|
|
1589
|
-
res.end(data);
|
|
1590
|
-
} catch {
|
|
1591
|
-
res.writeHead(404);
|
|
1592
|
-
res.end("not found");
|
|
1593
|
-
}
|
|
1594
|
-
});
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
1876
|
// src/exit.ts
|
|
1598
1877
|
var FALLBACK = "\u52A0\u8F7D\u6216\u8FD0\u884C\u5931\u8D25\uFF08\u8BE6\u60C5\u4E0D\u56DE\u663E\uFF09";
|
|
1599
1878
|
var FLUSH_TIMEOUT_MS = 2e3;
|
|
1879
|
+
async function flushStreams(streams, timer, clear) {
|
|
1880
|
+
for (const st of streams) {
|
|
1881
|
+
if (st.writableEnded || st.destroyed) continue;
|
|
1882
|
+
await new Promise((resolve) => {
|
|
1883
|
+
const done = timer(resolve, FLUSH_TIMEOUT_MS);
|
|
1884
|
+
const finish = () => {
|
|
1885
|
+
clear(done);
|
|
1886
|
+
resolve();
|
|
1887
|
+
};
|
|
1888
|
+
st.write("", finish);
|
|
1889
|
+
st.once("error", finish);
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1600
1893
|
function makeFail(raw) {
|
|
1601
1894
|
const deps = {
|
|
1602
1895
|
...raw,
|
|
@@ -1611,18 +1904,7 @@ function makeFail(raw) {
|
|
|
1611
1904
|
} catch {
|
|
1612
1905
|
}
|
|
1613
1906
|
deps.error(msg);
|
|
1614
|
-
|
|
1615
|
-
if (st.writableEnded || st.destroyed) continue;
|
|
1616
|
-
await new Promise((resolve) => {
|
|
1617
|
-
const done = deps.timer(resolve, FLUSH_TIMEOUT_MS);
|
|
1618
|
-
const finish = () => {
|
|
1619
|
-
deps.clear(done);
|
|
1620
|
-
resolve();
|
|
1621
|
-
};
|
|
1622
|
-
st.write("", finish);
|
|
1623
|
-
st.once("error", finish);
|
|
1624
|
-
});
|
|
1625
|
-
}
|
|
1907
|
+
await flushStreams(deps.streams, deps.timer, deps.clear);
|
|
1626
1908
|
};
|
|
1627
1909
|
return (e) => {
|
|
1628
1910
|
if (exiting) return exiting;
|
|
@@ -1646,8 +1928,8 @@ var fail = makeFail({ error: (m) => console.error(m), exit: (c) => process.exit(
|
|
|
1646
1928
|
installProcessHandlers(process, fail);
|
|
1647
1929
|
async function run() {
|
|
1648
1930
|
if (cmd === "mcp") {
|
|
1649
|
-
const defaultAssets =
|
|
1650
|
-
const deps = await loadDepsFromDir(
|
|
1931
|
+
const defaultAssets = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "..", "snapshot");
|
|
1932
|
+
const deps = await loadDepsFromDir(path2.resolve(opt("assets", defaultAssets)), { salt: "local", sha256: async (s) => createHash2("sha256").update(s).digest("hex") });
|
|
1651
1933
|
const it = boundedLines(process.stdin)[Symbol.asyncIterator]();
|
|
1652
1934
|
await createStdioServer(deps, { readLine: async () => {
|
|
1653
1935
|
const n = await it.next();
|
|
@@ -1667,12 +1949,8 @@ async function run() {
|
|
|
1667
1949
|
process.stdout.once("error", onError);
|
|
1668
1950
|
}
|
|
1669
1951
|
}) });
|
|
1670
|
-
} else if (cmd === "open") {
|
|
1671
|
-
const root = await fs3.realpath(path3.resolve(opt("dir", "./site/dist")));
|
|
1672
|
-
const port = Number(opt("port", "4321"));
|
|
1673
|
-
createStaticServer(root).on("error", fail).listen(port, "127.0.0.1", () => console.log(`http://127.0.0.1:${port}/`));
|
|
1674
1952
|
} else {
|
|
1675
|
-
console.log("usage: hub-kb mcp [--assets <dir>, default: bundled snapshot/]
|
|
1953
|
+
console.log("usage: hub-kb mcp [--assets <dir>, default: bundled snapshot/]");
|
|
1676
1954
|
process.exit(1);
|
|
1677
1955
|
}
|
|
1678
1956
|
}
|