@hcmai/sdk 0.3.11 → 0.3.12
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 +176 -166
- package/dist/index.js +171 -160
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1129,6 +1129,167 @@ async function describe(http, model, _opts = {}) {
|
|
|
1129
1129
|
};
|
|
1130
1130
|
}
|
|
1131
1131
|
|
|
1132
|
+
// src/fde/fingerprint.ts
|
|
1133
|
+
import { createHash } from "crypto";
|
|
1134
|
+
var GATE_COMPONENTS = [
|
|
1135
|
+
"tenantMetaManifestDigest",
|
|
1136
|
+
"extensionSource",
|
|
1137
|
+
"modelListHash"
|
|
1138
|
+
];
|
|
1139
|
+
var BLIND_SPOTS = "\u4EA7\u54C1\u5347\u7EA7\uFF1Amanifest \u53EA\u8BFB\u79DF\u6237 workspace \u7684 metas/ \u76EE\u5F55\uFF0C\u4EA7\u54C1\u5347\u7EA7\u6539\u7684\u662F\u5B89\u88C5\u5305\u5185 _metas/\uFF0C\u56DB\u5143\u7EC4\u4E0D\u4F1A\u53D8\u2014\u2014\u8BE5\u573A\u666F\u7684\u9632\u7EBF\u53EA\u6709\u884C\u4E3A\u63A2\u9488\u4E00\u6761\u3002 | \u540C\u5B57\u8282\u6570\u5185\u5BB9\u66FF\u6362\uFF1A\u6E05\u5355\u53EA\u7ED9 name/size/lastModified\uFF0C\u628A\u6807\u7B7E\u300C\u90E8\u95E8\u300D\u6539\u6210\u300C\u673A\u6784\u300D\u5728 UTF-8 \u4E0B\u6070\u597D\u90FD\u662F 6 \u5B57\u8282\uFF0C\u6458\u8981\u4E0D\u53D8\u3002\u21D2 \u5B57\u6BB5\u540D\u53EB ManifestDigest \u800C\u975E Fingerprint\uFF0C\u540D\u5B57\u5FC5\u987B\u8BF4\u5B9E\u8BDD\u3002";
|
|
1140
|
+
var MODELS_PATH = "/api/system/models";
|
|
1141
|
+
var MANIFEST_PATH = "/api/tenant-meta/list";
|
|
1142
|
+
var VERSION_PATH = "/api/version/backend";
|
|
1143
|
+
var META_PATH = "/api/models/{model}/meta";
|
|
1144
|
+
var EXTENSION_SOURCE_HEADER = "x-hcm-extension-source";
|
|
1145
|
+
var MAX_SOURCE_PROBES = 50;
|
|
1146
|
+
var MAX_MANIFEST_PAGES = 200;
|
|
1147
|
+
var GATE_SOURCES = [MODELS_PATH, MANIFEST_PATH, META_PATH];
|
|
1148
|
+
function modelEntries(body) {
|
|
1149
|
+
if (!isPlainObject2(body)) return [];
|
|
1150
|
+
const raw = body.models;
|
|
1151
|
+
if (!Array.isArray(raw)) return [];
|
|
1152
|
+
return raw.filter(isPlainObject2);
|
|
1153
|
+
}
|
|
1154
|
+
function modelKeys(body) {
|
|
1155
|
+
return modelEntries(body).map((m) => typeof m.modelKey === "string" ? m.modelKey : "").filter((k) => k !== "").sort();
|
|
1156
|
+
}
|
|
1157
|
+
async function probeExtensionSource(probe, keys) {
|
|
1158
|
+
let attempted = 0;
|
|
1159
|
+
let answered = 0;
|
|
1160
|
+
for (const key of [...keys].sort().slice(0, MAX_SOURCE_PROBES)) {
|
|
1161
|
+
attempted += 1;
|
|
1162
|
+
const out = await probe.get(META_PATH.replace("{model}", key));
|
|
1163
|
+
if (out.error !== "ok") continue;
|
|
1164
|
+
answered += 1;
|
|
1165
|
+
const value = out.headers[EXTENSION_SOURCE_HEADER];
|
|
1166
|
+
if (value) return { sourceKey: value, probeModelKey: key, attempted, answered };
|
|
1167
|
+
}
|
|
1168
|
+
return { sourceKey: "", probeModelKey: "", attempted, answered };
|
|
1169
|
+
}
|
|
1170
|
+
async function fetchManifestRows(probe, failures) {
|
|
1171
|
+
const rows = [];
|
|
1172
|
+
let page = 0;
|
|
1173
|
+
const pageSize = 200;
|
|
1174
|
+
for (; ; ) {
|
|
1175
|
+
const out = await probe.get(MANIFEST_PATH, { page, pageSize });
|
|
1176
|
+
if (out.error !== "ok" || !isPlainObject2(out.body)) {
|
|
1177
|
+
failures.push(MANIFEST_PATH);
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
const body = out.body;
|
|
1181
|
+
const chunk = body.data ?? [];
|
|
1182
|
+
if (!Array.isArray(chunk)) {
|
|
1183
|
+
failures.push(MANIFEST_PATH);
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
rows.push(...chunk.filter(isPlainObject2));
|
|
1187
|
+
const totalPages = typeof body.totalPages === "number" ? body.totalPages : 0;
|
|
1188
|
+
page += 1;
|
|
1189
|
+
if (page >= totalPages || chunk.length === 0) break;
|
|
1190
|
+
if (page >= MAX_MANIFEST_PAGES) {
|
|
1191
|
+
failures.push(`${MANIFEST_PATH}#page-cap`);
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return rows;
|
|
1196
|
+
}
|
|
1197
|
+
function manifestDigest(rows) {
|
|
1198
|
+
const lines = rows.map((r) => `${r.name}|${r.size}|${r.lastModified}`).sort();
|
|
1199
|
+
return sha256(lines.join("\n"));
|
|
1200
|
+
}
|
|
1201
|
+
async function computeReconSourceKey(probe) {
|
|
1202
|
+
const out = await probe.get(MODELS_PATH);
|
|
1203
|
+
const p = await probeExtensionSource(probe, modelKeys(out.body));
|
|
1204
|
+
return p.sourceKey || "unknown";
|
|
1205
|
+
}
|
|
1206
|
+
async function computeFingerprint(probe, opts = {}) {
|
|
1207
|
+
const ttlDays = opts.ttlDays ?? 30;
|
|
1208
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
1209
|
+
const failures = [];
|
|
1210
|
+
const modelsOut = await probe.get(MODELS_PATH);
|
|
1211
|
+
const modelsOk = modelsOut.error === "ok" && isPlainObject2(modelsOut.body);
|
|
1212
|
+
if (!modelsOk) failures.push(MODELS_PATH);
|
|
1213
|
+
const models = modelsOk ? modelEntries(modelsOut.body) : [];
|
|
1214
|
+
const keys = modelsOk ? modelKeys(modelsOut.body) : [];
|
|
1215
|
+
if (modelsOut.error === "ok" && keys.length === 0) {
|
|
1216
|
+
failures.push(`${MODELS_PATH}#empty-model-list`);
|
|
1217
|
+
}
|
|
1218
|
+
const p = await probeExtensionSource(probe, keys);
|
|
1219
|
+
let sourceKey = p.sourceKey;
|
|
1220
|
+
if (!sourceKey) {
|
|
1221
|
+
failures.push(
|
|
1222
|
+
p.answered > 0 ? `${META_PATH}#X-HCM-Extension-Source` : `${META_PATH}#no-meta-answered`
|
|
1223
|
+
);
|
|
1224
|
+
sourceKey = "unknown";
|
|
1225
|
+
}
|
|
1226
|
+
const hashLines = models.map((m) => `${m.modelKey ?? ""}|${m.type ?? ""}`).sort();
|
|
1227
|
+
const modelHash = sha256(hashLines.join("\n"));
|
|
1228
|
+
const rows = await fetchManifestRows(probe, failures);
|
|
1229
|
+
const versionOut = await probe.get(VERSION_PATH);
|
|
1230
|
+
let backendVersion;
|
|
1231
|
+
if (versionOut.error === "ok" && versionOut.body !== null && versionOut.body !== void 0) {
|
|
1232
|
+
backendVersion = versionOut.body;
|
|
1233
|
+
} else {
|
|
1234
|
+
failures.push(VERSION_PATH);
|
|
1235
|
+
backendVersion = { version: "unavailable" };
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
tenantMetaManifestDigest: manifestDigest(rows),
|
|
1239
|
+
manifestEntryCount: rows.length,
|
|
1240
|
+
extensionSource: sourceKey,
|
|
1241
|
+
// 🔴 记下**是从哪个模型探到的**:让「这个值是谁给的」在产物里留痕。
|
|
1242
|
+
extensionSourceProbe: p.probeModelKey,
|
|
1243
|
+
// 🔴 没有这两个数,`extensionSource: "unknown"` 就是一个**不可追问的结论**。
|
|
1244
|
+
extensionSourceProbeAttempted: p.attempted,
|
|
1245
|
+
extensionSourceProbeAnswered: p.answered,
|
|
1246
|
+
modelListHash: modelHash,
|
|
1247
|
+
modelCount: models.length,
|
|
1248
|
+
backendVersion,
|
|
1249
|
+
capabilities: probe.capabilities,
|
|
1250
|
+
createdAt: `${now().toISOString().slice(0, 19)}Z`,
|
|
1251
|
+
ttlDays,
|
|
1252
|
+
coverageNotes: {
|
|
1253
|
+
gateComponents: [...GATE_COMPONENTS],
|
|
1254
|
+
blindSpots: BLIND_SPOTS,
|
|
1255
|
+
// 空数组 = 三个端点都真取到了;非空 = 这份四元组是**残的**,
|
|
1256
|
+
// 上面那些 0 和空摘要不代表环境真的空。
|
|
1257
|
+
fetchFailures: failures
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function compareFingerprint(recorded, current) {
|
|
1262
|
+
const diffs = GATE_COMPONENTS.filter((c) => recorded[c] !== current[c]);
|
|
1263
|
+
if (recorded.capabilities !== current.capabilities) diffs.push("capabilities");
|
|
1264
|
+
for (const [fp, tag] of [
|
|
1265
|
+
[recorded, "recorded"],
|
|
1266
|
+
[current, "current"]
|
|
1267
|
+
]) {
|
|
1268
|
+
const notes = fp.coverageNotes;
|
|
1269
|
+
if (!isPlainObject2(notes) || !("fetchFailures" in notes)) {
|
|
1270
|
+
diffs.push(`incomplete:${tag}:coverageNotes-missing`);
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
const raw = notes.fetchFailures;
|
|
1274
|
+
const list = Array.isArray(raw) ? raw : [];
|
|
1275
|
+
const gateFailures = [...new Set(list.map(String))].filter((x) => GATE_SOURCES.some((s) => x.startsWith(s))).sort();
|
|
1276
|
+
for (const f of gateFailures) diffs.push(`incomplete:${tag}:${f}`);
|
|
1277
|
+
}
|
|
1278
|
+
return diffs;
|
|
1279
|
+
}
|
|
1280
|
+
function sha256(s) {
|
|
1281
|
+
return createHash("sha256").update(s, "utf8").digest("hex");
|
|
1282
|
+
}
|
|
1283
|
+
function isPlainObject2(v) {
|
|
1284
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// src/meta/models.ts
|
|
1288
|
+
async function listModels(http) {
|
|
1289
|
+
const resp = await http.get(MODELS_PATH);
|
|
1290
|
+
return modelEntries(resp.data?.data ?? resp.data);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1132
1293
|
// src/models/create.ts
|
|
1133
1294
|
async function create(http, model, data) {
|
|
1134
1295
|
const resp = await http.post(`/api/models/${model}`, data);
|
|
@@ -1378,6 +1539,10 @@ var HcmClient = class _HcmClient {
|
|
|
1378
1539
|
action(model, name, input) {
|
|
1379
1540
|
return action(this.http, model, name, input);
|
|
1380
1541
|
}
|
|
1542
|
+
/** 列出目标环境有哪些 Model —— `describe` 的上一步。 */
|
|
1543
|
+
listModels() {
|
|
1544
|
+
return listModels(this.http);
|
|
1545
|
+
}
|
|
1381
1546
|
describe(model, opts) {
|
|
1382
1547
|
return describe(this.http, model, opts);
|
|
1383
1548
|
}
|
|
@@ -4707,7 +4872,7 @@ function buildPushPlan(context, manifest2, localFiles, remoteFiles) {
|
|
|
4707
4872
|
for (const entry of status) {
|
|
4708
4873
|
const remote = remoteByLocal.get(entry.localPath);
|
|
4709
4874
|
const base = baseByLocal.get(entry.localPath);
|
|
4710
|
-
const remoteSha = remote ?
|
|
4875
|
+
const remoteSha = remote ? sha2562(remote.content.content) : void 0;
|
|
4711
4876
|
if (entry.status === "added") {
|
|
4712
4877
|
if (remote) {
|
|
4713
4878
|
conflicts.push(conflict(entry, "remote file already exists but was not in local snapshot"));
|
|
@@ -4840,7 +5005,7 @@ async function walk(root, relativeDir, files) {
|
|
|
4840
5005
|
localPath,
|
|
4841
5006
|
absolutePath,
|
|
4842
5007
|
content,
|
|
4843
|
-
sha256:
|
|
5008
|
+
sha256: sha2562(content),
|
|
4844
5009
|
size: Buffer.byteLength(content, "utf-8")
|
|
4845
5010
|
});
|
|
4846
5011
|
}
|
|
@@ -4953,7 +5118,7 @@ function localPathFromRemote(remotePrefix, remotePath) {
|
|
|
4953
5118
|
function isDirectoryMarkerPath(relativePath) {
|
|
4954
5119
|
return relativePath.endsWith("/.keep") || relativePath === ".keep";
|
|
4955
5120
|
}
|
|
4956
|
-
function
|
|
5121
|
+
function sha2562(content) {
|
|
4957
5122
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
4958
5123
|
}
|
|
4959
5124
|
async function writeText(file, content) {
|
|
@@ -5267,7 +5432,7 @@ function parseSettingAssignment(raw) {
|
|
|
5267
5432
|
}
|
|
5268
5433
|
|
|
5269
5434
|
// src/fde/layout.ts
|
|
5270
|
-
import { createHash as
|
|
5435
|
+
import { createHash as createHash3 } from "crypto";
|
|
5271
5436
|
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
5272
5437
|
import * as path6 from "path";
|
|
5273
5438
|
var UNSAFE = /[^A-Za-z0-9._-]/g;
|
|
@@ -5276,7 +5441,7 @@ function slug(value) {
|
|
|
5276
5441
|
}
|
|
5277
5442
|
function safeSegment(value) {
|
|
5278
5443
|
const raw = value || "unknown";
|
|
5279
|
-
const digest =
|
|
5444
|
+
const digest = createHash3("sha256").update(raw, "utf8").digest("hex").slice(0, 10);
|
|
5280
5445
|
return `${slug(raw)}-${digest}`;
|
|
5281
5446
|
}
|
|
5282
5447
|
function tenantSegment(value) {
|
|
@@ -5391,161 +5556,6 @@ var LayoutContainmentError = class extends Error {
|
|
|
5391
5556
|
}
|
|
5392
5557
|
};
|
|
5393
5558
|
|
|
5394
|
-
// src/fde/fingerprint.ts
|
|
5395
|
-
import { createHash as createHash3 } from "crypto";
|
|
5396
|
-
var GATE_COMPONENTS = [
|
|
5397
|
-
"tenantMetaManifestDigest",
|
|
5398
|
-
"extensionSource",
|
|
5399
|
-
"modelListHash"
|
|
5400
|
-
];
|
|
5401
|
-
var BLIND_SPOTS = "\u4EA7\u54C1\u5347\u7EA7\uFF1Amanifest \u53EA\u8BFB\u79DF\u6237 workspace \u7684 metas/ \u76EE\u5F55\uFF0C\u4EA7\u54C1\u5347\u7EA7\u6539\u7684\u662F\u5B89\u88C5\u5305\u5185 _metas/\uFF0C\u56DB\u5143\u7EC4\u4E0D\u4F1A\u53D8\u2014\u2014\u8BE5\u573A\u666F\u7684\u9632\u7EBF\u53EA\u6709\u884C\u4E3A\u63A2\u9488\u4E00\u6761\u3002 | \u540C\u5B57\u8282\u6570\u5185\u5BB9\u66FF\u6362\uFF1A\u6E05\u5355\u53EA\u7ED9 name/size/lastModified\uFF0C\u628A\u6807\u7B7E\u300C\u90E8\u95E8\u300D\u6539\u6210\u300C\u673A\u6784\u300D\u5728 UTF-8 \u4E0B\u6070\u597D\u90FD\u662F 6 \u5B57\u8282\uFF0C\u6458\u8981\u4E0D\u53D8\u3002\u21D2 \u5B57\u6BB5\u540D\u53EB ManifestDigest \u800C\u975E Fingerprint\uFF0C\u540D\u5B57\u5FC5\u987B\u8BF4\u5B9E\u8BDD\u3002";
|
|
5402
|
-
var MODELS_PATH = "/api/system/models";
|
|
5403
|
-
var MANIFEST_PATH = "/api/tenant-meta/list";
|
|
5404
|
-
var VERSION_PATH = "/api/version/backend";
|
|
5405
|
-
var META_PATH = "/api/models/{model}/meta";
|
|
5406
|
-
var EXTENSION_SOURCE_HEADER = "x-hcm-extension-source";
|
|
5407
|
-
var MAX_SOURCE_PROBES = 50;
|
|
5408
|
-
var MAX_MANIFEST_PAGES = 200;
|
|
5409
|
-
var GATE_SOURCES = [MODELS_PATH, MANIFEST_PATH, META_PATH];
|
|
5410
|
-
function modelEntries(body) {
|
|
5411
|
-
if (!isPlainObject2(body)) return [];
|
|
5412
|
-
const raw = body.models;
|
|
5413
|
-
if (!Array.isArray(raw)) return [];
|
|
5414
|
-
return raw.filter(isPlainObject2);
|
|
5415
|
-
}
|
|
5416
|
-
function modelKeys(body) {
|
|
5417
|
-
return modelEntries(body).map((m) => typeof m.modelKey === "string" ? m.modelKey : "").filter((k) => k !== "").sort();
|
|
5418
|
-
}
|
|
5419
|
-
async function probeExtensionSource(probe, keys) {
|
|
5420
|
-
let attempted = 0;
|
|
5421
|
-
let answered = 0;
|
|
5422
|
-
for (const key of [...keys].sort().slice(0, MAX_SOURCE_PROBES)) {
|
|
5423
|
-
attempted += 1;
|
|
5424
|
-
const out = await probe.get(META_PATH.replace("{model}", key));
|
|
5425
|
-
if (out.error !== "ok") continue;
|
|
5426
|
-
answered += 1;
|
|
5427
|
-
const value = out.headers[EXTENSION_SOURCE_HEADER];
|
|
5428
|
-
if (value) return { sourceKey: value, probeModelKey: key, attempted, answered };
|
|
5429
|
-
}
|
|
5430
|
-
return { sourceKey: "", probeModelKey: "", attempted, answered };
|
|
5431
|
-
}
|
|
5432
|
-
async function fetchManifestRows(probe, failures) {
|
|
5433
|
-
const rows = [];
|
|
5434
|
-
let page = 0;
|
|
5435
|
-
const pageSize = 200;
|
|
5436
|
-
for (; ; ) {
|
|
5437
|
-
const out = await probe.get(MANIFEST_PATH, { page, pageSize });
|
|
5438
|
-
if (out.error !== "ok" || !isPlainObject2(out.body)) {
|
|
5439
|
-
failures.push(MANIFEST_PATH);
|
|
5440
|
-
break;
|
|
5441
|
-
}
|
|
5442
|
-
const body = out.body;
|
|
5443
|
-
const chunk = body.data ?? [];
|
|
5444
|
-
if (!Array.isArray(chunk)) {
|
|
5445
|
-
failures.push(MANIFEST_PATH);
|
|
5446
|
-
break;
|
|
5447
|
-
}
|
|
5448
|
-
rows.push(...chunk.filter(isPlainObject2));
|
|
5449
|
-
const totalPages = typeof body.totalPages === "number" ? body.totalPages : 0;
|
|
5450
|
-
page += 1;
|
|
5451
|
-
if (page >= totalPages || chunk.length === 0) break;
|
|
5452
|
-
if (page >= MAX_MANIFEST_PAGES) {
|
|
5453
|
-
failures.push(`${MANIFEST_PATH}#page-cap`);
|
|
5454
|
-
break;
|
|
5455
|
-
}
|
|
5456
|
-
}
|
|
5457
|
-
return rows;
|
|
5458
|
-
}
|
|
5459
|
-
function manifestDigest(rows) {
|
|
5460
|
-
const lines = rows.map((r) => `${r.name}|${r.size}|${r.lastModified}`).sort();
|
|
5461
|
-
return sha2562(lines.join("\n"));
|
|
5462
|
-
}
|
|
5463
|
-
async function computeReconSourceKey(probe) {
|
|
5464
|
-
const out = await probe.get(MODELS_PATH);
|
|
5465
|
-
const p = await probeExtensionSource(probe, modelKeys(out.body));
|
|
5466
|
-
return p.sourceKey || "unknown";
|
|
5467
|
-
}
|
|
5468
|
-
async function computeFingerprint(probe, opts = {}) {
|
|
5469
|
-
const ttlDays = opts.ttlDays ?? 30;
|
|
5470
|
-
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
5471
|
-
const failures = [];
|
|
5472
|
-
const modelsOut = await probe.get(MODELS_PATH);
|
|
5473
|
-
const modelsOk = modelsOut.error === "ok" && isPlainObject2(modelsOut.body);
|
|
5474
|
-
if (!modelsOk) failures.push(MODELS_PATH);
|
|
5475
|
-
const models = modelsOk ? modelEntries(modelsOut.body) : [];
|
|
5476
|
-
const keys = modelsOk ? modelKeys(modelsOut.body) : [];
|
|
5477
|
-
if (modelsOut.error === "ok" && keys.length === 0) {
|
|
5478
|
-
failures.push(`${MODELS_PATH}#empty-model-list`);
|
|
5479
|
-
}
|
|
5480
|
-
const p = await probeExtensionSource(probe, keys);
|
|
5481
|
-
let sourceKey = p.sourceKey;
|
|
5482
|
-
if (!sourceKey) {
|
|
5483
|
-
failures.push(
|
|
5484
|
-
p.answered > 0 ? `${META_PATH}#X-HCM-Extension-Source` : `${META_PATH}#no-meta-answered`
|
|
5485
|
-
);
|
|
5486
|
-
sourceKey = "unknown";
|
|
5487
|
-
}
|
|
5488
|
-
const hashLines = models.map((m) => `${m.modelKey ?? ""}|${m.type ?? ""}`).sort();
|
|
5489
|
-
const modelHash = sha2562(hashLines.join("\n"));
|
|
5490
|
-
const rows = await fetchManifestRows(probe, failures);
|
|
5491
|
-
const versionOut = await probe.get(VERSION_PATH);
|
|
5492
|
-
let backendVersion;
|
|
5493
|
-
if (versionOut.error === "ok" && versionOut.body !== null && versionOut.body !== void 0) {
|
|
5494
|
-
backendVersion = versionOut.body;
|
|
5495
|
-
} else {
|
|
5496
|
-
failures.push(VERSION_PATH);
|
|
5497
|
-
backendVersion = { version: "unavailable" };
|
|
5498
|
-
}
|
|
5499
|
-
return {
|
|
5500
|
-
tenantMetaManifestDigest: manifestDigest(rows),
|
|
5501
|
-
manifestEntryCount: rows.length,
|
|
5502
|
-
extensionSource: sourceKey,
|
|
5503
|
-
// 🔴 记下**是从哪个模型探到的**:让「这个值是谁给的」在产物里留痕。
|
|
5504
|
-
extensionSourceProbe: p.probeModelKey,
|
|
5505
|
-
// 🔴 没有这两个数,`extensionSource: "unknown"` 就是一个**不可追问的结论**。
|
|
5506
|
-
extensionSourceProbeAttempted: p.attempted,
|
|
5507
|
-
extensionSourceProbeAnswered: p.answered,
|
|
5508
|
-
modelListHash: modelHash,
|
|
5509
|
-
modelCount: models.length,
|
|
5510
|
-
backendVersion,
|
|
5511
|
-
capabilities: probe.capabilities,
|
|
5512
|
-
createdAt: `${now().toISOString().slice(0, 19)}Z`,
|
|
5513
|
-
ttlDays,
|
|
5514
|
-
coverageNotes: {
|
|
5515
|
-
gateComponents: [...GATE_COMPONENTS],
|
|
5516
|
-
blindSpots: BLIND_SPOTS,
|
|
5517
|
-
// 空数组 = 三个端点都真取到了;非空 = 这份四元组是**残的**,
|
|
5518
|
-
// 上面那些 0 和空摘要不代表环境真的空。
|
|
5519
|
-
fetchFailures: failures
|
|
5520
|
-
}
|
|
5521
|
-
};
|
|
5522
|
-
}
|
|
5523
|
-
function compareFingerprint(recorded, current) {
|
|
5524
|
-
const diffs = GATE_COMPONENTS.filter((c) => recorded[c] !== current[c]);
|
|
5525
|
-
if (recorded.capabilities !== current.capabilities) diffs.push("capabilities");
|
|
5526
|
-
for (const [fp, tag] of [
|
|
5527
|
-
[recorded, "recorded"],
|
|
5528
|
-
[current, "current"]
|
|
5529
|
-
]) {
|
|
5530
|
-
const notes = fp.coverageNotes;
|
|
5531
|
-
if (!isPlainObject2(notes) || !("fetchFailures" in notes)) {
|
|
5532
|
-
diffs.push(`incomplete:${tag}:coverageNotes-missing`);
|
|
5533
|
-
continue;
|
|
5534
|
-
}
|
|
5535
|
-
const raw = notes.fetchFailures;
|
|
5536
|
-
const list = Array.isArray(raw) ? raw : [];
|
|
5537
|
-
const gateFailures = [...new Set(list.map(String))].filter((x) => GATE_SOURCES.some((s) => x.startsWith(s))).sort();
|
|
5538
|
-
for (const f of gateFailures) diffs.push(`incomplete:${tag}:${f}`);
|
|
5539
|
-
}
|
|
5540
|
-
return diffs;
|
|
5541
|
-
}
|
|
5542
|
-
function sha2562(s) {
|
|
5543
|
-
return createHash3("sha256").update(s, "utf8").digest("hex");
|
|
5544
|
-
}
|
|
5545
|
-
function isPlainObject2(v) {
|
|
5546
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5547
|
-
}
|
|
5548
|
-
|
|
5549
5559
|
// src/fde/doctor.ts
|
|
5550
5560
|
var DOCTOR_ANCHORS = [
|
|
5551
5561
|
"connectivity",
|
|
@@ -9275,6 +9285,7 @@ export {
|
|
|
9275
9285
|
isServerSlidingSession,
|
|
9276
9286
|
listEnvs,
|
|
9277
9287
|
listIdentities,
|
|
9288
|
+
listModels,
|
|
9278
9289
|
listProfiles,
|
|
9279
9290
|
listTenantMeta,
|
|
9280
9291
|
listWorkspaceFiles,
|