Harden public package installation and release validation
Some checks failed
Release package / package (macos-latest) (push) Has been cancelled
Release package / package (ubuntu-latest) (push) Has been cancelled
Release package / package (windows-latest) (push) Has been cancelled

This commit is contained in:
oliver 2026-10-11 13:32:36 +08:00
parent 9271494b37
commit 9b2166e5b6
41 changed files with 994 additions and 363 deletions

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -132,11 +132,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync(envRoot))
return envRoot;
const here = dirname(fileURLToPath(import.meta.url));
const checkout = [join(here, "..", ".git"), join(here, "..", "..", ".git")].some(existsSync);
const siblingCandidates = [
join(here, "..", "..", "..", "netx", "skills"),
join(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync(candidate))
return candidate;
}

View file

@ -15,10 +15,12 @@ import {
existsSync,
mkdirSync,
readFileSync,
renameSync,
unlinkSync,
writeFileSync
} from "node:fs";
import { createRequire as createRequire2 } from "node:module";
import { createHash } from "node:crypto";
import { createHash, randomUUID } from "node:crypto";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
@ -261,7 +263,11 @@ function extractPluginsBodyFromStandardPatch(patchYaml) {
`);
}
function pluginsBodyToAgentCordis(pluginsBody) {
const prefix = PATCH_PLUGIN_INDENT;
const normalized = pluginsBody.replace(/\r\n/g, `
`);
const prefix = /^([ \t]*)-\s/m.exec(normalized)?.[1];
if (prefix === undefined)
throw new Error("standard preset plugins must be a YAML list");
return `${pluginsBody.replace(/\r\n/g, `
`).split(`
`).map((line) => {
@ -269,7 +275,7 @@ function pluginsBodyToAgentCordis(pluginsBody) {
return "";
if (line.startsWith(prefix))
return line.slice(prefix.length);
return line.trimStart();
throw new Error("standard preset plugins have inconsistent indentation");
}).join(`
`).trimEnd()}
`;
@ -476,8 +482,8 @@ function installDirectoryPreset(artifacts, logger) {
logger.info("netxops: directory preset composed from %s → %s", artifacts.standardSource, dest);
}
function syncDeclarativeIntoProfilePatch(profileName, declarativePatch, logger) {
if (profileName !== "desktop" && profileName !== "web")
return;
if (!/^[\w-]+$/.test(profileName))
throw new Error("invalid Host profile name");
const profileDir = join(resolveDshHome(), "profiles", profileName);
const patchPath = join(profileDir, "cordis.patch.yml");
if (!existsSync(profileDir))
@ -486,12 +492,17 @@ function syncDeclarativeIntoProfilePatch(profileName, declarativePatch, logger)
`;
let patch = original;
patch = stripManagedPresetBlocks(patch);
if (/^\s*- id: preset-netxops\s*$/m.test(patch)) {
if (/^\s*-\s+id:\s*(?:preset-netxops|"preset-netxops"|'preset-netxops')\s*(?:#.*)?$/m.test(patch)) {
logger.warn("netxops: preserving user-declared preset-netxops in %s", patchPath);
return;
}
if (patch.trim() === "" || patch.trim() === "[]")
patch = "";
const contentLines = patch.replace(/^\uFEFF/, "").split(/\r?\n/).filter((line) => !/^\s*(?:#.*)?$/.test(line) && !/^(?:---|\.\.\.)[ \t]*(?:#.*)?$/.test(line));
if (contentLines.length === 1 && /^\[\][ \t]*(?:#.*)?$/.test(contentLines[0])) {
patch = patch.replace(/^\uFEFF/, "").replace(/^\[\][ \t]*(#.*)?$/m, (_line, comment) => comment ?? "");
} else if (contentLines.length > 0 && !/^\s*-\s/.test(contentLines[0])) {
throw new Error("profile patch must be a YAML block list; preserving the existing document");
}
patch = patch.replace(/^\.\.\.[ \t]*(#.*)?$/gm, (_line, comment) => comment ?? "");
if (!patch.endsWith(`
`) && patch.length > 0)
patch += `
@ -507,7 +518,19 @@ ${DESKTOP_PATCH_END}
logger.warn("netxops: profile patch changed during preset installation; preserving %s", patchPath);
return;
}
writeFileSync(patchPath, next, "utf8");
const temporary = join(profileDir, `.netxops-patch-${randomUUID()}.tmp`);
try {
writeFileSync(temporary, next, { encoding: "utf8", flag: "wx" });
if ((existsSync(patchPath) ? readFileSync(patchPath, "utf8") : `[]
`) !== original) {
logger.warn("netxops: profile patch changed during preset installation; preserving %s", patchPath);
return;
}
renameSync(temporary, patchPath);
} finally {
if (existsSync(temporary))
unlinkSync(temporary);
}
logger.info("netxops: declarative preset synced into profiles/%s/cordis.patch.yml (restart %s to load)", profileName, profileName === "desktop" ? "Desktop" : "dsh web");
}
function stripManagedPresetBlocks(patch) {
@ -1485,11 +1508,12 @@ function opsSkillsRoot() {
if (envRoot && existsSync3(envRoot))
return envRoot;
const here = dirname2(fileURLToPath2(import.meta.url));
const checkout = [join3(here, "..", ".git"), join3(here, "..", "..", ".git")].some(existsSync3);
const siblingCandidates = [
join3(here, "..", "..", "..", "netx", "skills"),
join3(here, "..", "..", "netx", "skills")
];
for (const candidate of siblingCandidates) {
for (const candidate of checkout ? siblingCandidates : []) {
if (existsSync3(candidate))
return candidate;
}
@ -7159,6 +7183,7 @@ function extractKbResolvePath(payload) {
}
export {
writePatchIfUnchanged,
syncDeclarativeIntoProfilePatch,
resolveToolCallTimeoutMs,
resolveTokenCredentialRef,
resolveLang,
@ -7167,8 +7192,10 @@ export {
name,
mergeHostConfigForHeal,
mergeConfigSkippingNullish,
installDirectoryPreset,
inject,
ensureAgentPresetInstalled,
composeNetxopsPresetArtifacts,
apply,
NETXOPS_SETTINGS_NAMESPACE,
NETXOPS_RPC_CHANNEL,