Merge 6f42fb105ff17f7a674412adc58091e1df899a95 into 598f1826d8b2bc969aace2c6459824737667218c

This commit is contained in:
Tomi 2026-03-21 03:15:10 +00:00 committed by GitHub
commit fa1bf4d91e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 44 deletions

View File

@ -169,6 +169,18 @@ function resolveSyntheticLocalProviderAuth(params: {
cfg: OpenClawConfig | undefined;
provider: string;
}): ResolvedProviderAuth | null {
// Check for known local providers first — these should always get synthetic auth,
// even when the user sets models.primary without running the provider setup wizard
// (i.e., no explicit models.providers.{name} config entry exists).
const normalizedProvider = normalizeProviderId(params.provider);
if (normalizedProvider === "ollama") {
return {
apiKey: OLLAMA_LOCAL_AUTH_MARKER,
source: "models.providers.ollama (synthetic local key)",
mode: "api-key",
};
}
const providerConfig = resolveProviderConfig(params.cfg, params.provider);
if (!providerConfig) {
return null;
@ -182,15 +194,6 @@ function resolveSyntheticLocalProviderAuth(params: {
return null;
}
const normalizedProvider = normalizeProviderId(params.provider);
if (normalizedProvider === "ollama") {
return {
apiKey: OLLAMA_LOCAL_AUTH_MARKER,
source: "models.providers.ollama (synthetic local key)",
mode: "api-key",
};
}
const authOverride = resolveProviderAuthOverride(params.cfg, params.provider);
if (authOverride && authOverride !== "api-key") {
return null;

View File

@ -304,54 +304,89 @@ export function registerNodesStatusCommands(nodes: Command) {
await runNodesCommand("list", async () => {
const connectedOnly = Boolean(opts.connected);
const sinceMs = parseSinceMs(opts.lastConnected, "Invalid --last-connected");
const result = await callGatewayCli("node.pair.list", opts, {});
const { pending, paired } = parsePairingList(result);
// Call both sources in parallel:
// - node.list: device pairing (always returns known nodes)
// - node.pair.list: gateway-owned node-pairing store — needed because `nodes approve`
// writes here and those nodes may not appear in the device pairing list (k8s volumes,
// separate data directories). Showing both sources fixes the "Paired: 0" bug.
const [nodeListResult, pairingResult] = await Promise.all([
callGatewayCli("node.list", opts, {}),
callGatewayCli("node.pair.list", opts, {}),
]);
const { pending, paired: pairingPaired } = parsePairingList(pairingResult);
const deviceNodes = parseNodeList(nodeListResult);
// Build lookup maps keyed by nodeId
const pairingById = new Map(pairingPaired.map((n) => [n.nodeId, n]));
const deviceById = new Map(deviceNodes.map((n) => [n.nodeId, n]));
// Merge both sources: start with device nodes, then add pairing-only nodes
const allNodesMap = new Map(deviceById);
for (const pNode of pairingPaired) {
if (!allNodesMap.has(pNode.nodeId)) {
allNodesMap.set(pNode.nodeId, {
nodeId: pNode.nodeId,
displayName: pNode.displayName,
platform: pNode.platform,
version: pNode.version,
coreVersion: pNode.coreVersion,
uiVersion: pNode.uiVersion,
remoteIp: pNode.remoteIp,
paired: true,
connected: false,
connectedAtMs: pNode.lastConnectedAtMs,
});
}
}
const allNodes = Array.from(allNodesMap.values());
const { heading, muted, warn } = getNodesTheme();
const tableWidth = getTerminalTableWidth();
const now = Date.now();
const hasFilters = connectedOnly || sinceMs !== undefined;
const pendingRows = hasFilters ? [] : pending;
const connectedById = hasFilters
? new Map(
parseNodeList(await callGatewayCli("node.list", opts, {})).map((node) => [
node.nodeId,
node,
]),
)
: null;
const filteredPaired = paired.filter((node) => {
if (connectedOnly) {
const live = connectedById?.get(node.nodeId);
if (!live?.connected) {
return false;
}
}
const filteredPaired = allNodes.filter((node) => {
if (!node.paired && !pairingById.has(node.nodeId)) return false;
if (connectedOnly && !node.connected) return false;
if (sinceMs !== undefined) {
const live = connectedById?.get(node.nodeId);
const pData = pairingById.get(node.nodeId);
const lastConnectedAtMs =
typeof node.lastConnectedAtMs === "number"
? node.lastConnectedAtMs
: typeof live?.connectedAtMs === "number"
? live.connectedAtMs
typeof pData?.lastConnectedAtMs === "number"
? pData.lastConnectedAtMs
: typeof node.connectedAtMs === "number"
? node.connectedAtMs
: undefined;
if (typeof lastConnectedAtMs !== "number") {
return false;
}
if (now - lastConnectedAtMs > sinceMs) {
return false;
}
if (typeof lastConnectedAtMs !== "number") return false;
if (now - lastConnectedAtMs > sinceMs) return false;
}
return true;
});
const totalPairedCount = allNodes.filter(
(n) => n.paired || pairingById.has(n.nodeId),
).length;
const filteredLabel =
hasFilters && filteredPaired.length !== paired.length ? ` (of ${paired.length})` : "";
hasFilters && filteredPaired.length !== totalPairedCount
? ` (of ${totalPairedCount})`
: "";
defaultRuntime.log(
`Pending: ${pendingRows.length} · Paired: ${filteredPaired.length}${filteredLabel}`,
);
if (opts.json) {
defaultRuntime.log(
JSON.stringify({ pending: pendingRows, paired: filteredPaired }, null, 2),
JSON.stringify(
{
pending: pendingRows,
paired: filteredPaired.map((n) => ({
...n,
...(pairingById.get(n.nodeId) ?? {}),
})),
},
null,
2,
),
);
return;
}
@ -370,12 +405,12 @@ export function registerNodesStatusCommands(nodes: Command) {
if (filteredPaired.length > 0) {
const pairedRows = filteredPaired.map((n) => {
const live = connectedById?.get(n.nodeId);
const pData = pairingById.get(n.nodeId);
const lastConnectedAtMs =
typeof n.lastConnectedAtMs === "number"
? n.lastConnectedAtMs
: typeof live?.connectedAtMs === "number"
? live.connectedAtMs
typeof pData?.lastConnectedAtMs === "number"
? pData.lastConnectedAtMs
: typeof n.connectedAtMs === "number"
? n.connectedAtMs
: undefined;
return {
Node: n.displayName?.trim() ? n.displayName.trim() : n.nodeId,
@ -402,6 +437,7 @@ export function registerNodesStatusCommands(nodes: Command) {
}).trimEnd(),
);
}
}
});
}),
);