Adapt settings client for DSH 0.2.0 configForms without breaking boot.

Soft-inject configForms/settingsScope and mark Config fields volatile so desktop 0.2.0 no longer waits forever on the removed settingsScope service.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-10-09 15:31:31 +08:00
parent 30d4df23fa
commit 9b8e873c18
8 changed files with 358 additions and 166 deletions

View file

@ -5236,30 +5236,34 @@ var NETXOPS_RPC_CHANNEL = "/netxops";
var NETXOPS_SETTINGS_NAMESPACE = "netxops";
var NETXOPS_PRESET_ID = "netxops";
var DEFAULT_TOKEN_REF = "NETX_API_TOKEN";
function vol(schema) {
const candidate = schema;
return typeof candidate.volatile === "function" ? candidate.volatile() : schema;
}
var Config = z.object({
apiUrl: z.string().default("http://127.0.0.1:8890"),
lang: z.string().default("zh"),
thinkingLanguage: z.string().default("auto"),
replyLanguage: z.string().default("follow-user"),
tokenCredentialRef: z.string().role("credential-ref").default(DEFAULT_TOKEN_REF),
toolCallTimeoutMs: z.number().step(1).min(1000).default(120000),
installAgentPreset: z.boolean().default(true),
alarmPushEnabled: z.boolean().default(false),
alarmDeliverDsh: z.boolean().default(true),
alarmDeliverIm: z.boolean().default(false),
imTargets: z.string().default(""),
imBotId: z.string().default(""),
imTargetId: z.string().default(""),
nmsProvider: z.string().default("zte-ume"),
groupOpsInPreset: z.boolean().default(true),
groupOpsPublic: z.boolean().default(false),
groupTopologyInPreset: z.boolean().default(false),
groupTopologyPublic: z.boolean().default(false),
groupBizMonitorInPreset: z.boolean().default(true),
groupBizMonitorPublic: z.boolean().default(false),
kbRoot: z.string().default(""),
groupKbInPreset: z.boolean().default(true),
groupKbPublic: z.boolean().default(false)
apiUrl: vol(z.string().default("http://127.0.0.1:8890")),
lang: vol(z.string().default("zh")),
thinkingLanguage: vol(z.string().default("auto")),
replyLanguage: vol(z.string().default("follow-user")),
tokenCredentialRef: vol(z.string().role("credential-ref").default(DEFAULT_TOKEN_REF)),
toolCallTimeoutMs: vol(z.number().step(1).min(1000).default(120000)),
installAgentPreset: vol(z.boolean().default(true)),
alarmPushEnabled: vol(z.boolean().default(false)),
alarmDeliverDsh: vol(z.boolean().default(true)),
alarmDeliverIm: vol(z.boolean().default(false)),
imTargets: vol(z.string().default("")),
imBotId: vol(z.string().default("")),
imTargetId: vol(z.string().default("")),
nmsProvider: vol(z.string().default("zte-ume")),
groupOpsInPreset: vol(z.boolean().default(true)),
groupOpsPublic: vol(z.boolean().default(false)),
groupTopologyInPreset: vol(z.boolean().default(false)),
groupTopologyPublic: vol(z.boolean().default(false)),
groupBizMonitorInPreset: vol(z.boolean().default(true)),
groupBizMonitorPublic: vol(z.boolean().default(false)),
kbRoot: vol(z.string().default("")),
groupKbInPreset: vol(z.boolean().default(true)),
groupKbPublic: vol(z.boolean().default(false))
});
function packageRoot() {
return join7(dirname4(fileURLToPath2(import.meta.url)), "..");
@ -5296,6 +5300,19 @@ async function resolveToken(ctx, refName) {
const hit = await ctx.credentials.resolve(credentialRef(refName));
return hit?.value ?? "";
}
function describeRows(describe) {
if (typeof describe !== "function")
return [];
try {
const raw = describe();
if (Array.isArray(raw))
return raw;
if (raw && typeof raw === "object" && Array.isArray(raw.namespaces)) {
return raw.namespaces;
}
} catch {}
return [];
}
function installNetxopsSettings(ctx, entry, hooks) {
const legacy = DshSettings.installSettingsSection;
if (typeof legacy === "function") {
@ -5303,7 +5320,33 @@ function installNetxopsSettings(ctx, entry, hooks) {
return;
}
ctx.inject(["settings"], (settingsCtx) => {
settingsCtx.settings.installSection(ctx, NETXOPS_SETTINGS_NAMESPACE, Config, entry, hooks);
const settings = settingsCtx.settings;
if (typeof settings.installSection === "function") {
settings.installSection(ctx, NETXOPS_SETTINGS_NAMESPACE, Config, entry, hooks);
return;
}
const readLive = () => {
const row = describeRows(settings.describe).find((item) => item.ns === NETXOPS_SETTINGS_NAMESPACE);
if (row?.value !== null && typeof row?.value === "object" && !Array.isArray(row.value)) {
return { ...entry, ...row.value };
}
return entry;
};
hooks.setSource(readLive);
hooks.onChange();
let lastFingerprint = JSON.stringify(readLive());
const timer = setInterval(() => {
const next = readLive();
const fingerprint = JSON.stringify(next);
if (fingerprint === lastFingerprint)
return;
lastFingerprint = fingerprint;
hooks.onChange();
}, 2000);
settingsCtx.effect(() => () => {
clearInterval(timer);
}, "netxops: settings describe poll");
settingsCtx.logger.info("netxops: following settings via describe() (no installSection — DSH ≥0.1.7 path)");
});
}
function apply(ctx, config = Config({})) {