@hcmai/sdk 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +464 -1
- package/dist/index.js +1236 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -177,25 +177,25 @@ var TokenStore = class _TokenStore {
|
|
|
177
177
|
throw e;
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
async setAccessToken(
|
|
180
|
+
async setAccessToken(token2) {
|
|
181
181
|
if (await this.canUseKeychain()) {
|
|
182
182
|
const keytar = await loadKeytar();
|
|
183
|
-
if (!keytar) return this.writeTokenFile("access",
|
|
183
|
+
if (!keytar) return this.writeTokenFile("access", token2);
|
|
184
184
|
const account = `${this.keychainAccountPrefix()}/access`;
|
|
185
|
-
await keytar.setPassword(SERVICE_PREFIX, account,
|
|
185
|
+
await keytar.setPassword(SERVICE_PREFIX, account, token2);
|
|
186
186
|
return `keychain:${SERVICE_PREFIX}/${account}`;
|
|
187
187
|
}
|
|
188
|
-
return this.writeTokenFile("access",
|
|
188
|
+
return this.writeTokenFile("access", token2);
|
|
189
189
|
}
|
|
190
|
-
async setRefreshToken(
|
|
190
|
+
async setRefreshToken(token2) {
|
|
191
191
|
if (await this.canUseKeychain()) {
|
|
192
192
|
const keytar = await loadKeytar();
|
|
193
|
-
if (!keytar) return this.writeTokenFile("refresh",
|
|
193
|
+
if (!keytar) return this.writeTokenFile("refresh", token2);
|
|
194
194
|
const account = `${this.keychainAccountPrefix()}/refresh`;
|
|
195
|
-
await keytar.setPassword(SERVICE_PREFIX, account,
|
|
195
|
+
await keytar.setPassword(SERVICE_PREFIX, account, token2);
|
|
196
196
|
return `keychain:${SERVICE_PREFIX}/${account}`;
|
|
197
197
|
}
|
|
198
|
-
return this.writeTokenFile("refresh",
|
|
198
|
+
return this.writeTokenFile("refresh", token2);
|
|
199
199
|
}
|
|
200
200
|
async getAccessToken() {
|
|
201
201
|
if (process.env.HCM_PAT) return process.env.HCM_PAT;
|
|
@@ -258,10 +258,10 @@ var TokenStore = class _TokenStore {
|
|
|
258
258
|
return false;
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
|
-
async writeTokenFile(kind,
|
|
261
|
+
async writeTokenFile(kind, token2) {
|
|
262
262
|
const file = `${this.metaFile()}.${kind}`;
|
|
263
263
|
await fs.mkdir(this.dataDir(), { recursive: true });
|
|
264
|
-
await fs.writeFile(file,
|
|
264
|
+
await fs.writeFile(file, token2, { mode: 384 });
|
|
265
265
|
if (kind === "access") {
|
|
266
266
|
console.warn(`[hcm] WARNING: OS keychain unavailable. Token saved to ${file} (0600).`);
|
|
267
267
|
}
|
|
@@ -308,14 +308,14 @@ async function loginPassword(input) {
|
|
|
308
308
|
},
|
|
309
309
|
{ headers: { "Content-Type": "application/json" } }
|
|
310
310
|
);
|
|
311
|
-
const { token, expiresIn, user } = resp.data;
|
|
312
|
-
if (!
|
|
311
|
+
const { token: token2, expiresIn, user } = resp.data;
|
|
312
|
+
if (!token2) {
|
|
313
313
|
throw new Error("Login response missing token");
|
|
314
314
|
}
|
|
315
315
|
const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;
|
|
316
316
|
const expiresAt = new Date(Date.now() + ttlSeconds * 1e3).toISOString();
|
|
317
317
|
const store = new TokenStore(input.profile);
|
|
318
|
-
const accessTokenRef = await store.setAccessToken(
|
|
318
|
+
const accessTokenRef = await store.setAccessToken(token2);
|
|
319
319
|
const record = {
|
|
320
320
|
scheme: "password",
|
|
321
321
|
accessTokenRef,
|
|
@@ -1068,18 +1068,18 @@ async function action(http, model, actionName, input) {
|
|
|
1068
1068
|
if (scope === "object" && !input.id) {
|
|
1069
1069
|
throw new Error(`object action ${model}.${actionName} requires id`);
|
|
1070
1070
|
}
|
|
1071
|
-
const
|
|
1071
|
+
const path12 = scope === "object" ? `/api/models/${model}/${input.id}/action/${actionName}` : `/api/models/${model}/action/${actionName}`;
|
|
1072
1072
|
const resp = await (async () => {
|
|
1073
1073
|
if (method === "GET") {
|
|
1074
|
-
return http.get(
|
|
1074
|
+
return http.get(path12, { params: queryParams(input) });
|
|
1075
1075
|
}
|
|
1076
1076
|
if (method === "DELETE") {
|
|
1077
|
-
return http.delete(
|
|
1077
|
+
return http.delete(path12, { params: queryParams(input) });
|
|
1078
1078
|
}
|
|
1079
1079
|
if (method === "PUT") {
|
|
1080
|
-
return http.put(
|
|
1080
|
+
return http.put(path12, scope === "object" ? objectBody(input) : classBody(input));
|
|
1081
1081
|
}
|
|
1082
|
-
return http.post(
|
|
1082
|
+
return http.post(path12, scope === "object" ? objectBody(input) : classBody(input));
|
|
1083
1083
|
})();
|
|
1084
1084
|
return resp.data;
|
|
1085
1085
|
}
|
|
@@ -1690,14 +1690,14 @@ async function loginPairing(input) {
|
|
|
1690
1690
|
{ code: input.code },
|
|
1691
1691
|
{ headers: { "Content-Type": "application/json" } }
|
|
1692
1692
|
);
|
|
1693
|
-
const { token, expiresIn, user } = resp.data;
|
|
1694
|
-
if (!
|
|
1693
|
+
const { token: token2, expiresIn, user } = resp.data;
|
|
1694
|
+
if (!token2) {
|
|
1695
1695
|
throw new Error("Pairing exchange response missing token");
|
|
1696
1696
|
}
|
|
1697
1697
|
const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;
|
|
1698
1698
|
const expiresAt = new Date(Date.now() + ttlSeconds * 1e3).toISOString();
|
|
1699
1699
|
const store = new TokenStore(input.profile);
|
|
1700
|
-
const accessTokenRef = await store.setAccessToken(
|
|
1700
|
+
const accessTokenRef = await store.setAccessToken(token2);
|
|
1701
1701
|
const record = {
|
|
1702
1702
|
scheme: "pairing",
|
|
1703
1703
|
accessTokenRef,
|
|
@@ -1730,10 +1730,10 @@ function detectPasswordChangeChallenge(err) {
|
|
|
1730
1730
|
const body = err?.response?.data;
|
|
1731
1731
|
const error = body?.error ?? body;
|
|
1732
1732
|
if (error?.code !== PASSWORD_CHANGE_REQUIRED) return null;
|
|
1733
|
-
const
|
|
1734
|
-
if (!
|
|
1733
|
+
const token2 = error?.details?.changePasswordToken;
|
|
1734
|
+
if (!token2) return null;
|
|
1735
1735
|
return {
|
|
1736
|
-
changePasswordToken: String(
|
|
1736
|
+
changePasswordToken: String(token2),
|
|
1737
1737
|
expiresInSeconds: Number(error?.details?.expiresInSeconds) || void 0
|
|
1738
1738
|
};
|
|
1739
1739
|
}
|
|
@@ -5035,18 +5035,18 @@ async function listTenantMeta(http, opts = {}) {
|
|
|
5035
5035
|
const items = payload?.items ?? payload?.list ?? payload?.records ?? [];
|
|
5036
5036
|
return { items, total: payload?.total, raw: payload };
|
|
5037
5037
|
}
|
|
5038
|
-
async function getTenantMeta(http,
|
|
5039
|
-
const resp = await http.get("/api/tenant-meta/meta", { params: { path:
|
|
5038
|
+
async function getTenantMeta(http, path12) {
|
|
5039
|
+
const resp = await http.get("/api/tenant-meta/meta", { params: { path: path12 } });
|
|
5040
5040
|
const payload = resp.data?.data ?? resp.data;
|
|
5041
5041
|
if (typeof payload === "string") return payload;
|
|
5042
5042
|
return String(payload?.content ?? "");
|
|
5043
5043
|
}
|
|
5044
|
-
async function saveTenantMeta(http,
|
|
5045
|
-
const resp = await http.put("/api/tenant-meta/meta", { content }, { params: { path:
|
|
5044
|
+
async function saveTenantMeta(http, path12, content) {
|
|
5045
|
+
const resp = await http.put("/api/tenant-meta/meta", { content }, { params: { path: path12 } });
|
|
5046
5046
|
return resp.data?.data ?? resp.data;
|
|
5047
5047
|
}
|
|
5048
|
-
async function deleteTenantMeta(http,
|
|
5049
|
-
const resp = await http.delete("/api/tenant-meta/meta", { params: { path:
|
|
5048
|
+
async function deleteTenantMeta(http, path12) {
|
|
5049
|
+
const resp = await http.delete("/api/tenant-meta/meta", { params: { path: path12 } });
|
|
5050
5050
|
return resp.data?.data ?? resp.data;
|
|
5051
5051
|
}
|
|
5052
5052
|
|
|
@@ -5148,14 +5148,14 @@ function resolveSkillInstallOrder(manifest2, targetId) {
|
|
|
5148
5148
|
throw invalid(`\u6280\u80FD\u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728 ${targetId}`);
|
|
5149
5149
|
}
|
|
5150
5150
|
const state = /* @__PURE__ */ new Map();
|
|
5151
|
-
const
|
|
5151
|
+
const path12 = [];
|
|
5152
5152
|
const order = [];
|
|
5153
5153
|
const visit = (id, requiredBy) => {
|
|
5154
5154
|
const current = state.get(id);
|
|
5155
5155
|
if (current === "done") return;
|
|
5156
5156
|
if (current === "visiting") {
|
|
5157
|
-
const cycleStart =
|
|
5158
|
-
const cycle = [...
|
|
5157
|
+
const cycleStart = path12.indexOf(id);
|
|
5158
|
+
const cycle = [...path12.slice(Math.max(0, cycleStart)), id];
|
|
5159
5159
|
throw invalid(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${cycle.join(" -> ")}`);
|
|
5160
5160
|
}
|
|
5161
5161
|
const skill = byId.get(id);
|
|
@@ -5164,11 +5164,11 @@ function resolveSkillInstallOrder(manifest2, targetId) {
|
|
|
5164
5164
|
}
|
|
5165
5165
|
assertSafeSkillId(skill.id);
|
|
5166
5166
|
state.set(id, "visiting");
|
|
5167
|
-
|
|
5167
|
+
path12.push(id);
|
|
5168
5168
|
for (const required of normalizeRequirements(skill.requires, skill.id)) {
|
|
5169
5169
|
visit(required, skill.id);
|
|
5170
5170
|
}
|
|
5171
|
-
|
|
5171
|
+
path12.pop();
|
|
5172
5172
|
state.set(id, "done");
|
|
5173
5173
|
order.push(skill);
|
|
5174
5174
|
};
|
|
@@ -5259,11 +5259,11 @@ async function resetSettingItem(http, domain, namespace, settingKey, revision) {
|
|
|
5259
5259
|
function parseSettingAssignment(raw) {
|
|
5260
5260
|
const eq = raw.indexOf("=");
|
|
5261
5261
|
if (eq <= 0) throw new Error(`--set \u9700\u8981 namespace.key=value \u5F62\u6001\uFF0C\u6536\u5230 "${raw}"`);
|
|
5262
|
-
const
|
|
5262
|
+
const path12 = raw.slice(0, eq).trim();
|
|
5263
5263
|
const value = raw.slice(eq + 1);
|
|
5264
|
-
const dot =
|
|
5265
|
-
if (dot <= 0) throw new Error(`--set \u7684\u952E\u9700\u542B namespace\uFF0C\u5982 security.password.passwordPolicy=complex\uFF0C\u6536\u5230 "${
|
|
5266
|
-
return { namespace:
|
|
5264
|
+
const dot = path12.lastIndexOf(".");
|
|
5265
|
+
if (dot <= 0) throw new Error(`--set \u7684\u952E\u9700\u542B namespace\uFF0C\u5982 security.password.passwordPolicy=complex\uFF0C\u6536\u5230 "${path12}"`);
|
|
5266
|
+
return { namespace: path12.slice(0, dot), settingKey: path12.slice(dot + 1), value };
|
|
5267
5267
|
}
|
|
5268
5268
|
|
|
5269
5269
|
// src/fde/layout.ts
|
|
@@ -5657,11 +5657,11 @@ function classifyStatus(status) {
|
|
|
5657
5657
|
return "bad-response";
|
|
5658
5658
|
}
|
|
5659
5659
|
var CAPABILITIES_HEADER = "x-hcm-capabilities";
|
|
5660
|
-
function buildUrl(
|
|
5660
|
+
function buildUrl(path12, params) {
|
|
5661
5661
|
const entries = Object.entries(params ?? {}).filter(([, v]) => v !== void 0 && v !== null);
|
|
5662
|
-
if (entries.length === 0) return
|
|
5662
|
+
if (entries.length === 0) return path12;
|
|
5663
5663
|
const qs = entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
5664
|
-
return `${
|
|
5664
|
+
return `${path12}${path12.includes("?") ? "&" : "?"}${qs}`;
|
|
5665
5665
|
}
|
|
5666
5666
|
function lowerHeaders(raw) {
|
|
5667
5667
|
const out = {};
|
|
@@ -5703,14 +5703,14 @@ function httpProbe(http, capabilities) {
|
|
|
5703
5703
|
const headers = { [CAPABILITIES_HEADER]: capabilities };
|
|
5704
5704
|
const probe = {
|
|
5705
5705
|
capabilities,
|
|
5706
|
-
get(
|
|
5706
|
+
get(path12, params) {
|
|
5707
5707
|
return once(
|
|
5708
|
-
() => http.get(buildUrl(
|
|
5708
|
+
() => http.get(buildUrl(path12, params), { headers, validateStatus: () => true })
|
|
5709
5709
|
);
|
|
5710
5710
|
},
|
|
5711
|
-
post(
|
|
5711
|
+
post(path12, body) {
|
|
5712
5712
|
return once(
|
|
5713
|
-
() => http.post(
|
|
5713
|
+
() => http.post(path12, body, {
|
|
5714
5714
|
headers: { ...headers, "content-type": "application/json" },
|
|
5715
5715
|
validateStatus: () => true
|
|
5716
5716
|
})
|
|
@@ -5822,8 +5822,8 @@ async function crawlMeta(probe, modelKeys2, metaType = DEFAULT_META_TYPE) {
|
|
|
5822
5822
|
const results = probe.getMany ? await probe.getMany([...paths.keys()]) : await sequentialGet(probe, [...paths.keys()]);
|
|
5823
5823
|
const metas = {};
|
|
5824
5824
|
const unavailable = [];
|
|
5825
|
-
for (const [
|
|
5826
|
-
const out = results[
|
|
5825
|
+
for (const [path12, key] of paths) {
|
|
5826
|
+
const out = results[path12];
|
|
5827
5827
|
if (out === void 0) {
|
|
5828
5828
|
unavailable.push({
|
|
5829
5829
|
modelKey: key,
|
|
@@ -6039,8 +6039,8 @@ async function collectActionGates(probe, modelKeys2, state = "") {
|
|
|
6039
6039
|
const statesEchoed = /* @__PURE__ */ new Set();
|
|
6040
6040
|
let noStateEcho = 0;
|
|
6041
6041
|
let answered = 0;
|
|
6042
|
-
for (const [
|
|
6043
|
-
const out = results[
|
|
6042
|
+
for (const [path12, model] of paths) {
|
|
6043
|
+
const out = results[path12];
|
|
6044
6044
|
if (out === void 0) {
|
|
6045
6045
|
unavailable.push({
|
|
6046
6046
|
modelKey: model,
|
|
@@ -6419,8 +6419,8 @@ async function collectMutation(probe, modelKeys2) {
|
|
|
6419
6419
|
const results = probe.getMany ? await probe.getMany([...paths.keys()]) : await sequentialGet(probe, [...paths.keys()]);
|
|
6420
6420
|
const mutation = {};
|
|
6421
6421
|
const problems = [];
|
|
6422
|
-
for (const [
|
|
6423
|
-
const out = results[
|
|
6422
|
+
for (const [path12, key] of paths) {
|
|
6423
|
+
const out = results[path12];
|
|
6424
6424
|
if (out === void 0) {
|
|
6425
6425
|
problems.push(`${key}: mutation-capability ${BATCH_LOST}`);
|
|
6426
6426
|
mutation[key] = Object.fromEntries(
|
|
@@ -6865,11 +6865,11 @@ async function collectCodeTables(probe, modelEntries2, opts = {}) {
|
|
|
6865
6865
|
keys.push(key);
|
|
6866
6866
|
}
|
|
6867
6867
|
for (const key of [...new Set(keys)].sort()) {
|
|
6868
|
-
const
|
|
6868
|
+
const path12 = listPath(key);
|
|
6869
6869
|
const started = tick();
|
|
6870
6870
|
let requests = 0;
|
|
6871
6871
|
let truncatedBy = null;
|
|
6872
|
-
const probeOut = await post.call(probe,
|
|
6872
|
+
const probeOut = await post.call(probe, path12, { page: 0, size: 0 });
|
|
6873
6873
|
requests += 1;
|
|
6874
6874
|
let total = null;
|
|
6875
6875
|
let reason = null;
|
|
@@ -6918,7 +6918,7 @@ async function collectCodeTables(probe, modelEntries2, opts = {}) {
|
|
|
6918
6918
|
failed = `\u89E6\u53D1\u5DE5\u5177\u7684\u6BCF\u8868${which === "requests" ? "\u8BF7\u6C42" : "\u5899\u949F"}\u4E0A\u9650\uFF08${which === "requests" ? `\u5DF2\u53D1 ${requests} \u6B21\u8BF7\u6C42` : `\u5DF2\u82B1 ${elapsed.toFixed(3)}s`}\uFF09\uFF0C\u505C\u5728\u7B2C ${page} \u9875\uFF0C\u5DF2\u6536 ${rows.length} \u884C / \u8BA1\u6570 ${total} \u884C\uFF0C\u672C\u8868\u6807 partial\u3002**\u8FD9\u662F\u5DE5\u5177\u7684\u622A\u65AD\uFF0C\u4E0D\u662F\u670D\u52A1\u7AEF\u7684\u6BDB\u75C5** \u2014\u2014 \u8C03\u5927 valuedomain.${which === "requests" ? "MAX_REQUESTS_PER_TABLE" : "MAX_SECONDS_PER_TABLE"} \u6216\u7ED9 collectCodeTables \u4F20 ${which === "requests" ? "maxRequests" : "maxSeconds"} \u540E\u91CD\u8DD1\u5C31\u80FD\u53D6\u5168`;
|
|
6919
6919
|
break;
|
|
6920
6920
|
}
|
|
6921
|
-
const out = await post.call(probe,
|
|
6921
|
+
const out = await post.call(probe, path12, { page, size: PAGE_SIZE });
|
|
6922
6922
|
requests += 1;
|
|
6923
6923
|
if (out.error !== "ok") {
|
|
6924
6924
|
failed = `\u7B2C ${page} \u9875\u5931\u8D25\uFF08HTTP ${out.status} / ${out.error}\uFF09`;
|
|
@@ -7551,16 +7551,1153 @@ async function runRecon(probe, layout, opts = {}) {
|
|
|
7551
7551
|
dir: layout.dir
|
|
7552
7552
|
};
|
|
7553
7553
|
}
|
|
7554
|
+
|
|
7555
|
+
// src/fde/gate0/aclass.ts
|
|
7556
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
7557
|
+
import * as path9 from "path";
|
|
7558
|
+
|
|
7559
|
+
// src/fde/gate0/mapping-artifact.ts
|
|
7560
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
7561
|
+
var MAPPING_ARTIFACT_VERSION = 1;
|
|
7562
|
+
var MappingArtifactError = class extends Error {
|
|
7563
|
+
constructor(reason, message) {
|
|
7564
|
+
super(`[${reason}] ${message}`);
|
|
7565
|
+
this.reason = reason;
|
|
7566
|
+
this.message = message;
|
|
7567
|
+
this.name = "MappingArtifactError";
|
|
7568
|
+
}
|
|
7569
|
+
reason;
|
|
7570
|
+
message;
|
|
7571
|
+
};
|
|
7572
|
+
function isRecord6(value) {
|
|
7573
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7574
|
+
}
|
|
7575
|
+
function readBusinessKeyFields(file) {
|
|
7576
|
+
let text;
|
|
7577
|
+
try {
|
|
7578
|
+
text = readFileSync4(file, "utf8");
|
|
7579
|
+
} catch (e) {
|
|
7580
|
+
const code = e?.code;
|
|
7581
|
+
if (code === "ENOENT") throw new MappingArtifactError("not-found", `\u627E\u4E0D\u5230\u6620\u5C04\u5DE5\u4EF6 ${file}`);
|
|
7582
|
+
throw new MappingArtifactError("unreadable", `${file} \u8BFB\u4E0D\u52A8\uFF08${String(e)}\uFF09`);
|
|
7583
|
+
}
|
|
7584
|
+
if (text.charCodeAt(0) === 65279) text = text.slice(1);
|
|
7585
|
+
let body;
|
|
7586
|
+
try {
|
|
7587
|
+
body = JSON.parse(text);
|
|
7588
|
+
} catch (e) {
|
|
7589
|
+
throw new MappingArtifactError("not-json", `${file} \u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${String(e)}`);
|
|
7590
|
+
}
|
|
7591
|
+
if (!isRecord6(body)) {
|
|
7592
|
+
throw new MappingArtifactError("bad-shape", `${file} \u7684\u6839\u4E0D\u662F\u5BF9\u8C61`);
|
|
7593
|
+
}
|
|
7594
|
+
if (!("version" in body)) {
|
|
7595
|
+
throw new MappingArtifactError("version-missing", `${file} \u6CA1\u6709 version \u5B57\u6BB5`);
|
|
7596
|
+
}
|
|
7597
|
+
if (body.version !== MAPPING_ARTIFACT_VERSION) {
|
|
7598
|
+
throw new MappingArtifactError(
|
|
7599
|
+
"unsupported-version",
|
|
7600
|
+
`${file} \u7684 version \u662F ${JSON.stringify(body.version)}\uFF0C\u672C\u5DE5\u5177\u53EA\u8BA4 ${MAPPING_ARTIFACT_VERSION} \u2014\u2014\u300C\u5C3D\u91CF\u6309\u8001\u7248\u672C\u8BFB\u300D\u4F1A\u628A\u65B0\u8BED\u4E49\u9759\u9ED8\u4E22\u6389`
|
|
7601
|
+
);
|
|
7602
|
+
}
|
|
7603
|
+
const models = body.models;
|
|
7604
|
+
if (!Array.isArray(models)) {
|
|
7605
|
+
throw new MappingArtifactError(
|
|
7606
|
+
"bad-shape",
|
|
7607
|
+
`${file} \u7684 models \u5E94\u5F53\u662F\u4E00\u4E2A\u6570\u7EC4\uFF0C\u5B9E\u9645\u662F ${models === null ? "null" : typeof models}`
|
|
7608
|
+
);
|
|
7609
|
+
}
|
|
7610
|
+
if (models.length === 0) {
|
|
7611
|
+
throw new MappingArtifactError(
|
|
7612
|
+
"empty",
|
|
7613
|
+
`${file} \u7684 models \u662F\u7A7A\u7684 \u2014\u2014 \u7A7A\u5DE5\u4EF6\u8DD1\u5B8C\u4EC0\u4E48\u4E5F\u4E0D\u4F1A\u53D1\u751F\uFF0C\u800C\u987E\u95EE\u4F1A\u4EE5\u4E3A\u300C\u5BFC\u8FC7\u4E86\u300D`
|
|
7614
|
+
);
|
|
7615
|
+
}
|
|
7616
|
+
return models.map((raw, i) => {
|
|
7617
|
+
if (!isRecord6(raw)) {
|
|
7618
|
+
throw new MappingArtifactError("bad-shape", `${file} \u7684 models[${i}] \u4E0D\u662F\u5BF9\u8C61`);
|
|
7619
|
+
}
|
|
7620
|
+
const businessKey = raw.businessKey;
|
|
7621
|
+
if (businessKey === void 0 || businessKey === null) return [];
|
|
7622
|
+
if (!isRecord6(businessKey)) {
|
|
7623
|
+
throw new MappingArtifactError("bad-shape", `${file} \u7684 models[${i}].businessKey \u4E0D\u662F\u5BF9\u8C61`);
|
|
7624
|
+
}
|
|
7625
|
+
const fields = businessKey.fields;
|
|
7626
|
+
if (fields === void 0 || fields === null) return [];
|
|
7627
|
+
if (!Array.isArray(fields)) {
|
|
7628
|
+
throw new MappingArtifactError(
|
|
7629
|
+
"bad-shape",
|
|
7630
|
+
`${file} \u7684 models[${i}].businessKey.fields \u4E0D\u662F\u6570\u7EC4`
|
|
7631
|
+
);
|
|
7632
|
+
}
|
|
7633
|
+
return fields.filter((f) => typeof f === "string" && f !== "");
|
|
7634
|
+
});
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
// src/fde/gate0/aclass.ts
|
|
7638
|
+
var ACLASS_KINDS = [
|
|
7639
|
+
"fieldKey",
|
|
7640
|
+
"fieldLabel",
|
|
7641
|
+
"enumValue",
|
|
7642
|
+
"modelName",
|
|
7643
|
+
"codeValue",
|
|
7644
|
+
"naturalKey",
|
|
7645
|
+
"caliber"
|
|
7646
|
+
];
|
|
7647
|
+
var FIELDS_INDEX = ["index", "fields.jsonl"];
|
|
7648
|
+
var ENUM_MIRROR = ["valuedomain", "_enums.jsonl"];
|
|
7649
|
+
var MODEL_ROSTER = ["models.json"];
|
|
7650
|
+
var VALUE_DOMAIN_DIR = "valuedomain";
|
|
7651
|
+
var ENUM_ELEMENT_KEYS = ["value", "label", "key"];
|
|
7652
|
+
var MAPPING_ARTIFACT_HINT = "work/mapping.json";
|
|
7653
|
+
var CLASS_SCOPE = "class";
|
|
7654
|
+
var ENTRY_SCOPE = "entry";
|
|
7655
|
+
var SCOPES = [CLASS_SCOPE, ENTRY_SCOPE];
|
|
7656
|
+
var REASON_UNREAD = "unread";
|
|
7657
|
+
var REASON_EMPTY = "empty";
|
|
7658
|
+
var CLASS_REASONS = [REASON_UNREAD, REASON_EMPTY];
|
|
7659
|
+
function unguardedKinds(problems) {
|
|
7660
|
+
const out = /* @__PURE__ */ new Set();
|
|
7661
|
+
for (const problem of problems) {
|
|
7662
|
+
if (!SCOPES.includes(problem.scope)) {
|
|
7663
|
+
throw new Error(
|
|
7664
|
+
`\u53F0\u8D26\u7684 scope \u53EA\u6709 ${SCOPES.join("/")} \u4E24\u4E2A\u53D6\u503C\uFF0C\u62FF\u5230\u7684\u662F ${JSON.stringify(problem.scope)}\uFF08${problem.message}\uFF09`
|
|
7665
|
+
);
|
|
7666
|
+
}
|
|
7667
|
+
if (problem.scope === CLASS_SCOPE) for (const kind of problem.kinds) out.add(kind);
|
|
7668
|
+
}
|
|
7669
|
+
return out;
|
|
7670
|
+
}
|
|
7671
|
+
function unguardedWord(kinds) {
|
|
7672
|
+
return kinds.join("/");
|
|
7673
|
+
}
|
|
7674
|
+
function token(value) {
|
|
7675
|
+
if (typeof value === "string") return value === "" ? null : value;
|
|
7676
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
7677
|
+
return null;
|
|
7678
|
+
}
|
|
7679
|
+
function scalarsWithin(value) {
|
|
7680
|
+
if (Array.isArray(value)) return value.flatMap(scalarsWithin);
|
|
7681
|
+
if (typeof value === "object" && value !== null) {
|
|
7682
|
+
return Object.values(value).flatMap(scalarsWithin);
|
|
7683
|
+
}
|
|
7684
|
+
const one = token(value);
|
|
7685
|
+
return one === null ? [] : [one];
|
|
7686
|
+
}
|
|
7687
|
+
function readText(file, kinds, scope, problems) {
|
|
7688
|
+
try {
|
|
7689
|
+
return readFileSync5(file, "utf8");
|
|
7690
|
+
} catch (e) {
|
|
7691
|
+
const code = e?.code;
|
|
7692
|
+
const name = path9.basename(file);
|
|
7693
|
+
problems.push({
|
|
7694
|
+
scope,
|
|
7695
|
+
reason: REASON_UNREAD,
|
|
7696
|
+
kinds,
|
|
7697
|
+
message: code === "ENOENT" ? `\u7F3A\u4EA7\u7269 ${name} \u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88` : `\u4EA7\u7269 ${name} \u8BFB\u4E0D\u52A8\uFF08${String(e)}\uFF09\u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7698
|
+
});
|
|
7699
|
+
return null;
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
function readJsonl(file, kinds, scope, problems) {
|
|
7703
|
+
const text = readText(file, kinds, scope, problems);
|
|
7704
|
+
if (text === null) return [];
|
|
7705
|
+
const name = path9.basename(file);
|
|
7706
|
+
const rows = [];
|
|
7707
|
+
text.split("\n").forEach((raw, i) => {
|
|
7708
|
+
const line = raw.trim();
|
|
7709
|
+
if (line === "") return;
|
|
7710
|
+
let row;
|
|
7711
|
+
try {
|
|
7712
|
+
row = JSON.parse(line);
|
|
7713
|
+
} catch (e) {
|
|
7714
|
+
problems.push({
|
|
7715
|
+
scope: ENTRY_SCOPE,
|
|
7716
|
+
reason: "bad-line",
|
|
7717
|
+
kinds,
|
|
7718
|
+
message: `${name}:${i + 1} \u4E0D\u662F\u5408\u6CD5 JSON\uFF08${String(e)}\uFF09\u21D2 \u8FD9\u4E00\u884C\u7684\u53D6\u503C\u6CA1\u6709\u8FDB ${unguardedWord(kinds)}`
|
|
7719
|
+
});
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
7723
|
+
problems.push({
|
|
7724
|
+
scope: ENTRY_SCOPE,
|
|
7725
|
+
reason: "bad-line",
|
|
7726
|
+
kinds,
|
|
7727
|
+
message: `${name}:${i + 1} \u4E0D\u662F\u5BF9\u8C61\uFF08${row === null ? "null" : typeof row}\uFF09\u21D2 \u8FD9\u4E00\u884C\u7684\u53D6\u503C\u6CA1\u6709\u8FDB ${unguardedWord(kinds)}`
|
|
7728
|
+
});
|
|
7729
|
+
return;
|
|
7730
|
+
}
|
|
7731
|
+
rows.push(row);
|
|
7732
|
+
});
|
|
7733
|
+
return rows;
|
|
7734
|
+
}
|
|
7735
|
+
var UNREADABLE = /* @__PURE__ */ Symbol("unreadable");
|
|
7736
|
+
function readJson(file, kinds, scope, problems) {
|
|
7737
|
+
const text = readText(file, kinds, scope, problems);
|
|
7738
|
+
if (text === null) return UNREADABLE;
|
|
7739
|
+
try {
|
|
7740
|
+
return JSON.parse(text);
|
|
7741
|
+
} catch (e) {
|
|
7742
|
+
problems.push({
|
|
7743
|
+
scope,
|
|
7744
|
+
reason: REASON_UNREAD,
|
|
7745
|
+
kinds,
|
|
7746
|
+
message: `\u4EA7\u7269 ${path9.basename(file)} \u4E0D\u662F\u5408\u6CD5 JSON\uFF08${String(e)}\uFF09\u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7747
|
+
});
|
|
7748
|
+
return UNREADABLE;
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
function listUnder(payload, key, file, kinds, scope, problems) {
|
|
7752
|
+
const name = path9.basename(file);
|
|
7753
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
7754
|
+
problems.push({
|
|
7755
|
+
scope,
|
|
7756
|
+
reason: REASON_UNREAD,
|
|
7757
|
+
kinds,
|
|
7758
|
+
message: `\u4EA7\u7269 ${name} \u4E0D\u662F\u5BF9\u8C61\uFF08${payload === null ? "null" : Array.isArray(payload) ? "array" : typeof payload}\uFF09\u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7759
|
+
});
|
|
7760
|
+
return [];
|
|
7761
|
+
}
|
|
7762
|
+
const body = payload;
|
|
7763
|
+
if (!(key in body)) {
|
|
7764
|
+
problems.push({
|
|
7765
|
+
scope,
|
|
7766
|
+
reason: REASON_UNREAD,
|
|
7767
|
+
kinds,
|
|
7768
|
+
message: `\u4EA7\u7269 ${name} \u91CC\u6CA1\u6709 ${key} \u8FD9\u4E2A\u952E\uFF08\u9876\u5C42\u952E\u662F ${JSON.stringify(Object.keys(body).sort())}\uFF09\u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7769
|
+
});
|
|
7770
|
+
return [];
|
|
7771
|
+
}
|
|
7772
|
+
const value = body[key];
|
|
7773
|
+
if (!Array.isArray(value)) {
|
|
7774
|
+
problems.push({
|
|
7775
|
+
scope,
|
|
7776
|
+
reason: REASON_UNREAD,
|
|
7777
|
+
kinds,
|
|
7778
|
+
message: `\u4EA7\u7269 ${name} \u7684 ${key} \u4E0D\u662F\u6570\u7EC4\uFF08${value === null ? "null" : typeof value}\uFF09\u21D2 ${unguardedWord(kinds)} \u8FD9\u51E0\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7779
|
+
});
|
|
7780
|
+
return [];
|
|
7781
|
+
}
|
|
7782
|
+
return value;
|
|
7783
|
+
}
|
|
7784
|
+
function deriveAclass(root, mappingArtifact = null) {
|
|
7785
|
+
const problems = [];
|
|
7786
|
+
const out = Object.fromEntries(ACLASS_KINDS.map((k) => [k, /* @__PURE__ */ new Set()]));
|
|
7787
|
+
for (const row of readJsonl(
|
|
7788
|
+
path9.join(root, ...FIELDS_INDEX),
|
|
7789
|
+
["fieldKey", "fieldLabel", "caliber"],
|
|
7790
|
+
CLASS_SCOPE,
|
|
7791
|
+
problems
|
|
7792
|
+
)) {
|
|
7793
|
+
const key = token(row.key);
|
|
7794
|
+
if (key !== null) out.fieldKey.add(key);
|
|
7795
|
+
const name = token(row.name);
|
|
7796
|
+
if (name !== null) out.fieldLabel.add(name);
|
|
7797
|
+
const synonyms = row.synonyms;
|
|
7798
|
+
for (const syn of Array.isArray(synonyms) ? synonyms : []) {
|
|
7799
|
+
const one = token(syn);
|
|
7800
|
+
if (one !== null) out.caliber.add(one);
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
for (const row of readJsonl(
|
|
7804
|
+
path9.join(root, ...ENUM_MIRROR),
|
|
7805
|
+
["enumValue"],
|
|
7806
|
+
CLASS_SCOPE,
|
|
7807
|
+
problems
|
|
7808
|
+
)) {
|
|
7809
|
+
const enums = row.enums;
|
|
7810
|
+
for (const element of Array.isArray(enums) ? enums : []) {
|
|
7811
|
+
if (typeof element !== "object" || element === null || Array.isArray(element)) continue;
|
|
7812
|
+
for (const field of ENUM_ELEMENT_KEYS) {
|
|
7813
|
+
const one = token(element[field]);
|
|
7814
|
+
if (one !== null) out.enumValue.add(one);
|
|
7815
|
+
}
|
|
7816
|
+
}
|
|
7817
|
+
}
|
|
7818
|
+
const rosterPath = path9.join(root, ...MODEL_ROSTER);
|
|
7819
|
+
const roster = readJson(rosterPath, ["modelName"], CLASS_SCOPE, problems);
|
|
7820
|
+
if (roster !== UNREADABLE) {
|
|
7821
|
+
const rows = listUnder(roster, "models", rosterPath, ["modelName"], CLASS_SCOPE, problems);
|
|
7822
|
+
const entries = modelEntries(roster);
|
|
7823
|
+
const keys = modelKeys(roster);
|
|
7824
|
+
for (const key of keys) out.modelName.add(key);
|
|
7825
|
+
for (const entry of entries) {
|
|
7826
|
+
const name = token(entry.name);
|
|
7827
|
+
if (name !== null) out.modelName.add(name);
|
|
7828
|
+
}
|
|
7829
|
+
if (rows.length !== entries.length) {
|
|
7830
|
+
problems.push({
|
|
7831
|
+
scope: ENTRY_SCOPE,
|
|
7832
|
+
reason: "dropped-entries",
|
|
7833
|
+
kinds: ["modelName"],
|
|
7834
|
+
message: `models.json \u91CC ${rows.length} \u6761\u6A21\u578B\u6761\u76EE\u6709 ${rows.length - entries.length} \u6761\u4E0D\u662F\u5BF9\u8C61 \u21D2 \u5B83\u4EEC\u7684\u6A21\u578B\u540D\u6CA1\u6709\u8FDB modelName`
|
|
7835
|
+
});
|
|
7836
|
+
}
|
|
7837
|
+
if (entries.length !== keys.length) {
|
|
7838
|
+
problems.push({
|
|
7839
|
+
scope: ENTRY_SCOPE,
|
|
7840
|
+
reason: "dropped-keys",
|
|
7841
|
+
kinds: ["modelName"],
|
|
7842
|
+
message: `models.json \u91CC ${entries.length} \u6761\u6A21\u578B\u6761\u76EE\u6709 ${entries.length - keys.length} \u6761\u6CA1\u6709 modelKey \u21D2 \u5B83\u4EEC\u7684\u82F1\u6587\u6A21\u578B\u540D\u6CA1\u6709\u8FDB modelName`
|
|
7843
|
+
});
|
|
7844
|
+
}
|
|
7845
|
+
}
|
|
7846
|
+
const valueDomain = path9.join(root, VALUE_DOMAIN_DIR);
|
|
7847
|
+
if (existsSync4(valueDomain) && statSync2(valueDomain).isDirectory()) {
|
|
7848
|
+
const tables = readdirSync2(valueDomain).filter((f) => f.endsWith(".json") && !RESERVED_FILENAMES.includes(f)).sort();
|
|
7849
|
+
for (const name of tables) {
|
|
7850
|
+
const file = path9.join(valueDomain, name);
|
|
7851
|
+
const payload = readJson(file, ["codeValue"], ENTRY_SCOPE, problems);
|
|
7852
|
+
if (payload === UNREADABLE) continue;
|
|
7853
|
+
for (const row of listUnder(payload, "rows", file, ["codeValue"], ENTRY_SCOPE, problems)) {
|
|
7854
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
7855
|
+
problems.push({
|
|
7856
|
+
scope: ENTRY_SCOPE,
|
|
7857
|
+
reason: "bad-row",
|
|
7858
|
+
kinds: ["codeValue"],
|
|
7859
|
+
message: `${name} \u7684 rows \u91CC\u6709\u4E00\u4E2A\u5143\u7D20\u4E0D\u662F\u5BF9\u8C61\uFF08${row === null ? "null" : typeof row}\uFF09\u21D2 \u5B83\u7684\u53D6\u503C\u6CA1\u6709\u8FDB codeValue`
|
|
7860
|
+
});
|
|
7861
|
+
continue;
|
|
7862
|
+
}
|
|
7863
|
+
for (const value of scalarsWithin(row)) out.codeValue.add(value);
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7866
|
+
} else {
|
|
7867
|
+
problems.push({
|
|
7868
|
+
scope: CLASS_SCOPE,
|
|
7869
|
+
reason: REASON_UNREAD,
|
|
7870
|
+
kinds: ["codeValue"],
|
|
7871
|
+
message: `\u7F3A\u4EA7\u7269\u76EE\u5F55 ${VALUE_DOMAIN_DIR}/ \u21D2 codeValue \u8FD9\u4E00\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7872
|
+
});
|
|
7873
|
+
}
|
|
7874
|
+
let businessKeys = null;
|
|
7875
|
+
if (mappingArtifact === null) {
|
|
7876
|
+
problems.push({
|
|
7877
|
+
scope: CLASS_SCOPE,
|
|
7878
|
+
reason: REASON_UNREAD,
|
|
7879
|
+
kinds: ["naturalKey"],
|
|
7880
|
+
message: `\u6CA1\u6709\u6307\u5B9A\u6620\u5C04\u5DE5\u4EF6 \u21D2 naturalKey \u8FD9\u4E00\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88\uFF08\u628A ${MAPPING_ARTIFACT_HINT} \u7684\u8DEF\u5F84\u4F20\u8FDB\u6765\uFF09`
|
|
7881
|
+
});
|
|
7882
|
+
} else {
|
|
7883
|
+
try {
|
|
7884
|
+
businessKeys = readBusinessKeyFields(mappingArtifact);
|
|
7885
|
+
} catch (e) {
|
|
7886
|
+
const err = e;
|
|
7887
|
+
problems.push({
|
|
7888
|
+
scope: CLASS_SCOPE,
|
|
7889
|
+
reason: REASON_UNREAD,
|
|
7890
|
+
kinds: ["naturalKey"],
|
|
7891
|
+
message: `\u6620\u5C04\u5DE5\u4EF6\u8BFB\u4E0D\u52A8\uFF08[${err.reason ?? "unknown"}] ${err.message}\uFF09\u21D2 naturalKey \u8FD9\u4E00\u7C7B\u6B64\u523B\u65E0\u4EBA\u5B88`
|
|
7892
|
+
});
|
|
7893
|
+
}
|
|
7894
|
+
}
|
|
7895
|
+
for (const fields of businessKeys ?? []) {
|
|
7896
|
+
for (const field of fields) out.naturalKey.add(field);
|
|
7897
|
+
if (fields.length > 1) {
|
|
7898
|
+
out.naturalKey.add(fields.join("+"));
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
const named = unguardedKinds(problems);
|
|
7902
|
+
for (const kind of ACLASS_KINDS) {
|
|
7903
|
+
if (out[kind].size === 0 && !named.has(kind)) {
|
|
7904
|
+
problems.push({
|
|
7905
|
+
scope: CLASS_SCOPE,
|
|
7906
|
+
reason: REASON_EMPTY,
|
|
7907
|
+
kinds: [kind],
|
|
7908
|
+
message: `${kind}\uFF1A\u4EA7\u7269\u8BFB\u5230\u4E86\uFF0C\u4F46\u4E00\u6761\u53D6\u503C\u90FD\u6CA1\u6D3E\u751F\u51FA\u6765 \u21D2 \u95E8 0 \u5BF9\u8FD9\u4E00\u7C7B\u6052\u7EFF\uFF08\u540C\u6279\u53F0\u8D26\u91CC\u82E5\u6709\u6761\u76EE\u7EA7\u95EE\u9898\uFF0C\u771F\u56E0\u5728\u90A3\u91CC\uFF09`
|
|
7909
|
+
});
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7912
|
+
return { aclass: out, problems };
|
|
7913
|
+
}
|
|
7914
|
+
|
|
7915
|
+
// src/fde/gate0/facts.ts
|
|
7916
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
7917
|
+
import * as path10 from "path";
|
|
7918
|
+
var REGISTRY_SCOPE = "registry";
|
|
7919
|
+
var ENTRY_SCOPE2 = "entry";
|
|
7920
|
+
var REASON_NO_ENTRIES = "no-entries";
|
|
7921
|
+
var REASON_PROBE_UNVERIFIED = "probe-unverified";
|
|
7922
|
+
var REGISTRY_REASONS = [REASON_NO_ENTRIES, REASON_PROBE_UNVERIFIED];
|
|
7923
|
+
var REASON_PROBE_SHAPE = "probe-shape";
|
|
7924
|
+
var REASON_PROBE_ABSENT = "probe-absent";
|
|
7925
|
+
var REASON_DUPLICATE_TOKEN = "duplicate-token";
|
|
7926
|
+
var ENTRY_REASONS = [
|
|
7927
|
+
REASON_PROBE_SHAPE,
|
|
7928
|
+
REASON_PROBE_ABSENT,
|
|
7929
|
+
REASON_DUPLICATE_TOKEN
|
|
7930
|
+
];
|
|
7931
|
+
var FACTS_REASONS = [...REGISTRY_REASONS, ...ENTRY_REASONS];
|
|
7932
|
+
var PROBE_SHAPE = /^(?<file>[A-Za-z0-9_./-]+\.test\.ts)::(?<mark>[A-Za-z0-9_-]+)$/;
|
|
7933
|
+
var PROBE_MARK_PREFIX = "// P:";
|
|
7934
|
+
var PROTOCOL_FACTS = [
|
|
7935
|
+
{
|
|
7936
|
+
token: "/api/models/{model}/meta",
|
|
7937
|
+
probe: "packages/core/tests/fde-recon-crawl.test.ts::meta-path-wire-shape",
|
|
7938
|
+
note: "\u5B57\u6BB5\u5143\u6570\u636E\u8D70\u88F8\u8DEF\u5F84 GET\uFF0C\u9ED8\u8BA4\u4E0D\u7F00 ?type=\u3002\u63A2\u9488\u65AD\u7684\u662F**\u5B9E\u9645\u53D1\u51FA\u53BB\u7684 URL**\uFF0C\u4E0D\u662F\u62FC\u4E32\u51FD\u6570\u7684\u8FD4\u56DE\u503C\u3002"
|
|
7939
|
+
},
|
|
7940
|
+
{
|
|
7941
|
+
token: "/api/models/{model}/list/dsl",
|
|
7942
|
+
probe: "packages/core/tests/fde-recon-valuedomain.test.ts::list-dsl-count-probe",
|
|
7943
|
+
note: "\u5217\u8868\u67E5\u8BE2\u8D70 POST DSL\u3002\u63A2\u9488\u65AD\u7684\u662F\u884C\u4E3A\uFF1Asize=0 \u7684\u90A3\u4E00\u53D1\u5148\u51FA\u4E14\u8FD4\u56DE totalElements\uFF0C\u53D6\u9875\u5224\u636E\u4EE5\u6B64\u4E3A\u524D\u63D0\u3002"
|
|
7944
|
+
},
|
|
7945
|
+
{
|
|
7946
|
+
token: "/api/auth/login",
|
|
7947
|
+
probe: "packages/core/tests/fde-gate0-facts.test.ts::login-body-carries-tenant",
|
|
7948
|
+
note: "\u767B\u5F55\u7684 tenantId \u5728**\u8BF7\u6C42\u4F53**\u91CC\uFF0C\u4E0D\u5728 header\u3002\u63A2\u9488\u65AD\u7684\u662F\u5B9E\u9645\u53D1\u51FA\u53BB\u7684\u8BF7\u6C42\u4F53\u4E0E\u8BF7\u6C42\u5934\uFF0C\u5220\u6389 tenantId \u6216\u632A\u8FDB header \u90FD\u4F1A\u7EA2\u3002"
|
|
7949
|
+
}
|
|
7950
|
+
];
|
|
7951
|
+
function loadProtocolFacts(entries = PROTOCOL_FACTS, options = {}) {
|
|
7952
|
+
const problems = [];
|
|
7953
|
+
const probeRoot = options.probeRoot;
|
|
7954
|
+
if (probeRoot === void 0 && entries !== PROTOCOL_FACTS) {
|
|
7955
|
+
problems.push({
|
|
7956
|
+
scope: REGISTRY_SCOPE,
|
|
7957
|
+
reason: REASON_PROBE_UNVERIFIED,
|
|
7958
|
+
index: 0,
|
|
7959
|
+
token: null,
|
|
7960
|
+
message: "\u8FD9\u4EFD\u767B\u8BB0\u518C\u4E0D\u662F\u968F\u5305\u90A3\u4E00\u4EFD\uFF0C\u53C8\u6CA1\u6709\u7ED9\u63A2\u9488\u6839\u76EE\u5F55 \u21D2 \u4E00\u6761\u63A2\u9488\u90FD\u6CA1\u9A8C\u8FC7 \u2014\u2014\u300C\u63A2\u9488\u90FD\u5728\u300D\u4E0E\u300C\u6CA1\u67E5\u63A2\u9488\u300D\u5728\u8FD4\u56DE\u503C\u4E0A\u540C\u5F62\uFF0C\u6240\u4EE5\u8FD9\u91CC\u8BB0\u4E00\u7B14\uFF0C\u4E0D\u88C5\u4F5C\u9A8C\u8FC7"
|
|
7961
|
+
});
|
|
7962
|
+
}
|
|
7963
|
+
if (entries.length === 0) {
|
|
7964
|
+
problems.push({
|
|
7965
|
+
scope: REGISTRY_SCOPE,
|
|
7966
|
+
reason: REASON_NO_ENTRIES,
|
|
7967
|
+
index: 0,
|
|
7968
|
+
token: null,
|
|
7969
|
+
message: "\u767B\u8BB0\u518C\u4E00\u6761\u90FD\u6CA1\u6709 \u2014\u2014 **\u67E5\u8FC7\u4E14\u4E3A\u7A7A**\uFF0C\u4E0D\u662F\u300C\u6CA1\u67E5\u300D"
|
|
7970
|
+
});
|
|
7971
|
+
}
|
|
7972
|
+
const times = /* @__PURE__ */ new Map();
|
|
7973
|
+
for (const entry of entries) times.set(entry.token, (times.get(entry.token) ?? 0) + 1);
|
|
7974
|
+
const facts = [];
|
|
7975
|
+
entries.forEach((entry, position) => {
|
|
7976
|
+
const index = position + 1;
|
|
7977
|
+
if ((times.get(entry.token) ?? 0) > 1) {
|
|
7978
|
+
problems.push({
|
|
7979
|
+
scope: ENTRY_SCOPE2,
|
|
7980
|
+
reason: REASON_DUPLICATE_TOKEN,
|
|
7981
|
+
index,
|
|
7982
|
+
token: entry.token,
|
|
7983
|
+
message: `${entry.token} \u767B\u8BB0\u4E86\u4E0D\u6B62\u4E00\u6B21 \u21D2 \u4E24\u6761\u90FD\u4E0D\u8FDB P\uFF08\u7559\u4E0B\u4EFB\u4F55\u4E00\u8FB9\u90FD\u8981\u56DE\u7B54\u300C\u4EE5\u54EA\u4EFD\u4E3A\u51C6\u300D\uFF09`
|
|
7984
|
+
});
|
|
7985
|
+
return;
|
|
7986
|
+
}
|
|
7987
|
+
const shape = PROBE_SHAPE.exec(entry.probe);
|
|
7988
|
+
if (!shape?.groups) {
|
|
7989
|
+
problems.push({
|
|
7990
|
+
scope: ENTRY_SCOPE2,
|
|
7991
|
+
reason: REASON_PROBE_SHAPE,
|
|
7992
|
+
index,
|
|
7993
|
+
token: entry.token,
|
|
7994
|
+
message: `${entry.token} \u7684\u63A2\u9488 ${JSON.stringify(entry.probe)} \u4E0D\u662F \`<\u6D4B\u8BD5\u6587\u4EF6>.test.ts::<\u6807\u8BB0>\` \u5F62\u6001 \u2014\u2014 \u53EA\u7ED9\u6587\u4EF6\u8DEF\u5F84\u662F\u540C\u4E49\u53CD\u590D\uFF08\u300C\u90A3\u4E2A\u6587\u4EF6\u5B58\u5728\u300D\u4E0D\u7B97\u80CC\u4E66\uFF09`
|
|
7995
|
+
});
|
|
7996
|
+
return;
|
|
7997
|
+
}
|
|
7998
|
+
if (probeRoot !== void 0) {
|
|
7999
|
+
const file = path10.join(probeRoot, shape.groups.file);
|
|
8000
|
+
const mark = `${PROBE_MARK_PREFIX} ${shape.groups.mark}`;
|
|
8001
|
+
const found = existsSync5(file) && readFileSync6(file, "utf8").includes(mark);
|
|
8002
|
+
if (!found) {
|
|
8003
|
+
problems.push({
|
|
8004
|
+
scope: ENTRY_SCOPE2,
|
|
8005
|
+
reason: REASON_PROBE_ABSENT,
|
|
8006
|
+
index,
|
|
8007
|
+
token: entry.token,
|
|
8008
|
+
message: `${entry.token} \u7684\u63A2\u9488\u6307\u5411 ${entry.probe}\uFF0C\u4F46\u90A3\u91CC\u627E\u4E0D\u5230 \`${mark}\` \u8FD9\u6761\u6807\u8BB0 \u21D2 \u8FD9\u6761\u767B\u8BB0\u6CA1\u6709\u80CC\u4E66\uFF0C\u4E0D\u8FDB P`
|
|
8009
|
+
});
|
|
8010
|
+
return;
|
|
8011
|
+
}
|
|
8012
|
+
}
|
|
8013
|
+
facts.push(entry.token);
|
|
8014
|
+
});
|
|
8015
|
+
return { facts, problems };
|
|
8016
|
+
}
|
|
8017
|
+
|
|
8018
|
+
// src/fde/gate0/scan.ts
|
|
8019
|
+
import { readFileSync as readFileSync7, readdirSync as readdirSync3, readlinkSync, statSync as statSync3 } from "fs";
|
|
8020
|
+
import * as path11 from "path";
|
|
8021
|
+
var CHANNEL_ACLASS = "aclass";
|
|
8022
|
+
var CHANNEL_ARTIFACT = "artifact-path";
|
|
8023
|
+
var CHANNEL_UNREGISTERED = "protocol-unregistered";
|
|
8024
|
+
var CHANNEL_REGISTERED_IS_ACLASS = "registered-but-is-aclass";
|
|
8025
|
+
var CHANNEL_NO_PROBE = "no-probe";
|
|
8026
|
+
var CHANNELS = [
|
|
8027
|
+
CHANNEL_ACLASS,
|
|
8028
|
+
CHANNEL_ARTIFACT,
|
|
8029
|
+
CHANNEL_UNREGISTERED,
|
|
8030
|
+
CHANNEL_REGISTERED_IS_ACLASS,
|
|
8031
|
+
CHANNEL_NO_PROBE
|
|
8032
|
+
];
|
|
8033
|
+
var REASON_NO_SKILL_ROOT = "no-skill-root";
|
|
8034
|
+
var REASON_NOTHING_SCANNED = "nothing-scanned";
|
|
8035
|
+
var REASON_FILE_UNREADABLE = "file-unreadable";
|
|
8036
|
+
var REASON_ACLASS_EMPTY = "aclass-empty";
|
|
8037
|
+
var REASON_ACLASS_PARTIALLY_UNREAD = "aclass-partially-unread";
|
|
8038
|
+
var REASON_REGISTRY = "registry-not-established";
|
|
8039
|
+
var REASON_CHANNEL_FOUR_UNCHECKED = "channel-four-unchecked";
|
|
8040
|
+
var REASON_BASELINE_ROUND_NOT_COMPLETE = "baseline-round-not-complete";
|
|
8041
|
+
var REASON_SYMLINK_NOT_SCANNED = "symlink-not-scanned";
|
|
8042
|
+
var PREMISE_REASONS = [
|
|
8043
|
+
REASON_NO_SKILL_ROOT,
|
|
8044
|
+
REASON_NOTHING_SCANNED,
|
|
8045
|
+
REASON_FILE_UNREADABLE,
|
|
8046
|
+
REASON_ACLASS_EMPTY,
|
|
8047
|
+
REASON_ACLASS_PARTIALLY_UNREAD,
|
|
8048
|
+
REASON_REGISTRY,
|
|
8049
|
+
REASON_CHANNEL_FOUR_UNCHECKED,
|
|
8050
|
+
REASON_BASELINE_ROUND_NOT_COMPLETE,
|
|
8051
|
+
REASON_SYMLINK_NOT_SCANNED
|
|
8052
|
+
];
|
|
8053
|
+
var PREMISE_REGISTRY_REASONS = REGISTRY_REASONS.filter(
|
|
8054
|
+
(r) => r !== REASON_NO_ENTRIES
|
|
8055
|
+
);
|
|
8056
|
+
var WORD_RE = /[A-Za-z0-9_]+/g;
|
|
8057
|
+
var SIMPLE_TOKEN = /^[A-Za-z0-9_]+$/;
|
|
8058
|
+
var WORD_CHARS = new Set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
|
|
8059
|
+
function isWordChar(char) {
|
|
8060
|
+
return char !== void 0 && WORD_CHARS.has(char);
|
|
8061
|
+
}
|
|
8062
|
+
var ENDPOINT_PREFIXES = ["api", "ws"];
|
|
8063
|
+
var ENDPOINT_PATH = `/(?:${ENDPOINT_PREFIXES.join("|")})(?:/(?:\\{[A-Za-z0-9_]+\\}|[A-Za-z0-9_.\\-]+))+`;
|
|
8064
|
+
var BARE_ENDPOINT_RE = new RegExp(`(?<![A-Za-z0-9_.\\-/])${ENDPOINT_PATH}`, "g");
|
|
8065
|
+
var URL_ENDPOINT_RE = new RegExp(
|
|
8066
|
+
`[A-Za-z][A-Za-z0-9+.\\-]*://[^\\s"'\`<>]*?(${ENDPOINT_PATH})`,
|
|
8067
|
+
"g"
|
|
8068
|
+
);
|
|
8069
|
+
var HOSTLESS_HOST = `(?:(?:[A-Za-z0-9_\\-]+\\.)+[A-Za-z]{2,}(?::[0-9]+)?|[A-Za-z0-9_\\-]+:[0-9]+)`;
|
|
8070
|
+
var HOSTLESS_ENDPOINT_RE = new RegExp(
|
|
8071
|
+
`(?<![A-Za-z0-9_.\\-/:@])${HOSTLESS_HOST}(${ENDPOINT_PATH})`,
|
|
8072
|
+
"g"
|
|
8073
|
+
);
|
|
8074
|
+
var ERROR_CODE_RE = /(?<![A-Za-z0-9_])[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+(?![A-Za-z0-9_])/g;
|
|
8075
|
+
var PATHISH_RE = /(?<![A-Za-z0-9_./-])\.?[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+/g;
|
|
8076
|
+
var ARTIFACT_SUFFIXES = [".json", ".jsonl"];
|
|
8077
|
+
var PRODUCT_FILE_RE = new RegExp(
|
|
8078
|
+
`(?<![A-Za-z0-9_./\\-])[A-Za-z0-9_.\\-]+(?:${ARTIFACT_SUFFIXES.map((s) => s.replace(".", "\\.")).join("|")})(?![A-Za-z0-9_])`,
|
|
8079
|
+
"g"
|
|
8080
|
+
);
|
|
8081
|
+
var PLACEHOLDER_RE2 = /\{[A-Za-z0-9_]*\}/g;
|
|
8082
|
+
var ARTIFACT_ROOT = ".hcmnx";
|
|
8083
|
+
var RECON_DIRNAME = "recon";
|
|
8084
|
+
var RECON_SEGMENTS_BELOW_ROOT = 2;
|
|
8085
|
+
var PRODUCT_RELATIVE_SHAPES = [
|
|
8086
|
+
["index", ".md"],
|
|
8087
|
+
["index", ".jsonl"],
|
|
8088
|
+
["mapping", ".json"],
|
|
8089
|
+
["meta", ".json"],
|
|
8090
|
+
["valuedomain", ".json"],
|
|
8091
|
+
["valuedomain", ".jsonl"]
|
|
8092
|
+
];
|
|
8093
|
+
var REASON_ENVIRONMENT_FACT = "environment-fact";
|
|
8094
|
+
var REASON_PRODUCT_TREE_PATH = "product-tree-path";
|
|
8095
|
+
var REASON_PRODUCT_RELATIVE_PATH = "product-relative-path";
|
|
8096
|
+
var REASON_PRODUCT_FILE_NAME = "product-file-name";
|
|
8097
|
+
var REASON_ENDPOINT = "endpoint";
|
|
8098
|
+
var REASON_ERROR_CODE = "error-code";
|
|
8099
|
+
var REASON_REGISTERED_IS_ACLASS = "registered-is-aclass";
|
|
8100
|
+
var REASON_REJECTED_REGISTRATION = "rejected-registration";
|
|
8101
|
+
var RED_REASONS = {
|
|
8102
|
+
[CHANNEL_ACLASS]: [REASON_ENVIRONMENT_FACT],
|
|
8103
|
+
[CHANNEL_ARTIFACT]: [
|
|
8104
|
+
REASON_PRODUCT_TREE_PATH,
|
|
8105
|
+
REASON_PRODUCT_RELATIVE_PATH,
|
|
8106
|
+
REASON_PRODUCT_FILE_NAME
|
|
8107
|
+
],
|
|
8108
|
+
[CHANNEL_UNREGISTERED]: [REASON_ENDPOINT, REASON_ERROR_CODE],
|
|
8109
|
+
[CHANNEL_REGISTERED_IS_ACLASS]: [REASON_REGISTERED_IS_ACLASS],
|
|
8110
|
+
[CHANNEL_NO_PROBE]: [REASON_REJECTED_REGISTRATION]
|
|
8111
|
+
};
|
|
8112
|
+
var ARTIFACT_WORDING = {
|
|
8113
|
+
[REASON_PRODUCT_TREE_PATH]: (t) => `\u51FA\u73B0**\u4EA7\u7269\u6811\u91CC\u7684**\u8DEF\u5F84 ${JSON.stringify(t)}`,
|
|
8114
|
+
[REASON_PRODUCT_RELATIVE_PATH]: (t) => `\u51FA\u73B0**\u4EA7\u7269\u6839\u76F8\u5BF9\u8DEF\u5F84** ${JSON.stringify(t)}\uFF08\u4EA7\u7269\u5B50\u76EE\u5F55\u4E0B\u7684\u90A3\u4E00\u4EFD\uFF09`,
|
|
8115
|
+
[REASON_PRODUCT_FILE_NAME]: (t) => `\u51FA\u73B0\u4EA7\u7269\u6587\u4EF6\u540D\u5F62\u6001 ${JSON.stringify(t)}\uFF08\u4EE5 .json/.jsonl \u6536\u5C3E\uFF1B**\u8FD9\u4E00\u6863\u4F1A\u8FC7\u5224**\uFF0C\u5B83\u4E0E\u4EA7\u7269\u65E0\u5173\u65F6\u8BF7\u7167\u76F4\u8BF4\u660E\uFF09`
|
|
8116
|
+
};
|
|
8117
|
+
function artifactWordingFor(reason) {
|
|
8118
|
+
const wording = ARTIFACT_WORDING[reason];
|
|
8119
|
+
if (wording === void 0) {
|
|
8120
|
+
throw new Error(
|
|
8121
|
+
`\u4EA7\u7269\u8DEF\u5F84\u6210\u56E0 ${JSON.stringify(reason)} \u6CA1\u6709\u5BF9\u5E94\u7684\u63AA\u8F9E\uFF08\u8BA4\u5F97\u7684\u662F ${Object.keys(ARTIFACT_WORDING).join("/")}\uFF09`
|
|
8122
|
+
);
|
|
8123
|
+
}
|
|
8124
|
+
return wording;
|
|
8125
|
+
}
|
|
8126
|
+
var ACLASS_EMPTY_WHY = {
|
|
8127
|
+
[REASON_UNREAD]: "\uFF08\u4EA7\u7269/\u5DE5\u4EF6\u90A3\u4E00\u5934\u6839\u672C\u6CA1\u8BFB\u5230\uFF09",
|
|
8128
|
+
[REASON_EMPTY]: "\uFF08\u4EA7\u7269\u8BFB\u5230\u4E86\uFF0C\u4E00\u6761\u53D6\u503C\u90FD\u6CA1\u6D3E\u751F\u51FA\u6765\uFF09"
|
|
8129
|
+
};
|
|
8130
|
+
var GAP_NOISE_TOKEN = "pure-digit-or-single-char";
|
|
8131
|
+
var GAP_CJK_INSIDE_LONGER_RUN = "cjk-inside-longer-run";
|
|
8132
|
+
var GAP_REASONS = [GAP_NOISE_TOKEN, GAP_CJK_INSIDE_LONGER_RUN];
|
|
8133
|
+
function checkReds(reds) {
|
|
8134
|
+
for (const red of reds) {
|
|
8135
|
+
if (!CHANNELS.includes(red.channel)) {
|
|
8136
|
+
throw new Error(
|
|
8137
|
+
`\u5224\u7EA2\u7684 channel \u53EA\u6709 ${CHANNELS.join("/")} \u8FD9\u51E0\u4E2A\u53D6\u503C\uFF0C\u62FF\u5230\u7684\u662F ${JSON.stringify(red.channel)}\uFF08${red.message}\uFF09`
|
|
8138
|
+
);
|
|
8139
|
+
}
|
|
8140
|
+
if (!RED_REASONS[red.channel].includes(red.reason)) {
|
|
8141
|
+
throw new Error(
|
|
8142
|
+
`${JSON.stringify(red.reason)} \u8FD9\u4E2A reason \u4E0D\u5C5E\u4E8E ${red.channel} \u8FD9\u6761\u901A\u9053\uFF08\u8BA4\u5F97\u7684\u662F ${RED_REASONS[red.channel].join("/")}\uFF1B${red.message}\uFF09`
|
|
8143
|
+
);
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
return [...reds];
|
|
8147
|
+
}
|
|
8148
|
+
function checkGaps(gaps) {
|
|
8149
|
+
for (const gap of gaps) {
|
|
8150
|
+
if (!GAP_REASONS.includes(gap.reason)) {
|
|
8151
|
+
throw new Error(
|
|
8152
|
+
`\u7F3A\u53E3\u53F0\u8D26\u7684 reason \u53EA\u6709 ${GAP_REASONS.join("/")} \u4E24\u4E2A\u53D6\u503C\uFF0C\u62FF\u5230\u7684\u662F ${JSON.stringify(gap.reason)}\uFF08${gap.why}\uFF09`
|
|
8153
|
+
);
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
return [...gaps];
|
|
8157
|
+
}
|
|
8158
|
+
function checkPremise(problems) {
|
|
8159
|
+
for (const problem of problems) {
|
|
8160
|
+
if (!PREMISE_REASONS.includes(problem.reason)) {
|
|
8161
|
+
throw new Error(
|
|
8162
|
+
`\u524D\u63D0\u7EA7\u53F0\u8D26\u7684 reason \u4E0D\u5728\u53D6\u503C\u57DF\u91CC\uFF1A${JSON.stringify(problem.reason)}\uFF08${problem.message}\uFF09`
|
|
8163
|
+
);
|
|
8164
|
+
}
|
|
8165
|
+
}
|
|
8166
|
+
return [...problems];
|
|
8167
|
+
}
|
|
8168
|
+
function baselineRoundPremise(round) {
|
|
8169
|
+
const state = round && typeof round === "object" ? round.state : void 0;
|
|
8170
|
+
if (state === "complete") return [];
|
|
8171
|
+
return [
|
|
8172
|
+
{
|
|
8173
|
+
reason: REASON_BASELINE_ROUND_NOT_COMPLETE,
|
|
8174
|
+
message: `\u8FD9\u4EFD\u57FA\u7EBF\u7684\u8F6E\u6B21\u72B6\u6001\u662F ${JSON.stringify(state)}\uFF08\u4E0D\u662F complete\uFF09\u21D2 A \u7C7B\u4E8B\u5B9E\u662F\u4ECE**\u6DF7\u7740\u4E0D\u540C\u8F6E\u6B21 / \u5224\u4E0D\u4E86\u8F6E\u6B21**\u7684\u4EA7\u7269\u91CC\u73B0\u7B97\u7684\uFF0C\u300C\u5224\u7EA2 0 \u6761\u300D\u5BF9\u54EA\u4E00\u8F6E\u90FD\u4E0D\u7B97\u6570\u3002\u5148\u8DD1 \`hcm delivery recon round\` \u770B\u662F\u54EA\u51E0\u4EFD\uFF0C\u6216\u91CD\u8DD1\u4E00\u6B21 \`hcm delivery recon\`\u3002\u26A0\uFE0F **\`hcm delivery recon\` \u8981\u8FDE\u5BA2\u6237\u5185\u7F51\u7684 HCM** \u2014\u2014 \u62FF\u4E0D\u5230\u7F51\u7684\u673A\u5668\u4E0A\u8FD9\u6761\u51FA\u8DEF\u8D70\u4E0D\u901A\uFF0C\u6B64\u65F6**\u522B\u628A\u8FD9\u4EFD\u57FA\u7EBF\u5F53 complete \u7528**\uFF0C\u628A\u5B83\u5E26\u56DE\u8FDE\u5F97\u4E0A\u7684\u5730\u65B9\u91CD\u722C`
|
|
8175
|
+
}
|
|
8176
|
+
];
|
|
8177
|
+
}
|
|
8178
|
+
function inGap(token2) {
|
|
8179
|
+
return [...token2].length <= 1 || /^\p{Nd}+$/u.test(token2);
|
|
8180
|
+
}
|
|
8181
|
+
function allMatches(regex, line) {
|
|
8182
|
+
const out = [];
|
|
8183
|
+
const re = new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`);
|
|
8184
|
+
let m;
|
|
8185
|
+
while ((m = re.exec(line)) !== null) {
|
|
8186
|
+
if (m[0] === "") {
|
|
8187
|
+
re.lastIndex += 1;
|
|
8188
|
+
continue;
|
|
8189
|
+
}
|
|
8190
|
+
out.push(m);
|
|
8191
|
+
}
|
|
8192
|
+
return out;
|
|
8193
|
+
}
|
|
8194
|
+
function overlaps(span, spans) {
|
|
8195
|
+
return spans.some(([s, e]) => span[0] < e && s < span[1]);
|
|
8196
|
+
}
|
|
8197
|
+
function endpointsIn(line) {
|
|
8198
|
+
const out = [];
|
|
8199
|
+
for (const regex of [URL_ENDPOINT_RE, HOSTLESS_ENDPOINT_RE]) {
|
|
8200
|
+
for (const m of allMatches(regex, line)) {
|
|
8201
|
+
const span = [m.index, m.index + m[0].length];
|
|
8202
|
+
if (!overlaps(
|
|
8203
|
+
span,
|
|
8204
|
+
out.map(([, s]) => s)
|
|
8205
|
+
)) {
|
|
8206
|
+
out.push([m[1], span]);
|
|
8207
|
+
}
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
for (const m of allMatches(BARE_ENDPOINT_RE, line)) {
|
|
8211
|
+
const span = [m.index, m.index + m[0].length];
|
|
8212
|
+
if (!overlaps(
|
|
8213
|
+
span,
|
|
8214
|
+
out.map(([, s]) => s)
|
|
8215
|
+
)) {
|
|
8216
|
+
out.push([m[0], span]);
|
|
8217
|
+
}
|
|
8218
|
+
}
|
|
8219
|
+
return out;
|
|
8220
|
+
}
|
|
8221
|
+
function normalize(token2) {
|
|
8222
|
+
return token2.replace(PLACEHOLDER_RE2, "{}");
|
|
8223
|
+
}
|
|
8224
|
+
function factsIndex(facts) {
|
|
8225
|
+
return facts.map((token2) => segmentsOf(normalize(token2)));
|
|
8226
|
+
}
|
|
8227
|
+
function covered(token2, index) {
|
|
8228
|
+
const segments = segmentsOf(normalize(token2));
|
|
8229
|
+
return index.some(
|
|
8230
|
+
(template) => template.length === segments.length && template.every((t, i) => t === "{}" || t === segments[i])
|
|
8231
|
+
);
|
|
8232
|
+
}
|
|
8233
|
+
var SEGMENT_SEPARATOR = "/";
|
|
8234
|
+
function segmentsOf(token2) {
|
|
8235
|
+
return token2.split(SEGMENT_SEPARATOR);
|
|
8236
|
+
}
|
|
8237
|
+
function artifactReason(token2) {
|
|
8238
|
+
const segments = segmentsOf(token2);
|
|
8239
|
+
const head = segments.slice(0, -1);
|
|
8240
|
+
const last = segments[segments.length - 1] ?? "";
|
|
8241
|
+
if (head.includes(ARTIFACT_ROOT)) return REASON_PRODUCT_TREE_PATH;
|
|
8242
|
+
for (let i = 0; i < head.length; i += 1) {
|
|
8243
|
+
if (head[i] === RECON_DIRNAME && segments.length - i - 1 > RECON_SEGMENTS_BELOW_ROOT) {
|
|
8244
|
+
return REASON_PRODUCT_TREE_PATH;
|
|
8245
|
+
}
|
|
8246
|
+
}
|
|
8247
|
+
if (segments.length === 2 && PRODUCT_RELATIVE_SHAPES.some(
|
|
8248
|
+
([subdir, suffix]) => segments[0] === subdir && last.endsWith(suffix)
|
|
8249
|
+
)) {
|
|
8250
|
+
return REASON_PRODUCT_RELATIVE_PATH;
|
|
8251
|
+
}
|
|
8252
|
+
if (ARTIFACT_SUFFIXES.some((suffix) => last.endsWith(suffix))) return REASON_PRODUCT_FILE_NAME;
|
|
8253
|
+
return null;
|
|
8254
|
+
}
|
|
8255
|
+
var CJK_RANGES = [
|
|
8256
|
+
[13312, 19903],
|
|
8257
|
+
[19968, 40959],
|
|
8258
|
+
[63744, 64255]
|
|
8259
|
+
];
|
|
8260
|
+
function isCjk(char) {
|
|
8261
|
+
const code = char?.codePointAt(0);
|
|
8262
|
+
return code !== void 0 && CJK_RANGES.some(([low, high]) => code >= low && code <= high);
|
|
8263
|
+
}
|
|
8264
|
+
function boundedFind(line, token2) {
|
|
8265
|
+
let start = line.indexOf(token2);
|
|
8266
|
+
while (start >= 0) {
|
|
8267
|
+
const end = start + token2.length;
|
|
8268
|
+
const leftOk = start === 0 || !(isWordChar(token2[0]) && isWordChar(line[start - 1]));
|
|
8269
|
+
const rightOk = end >= line.length || !(isWordChar(token2[token2.length - 1]) && isWordChar(line[end]));
|
|
8270
|
+
if (leftOk && rightOk) return start;
|
|
8271
|
+
start = line.indexOf(token2, start + 1);
|
|
8272
|
+
}
|
|
8273
|
+
return -1;
|
|
8274
|
+
}
|
|
8275
|
+
function cjkRunAt(line, start, token2) {
|
|
8276
|
+
const end = start + token2.length;
|
|
8277
|
+
if (isCjk(token2[0]) && start > 0 && isCjk(line[start - 1])) return true;
|
|
8278
|
+
return isCjk(token2[token2.length - 1]) && end < line.length && isCjk(line[end]);
|
|
8279
|
+
}
|
|
8280
|
+
var Matcher = class {
|
|
8281
|
+
owners;
|
|
8282
|
+
simple;
|
|
8283
|
+
byFirst;
|
|
8284
|
+
constructor(aclass) {
|
|
8285
|
+
this.owners = /* @__PURE__ */ new Map();
|
|
8286
|
+
for (const kind of ACLASS_KINDS) {
|
|
8287
|
+
for (const token2 of [...aclass[kind] ?? []].sort()) {
|
|
8288
|
+
const owned = this.owners.get(token2);
|
|
8289
|
+
if (owned) owned.push(kind);
|
|
8290
|
+
else this.owners.set(token2, [kind]);
|
|
8291
|
+
}
|
|
8292
|
+
}
|
|
8293
|
+
this.simple = new Set([...this.owners.keys()].filter((t) => SIMPLE_TOKEN.test(t)));
|
|
8294
|
+
this.byFirst = /* @__PURE__ */ new Map();
|
|
8295
|
+
for (const token2 of this.owners.keys()) {
|
|
8296
|
+
if (this.simple.has(token2)) continue;
|
|
8297
|
+
const first = token2[0];
|
|
8298
|
+
if (first === void 0) continue;
|
|
8299
|
+
const bucket = this.byFirst.get(first);
|
|
8300
|
+
if (bucket) bucket.push(token2);
|
|
8301
|
+
else this.byFirst.set(first, [token2]);
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
/**
|
|
8305
|
+
* → 这一行命中的 `[(token, **有边界的**那一处的位置)]`,同一 token 一行只报一次。
|
|
8306
|
+
*
|
|
8307
|
+
* 🔴 **位置必须来自 `boundedFind`,不许再算一遍 `indexOf`**。起手那版用 `boundedFind`
|
|
8308
|
+
* 确认「这一行**存在**一处合法边界命中」,返回的却是第一处裸子串 —— 可能根本不是那一处。
|
|
8309
|
+
* 下游把这个位置交给 `cjkRunAt` 做分桶,于是**在同一行前面加一处诱饵写法就能把一条
|
|
8310
|
+
* 真红压掉**。辖区 = 首/末字符为 CJK、另一端为词字符的**混合形态** token;纯 ASCII 与
|
|
8311
|
+
* 纯 CJK 两个位置必然相等,所以此前每一项变异都照不到它。
|
|
8312
|
+
* 「存在一处合法命中」是「这一处合法」的**必要条件**,不是充分条件。
|
|
8313
|
+
*
|
|
8314
|
+
* 🔴 **`sort` 不是洁癖,它是这份输出的确定性本身**:按插入序遍历会让非纯词形 token 的
|
|
8315
|
+
* 先后随输入变化,集合、rc、判词全都一样,只有顺序变 —— 那正是「静默」的定义,
|
|
8316
|
+
* 而它坏的是**验证仪器**:任何「前后两版输出逐字相等」的复核都会假红。
|
|
8317
|
+
*/
|
|
8318
|
+
hits(line) {
|
|
8319
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8320
|
+
const result = [];
|
|
8321
|
+
for (const m of allMatches(WORD_RE, line)) {
|
|
8322
|
+
const token2 = m[0];
|
|
8323
|
+
if (token2 !== void 0 && this.simple.has(token2) && !seen.has(token2)) {
|
|
8324
|
+
seen.add(token2);
|
|
8325
|
+
result.push([token2, boundedFind(line, token2)]);
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
for (const char of [.../* @__PURE__ */ new Set([...line])].sort()) {
|
|
8329
|
+
for (const token2 of this.byFirst.get(char) ?? []) {
|
|
8330
|
+
if (seen.has(token2)) continue;
|
|
8331
|
+
const start = boundedFind(line, token2);
|
|
8332
|
+
if (start >= 0) {
|
|
8333
|
+
seen.add(token2);
|
|
8334
|
+
result.push([token2, start]);
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8338
|
+
return result;
|
|
8339
|
+
}
|
|
8340
|
+
};
|
|
8341
|
+
function classReasons(problems) {
|
|
8342
|
+
const out = /* @__PURE__ */ new Map();
|
|
8343
|
+
for (const problem of problems ?? []) {
|
|
8344
|
+
if (problem.scope === CLASS_SCOPE) {
|
|
8345
|
+
for (const kind of problem.kinds) out.set(kind, problem.reason);
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
return out;
|
|
8349
|
+
}
|
|
8350
|
+
var INVISIBLE_RE = /\p{Cf}/u;
|
|
8351
|
+
var FULLWIDTH_ALNUM_RANGES = [
|
|
8352
|
+
[65296, 65305, 65296 - 48],
|
|
8353
|
+
[65313, 65338, 65313 - 65],
|
|
8354
|
+
[65345, 65370, 65345 - 97]
|
|
8355
|
+
];
|
|
8356
|
+
var FOLDED_NOTE = "\uFF1B\u26A0\uFE0F \u8FD9\u4E00\u5904\u662F**\u5F52\u4E00\u4E4B\u540E**\u624D\u8BA4\u51FA\u6765\u7684\uFF1A\u539F\u6587\u91CC\u5939\u4E86\u4E0D\u53EF\u89C1\u5B57\u7B26\u6216\u5168\u89D2\u5B57\u7B26\uFF0C\u76F4\u63A5\u6309\u4E0A\u9762\u8FD9\u4E2A\u8BCD\u53BB grep \u4F1A\u641C\u4E0D\u5230";
|
|
8357
|
+
var foldedOriginal = (literal) => `\uFF0C\u539F\u6587\u91CC\u90A3\u4E00\u6BB5\u662F ${JSON.stringify(literal)}`;
|
|
8358
|
+
function foldWithMap(line) {
|
|
8359
|
+
const out = [];
|
|
8360
|
+
const origin = [];
|
|
8361
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
8362
|
+
const char = line[i];
|
|
8363
|
+
if (INVISIBLE_RE.test(char)) continue;
|
|
8364
|
+
const code = char.charCodeAt(0);
|
|
8365
|
+
let folded = char;
|
|
8366
|
+
for (const [low, high, offset] of FULLWIDTH_ALNUM_RANGES) {
|
|
8367
|
+
if (code >= low && code <= high) {
|
|
8368
|
+
folded = String.fromCharCode(code - offset);
|
|
8369
|
+
break;
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
out.push(folded);
|
|
8373
|
+
origin.push(i);
|
|
8374
|
+
}
|
|
8375
|
+
return [out.join(""), origin];
|
|
8376
|
+
}
|
|
8377
|
+
function foldConfusables(line) {
|
|
8378
|
+
return foldWithMap(line)[0];
|
|
8379
|
+
}
|
|
8380
|
+
function foldedNoteFor(original, folded, token2) {
|
|
8381
|
+
if (original.includes(token2)) return "";
|
|
8382
|
+
let start = boundedFind(folded, token2);
|
|
8383
|
+
if (start < 0) start = folded.indexOf(token2);
|
|
8384
|
+
if (start < 0) {
|
|
8385
|
+
return FOLDED_NOTE;
|
|
8386
|
+
}
|
|
8387
|
+
const [, origin] = foldWithMap(original);
|
|
8388
|
+
const from = origin[start];
|
|
8389
|
+
const to = origin[start + token2.length - 1];
|
|
8390
|
+
if (from === void 0 || to === void 0) return FOLDED_NOTE;
|
|
8391
|
+
return FOLDED_NOTE + foldedOriginal(original.slice(from, to + 1));
|
|
8392
|
+
}
|
|
8393
|
+
function judgeLine(line, lineno, rel, matcher, index, reds, gaps) {
|
|
8394
|
+
const folded = foldConfusables(line);
|
|
8395
|
+
const lineReds = [];
|
|
8396
|
+
const lineGaps = [];
|
|
8397
|
+
judgePass(line, lineno, rel, matcher, index, lineReds, lineGaps);
|
|
8398
|
+
if (folded !== line) judgePass(folded, lineno, rel, matcher, index, lineReds, lineGaps);
|
|
8399
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8400
|
+
const unique2 = [];
|
|
8401
|
+
for (const red of lineReds) {
|
|
8402
|
+
const key = `${red.channel} ${red.token}`;
|
|
8403
|
+
if (seen.has(key)) continue;
|
|
8404
|
+
seen.add(key);
|
|
8405
|
+
unique2.push({ ...red, message: red.message + foldedNoteFor(line, folded, red.token) });
|
|
8406
|
+
}
|
|
8407
|
+
reds.push(...unique2);
|
|
8408
|
+
const redTokens = new Set(unique2.filter((r) => r.channel === CHANNEL_ACLASS).map((r) => r.token));
|
|
8409
|
+
const seenGaps = /* @__PURE__ */ new Set();
|
|
8410
|
+
for (const gap of lineGaps) {
|
|
8411
|
+
const key = `${gap.reason} ${gap.token}`;
|
|
8412
|
+
if (seenGaps.has(key) || folded !== line && redTokens.has(gap.token)) continue;
|
|
8413
|
+
seenGaps.add(key);
|
|
8414
|
+
gaps.push({ ...gap, why: gap.why + foldedNoteFor(line, folded, gap.token) });
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
function judgePass(line, lineno, rel, matcher, index, reds, gaps) {
|
|
8418
|
+
for (const [token2, start] of matcher.hits(line)) {
|
|
8419
|
+
const kinds = matcher.owners.get(token2) ?? [];
|
|
8420
|
+
if (inGap(token2)) {
|
|
8421
|
+
gaps.push({
|
|
8422
|
+
reason: GAP_NOISE_TOKEN,
|
|
8423
|
+
token: token2,
|
|
8424
|
+
kinds,
|
|
8425
|
+
path: rel,
|
|
8426
|
+
line: lineno,
|
|
8427
|
+
why: `${rel}:${lineno} \u7EAF\u6570\u5B57 / \u5355\u5B57\u7B26 ${JSON.stringify(token2)} \u2014\u2014 \u5224\u7EA2\u8FD9\u4E00\u5C42**\u58F0\u660E\u7684\u7F3A\u53E3**\uFF0C\u67E5\u8FC7\u3001\u6309\u7F3A\u53E3\u653E\u884C`
|
|
8428
|
+
});
|
|
8429
|
+
continue;
|
|
8430
|
+
}
|
|
8431
|
+
if (cjkRunAt(line, start, token2)) {
|
|
8432
|
+
gaps.push({
|
|
8433
|
+
reason: GAP_CJK_INSIDE_LONGER_RUN,
|
|
8434
|
+
token: token2,
|
|
8435
|
+
kinds,
|
|
8436
|
+
path: rel,
|
|
8437
|
+
line: lineno,
|
|
8438
|
+
why: `${rel}:${lineno} ${JSON.stringify(token2)}\uFF08A \u7C7B ${kinds.join("/")}\uFF09\u843D\u5728\u4E00\u4E32\u66F4\u957F\u7684\u4E2D\u6587\u91CC \u2014\u2014 \u4E2D\u6587\u6CA1\u6709\u5206\u8BCD\u8FB9\u754C\uFF0C**\u8BF4\u4E0D\u51C6**\u5B83\u662F\u4E0D\u662F\u90A3\u4E2A\u53D6\u503C\uFF1B\u4E0D\u9876 rc\uFF0C\u8BF7\u81EA\u5DF1\u770B\u4E00\u773C`
|
|
8439
|
+
});
|
|
8440
|
+
continue;
|
|
8441
|
+
}
|
|
8442
|
+
reds.push({
|
|
8443
|
+
channel: CHANNEL_ACLASS,
|
|
8444
|
+
reason: REASON_ENVIRONMENT_FACT,
|
|
8445
|
+
token: token2,
|
|
8446
|
+
path: rel,
|
|
8447
|
+
line: lineno,
|
|
8448
|
+
kinds,
|
|
8449
|
+
message: `${rel}:${lineno} \u51FA\u73B0\u73AF\u5883\u4E8B\u5B9E ${JSON.stringify(token2)}\uFF08A \u7C7B ${kinds.join("/")}\uFF09\u2014\u2014 \u6362\u4E2A\u5BA2\u6237\u5C31\u5168\u9519\uFF0C**\u65E0\u8C41\u514D**`
|
|
8450
|
+
});
|
|
8451
|
+
}
|
|
8452
|
+
const spans = [];
|
|
8453
|
+
for (const [token2, span] of endpointsIn(line)) {
|
|
8454
|
+
spans.push(span);
|
|
8455
|
+
judgeProtocol(token2, REASON_ENDPOINT, "\u7AEF\u70B9", lineno, rel, matcher, index, reds);
|
|
8456
|
+
}
|
|
8457
|
+
for (const regex of [PATHISH_RE, PRODUCT_FILE_RE]) {
|
|
8458
|
+
for (const m of allMatches(regex, line)) {
|
|
8459
|
+
const span = [m.index, m.index + m[0].length];
|
|
8460
|
+
if (overlaps(span, spans)) continue;
|
|
8461
|
+
const token2 = m[0];
|
|
8462
|
+
const reason = artifactReason(token2);
|
|
8463
|
+
if (reason === null) continue;
|
|
8464
|
+
spans.push(span);
|
|
8465
|
+
reds.push({
|
|
8466
|
+
channel: CHANNEL_ARTIFACT,
|
|
8467
|
+
reason,
|
|
8468
|
+
token: token2,
|
|
8469
|
+
path: rel,
|
|
8470
|
+
line: lineno,
|
|
8471
|
+
kinds: [],
|
|
8472
|
+
message: `${rel}:${lineno} ${artifactWordingFor(reason)(token2)} \u2014\u2014 \u4EA7\u7269\u8DEF\u5F84**\u65E0\u8C41\u514D**\uFF0C\u767B\u8BB0\u518C\u4E0D\u53D7\u7406\u5B83\uFF1Bskill \u6B63\u6587\u8BE5\u8BF4\u7684\u662F\u8DD1\u54EA\u6761\u547D\u4EE4\uFF0C\u4E0D\u662F\u53BB\u8BFB\u54EA\u4E2A\u6587\u4EF6`
|
|
8473
|
+
});
|
|
8474
|
+
}
|
|
8475
|
+
}
|
|
8476
|
+
for (const m of allMatches(ERROR_CODE_RE, line)) {
|
|
8477
|
+
const span = [m.index, m.index + m[0].length];
|
|
8478
|
+
if (overlaps(span, spans)) continue;
|
|
8479
|
+
spans.push(span);
|
|
8480
|
+
judgeProtocol(m[0], REASON_ERROR_CODE, "\u9519\u8BEF\u7801", lineno, rel, matcher, index, reds);
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
function judgeProtocol(token2, reason, shape, lineno, rel, matcher, index, reds) {
|
|
8484
|
+
if (matcher.owners.has(token2)) return;
|
|
8485
|
+
if (covered(token2, index)) return;
|
|
8486
|
+
reds.push({
|
|
8487
|
+
channel: CHANNEL_UNREGISTERED,
|
|
8488
|
+
reason,
|
|
8489
|
+
token: token2,
|
|
8490
|
+
path: rel,
|
|
8491
|
+
line: lineno,
|
|
8492
|
+
kinds: [],
|
|
8493
|
+
message: `${rel}:${lineno} \u51FA\u73B0${shape} ${JSON.stringify(token2)}\uFF0C\u800C\u5B83**\u6CA1\u6709\u767B\u8BB0**\u5728\u968F\u5305\u7684\u534F\u8BAE\u4E8B\u5B9E\u767B\u8BB0\u518C\u91CC \u21D2 \u8981\u4E48\u6539\u5199\u6210\u4E0D\u4F9D\u8D56\u5B83\u7684\u8BF4\u6CD5\uFF0C\u8981\u4E48\u7ED9 CLI \u63D0\u4E00\u6761\u767B\u8BB0\uFF08\u767B\u8BB0\u5FC5\u987B\u9644\u4E00\u4E2A\u65AD\u884C\u4E3A\u7684\u63A2\u9488\uFF09`
|
|
8494
|
+
});
|
|
8495
|
+
}
|
|
8496
|
+
function scan(skillRoot, aclass, facts, options = {}) {
|
|
8497
|
+
const reds = [];
|
|
8498
|
+
const premise = [];
|
|
8499
|
+
const gaps = [];
|
|
8500
|
+
const scanned = [];
|
|
8501
|
+
const matcher = new Matcher(aclass);
|
|
8502
|
+
const index = factsIndex(facts);
|
|
8503
|
+
const reasons = classReasons(options.aclassProblems);
|
|
8504
|
+
for (const kind of ACLASS_KINDS) {
|
|
8505
|
+
if ((aclass[kind]?.size ?? 0) > 0) continue;
|
|
8506
|
+
const reason = reasons.get(kind);
|
|
8507
|
+
let why = "";
|
|
8508
|
+
if (reason !== void 0) {
|
|
8509
|
+
if (!(reason in ACLASS_EMPTY_WHY)) {
|
|
8510
|
+
throw new Error(
|
|
8511
|
+
`\u7C7B\u7EA7\u6210\u56E0 ${JSON.stringify(reason)} \u6CA1\u6709\u5BF9\u5E94\u7684\u63AA\u8F9E\uFF08\u8BA4\u5F97\u7684\u662F ${CLASS_REASONS.join("/")}\uFF09`
|
|
8512
|
+
);
|
|
8513
|
+
}
|
|
8514
|
+
why = ACLASS_EMPTY_WHY[reason];
|
|
8515
|
+
}
|
|
8516
|
+
premise.push({
|
|
8517
|
+
reason: REASON_ACLASS_EMPTY,
|
|
8518
|
+
message: `A \u7C7B\u5B50\u96C6 ${kind} \u662F\u7A7A\u7684${why} \u21D2 \u95E8 0 \u5BF9\u8FD9\u4E00\u7C7B**\u6052\u7EFF**\uFF0C\u8FD9\u4E00\u8F6E\u7684\u7EFF\u4E0D\u7B97\u6570`
|
|
8519
|
+
});
|
|
8520
|
+
}
|
|
8521
|
+
for (const problem of options.aclassProblems ?? []) {
|
|
8522
|
+
if (problem.scope !== ENTRY_SCOPE) continue;
|
|
8523
|
+
premise.push({
|
|
8524
|
+
reason: REASON_ACLASS_PARTIALLY_UNREAD,
|
|
8525
|
+
message: `A \u7C7B\u5B50\u96C6 ${problem.kinds.join("/")} \u5C11\u4E86\u4E00\u90E8\u5206\uFF08${problem.reason}\uFF09\uFF1A${problem.message} \u21D2 \u5C11\u6389\u7684\u90A3\u90E8\u5206\u95E8 0 \u5224\u4E0D\u51FA\u6765\uFF0C\u8FD9\u4E00\u8F6E\u7684\u7EFF\u4E0D\u7B97\u6570`
|
|
8526
|
+
});
|
|
8527
|
+
}
|
|
8528
|
+
let factsProblems = options.factsProblems;
|
|
8529
|
+
if (factsProblems === void 0) {
|
|
8530
|
+
premise.push({
|
|
8531
|
+
reason: REASON_CHANNEL_FOUR_UNCHECKED,
|
|
8532
|
+
message: "\u6CA1\u6709\u628A loadProtocolFacts \u7684\u53F0\u8D26\u4F20\u8FDB\u6765 \u21D2 \u901A\u9053\u56DB\uFF08\u767B\u8BB0\u9879\u88AB\u62D2\uFF09\u4E00\u6761\u90FD\u6CA1\u67E5\u8FC7\u2014\u2014\u300C\u767B\u8BB0\u518C\u5E72\u51C0\u300D\u4E0E\u300C\u6CA1\u67E5\u767B\u8BB0\u518C\u300D\u5728\u8FD4\u56DE\u503C\u4E0A\u540C\u5F62"
|
|
8533
|
+
});
|
|
8534
|
+
factsProblems = [];
|
|
8535
|
+
}
|
|
8536
|
+
for (const problem of factsProblems) {
|
|
8537
|
+
if (problem.scope === REGISTRY_SCOPE && PREMISE_REGISTRY_REASONS.includes(problem.reason)) {
|
|
8538
|
+
premise.push({
|
|
8539
|
+
reason: REASON_REGISTRY,
|
|
8540
|
+
message: `\u767B\u8BB0\u518C\u8FD9\u4E00\u5C42\u6CA1\u7ACB\u4F4F\uFF08${problem.reason}\uFF09\uFF1A${problem.message} \u21D2 \u8C41\u514D\u901A\u9053 P \u4E0D\u53EF\u4FE1`
|
|
8541
|
+
});
|
|
8542
|
+
}
|
|
8543
|
+
}
|
|
8544
|
+
for (const problem of factsProblems) {
|
|
8545
|
+
if (problem.scope === REGISTRY_SCOPE) continue;
|
|
8546
|
+
reds.push({
|
|
8547
|
+
channel: CHANNEL_NO_PROBE,
|
|
8548
|
+
reason: REASON_REJECTED_REGISTRATION,
|
|
8549
|
+
token: problem.token ?? `(\u7B2C${problem.index}\u6761)`,
|
|
8550
|
+
path: null,
|
|
8551
|
+
line: 0,
|
|
8552
|
+
kinds: [],
|
|
8553
|
+
message: `\u767B\u8BB0\u518C\u7B2C ${problem.index} \u6761\u88AB\u62D2\uFF08${problem.reason}\uFF09\uFF1A${problem.message}`
|
|
8554
|
+
});
|
|
8555
|
+
}
|
|
8556
|
+
for (const token2 of [...facts].sort()) {
|
|
8557
|
+
const kinds = matcher.owners.get(token2);
|
|
8558
|
+
if (!kinds) continue;
|
|
8559
|
+
reds.push({
|
|
8560
|
+
channel: CHANNEL_REGISTERED_IS_ACLASS,
|
|
8561
|
+
reason: REASON_REGISTERED_IS_ACLASS,
|
|
8562
|
+
token: token2,
|
|
8563
|
+
path: null,
|
|
8564
|
+
line: 0,
|
|
8565
|
+
kinds,
|
|
8566
|
+
message: `\u767B\u8BB0\u518C\u767B\u8BB0\u4E86 ${JSON.stringify(token2)}\uFF0C\u800C\u5B83\u662F A \u7C7B\u73AF\u5883\u4E8B\u5B9E\uFF08${kinds.join("/")}\uFF09\u2014\u2014 recon \u67E5\u5F97\u5230\u7684\u4E1C\u897F\u4E0D\u662F\u7248\u672C\u7EA7\u534F\u8BAE\uFF0C\u767B\u8BB0\u5B83\u4E70\u4E0D\u5230\u4EFB\u4F55\u8C41\u514D`
|
|
8567
|
+
});
|
|
8568
|
+
}
|
|
8569
|
+
let files = [];
|
|
8570
|
+
if (!isDirectory(skillRoot)) {
|
|
8571
|
+
premise.push({
|
|
8572
|
+
reason: REASON_NO_SKILL_ROOT,
|
|
8573
|
+
message: `\u626B\u63CF\u9762 ${skillRoot} \u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55 \u21D2 \u4E00\u4E2A\u5B57\u90FD\u6CA1\u626B\u8FC7\uFF08\u8FD9\u4E0D\u662F\u300C\u626B\u8FC7\u3001\u5E72\u51C0\u300D\uFF09`
|
|
8574
|
+
});
|
|
8575
|
+
} else {
|
|
8576
|
+
const walked = walk2(skillRoot);
|
|
8577
|
+
files = walked.files;
|
|
8578
|
+
for (const link of walked.unscannedLinks) {
|
|
8579
|
+
let target;
|
|
8580
|
+
try {
|
|
8581
|
+
target = readlinkSync(link);
|
|
8582
|
+
} catch {
|
|
8583
|
+
target = "(\u8BFB\u4E0D\u51FA\u94FE\u63A5\u76EE\u6807)";
|
|
8584
|
+
}
|
|
8585
|
+
premise.push({
|
|
8586
|
+
reason: REASON_SYMLINK_NOT_SCANNED,
|
|
8587
|
+
message: `${path11.relative(skillRoot, link)} \u662F\u4E00\u4E2A\u7B26\u53F7\u94FE\u63A5\uFF08\u6307\u5411 ${JSON.stringify(target)}\uFF09\u21D2 \u95E8 0 **\u6CA1\u6709\u987A\u7740\u5B83\u626B\u8FC7\u4E00\u4E2A\u5B57**\uFF0C\u8FD9\u4E00\u8F6E\u7684\u7EFF\u4E0D\u7B97\u6570\u3002\u51FA\u8DEF\uFF1A\u628A\u8FD9\u6761\u94FE\u4ECE skill \u91CC\u53BB\u6389 \u2014\u2014 \u94FE\u540E\u9762\u90A3\u4EFD\u5185\u5BB9\u672C\u8BE5\u88AB\u5224\u5C31 \`cp -RL\` \u62F7\u6210\u771F\u5185\u5BB9\uFF08\u26A0\uFE0F \u73AF\u8DEF\u4E0E\u65AD\u94FE\u4E0A\u5B83\u4F1A\u5F53\u573A\u62A5\u9519\uFF0C\u90A3\u4E24\u79CD\u76F4\u63A5\u5220\uFF09\uFF1B\u5185\u5BB9\u5DF2\u7ECF\u5728 skill \u91CC\uFF08\u6307\u5411\u540C\u6811\u7684\u522B\u540D\uFF09\u4E5F\u76F4\u63A5\u5220\u3002**\u4E0D\u8DDF\u968F**\u662F\u523B\u610F\u7684 \u2014\u2014 \u8DDF\u8FC7\u53BB\u4F1A\u8FDE\u4E0A\u73AF\u8DEF\u3001\u6216\u8D8A\u51FA skill \u76EE\u5F55\u626B\u5230\u65E0\u5173\u6587\u4EF6`
|
|
8588
|
+
});
|
|
8589
|
+
}
|
|
8590
|
+
if (files.length === 0) {
|
|
8591
|
+
premise.push({
|
|
8592
|
+
reason: REASON_NOTHING_SCANNED,
|
|
8593
|
+
message: `\u626B\u63CF\u9762 ${skillRoot} \u5728\u76D8\u4E0A\uFF0C\u4F46\u91CC\u9762\u4E00\u4E2A\u6587\u4EF6\u90FD\u6CA1\u6709 \u2014\u2014 \u67E5\u8FC7\u4E14\u4E3A\u7A7A`
|
|
8594
|
+
});
|
|
8595
|
+
}
|
|
8596
|
+
}
|
|
8597
|
+
for (const file of files) {
|
|
8598
|
+
let text;
|
|
8599
|
+
try {
|
|
8600
|
+
text = readFileSync7(file, "utf8");
|
|
8601
|
+
} catch (e) {
|
|
8602
|
+
premise.push({
|
|
8603
|
+
reason: REASON_FILE_UNREADABLE,
|
|
8604
|
+
message: `${file} \u8BFB\u4E0D\u52A8\uFF08${String(e)}\uFF09\u21D2 \u5224\u4E0D\u4E86\u5B83\u5E72\u4E0D\u5E72\u51C0\uFF0C\u95E8 0 \u4E0D\u80FD\u5BF9\u7740\u5B83\u8BF4\u7EFF`
|
|
8605
|
+
});
|
|
8606
|
+
continue;
|
|
8607
|
+
}
|
|
8608
|
+
const rel = path11.relative(skillRoot, file);
|
|
8609
|
+
scanned.push(rel);
|
|
8610
|
+
text.split("\n").forEach((line, i) => {
|
|
8611
|
+
judgeLine(line, i + 1, rel, matcher, index, reds, gaps);
|
|
8612
|
+
});
|
|
8613
|
+
}
|
|
8614
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8615
|
+
const unique2 = [];
|
|
8616
|
+
for (const red of checkReds(reds)) {
|
|
8617
|
+
const key = `${red.channel} ${red.token} ${red.path} ${red.line}`;
|
|
8618
|
+
if (seen.has(key)) continue;
|
|
8619
|
+
seen.add(key);
|
|
8620
|
+
unique2.push(red);
|
|
8621
|
+
}
|
|
8622
|
+
const channels = Object.fromEntries(
|
|
8623
|
+
CHANNELS.map((name) => [name, unique2.filter((r) => r.channel === name)])
|
|
8624
|
+
);
|
|
8625
|
+
return {
|
|
8626
|
+
red: unique2,
|
|
8627
|
+
channels,
|
|
8628
|
+
scanned,
|
|
8629
|
+
gaps: checkGaps(gaps),
|
|
8630
|
+
premise: checkPremise(premise)
|
|
8631
|
+
};
|
|
8632
|
+
}
|
|
8633
|
+
function isDirectory(target) {
|
|
8634
|
+
try {
|
|
8635
|
+
return statSync3(target).isDirectory();
|
|
8636
|
+
} catch {
|
|
8637
|
+
return false;
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
function walk2(root) {
|
|
8641
|
+
const files = [];
|
|
8642
|
+
const unscannedLinks = [];
|
|
8643
|
+
const visit = (dir) => {
|
|
8644
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
8645
|
+
const full = path11.join(dir, entry.name);
|
|
8646
|
+
let followed = null;
|
|
8647
|
+
try {
|
|
8648
|
+
followed = statSync3(full);
|
|
8649
|
+
} catch {
|
|
8650
|
+
followed = null;
|
|
8651
|
+
}
|
|
8652
|
+
if (followed?.isFile()) {
|
|
8653
|
+
files.push(full);
|
|
8654
|
+
} else if (entry.isSymbolicLink()) {
|
|
8655
|
+
unscannedLinks.push(full);
|
|
8656
|
+
} else if (followed?.isDirectory()) {
|
|
8657
|
+
visit(full);
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
};
|
|
8661
|
+
visit(root);
|
|
8662
|
+
return { files: files.sort(), unscannedLinks: unscannedLinks.sort() };
|
|
8663
|
+
}
|
|
8664
|
+
function exitCode(result) {
|
|
8665
|
+
if (result.premise.length > 0) return 2;
|
|
8666
|
+
if (result.red.length > 0) return 1;
|
|
8667
|
+
return 0;
|
|
8668
|
+
}
|
|
7554
8669
|
export {
|
|
8670
|
+
ACLASS_EMPTY_WHY,
|
|
8671
|
+
ACLASS_KINDS,
|
|
8672
|
+
ARTIFACT_ROOT,
|
|
8673
|
+
ARTIFACT_SUFFIXES,
|
|
8674
|
+
ARTIFACT_WORDING,
|
|
7555
8675
|
AUTH_INFO_PATH,
|
|
7556
8676
|
BLIND_SPOTS,
|
|
8677
|
+
CHANNELS,
|
|
8678
|
+
CHANNEL_ACLASS,
|
|
8679
|
+
CHANNEL_ARTIFACT,
|
|
8680
|
+
CHANNEL_NO_PROBE,
|
|
8681
|
+
CHANNEL_REGISTERED_IS_ACLASS,
|
|
8682
|
+
CHANNEL_UNREGISTERED,
|
|
8683
|
+
CLASS_REASONS,
|
|
8684
|
+
CLASS_SCOPE,
|
|
7557
8685
|
CliError,
|
|
7558
8686
|
CliErrorCode,
|
|
7559
8687
|
DEFAULT_AGENT_ID,
|
|
7560
8688
|
DEFAULT_SESSION_TTL_SECONDS,
|
|
7561
8689
|
DOCTOR_ANCHORS,
|
|
8690
|
+
ENDPOINT_PREFIXES,
|
|
8691
|
+
ENTRY_SCOPE,
|
|
8692
|
+
ENUM_ELEMENT_KEYS,
|
|
7562
8693
|
EXTENSION_SOURCE_HEADER,
|
|
7563
8694
|
ExitCode,
|
|
8695
|
+
FACTS_REASONS,
|
|
8696
|
+
FOLDED_NOTE,
|
|
8697
|
+
FULLWIDTH_ALNUM_RANGES,
|
|
8698
|
+
GAP_CJK_INSIDE_LONGER_RUN,
|
|
8699
|
+
GAP_NOISE_TOKEN,
|
|
8700
|
+
GAP_REASONS,
|
|
7564
8701
|
GATE_COMPONENTS,
|
|
7565
8702
|
HcmClient,
|
|
7566
8703
|
KIT_CARRY_OVER_KEY,
|
|
@@ -7568,6 +8705,7 @@ export {
|
|
|
7568
8705
|
LEDGER_MISSING_WORD,
|
|
7569
8706
|
LayoutContainmentError,
|
|
7570
8707
|
MANIFEST_PATH,
|
|
8708
|
+
MAPPING_ARTIFACT_VERSION,
|
|
7571
8709
|
MAX_MANIFEST_PAGES,
|
|
7572
8710
|
MAX_SOURCE_PROBES,
|
|
7573
8711
|
META_PATH,
|
|
@@ -7578,16 +8716,37 @@ export {
|
|
|
7578
8716
|
MINI_REMOTE_ROOT,
|
|
7579
8717
|
MINI_STATE_DIR,
|
|
7580
8718
|
MODELS_PATH,
|
|
8719
|
+
MappingArtifactError,
|
|
8720
|
+
Matcher,
|
|
7581
8721
|
PASSWORD_CHANGE_REQUIRED,
|
|
8722
|
+
PREMISE_REASONS,
|
|
8723
|
+
PREMISE_REGISTRY_REASONS,
|
|
7582
8724
|
PROACTIVE_REFRESH_MARGIN_SECONDS,
|
|
8725
|
+
PROBE_MARK_PREFIX,
|
|
8726
|
+
PRODUCT_RELATIVE_SHAPES,
|
|
7583
8727
|
PRODUCT_STATE_WORDS,
|
|
8728
|
+
PROTOCOL_FACTS,
|
|
8729
|
+
REASON_DUPLICATE_TOKEN,
|
|
8730
|
+
REASON_EMPTY,
|
|
8731
|
+
REASON_NO_ENTRIES,
|
|
8732
|
+
REASON_PROBE_ABSENT,
|
|
8733
|
+
REASON_PROBE_SHAPE,
|
|
8734
|
+
REASON_PROBE_UNVERIFIED,
|
|
8735
|
+
REASON_UNREAD,
|
|
8736
|
+
RECON_DIRNAME,
|
|
8737
|
+
RECON_SEGMENTS_BELOW_ROOT,
|
|
8738
|
+
RED_REASONS,
|
|
8739
|
+
REGISTRY_REASONS,
|
|
8740
|
+
REGISTRY_SCOPE,
|
|
7584
8741
|
ROUND_EXIT_CODES,
|
|
7585
8742
|
ROUND_STAMPED_PRODUCTS,
|
|
7586
8743
|
ROUND_STATES,
|
|
7587
8744
|
ROUND_SUBCOMMAND,
|
|
7588
8745
|
ReconLayout,
|
|
7589
8746
|
RefStore,
|
|
8747
|
+
SCOPES,
|
|
7590
8748
|
SDK_VERSION,
|
|
8749
|
+
SEGMENT_SEPARATOR,
|
|
7591
8750
|
TargetModelNotFound,
|
|
7592
8751
|
TokenStore,
|
|
7593
8752
|
VERSION_PATH,
|
|
@@ -7595,10 +8754,13 @@ export {
|
|
|
7595
8754
|
absoluteDownloadUrl,
|
|
7596
8755
|
adminResetPassword,
|
|
7597
8756
|
archiveDir,
|
|
8757
|
+
artifactReason,
|
|
7598
8758
|
assertSafeReferencePath,
|
|
7599
8759
|
assertSafeSkillFilePath,
|
|
7600
8760
|
assertSafeSkillId,
|
|
8761
|
+
baselineRoundPremise,
|
|
7601
8762
|
bootstrapTenant,
|
|
8763
|
+
boundedFind,
|
|
7602
8764
|
buildAnswer,
|
|
7603
8765
|
buildConfirm,
|
|
7604
8766
|
buildInterrupt,
|
|
@@ -7610,6 +8772,10 @@ export {
|
|
|
7610
8772
|
buildSteer,
|
|
7611
8773
|
camelizeKeys,
|
|
7612
8774
|
changePassword,
|
|
8775
|
+
checkGaps,
|
|
8776
|
+
checkPremise,
|
|
8777
|
+
checkReds,
|
|
8778
|
+
cjkRunAt,
|
|
7613
8779
|
classifyMessage,
|
|
7614
8780
|
clearMetaCache,
|
|
7615
8781
|
clearModelCache,
|
|
@@ -7617,6 +8783,7 @@ export {
|
|
|
7617
8783
|
computeFingerprint,
|
|
7618
8784
|
computeReconSourceKey,
|
|
7619
8785
|
conversationStateFile,
|
|
8786
|
+
covered,
|
|
7620
8787
|
create,
|
|
7621
8788
|
createConversation,
|
|
7622
8789
|
createHttpClient,
|
|
@@ -7629,6 +8796,7 @@ export {
|
|
|
7629
8796
|
deleteIdentity,
|
|
7630
8797
|
deleteTenantMeta,
|
|
7631
8798
|
deleteWorkspaceFile,
|
|
8799
|
+
deriveAclass,
|
|
7632
8800
|
deriveNamelessToolLabel,
|
|
7633
8801
|
describePrincipal,
|
|
7634
8802
|
detectDelegationPause,
|
|
@@ -7636,12 +8804,15 @@ export {
|
|
|
7636
8804
|
detectPasswordChangeChallenge,
|
|
7637
8805
|
displayWidth,
|
|
7638
8806
|
downloadDocument,
|
|
8807
|
+
endpointsIn,
|
|
7639
8808
|
ensureSessionFresh,
|
|
7640
8809
|
envDir,
|
|
7641
8810
|
envFile,
|
|
7642
8811
|
envsDir,
|
|
8812
|
+
exitCode,
|
|
7643
8813
|
exitCodeFor,
|
|
7644
8814
|
extractNextSteps,
|
|
8815
|
+
factsIndex,
|
|
7645
8816
|
fetchConversationTimeline,
|
|
7646
8817
|
fetchConversationTimelineStrict,
|
|
7647
8818
|
fetchRecentConversations,
|
|
@@ -7653,6 +8824,8 @@ export {
|
|
|
7653
8824
|
filterByStage,
|
|
7654
8825
|
findLastAssistantSeq,
|
|
7655
8826
|
flattenSkills,
|
|
8827
|
+
foldConfusables,
|
|
8828
|
+
foldedNoteFor,
|
|
7656
8829
|
formatMiniVerifyReport,
|
|
7657
8830
|
formatObject,
|
|
7658
8831
|
formatRows,
|
|
@@ -7668,10 +8841,12 @@ export {
|
|
|
7668
8841
|
identitiesDir,
|
|
7669
8842
|
identityDir,
|
|
7670
8843
|
identityMetaFile,
|
|
8844
|
+
inGap,
|
|
7671
8845
|
inferEnvName,
|
|
7672
8846
|
inferMiniWritableTemplateOptionsFromModel,
|
|
7673
8847
|
initMiniAppProject,
|
|
7674
8848
|
inspectRound,
|
|
8849
|
+
isCjk,
|
|
7675
8850
|
isDelegationToolName,
|
|
7676
8851
|
isServerSlidingSession,
|
|
7677
8852
|
listEnvs,
|
|
@@ -7684,6 +8859,7 @@ export {
|
|
|
7684
8859
|
loadGlobalConfig,
|
|
7685
8860
|
loadIdentity,
|
|
7686
8861
|
loadProfile,
|
|
8862
|
+
loadProtocolFacts,
|
|
7687
8863
|
loadWorkspaceFileContent,
|
|
7688
8864
|
loginClientCredentials,
|
|
7689
8865
|
loginPairing,
|
|
@@ -7695,6 +8871,7 @@ export {
|
|
|
7695
8871
|
modelKeys,
|
|
7696
8872
|
needsRefresh,
|
|
7697
8873
|
newRoundId,
|
|
8874
|
+
normalize,
|
|
7698
8875
|
normalizeReferences,
|
|
7699
8876
|
oneShot,
|
|
7700
8877
|
parseConfirmToolName,
|
|
@@ -7711,6 +8888,7 @@ export {
|
|
|
7711
8888
|
profileFile,
|
|
7712
8889
|
pullMiniAppProject,
|
|
7713
8890
|
pushMiniAppProject,
|
|
8891
|
+
readBusinessKeyFields,
|
|
7714
8892
|
readMiniContext,
|
|
7715
8893
|
readPullManifest,
|
|
7716
8894
|
refreshToken,
|
|
@@ -7738,6 +8916,8 @@ export {
|
|
|
7738
8916
|
saveProfile,
|
|
7739
8917
|
saveTenantMeta,
|
|
7740
8918
|
saveWorkspaceFileContent,
|
|
8919
|
+
scan,
|
|
8920
|
+
segmentsOf,
|
|
7741
8921
|
sendMessageAndStream,
|
|
7742
8922
|
snakeToCamel,
|
|
7743
8923
|
tenantSegment,
|
|
@@ -7747,6 +8927,7 @@ export {
|
|
|
7747
8927
|
toTable,
|
|
7748
8928
|
toYaml,
|
|
7749
8929
|
truncateDisplay,
|
|
8930
|
+
unguardedKinds,
|
|
7750
8931
|
update,
|
|
7751
8932
|
uploadDocument,
|
|
7752
8933
|
validateMiniProject
|