Fix empty Bearer on netx__* by waiting for credentials.

Inject credentials before publish, read the live token per HTTP request, and surface netx_token_missing instead of a silent 401.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 02:11:20 +08:00
parent 02cc68bee3
commit 027f83dd00
7 changed files with 77 additions and 37 deletions

View file

@ -57,12 +57,6 @@ function encodeQuery(params) {
}
function createNetxClient(connection) {
const base = connection.apiUrl.replace(/\/$/, "");
const headers = {
accept: "application/json"
};
if (connection.token.trim().length > 0) {
headers.authorization = `Bearer ${connection.token.trim()}`;
}
const langParams = () => {
const lang = connection.lang.trim().toLowerCase();
if (lang.startsWith("en"))
@ -70,6 +64,14 @@ function createNetxClient(connection) {
return {};
};
async function request(method, path, options = {}) {
const token = connection.getToken().trim();
if (token.length === 0) {
return {
ok: false,
error: "netx_token_missing",
detail: "Set credential NETX_API_TOKEN (Plugins → Netx Ops or scripts/set-netx-token)."
};
}
const merged = { ...langParams(), ...options.params };
const url = `${base}${path}${encodeQuery(merged)}`;
const timeoutMs = options.timeoutMs ?? connection.timeoutMs;
@ -82,9 +84,15 @@ function createNetxClient(connection) {
};
options.signal?.addEventListener("abort", onOuterAbort, { once: true });
try {
const headers = {
accept: "application/json",
authorization: `Bearer ${token}`
};
if (options.body !== undefined)
headers["content-type"] = "application/json";
const init = {
method,
headers: options.body === undefined ? headers : { ...headers, "content-type": "application/json" },
headers,
signal: controller.signal
};
if (options.body !== undefined)
@ -515,9 +523,9 @@ function tool(name, description, parameters, handler, getClient, timeoutMs) {
function registerNetxTools(ctx, connection) {
const client = createNetxClient({
apiUrl: connection.apiUrl,
token: connection.token,
lang: connection.lang,
timeoutMs: Math.min(connection.toolCallTimeoutMs, 45000)
timeoutMs: Math.min(connection.toolCallTimeoutMs, 45000),
getToken: () => getNetxConnection()?.token ?? ""
});
const getClient = () => client;
const t = connection.toolCallTimeoutMs;
@ -662,7 +670,8 @@ function apply(ctx) {
return;
}
unregister = registerNetxTools(ctx, connection);
ctx.logger.info("netxops-tools: registered netx__* for Ops preset → %s", connection.apiUrl);
const tokenConfigured = connection.token.trim().length > 0;
ctx.logger.info("netxops-tools: registered netx__* for Ops preset → %s tokenConfigured=%s", connection.apiUrl, tokenConfigured);
};
remount();
const stopWatch = watchNetxConnection(() => {

View file

@ -27,6 +27,7 @@ function publishNetxConnection(next) {
// src/index.ts
var name = "netxops";
var inject = ["credentials"];
var NETXOPS_SETTINGS_NAMESPACE = "netxops";
var NETXOPS_PRESET_ID = "netxops";
var DEFAULT_TOKEN_REF = "NETX_API_TOKEN";
@ -69,10 +70,7 @@ function ensureAgentPresetInstalled(logger) {
}
}
async function resolveToken(ctx, refName) {
const credentials = ctx.get("credentials");
if (credentials === undefined)
return "";
const hit = await credentials.resolve(credentialRef(refName));
const hit = await ctx.credentials.resolve(credentialRef(refName));
return hit?.value ?? "";
}
function installNetxopsSettings(ctx, entry, hooks) {
@ -99,13 +97,19 @@ function apply(ctx, config = Config({})) {
const token = await resolveToken(ctx, current.tokenCredentialRef);
if (gen !== generation)
return;
const apiUrl = current.apiUrl.replace(/\/$/, "");
const tokenConfigured = token.trim().length > 0;
publishNetxConnection({
apiUrl: current.apiUrl.replace(/\/$/, ""),
apiUrl,
token,
lang: current.lang,
toolCallTimeoutMs: current.toolCallTimeoutMs
});
ctx.logger.info("netxops: published connection → %s", current.apiUrl.replace(/\/$/, ""));
if (!tokenConfigured) {
ctx.logger.warn("netxops: published connection → %s tokenConfigured=false (set credential %s)", apiUrl, current.tokenCredentialRef);
} else {
ctx.logger.info("netxops: published connection → %s tokenConfigured=true", apiUrl);
}
}).catch((error) => {
ctx.logger.error("netxops: connection publish error: %s", error);
});
@ -129,6 +133,7 @@ function apply(ctx, config = Config({})) {
}
export {
name,
inject,
ensureAgentPresetInstalled,
apply,
NETXOPS_SETTINGS_NAMESPACE,