mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-20 02:33:15 +08:00
Harden public package installation and release validation
This commit is contained in:
parent
9271494b37
commit
9b2166e5b6
41 changed files with 994 additions and 363 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
47
lib/index.js
47
lib/index.js
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue