@ladderkitlab/hub-kb 0.1.0 → 0.2.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 +345 -234
- package/package.json +1 -1
- package/snapshot/index.json +1 -1
- package/snapshot/projection.json +1 -1
- package/snapshot/release-manifest.json +8 -7
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
|
}
|
|
@@ -477,6 +360,16 @@ function isContent(s, raw) {
|
|
|
477
360
|
}
|
|
478
361
|
return decoded.trim() !== "";
|
|
479
362
|
}
|
|
363
|
+
function hasRawValueContent(s, start, end) {
|
|
364
|
+
let raw = "";
|
|
365
|
+
for (let k = start; k < end; ) {
|
|
366
|
+
s.budget.tick();
|
|
367
|
+
const a = placeholderAt(s.text, k);
|
|
368
|
+
if (a > 0 && k + a <= end) k += a;
|
|
369
|
+
else raw += s.text[k++];
|
|
370
|
+
}
|
|
371
|
+
return s.budget.exhausted || isContent(s, raw);
|
|
372
|
+
}
|
|
480
373
|
function interpretUrlValue(s, start) {
|
|
481
374
|
const cached = s.values.get(start);
|
|
482
375
|
if (cached) {
|
|
@@ -562,9 +455,11 @@ var PROSE_CREDENTIAL_NAMES = ["token", "secret", "password", "pwd", "session_id"
|
|
|
562
455
|
var PROSE_KEY_NAME = "key";
|
|
563
456
|
var PROSE_KEY_MIN_LENGTH = 8;
|
|
564
457
|
var AUTH_SCHEME_NAME = "bearer";
|
|
458
|
+
var AUTH_HEADER_NAME = "authorization";
|
|
565
459
|
var PROXY_URI_SCHEMES = ["ss", "ssr", "vmess", "vless", "trojan", "hysteria", "hysteria2", "hy2", "tuic", "anytls"];
|
|
460
|
+
var SOCKS_URI_SCHEMES = ["socks", "socks4", "socks4a", "socks5", "socks5h"];
|
|
566
461
|
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);
|
|
462
|
+
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
463
|
var PROSE_NAME_FIRST = new Set(PROSE_NAMES_LONGEST_FIRST.map((n) => n[0]));
|
|
569
464
|
var isAsciiName = (name) => name.charCodeAt(0) < 128;
|
|
570
465
|
var SEPARATORS = "=:\uFF1A";
|
|
@@ -572,6 +467,18 @@ var isAlnumCode = (x) => x >= 48 && x <= 57 || x >= 65 && x <= 90 || x >= 97 &&
|
|
|
572
467
|
var isAlnumAt = (t, i) => i >= 0 && i < t.length && isAlnumCode(t.charCodeAt(i));
|
|
573
468
|
var isConnector = (c) => c === "_" || c === "-" || c === ".";
|
|
574
469
|
var isTokenCharAt = (t, i) => i < t.length && (isAlnumCode(t.charCodeAt(i)) || t[i] === "_" || t[i] === "-");
|
|
470
|
+
var isB64TokenCharAt = (t, i) => isTokenCharAt(t, i) || i < t.length && ".~+/".includes(t[i]);
|
|
471
|
+
var isUpperCode = (x) => x >= 65 && x <= 90;
|
|
472
|
+
var isLowerOrDigitCode = (x) => x >= 97 && x <= 122 || x >= 48 && x <= 57;
|
|
473
|
+
var camelBoundaryAt = (t, i) => i > 0 && isUpperCode(t.charCodeAt(i)) && isLowerOrDigitCode(t.charCodeAt(i - 1));
|
|
474
|
+
var B64_LONG_MIN = 40;
|
|
475
|
+
function b64LongEnd(s, k) {
|
|
476
|
+
const t = s.text;
|
|
477
|
+
let j = k;
|
|
478
|
+
while (j < s.n && (isAlnumAt(t, j) || t[j] === "+" || t[j] === "/")) j++;
|
|
479
|
+
s.budget.tick(j - k + 1);
|
|
480
|
+
return j - k >= B64_LONG_MIN ? j : -1;
|
|
481
|
+
}
|
|
575
482
|
function compoundNameStart(text, nameStart, s) {
|
|
576
483
|
let start = nameStart;
|
|
577
484
|
for (; ; ) {
|
|
@@ -613,7 +520,8 @@ function keyThresholdEnd(s, from) {
|
|
|
613
520
|
let c = 0;
|
|
614
521
|
while (c < PROSE_KEY_MIN_LENGTH && isTokenCharAt(t, k + c)) c++;
|
|
615
522
|
s.budget.tick(k - from + c + 1);
|
|
616
|
-
|
|
523
|
+
if (c >= PROSE_KEY_MIN_LENGTH) return k + PROSE_KEY_MIN_LENGTH;
|
|
524
|
+
return b64LongEnd(s, k) < 0 ? -1 : k + PROSE_KEY_MIN_LENGTH;
|
|
617
525
|
}
|
|
618
526
|
function proseNameAt(text, i) {
|
|
619
527
|
if (!PROSE_NAME_FIRST.has(text[i].toLowerCase())) return null;
|
|
@@ -652,7 +560,8 @@ function proseNamedHits(s) {
|
|
|
652
560
|
break;
|
|
653
561
|
}
|
|
654
562
|
const name = proseNameAt(t, i);
|
|
655
|
-
|
|
563
|
+
const camel = name !== null && isAsciiName(name) && isAlnumAt(t, i - 1) && camelBoundaryAt(t, i);
|
|
564
|
+
if (!name || isAsciiName(name) && isAlnumAt(t, i - 1) && !camel) {
|
|
656
565
|
i++;
|
|
657
566
|
continue;
|
|
658
567
|
}
|
|
@@ -661,7 +570,7 @@ function proseNamedHits(s) {
|
|
|
661
570
|
let k = nameEnd;
|
|
662
571
|
while (k < n && isInlineWs(t[k])) k++;
|
|
663
572
|
let c = 0;
|
|
664
|
-
while (c < PROSE_KEY_MIN_LENGTH &&
|
|
573
|
+
while (c < PROSE_KEY_MIN_LENGTH && isB64TokenCharAt(t, k + c)) c++;
|
|
665
574
|
s.budget.tick(k - nameEnd + c + 1);
|
|
666
575
|
if (k === nameEnd || c < PROSE_KEY_MIN_LENGTH) {
|
|
667
576
|
i++;
|
|
@@ -700,9 +609,43 @@ function proseNamedHits(s) {
|
|
|
700
609
|
i++;
|
|
701
610
|
continue;
|
|
702
611
|
}
|
|
703
|
-
|
|
612
|
+
let first = k;
|
|
613
|
+
if (name === AUTH_HEADER_NAME) {
|
|
614
|
+
let w = k;
|
|
615
|
+
while (w < n && /[A-Za-z]/.test(t[w])) w++;
|
|
616
|
+
let v = w;
|
|
617
|
+
while (v < n && isInlineWs(t[v])) v++;
|
|
618
|
+
s.budget.tick(v - k + 1);
|
|
619
|
+
if (w > k && v > w) {
|
|
620
|
+
const v0 = v;
|
|
621
|
+
for (; ; ) {
|
|
622
|
+
const a = placeholderAt(t, v);
|
|
623
|
+
if (a > 0) {
|
|
624
|
+
v += a;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (v < n && isInlineWs(t[v])) {
|
|
628
|
+
v++;
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
s.budget.tick(v - v0 + 1);
|
|
634
|
+
if (v >= n || !isPrintableAscii(t.charCodeAt(v)) || isWs(t[v])) {
|
|
635
|
+
i++;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
first = v;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
headEnd = first + 1;
|
|
704
642
|
}
|
|
705
|
-
|
|
643
|
+
let runStart = i;
|
|
644
|
+
if (camel) {
|
|
645
|
+
while (runStart > 0 && isAlnumAt(t, runStart - 1)) runStart--;
|
|
646
|
+
s.budget.tick(i - runStart + 1);
|
|
647
|
+
}
|
|
648
|
+
const start = compoundNameStart(t, runStart, s);
|
|
706
649
|
let urlEnd = -1;
|
|
707
650
|
if (t[j] === "=" && j === nameEnd && inUrlParamName(s, start)) {
|
|
708
651
|
const v = interpretUrlValue(s, sepEnd);
|
|
@@ -748,6 +691,44 @@ function urlCredentialSpans(s) {
|
|
|
748
691
|
break;
|
|
749
692
|
}
|
|
750
693
|
const c = t[i];
|
|
694
|
+
if (c === "%" && i + 2 < n) {
|
|
695
|
+
const head = t.slice(i, i + 3).toUpperCase();
|
|
696
|
+
if (head === "%3F" || head === "%26" || head === "%23") {
|
|
697
|
+
let j = i + 3;
|
|
698
|
+
while (j < n) {
|
|
699
|
+
if (PARAM_NAME_CHAR(t, j)) {
|
|
700
|
+
j++;
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (t[j] === "%" && isHex(t[j + 1]) && isHex(t[j + 2]) && !["%3D", "%26", "%3F", "%23"].includes(t.slice(j, j + 3).toUpperCase())) {
|
|
704
|
+
j += 3;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
s.budget.tick(j - i + 1);
|
|
710
|
+
if (j > i + 3 && t.slice(j, j + 3).toUpperCase() === "%3D") {
|
|
711
|
+
const cls2 = urlNameClass(t.slice(i + 3, j).replace(/%25/gi, "%"));
|
|
712
|
+
if (!cls2) {
|
|
713
|
+
i = j;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const vStart = j + 3;
|
|
717
|
+
let k = vStart;
|
|
718
|
+
while (k < n && t[k] !== "&" && t[k] !== "#" && !isWs(t[k]) && !(t[k] === "%" && t.slice(k, k + 3).toUpperCase() === "%26")) k++;
|
|
719
|
+
s.budget.tick(k - vStart + 1);
|
|
720
|
+
if (k > vStart && hasRawValueContent(s, vStart, k)) {
|
|
721
|
+
out.push({ index: cls2 === "url" ? vStart : i + 3, end: k });
|
|
722
|
+
i = k;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
i = k > vStart ? k : j;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
i++;
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
751
732
|
if (c !== "?" && c !== "&" && c !== "#" && c !== ";") {
|
|
752
733
|
i++;
|
|
753
734
|
continue;
|
|
@@ -795,6 +776,15 @@ function httpHeadAt(t, i) {
|
|
|
795
776
|
const head = t.slice(i, i + 8).toLowerCase();
|
|
796
777
|
return head.startsWith("https://") ? 8 : head.startsWith("http://") ? 7 : 0;
|
|
797
778
|
}
|
|
779
|
+
var SOCKS_HEADS = [...SOCKS_URI_SCHEMES].sort((x, y) => y.length - x.length).map((x) => `${x}://`);
|
|
780
|
+
function userinfoHeadAt(t, i) {
|
|
781
|
+
const h = httpHeadAt(t, i);
|
|
782
|
+
if (h) return h;
|
|
783
|
+
if (t[i] !== "s" && t[i] !== "S") return 0;
|
|
784
|
+
const head = t.slice(i, i + 10).toLowerCase();
|
|
785
|
+
for (const p of SOCKS_HEADS) if (head.startsWith(p)) return p.length;
|
|
786
|
+
return 0;
|
|
787
|
+
}
|
|
798
788
|
function valueLimits(s, creds = urlCredentialSpans(s)) {
|
|
799
789
|
const t = s.text;
|
|
800
790
|
const ends = creds.filter((sp) => sp.end < s.n && (t[sp.end] === "&" || t[sp.end] === "#" || t[sp.end] === ";" || CJK_STOPS.includes(t[sp.end])));
|
|
@@ -894,12 +884,24 @@ function userinfoSpans(s) {
|
|
|
894
884
|
s.unfinished = Math.min(s.unfinished, i);
|
|
895
885
|
break;
|
|
896
886
|
}
|
|
897
|
-
const len =
|
|
887
|
+
const len = userinfoHeadAt(t, i);
|
|
898
888
|
if (!len) {
|
|
899
889
|
i++;
|
|
900
890
|
continue;
|
|
901
891
|
}
|
|
902
892
|
const from = i + len;
|
|
893
|
+
if (!httpHeadAt(t, i)) {
|
|
894
|
+
let j2 = from;
|
|
895
|
+
let at2 = -1;
|
|
896
|
+
while (j2 < n && !isWs(t[j2]) && t[j2] !== "?" && t[j2] !== "#") {
|
|
897
|
+
if (t[j2] === "@") at2 = j2;
|
|
898
|
+
j2++;
|
|
899
|
+
}
|
|
900
|
+
s.budget.tick(j2 - i);
|
|
901
|
+
if (at2 > from && userinfoHasContent(s, from, at2)) out.push({ index: from, end: at2 });
|
|
902
|
+
i = j2 > from ? j2 : from;
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
903
905
|
let j = from;
|
|
904
906
|
let at = -1;
|
|
905
907
|
while (j < n && !isWs(t[j]) && t[j] !== "/" && t[j] !== "?" && t[j] !== "#") {
|
|
@@ -1172,8 +1174,12 @@ var RULES = [
|
|
|
1172
1174
|
];
|
|
1173
1175
|
var RULE_NAMES = RULES.map(([name]) => name);
|
|
1174
1176
|
var TOKEN_ORDER = RULE_NAMES.indexOf("token");
|
|
1177
|
+
function foldFullwidth(text) {
|
|
1178
|
+
if (!/[\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF1D]/.test(text)) return text;
|
|
1179
|
+
return text.replace(/[\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF1D]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 65248));
|
|
1180
|
+
}
|
|
1175
1181
|
function sensitiveMatches(text, opts = {}) {
|
|
1176
|
-
const s = newScan(text, opts.budget);
|
|
1182
|
+
const s = newScan(foldFullwidth(text), opts.budget);
|
|
1177
1183
|
const out = [];
|
|
1178
1184
|
let exhaustedAt = -1;
|
|
1179
1185
|
for (let order = 0; order < RULES.length; order++) {
|
|
@@ -1252,8 +1258,8 @@ var USAGE_RULES = [
|
|
|
1252
1258
|
"",
|
|
1253
1259
|
"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
1260
|
`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\
|
|
1261
|
+
"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",
|
|
1262
|
+
"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
1263
|
" - \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
1264
|
` - \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
1265
|
` - \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`,
|
|
@@ -1263,34 +1269,179 @@ var USAGE_RULES = [
|
|
|
1263
1269
|
var SKILL_DESCRIPTION = `\u4E2D\u7ACB\u7684\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
1270
|
|
|
1265
1271
|
// ../runtime-core/src/mcp.ts
|
|
1272
|
+
function siteOriginOf(manifest) {
|
|
1273
|
+
const v = typeof manifest === "object" && manifest !== null ? manifest.site_origin : void 0;
|
|
1274
|
+
if (typeof v !== "string" || !/^https:\/\/[A-Za-z0-9.-]+(:[0-9]+)?$/.test(v)) return void 0;
|
|
1275
|
+
try {
|
|
1276
|
+
if (new URL(v).port === "0") return void 0;
|
|
1277
|
+
} catch {
|
|
1278
|
+
return void 0;
|
|
1279
|
+
}
|
|
1280
|
+
return v;
|
|
1281
|
+
}
|
|
1266
1282
|
var ID = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
1267
1283
|
var MAX_BYTES = 131072;
|
|
1268
1284
|
var MAX_ID_UNITS = 256;
|
|
1269
1285
|
function channelOf(v, cfg) {
|
|
1270
1286
|
return typeof v === "string" && (cfg.channels ?? []).includes(v) ? v : null;
|
|
1271
1287
|
}
|
|
1288
|
+
var LIST_ENTRIES_BUDGET = 38e3;
|
|
1272
1289
|
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
|
|
1290
|
+
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
1291
|
|
|
1275
1292
|
${USAGE_RULES}`;
|
|
1276
1293
|
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
1294
|
{ 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\
|
|
1295
|
+
{ 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
1296
|
];
|
|
1281
1297
|
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
|
|
1298
|
+
function aliasOrder(ids, index, query, t2s) {
|
|
1299
|
+
const score = /* @__PURE__ */ new Map();
|
|
1300
|
+
if (index.docs.length > 0) {
|
|
1301
|
+
const qt = [...new Set(tokens(query, index.config, t2s))];
|
|
1302
|
+
const n = index.docs.length;
|
|
1303
|
+
const df = /* @__PURE__ */ new Map();
|
|
1304
|
+
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);
|
|
1305
|
+
for (const d of index.docs) {
|
|
1306
|
+
let s = 0;
|
|
1307
|
+
for (const [f, toks] of Object.entries(d.fields)) {
|
|
1308
|
+
const w = index.config.weights[f] ?? 0;
|
|
1309
|
+
if (!w) continue;
|
|
1310
|
+
const set = new Set(toks);
|
|
1311
|
+
for (const t of qt) if (set.has(t)) s += w * Math.log(1 + n / (df.get(t) ?? 1));
|
|
1312
|
+
}
|
|
1313
|
+
score.set(d.id, s);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return [...ids].sort((a, b) => (score.get(b) ?? 0) - (score.get(a) ?? 0) || (a < b ? -1 : a > b ? 1 : 0));
|
|
1317
|
+
}
|
|
1318
|
+
function catalogOf(projection, index, query, t2s) {
|
|
1283
1319
|
const ids = Object.keys(projection.entries).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1284
|
-
|
|
1320
|
+
const entries = ids.map((id) => {
|
|
1285
1321
|
const e = projection.entries[id];
|
|
1286
|
-
return { id: e.id, type: e.type, title: e.title
|
|
1287
|
-
})
|
|
1322
|
+
return { id: e.id, type: e.type, title: e.title };
|
|
1323
|
+
});
|
|
1324
|
+
const byId = new Map(entries.map((e) => [e.id, e]));
|
|
1325
|
+
const base = { content_version: projection.content_version, entries };
|
|
1326
|
+
let size = bytes(catalogText(base)) + bytes(JSON.stringify(base));
|
|
1327
|
+
for (const id of aliasOrder(ids, index, query, t2s)) {
|
|
1328
|
+
const e = byId.get(id);
|
|
1329
|
+
const aliases = projection.entries[id]?.aliases ?? [];
|
|
1330
|
+
if (!e || aliases.length === 0) continue;
|
|
1331
|
+
const extra = aliases.reduce((a, x) => a + bytes(" | " + field(x)), 0) + bytes(`,"aliases":${JSON.stringify(aliases)}`);
|
|
1332
|
+
if (size + extra > LIST_ENTRIES_BUDGET) break;
|
|
1333
|
+
e.aliases = [...aliases];
|
|
1334
|
+
size += extra;
|
|
1335
|
+
}
|
|
1336
|
+
return { content_version: projection.content_version, entries };
|
|
1288
1337
|
}
|
|
1289
1338
|
var field = (s) => s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|\r|\n/g, " ");
|
|
1290
1339
|
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");
|
|
1340
|
+
return [`content_version: ${c.content_version}`, ...c.entries.map((e) => [e.id, e.type, e.title, ...e.aliases ?? []].map(field).join(" | "))].join("\n");
|
|
1292
1341
|
}
|
|
1293
1342
|
var bytes = (s) => new TextEncoder().encode(s).length;
|
|
1343
|
+
var PART_BYTES = 17e3;
|
|
1344
|
+
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
|
|
1345
|
+
var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*\r?\n?$/;
|
|
1346
|
+
function linesWithFence(s, start = null) {
|
|
1347
|
+
const lines = [];
|
|
1348
|
+
let fence = start;
|
|
1349
|
+
for (const line of s.match(/[^\n]*\n|[^\n]+$/g) ?? []) {
|
|
1350
|
+
lines.push({ line, inFence: fence !== null });
|
|
1351
|
+
if (fence === null) {
|
|
1352
|
+
const m = FENCE_OPEN.exec(line);
|
|
1353
|
+
if (m) fence = { ch: m[1][0], len: m[1].length };
|
|
1354
|
+
} else {
|
|
1355
|
+
const m = FENCE_CLOSE.exec(line);
|
|
1356
|
+
if (m && m[1][0] === fence.ch && m[1].length >= fence.len) fence = null;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return { lines, end: fence };
|
|
1360
|
+
}
|
|
1361
|
+
function groupLines(s, isStart) {
|
|
1362
|
+
const ls = linesWithFence(s).lines;
|
|
1363
|
+
const out = [];
|
|
1364
|
+
ls.forEach((l, i) => {
|
|
1365
|
+
if (i === 0 || isStart(ls, i)) out.push(l.line);
|
|
1366
|
+
else out[out.length - 1] += l.line;
|
|
1367
|
+
});
|
|
1368
|
+
return out;
|
|
1369
|
+
}
|
|
1370
|
+
var cost = (s) => bytes(JSON.stringify(s)) - 2;
|
|
1371
|
+
function codePointChunks(s, limit) {
|
|
1372
|
+
const out = [];
|
|
1373
|
+
let cur = "";
|
|
1374
|
+
let n = 0;
|
|
1375
|
+
for (const cp of s) {
|
|
1376
|
+
const b = cost(cp);
|
|
1377
|
+
if (n + b > limit && cur) {
|
|
1378
|
+
out.push(cur);
|
|
1379
|
+
cur = "";
|
|
1380
|
+
n = 0;
|
|
1381
|
+
}
|
|
1382
|
+
cur += cp;
|
|
1383
|
+
n += b;
|
|
1384
|
+
}
|
|
1385
|
+
if (cur) out.push(cur);
|
|
1386
|
+
return out;
|
|
1387
|
+
}
|
|
1388
|
+
function pack(units, limit, split) {
|
|
1389
|
+
const out = [];
|
|
1390
|
+
let cur = "";
|
|
1391
|
+
let n = 0;
|
|
1392
|
+
const add = (u) => {
|
|
1393
|
+
const b = cost(u);
|
|
1394
|
+
if (n + b <= limit) {
|
|
1395
|
+
cur += u;
|
|
1396
|
+
n += b;
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (cur) out.push(cur);
|
|
1400
|
+
cur = u;
|
|
1401
|
+
n = b;
|
|
1402
|
+
};
|
|
1403
|
+
for (const u of units) {
|
|
1404
|
+
if (cost(u) <= limit) add(u);
|
|
1405
|
+
else for (const piece of split(u)) add(piece);
|
|
1406
|
+
}
|
|
1407
|
+
if (cur || out.length === 0) out.push(cur);
|
|
1408
|
+
return out;
|
|
1409
|
+
}
|
|
1410
|
+
function entryParts(markdown, limit) {
|
|
1411
|
+
const byLine = (u) => pack(groupLines(u, () => true), limit, (x) => codePointChunks(x, limit));
|
|
1412
|
+
const byParagraph = (u) => pack(groupLines(u, (ls, i) => !ls[i - 1].inFence && ls[i - 1].line.trim() === ""), limit, byLine);
|
|
1413
|
+
return pack(groupLines(markdown, (ls, i) => !ls[i].inFence && ls[i].line.startsWith("## ")), limit, byParagraph);
|
|
1414
|
+
}
|
|
1415
|
+
function partHeadings(segs) {
|
|
1416
|
+
const clip = (line) => {
|
|
1417
|
+
const cps = [...line];
|
|
1418
|
+
return cps.length > 40 ? cps.slice(0, 40).join("") + "\u2026" : line;
|
|
1419
|
+
};
|
|
1420
|
+
const { lines } = linesWithFence(segs.join(""));
|
|
1421
|
+
const starts = [];
|
|
1422
|
+
let off = 0;
|
|
1423
|
+
for (const l of lines) {
|
|
1424
|
+
starts.push(off);
|
|
1425
|
+
off += l.line.length;
|
|
1426
|
+
}
|
|
1427
|
+
let lastH2 = "";
|
|
1428
|
+
let segStart = 0;
|
|
1429
|
+
let i = 0;
|
|
1430
|
+
return segs.map((seg) => {
|
|
1431
|
+
const segEnd = segStart + seg.length;
|
|
1432
|
+
while (i < lines.length && starts[i] + lines[i].line.length <= segStart) i++;
|
|
1433
|
+
let heading;
|
|
1434
|
+
if (i < lines.length && starts[i] === segStart) {
|
|
1435
|
+
let k = i;
|
|
1436
|
+
while (k < lines.length && starts[k] < segEnd && lines[k].line.trim() === "") k++;
|
|
1437
|
+
if (k < lines.length && starts[k] < segEnd && !lines[k].inFence && /^#{1,2} /.test(lines[k].line)) heading = lines[k].line.trim();
|
|
1438
|
+
}
|
|
1439
|
+
const label = heading ?? (lastH2 ? `${lastH2}\uFF08\u7EED\uFF09` : seg.split("\n").find((l) => l.trim() !== "")?.trim() ?? "");
|
|
1440
|
+
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();
|
|
1441
|
+
segStart = segEnd;
|
|
1442
|
+
return clip(label);
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1294
1445
|
function vocabulary(index) {
|
|
1295
1446
|
const v = /* @__PURE__ */ new Set();
|
|
1296
1447
|
for (const d of index.docs) for (const toks of Object.values(d.fields)) for (const t of toks) v.add(t);
|
|
@@ -1328,47 +1479,38 @@ async function handleToolCall(name, args, deps, measure = (r) => bytes(JSON.stri
|
|
|
1328
1479
|
if (!tool) return { ok: false, code: -32602, message: "unknown tool" };
|
|
1329
1480
|
const props = tool.inputSchema.properties;
|
|
1330
1481
|
for (const k of Object.keys(a)) if (!(k in props)) return { ok: false, code: -32602, message: "unexpected argument" };
|
|
1482
|
+
const typeOk = (v, p) => p.type === "integer" ? Number.isInteger(v) && (p.minimum === void 0 || v >= p.minimum) : typeof v === p.type;
|
|
1331
1483
|
for (const [k, v] of Object.entries(a)) {
|
|
1332
1484
|
const p = props[k];
|
|
1333
|
-
if (v !== void 0 && (
|
|
1485
|
+
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
1486
|
}
|
|
1335
1487
|
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
1488
|
if (name === "list_entries") {
|
|
1348
1489
|
const query = typeof a.query === "string" ? a.query : "";
|
|
1349
1490
|
if (query.length < 1 || query.length > 200) return { ok: false, code: -32602, message: "query must be 1..200 chars" };
|
|
1350
1491
|
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) };
|
|
1492
|
+
return { ok: true, result: catalogOf(deps.projection, deps.index, query, deps.t2s) };
|
|
1352
1493
|
}
|
|
1353
1494
|
if (name === "get_entry") {
|
|
1354
1495
|
const id = typeof a.id === "string" ? a.id : "";
|
|
1355
1496
|
if (!ID.test(id)) return { ok: false, code: -32602, message: "bad id" };
|
|
1356
1497
|
const e = Object.hasOwn(deps.projection.entries, id) ? deps.projection.entries[id] : void 0;
|
|
1357
1498
|
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
|
-
|
|
1499
|
+
const segs = entryParts(e.markdown, PART_BYTES);
|
|
1500
|
+
const part = typeof a.part === "number" ? a.part : 1;
|
|
1501
|
+
if (part > segs.length) return { ok: false, code: -32602, message: "part out of range" };
|
|
1502
|
+
const headings = partHeadings(segs);
|
|
1503
|
+
const others = headings.map((h, i) => `${i + 1}\u300C${h}\u300D`).filter((_, i) => i + 1 !== part).join("\u3001");
|
|
1504
|
+
const url = deps.siteOrigin ? `${deps.siteOrigin}/${e.type}/${e.id}/` : void 0;
|
|
1505
|
+
const freshness = freshnessAt(e, deps.now?.() ?? /* @__PURE__ */ new Date());
|
|
1506
|
+
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` : "";
|
|
1507
|
+
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");
|
|
1508
|
+
const md0 = lead ? `${lead}
|
|
1509
|
+
|
|
1510
|
+
${segs[part - 1]}` : segs[part - 1];
|
|
1511
|
+
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 });
|
|
1512
|
+
const result = make(md0, false);
|
|
1513
|
+
if (measure(result) > MAX_BYTES) return { ok: false, code: -32603, message: "entry too large" };
|
|
1372
1514
|
return { ok: true, result };
|
|
1373
1515
|
}
|
|
1374
1516
|
return { ok: false, code: -32602, message: "unknown tool" };
|
|
@@ -1391,7 +1533,7 @@ function metaVersion(m) {
|
|
|
1391
1533
|
return m.params._meta[PV];
|
|
1392
1534
|
}
|
|
1393
1535
|
function acceptedId(rawId) {
|
|
1394
|
-
if (rawId !== void 0 && rawId !== null && typeof rawId !== "number" && (typeof rawId !== "string" || rawId.length > MAX_ID_UNITS || credentialRules(rawId).
|
|
1536
|
+
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
1537
|
return rawId;
|
|
1396
1538
|
}
|
|
1397
1539
|
function modernMetaError(meta) {
|
|
@@ -1486,12 +1628,14 @@ async function loadDepsFromDir(dir, extra) {
|
|
|
1486
1628
|
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
1629
|
return JSON.parse(buf.toString("utf8"));
|
|
1488
1630
|
}));
|
|
1489
|
-
|
|
1631
|
+
const siteOrigin = siteOriginOf(manifest);
|
|
1632
|
+
return { index, projection, t2s: makeTableT2s(table), salt: extra.salt, sha256: extra.sha256, redact: redactSensitive, ...siteOrigin ? { siteOrigin } : {} };
|
|
1490
1633
|
}
|
|
1491
1634
|
var TOO_LARGE = Symbol("request too large");
|
|
1635
|
+
var BAD_UTF8 = Symbol("invalid utf-8");
|
|
1492
1636
|
async function* boundedLines(input, maxBytes = MAX_JSON_BODY_BYTES) {
|
|
1493
1637
|
const enc = new TextEncoder();
|
|
1494
|
-
const dec = new TextDecoder();
|
|
1638
|
+
const dec = new TextDecoder("utf-8", { fatal: true });
|
|
1495
1639
|
let parts = [];
|
|
1496
1640
|
let size = 0;
|
|
1497
1641
|
let skipping = false;
|
|
@@ -1504,7 +1648,12 @@ async function* boundedLines(input, maxBytes = MAX_JSON_BODY_BYTES) {
|
|
|
1504
1648
|
}
|
|
1505
1649
|
parts = [];
|
|
1506
1650
|
size = 0;
|
|
1507
|
-
|
|
1651
|
+
let line;
|
|
1652
|
+
try {
|
|
1653
|
+
line = dec.decode(buf);
|
|
1654
|
+
} catch {
|
|
1655
|
+
return BAD_UTF8;
|
|
1656
|
+
}
|
|
1508
1657
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
1509
1658
|
};
|
|
1510
1659
|
for await (const raw of input) {
|
|
@@ -1541,13 +1690,18 @@ async function createStdioServer(deps, io) {
|
|
|
1541
1690
|
await io.write(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32600, message: "request too large" } }));
|
|
1542
1691
|
continue;
|
|
1543
1692
|
}
|
|
1693
|
+
const parseError = JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
|
|
1694
|
+
if (line === BAD_UTF8) {
|
|
1695
|
+
await io.write(parseError);
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1544
1698
|
const trimmed = line.trim();
|
|
1545
1699
|
if (!trimmed) continue;
|
|
1546
1700
|
let msg;
|
|
1547
1701
|
try {
|
|
1548
1702
|
msg = JSON.parse(trimmed);
|
|
1549
1703
|
} catch {
|
|
1550
|
-
await io.write(
|
|
1704
|
+
await io.write(parseError);
|
|
1551
1705
|
continue;
|
|
1552
1706
|
}
|
|
1553
1707
|
const r = await handleJsonRpc(msg, deps);
|
|
@@ -1555,45 +1709,6 @@ async function createStdioServer(deps, io) {
|
|
|
1555
1709
|
}
|
|
1556
1710
|
}
|
|
1557
1711
|
|
|
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
1712
|
// src/exit.ts
|
|
1598
1713
|
var FALLBACK = "\u52A0\u8F7D\u6216\u8FD0\u884C\u5931\u8D25\uFF08\u8BE6\u60C5\u4E0D\u56DE\u663E\uFF09";
|
|
1599
1714
|
var FLUSH_TIMEOUT_MS = 2e3;
|
|
@@ -1646,8 +1761,8 @@ var fail = makeFail({ error: (m) => console.error(m), exit: (c) => process.exit(
|
|
|
1646
1761
|
installProcessHandlers(process, fail);
|
|
1647
1762
|
async function run() {
|
|
1648
1763
|
if (cmd === "mcp") {
|
|
1649
|
-
const defaultAssets =
|
|
1650
|
-
const deps = await loadDepsFromDir(
|
|
1764
|
+
const defaultAssets = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "..", "snapshot");
|
|
1765
|
+
const deps = await loadDepsFromDir(path2.resolve(opt("assets", defaultAssets)), { salt: "local", sha256: async (s) => createHash2("sha256").update(s).digest("hex") });
|
|
1651
1766
|
const it = boundedLines(process.stdin)[Symbol.asyncIterator]();
|
|
1652
1767
|
await createStdioServer(deps, { readLine: async () => {
|
|
1653
1768
|
const n = await it.next();
|
|
@@ -1667,12 +1782,8 @@ async function run() {
|
|
|
1667
1782
|
process.stdout.once("error", onError);
|
|
1668
1783
|
}
|
|
1669
1784
|
}) });
|
|
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
1785
|
} else {
|
|
1675
|
-
console.log("usage: hub-kb mcp [--assets <dir>, default: bundled snapshot/]
|
|
1786
|
+
console.log("usage: hub-kb mcp [--assets <dir>, default: bundled snapshot/]");
|
|
1676
1787
|
process.exit(1);
|
|
1677
1788
|
}
|
|
1678
1789
|
}
|