mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-20 05:41:40 +00:00
* fix(core): make indexed access explicit in auto-reply, infra, and config Part 1/3 of the src NUIA phase-3b burn-down (#104600): iteration and destructuring over index reads, boundary guards on parsed input, and named invariants. Config path walkers bind the path head once; SQLite migration key handling is hoisted without query-shape changes. * fix(core): make indexed access explicit in cli, gateway, commands, security, shared Part 2/3: argv/token selection restructured, gateway event/attachment invariants named, security parsers stay fail-closed (invariant violations throw), edit-distance matrices access checked entries. * fix(core): make indexed access explicit across remaining src surfaces Part 3/3: channels, plugins, process, cron, plugin-sdk, media, logging, tui, hooks, daemon, and small directories. Latent bug fixed: a tailnet resolver could leak undefined through a string|null contract and now fails with a descriptive local error. * fix(core): keep optional boundaries optional after per-commit review Review findings: expectDefined misused where absence is a legitimate state. CLI --profile/route-args missing next tokens take their existing miss paths; help normalization compares --help against the last positional again; first-time plugin install spreads absent cfg.plugins; denylist scan iterates manifest dependency entries instead of throwing on omitted sections; tailnet resolver returns a guaranteed string at the source instead of a caller-side undefined throw. * refactor(core): closed-key provider labels and honest optional passthroughs PROVIDER_LABELS becomes a satisfies-typed closed record (static reads provably defined; dynamic lookups go through providerUsageLabel with honest string|undefined). Status-scan overview passes its optional params through unchanged instead of asserting them. * fix(channels): make getChatChannelMeta honestly optional The original signature claimed ChatChannelMeta while leaking undefined on bundled channel id metadata drift; three of four callers already handled absence. The return type now says so, and the one assuming caller falls back to the raw channel label. * fix(core): index-safety for post-rebase main drift Covers the sqlite-sessions flip and auth-source-plan code that landed mid-phase, plus the channel-validation test consuming the now honestly optional getChatChannelMeta. * refactor(channels): split chat-meta accessors along the SDK contract getChatChannelMeta keeps its shipped plugin-SDK signature (defined for bundled ids, fail-loud on impossible misses); new findChatChannelMeta carries the drift-tolerant optional contract for core auto-enable and formatting paths. * fix(qa-channel): own channel metadata instead of a guaranteed-undefined catalog lookup qa-channel spread getChatChannelMeta over an id that is never in the bundled catalog, shipping an empty setup meta by accident; the fail-loud SDK accessor exposed it. The channel now declares its metadata once. * fix(gateway): heartbeat projection lookahead is optional at the transcript tail expectDefined wrapped messages[i + 1] whose absence on the final message is the normal case; the adjacent ternary already handled it. Restores the plain optional read with an explicit guard in the pair condition. * fix(plugin-sdk): channel plugin factory tolerates non-bundled channel ids again createChannelPluginBase spreads bundled catalog meta for ANY channel id, where absence is the normal case for external plugins; the resolver is honestly optional again while the exported bundled-id accessor keeps the fail-loud contract. * fix(core): spreads of optional config sections stay optional Fresh-setup and first-install paths (crestodian setup inference, hook installs, agent config base, target agent models) legitimately lack the section being rebuilt; spreading undefined is the shipped {} semantics. Removes the remaining gratuitous assertion wraps found by tree audit.
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
import { expectDefined } from "@openclaw/normalization-core";
|
|
// Provider flow runtime helpers load provider setup behavior behind runtime imports.
|
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import * as providerWizard from "../plugins/provider-wizard.js";
|
|
import type { ProviderModelPickerEntry } from "../plugins/provider-wizard.js";
|
|
import * as providersRuntime from "../plugins/providers.runtime.js";
|
|
import type { ProviderPlugin } from "../plugins/types.js";
|
|
import type { FlowContribution } from "./types.js";
|
|
import { sortFlowContributionsByLabel } from "./types.js";
|
|
|
|
// Runtime-backed provider entries for model-picker setup flows.
|
|
type ProviderModelPickerFlowEntry = ProviderModelPickerEntry;
|
|
|
|
type ProviderModelPickerFlowContribution = FlowContribution & {
|
|
kind: "provider";
|
|
surface: "model-picker";
|
|
providerId: string;
|
|
option: ProviderModelPickerFlowEntry;
|
|
source: "runtime";
|
|
};
|
|
|
|
function resolveProviderDocsById(params?: {
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): Map<string, string> {
|
|
return new Map(
|
|
providersRuntime
|
|
.resolvePluginProviders({
|
|
config: params?.config,
|
|
workspaceDir: params?.workspaceDir,
|
|
env: params?.env,
|
|
mode: "setup",
|
|
})
|
|
.filter((provider): provider is ProviderPlugin & { docsPath: string } =>
|
|
Boolean(normalizeOptionalString(provider.docsPath)),
|
|
)
|
|
.map((provider) => [provider.id, normalizeOptionalString(provider.docsPath)!]),
|
|
);
|
|
}
|
|
|
|
/** Resolves provider model-picker options without exposing contribution metadata. */
|
|
export function resolveProviderModelPickerFlowEntries(params?: {
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): ProviderModelPickerFlowEntry[] {
|
|
return resolveProviderModelPickerFlowContributions(params).map(
|
|
(contribution) => contribution.option,
|
|
);
|
|
}
|
|
|
|
/** Resolves provider model-picker contributions with docs metadata for setup UIs. */
|
|
export function resolveProviderModelPickerFlowContributions(params?: {
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): ProviderModelPickerFlowContribution[] {
|
|
const docsByProvider = resolveProviderDocsById(params ?? {});
|
|
return sortFlowContributionsByLabel(
|
|
providerWizard.resolveProviderModelPickerEntries(params ?? {}).map((entry) => {
|
|
const providerId = entry.value.startsWith("provider-plugin:")
|
|
? expectDefined(
|
|
entry.value.slice("provider-plugin:".length).split(":").at(0),
|
|
"provider id",
|
|
)
|
|
: entry.value;
|
|
const docsPath = docsByProvider.get(providerId);
|
|
// Provider-plugin values encode plugin/provider in the option value; docs attach by provider id.
|
|
return {
|
|
id: `provider:model-picker:${entry.value}`,
|
|
kind: "provider" as const,
|
|
surface: "model-picker" as const,
|
|
providerId,
|
|
option: {
|
|
value: entry.value,
|
|
label: entry.label,
|
|
...(entry.hint ? { hint: entry.hint } : {}),
|
|
...(docsPath ? { docs: { path: docsPath } } : {}),
|
|
},
|
|
source: "runtime" as const,
|
|
};
|
|
}),
|
|
);
|
|
}
|