mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor: consolidate coercion ownership (#122299)
* refactor: consolidate coercion ownership Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries. The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries. * fix: guard integer option ownership Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation. * fix: keep integer helpers on numeric facade Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract. * fix: point numeric coercion to number runtime Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
This commit is contained in:
committed by
GitHub
parent
88dba70ec5
commit
964c8c84c1
@@ -1 +1 @@
|
|||||||
{"contentHash":"ca54298c1a0adf9550f58fc13881d7e7f37e4c755a5f917a595bc4f0177d9e20","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"}
|
{"contentHash":"b017f177c844e59f8c9771773fd9fe13bd49777097859ecec3e90057d6cc3709","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"00743220201e85945df77cc8e276a4842f93602c971e06459ea00dd029f063ff","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"}
|
{"contentHash":"9695a1112df503dd92e36a5502aa3930289d6828a562917ea5b8d6b7c25a0755","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"b0d043c2fc4d36023fa7bd6472c9b9cc5f0ee17ff4375d598e59fecb45cbe85c","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"}
|
{"contentHash":"96449168bf95c5c22ff75ba7d50d21cb2d9c73666c5ceef95450d76273cd36ca","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"555310d087ce5a7a801f6291ee837283eb182e9bf86f294e11dc0d4559c9c268","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"}
|
{"contentHash":"f1e6d92d125cdaca72317819b5ba7619cbfcb67ee72752bb009af78a8f753b38","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"08111748e2944e6bf1571eda856a46a00c813e2d90fd3da04a9c473b033bc38c","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"}
|
{"contentHash":"1b3770f22431dbbce8ffcbffd4496a95d2afaac82172aa48b73c9c8bceac7ca5","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"4561d105811249b490dcc691888e082fbb5365b0652fee8e97d272c55f7c1eba","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"}
|
{"contentHash":"fdea77b1259c9799177f9c2b327a6d759058d0df0ef339834b6c0a6a9d04b130","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"8bef6c7c70abc4745029421fef29125646effa27bb8947541e0ca9bea4e2da9b","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"}
|
{"contentHash":"ddc4dd3f139c5858912bad00560051731ef386eaec43957343b01f76224ecca3","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"140e69b3a97558ca94edd1fc83fc528e33565d0b3b7b16a2fb7dbf9bd9deb0be","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"}
|
{"contentHash":"8f806aa43ddd9298176cacb5971b8ddac7f2c1d25101deaa92b3c9e1f788e09a","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"af02ae51d5c0941991928b998ecac5584639e50912532082fa0d16fe0a40e43f","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"}
|
{"contentHash":"11138710259d030a81c55a9e506254428328931945b6c6b79fc969e832d56901","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"e0523b4936d7c7ec043265de9e2a8f6289d32dacba0a28c3999ba3a0975dfc21","entrypoint":"error-runtime","importSpecifier":"openclaw/plugin-sdk/error-runtime"}
|
{"contentHash":"1f61b7d845d0172c62a5413cd96559796dbe338b38866f91782ff74485aef96e","entrypoint":"error-runtime","importSpecifier":"openclaw/plugin-sdk/error-runtime"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"de31ab324cc57e8f525cac7559813e83a434c3cb114124388558c0b35e094adc","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"}
|
{"contentHash":"180d24e66df9e1242f1ba78f4aea182c79340280d022db048b227071892fbb59","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"f559df4c86dd30b151db7e159944df99f5870a8564b4ef07adfbff4826737aa4","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"}
|
{"contentHash":"5a99888e906478be43c13f67f99fb2f74c7396eb834202e7d872f0e2ee60f144","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"9f9e1c49a46f04964615be21556834f4d7d6042aedef30812973efbc2af9871c","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"}
|
{"contentHash":"1896a2342b27ee5f055bf77f9a826f76ddc6520c9dce44789d6d612cfcdd17dc","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"b1883cb0c123488b0d8f93934cc27dca32e349d17bab1119e0d273655b368c4a","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"}
|
{"contentHash":"fcdd433db990480373e6caea357c69cbbc754bcbf492fe3371cd21491d9bd04f","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"7923737351da1ca1c0c0b3c3ce3d105a61739f633d27da4cc8b92c957ccf397f","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"}
|
{"contentHash":"9459eb7103be5db943a12ebbf69b62f9a06b60cf5a24df9a8b72fa2bd4242dce","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"14d56ffc88afba3e8c83ace0894f0187860a45b739d562b96e290d2235c45294","entrypoint":"string-coerce-runtime","importSpecifier":"openclaw/plugin-sdk/string-coerce-runtime"}
|
{"contentHash":"347f68c5497a100307cffbb43c2f88e653bed7b7451066b7eb19266dd3c16660","entrypoint":"string-coerce-runtime","importSpecifier":"openclaw/plugin-sdk/string-coerce-runtime"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"contentHash":"b2281fac79519417397a7670e25392c7e7e592a84e3ed00459b947b6bef2955b","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"}
|
{"contentHash":"1d4374297f908a74d78873c746fc0624240182a96c28a064a47f91f6260049bb","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"contentHash":"14ae7f9d841c6c4e2702239ed2aeb126f89039df8434bf12773a495486a04ff4","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"}
|
{"contentHash":"f5bf0f3889cd0e4a5b1de4e110098400573d46c508c962c386db3334246bc9fe","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { createRequire } from "node:module";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
||||||
|
import { isRecord as isConfigRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import {
|
import {
|
||||||
parse as parseToml,
|
parse as parseToml,
|
||||||
stringify as stringifyToml,
|
stringify as stringifyToml,
|
||||||
@@ -802,10 +803,6 @@ function extractConfiguredAdapterArgs(params: {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isConfigRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeConfigRecords(
|
function mergeConfigRecords(
|
||||||
base: Record<string, unknown>,
|
base: Record<string, unknown>,
|
||||||
override: Record<string, unknown>,
|
override: Record<string, unknown>,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
import {
|
||||||
|
parseStrictPositiveInteger,
|
||||||
|
resolveIntegerOption,
|
||||||
|
} from "openclaw/plugin-sdk/number-runtime";
|
||||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import { isPathInside } from "openclaw/plugin-sdk/security-runtime";
|
import { isPathInside } from "openclaw/plugin-sdk/security-runtime";
|
||||||
import {
|
import {
|
||||||
@@ -52,10 +55,7 @@ function parseOptionalPositiveInt(value: unknown, fallback: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clampInt(value: number | undefined, fallback: number, min: number, max: number): number {
|
function clampInt(value: number | undefined, fallback: number, min: number, max: number): number {
|
||||||
if (!Number.isFinite(value)) {
|
return resolveIntegerOption(value, fallback, { min, max });
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
return Math.max(min, Math.min(max, Math.floor(value as number)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTranscriptDir(value: unknown): string {
|
function normalizeTranscriptDir(value: unknown): string {
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import { join } from "node:path";
|
|||||||
import type { GoogleAuthOptions } from "google-auth-library";
|
import type { GoogleAuthOptions } from "google-auth-library";
|
||||||
import { resolveProviderEndpoint } from "openclaw/plugin-sdk/provider-http";
|
import { resolveProviderEndpoint } from "openclaw/plugin-sdk/provider-http";
|
||||||
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
||||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
normalizeLowercaseStringOrEmpty,
|
||||||
|
normalizeOptionalString as normalizeOptionalSecretInput,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
const ANTHROPIC_VERTEX_DEFAULT_REGION = "global";
|
const ANTHROPIC_VERTEX_DEFAULT_REGION = "global";
|
||||||
const ANTHROPIC_VERTEX_REGION_RE = /^[a-z0-9-]+$/;
|
const ANTHROPIC_VERTEX_REGION_RE = /^[a-z0-9-]+$/;
|
||||||
@@ -19,14 +22,6 @@ type AnthropicVertexAdcCredentials = NonNullable<GoogleAuthOptions["credentials"
|
|||||||
quota_project_id?: unknown;
|
quota_project_id?: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizeOptionalSecretInput(value: unknown): string | undefined {
|
|
||||||
if (typeof value !== "string") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const trimmed = value.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Resolve the configured Vertex region, defaulting to global. */
|
/** Resolve the configured Vertex region, defaulting to global. */
|
||||||
export function resolveAnthropicVertexRegion(env: NodeJS.ProcessEnv = process.env): string {
|
export function resolveAnthropicVertexRegion(env: NodeJS.ProcessEnv = process.env): string {
|
||||||
const region =
|
const region =
|
||||||
|
|||||||
@@ -144,12 +144,6 @@ function readPerTokenPrice(value: unknown): number | undefined {
|
|||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readStringArray(value: unknown): string[] {
|
|
||||||
return Array.isArray(value)
|
|
||||||
? value.filter((item): item is string => typeof item === "string")
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyLiveReasoningEffortCompat(
|
function applyLiveReasoningEffortCompat(
|
||||||
fallbackCompat: ModelCompatConfig,
|
fallbackCompat: ModelCompatConfig,
|
||||||
supportsReasoningEffort: boolean,
|
supportsReasoningEffort: boolean,
|
||||||
@@ -177,7 +171,7 @@ function projectLiveModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasLiveFeatures = Array.isArray(row.supported_features);
|
const hasLiveFeatures = Array.isArray(row.supported_features);
|
||||||
const features = new Set(readStringArray(row.supported_features));
|
const features = new Set(filterStringEntries(row.supported_features));
|
||||||
const pricing = asNonArrayRecord(row.pricing);
|
const pricing = asNonArrayRecord(row.pricing);
|
||||||
const inputPrice = readPerTokenPrice(pricing.prompt);
|
const inputPrice = readPerTokenPrice(pricing.prompt);
|
||||||
const outputPrice = readPerTokenPrice(pricing.completion);
|
const outputPrice = readPerTokenPrice(pricing.completion);
|
||||||
@@ -256,4 +250,4 @@ export function resolveBasetenDynamicModel(modelId: string) {
|
|||||||
compat: buildBasetenModelCompat(id),
|
compat: buildBasetenModelCompat(id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
import { asNonArrayRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { asNonArrayRecord, filterStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { resolveOptionalIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
||||||
/**
|
/**
|
||||||
* Runtime dependency barrel for the Browser agent tool.
|
* Runtime dependency barrel for the Browser agent tool.
|
||||||
*
|
*
|
||||||
@@ -9,11 +10,14 @@ import { getRuntimeConfig } from "./sdk-config.js";
|
|||||||
export { getRuntimeConfig };
|
export { getRuntimeConfig };
|
||||||
/** Resolve global image downscaling for screenshots returned to agent tools. */
|
/** Resolve global image downscaling for screenshots returned to agent tools. */
|
||||||
export function resolveRuntimeImageSanitization(): { maxDimensionPx: number } | undefined {
|
export function resolveRuntimeImageSanitization(): { maxDimensionPx: number } | undefined {
|
||||||
const configured = getRuntimeConfig().agents?.defaults?.imageMaxDimensionPx;
|
const maxDimensionPx = resolveOptionalIntegerOption(
|
||||||
if (typeof configured !== "number" || !Number.isFinite(configured)) {
|
getRuntimeConfig().agents?.defaults?.imageMaxDimensionPx,
|
||||||
|
{ min: 1 },
|
||||||
|
);
|
||||||
|
if (maxDimensionPx === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return { maxDimensionPx: Math.max(1, Math.floor(configured)) };
|
return { maxDimensionPx };
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
callGatewayTool,
|
callGatewayTool,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Chrome DevTools Protocol URL, fetch, and socket helpers.
|
* Chrome DevTools Protocol URL, fetch, and socket helpers.
|
||||||
*
|
|
||||||
* Handles CDP URL normalization, SSRF-guarded HTTP discovery, credential
|
* Handles CDP URL normalization, SSRF-guarded HTTP discovery, credential
|
||||||
* redaction/headers, and request/response correlation over WebSocket.
|
* redaction/headers, and request/response correlation over WebSocket.
|
||||||
*/
|
*/
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { parseBrowserHttpUrl, redactCdpUrl } from "openclaw/plugin-sdk/browser-config";
|
import { parseBrowserHttpUrl, redactCdpUrl } from "openclaw/plugin-sdk/browser-config";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
||||||
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
||||||
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||||
@@ -35,8 +35,7 @@ import { normalizeBrowserTimerDelayMs } from "./timer-delay.js";
|
|||||||
|
|
||||||
const CDP_URL_IN_TEXT_RE = /\b(?:https?|wss?):\/\/[^\s"'<>`]+/gi;
|
const CDP_URL_IN_TEXT_RE = /\b(?:https?|wss?):\/\/[^\s"'<>`]+/gi;
|
||||||
|
|
||||||
export { isLoopbackHost };
|
export { isLoopbackHost, parseBrowserHttpUrl, redactCdpUrl };
|
||||||
export { parseBrowserHttpUrl, redactCdpUrl };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true when the URL uses a WebSocket protocol (ws: or wss:).
|
* Returns true when the URL uses a WebSocket protocol (ws: or wss:).
|
||||||
@@ -529,7 +528,7 @@ function createCdpSender(ws: WebSocket, opts?: { commandTimeoutMs?: number }) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
pending.delete(id);
|
pending.delete(id);
|
||||||
clearPendingTimer(entry);
|
clearPendingTimer(entry);
|
||||||
reject(err instanceof Error ? err : new Error(String(err)));
|
reject(toStringifiedError(err));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -549,7 +548,7 @@ function createCdpSender(ws: WebSocket, opts?: { commandTimeoutMs?: number }) {
|
|||||||
// non-Error branch would require synthetically emitting on the socket,
|
// non-Error branch would require synthetically emitting on the socket,
|
||||||
// which the library treats as an unhandled error and hangs the test.
|
// which the library treats as an unhandled error and hangs the test.
|
||||||
/* c8 ignore next */
|
/* c8 ignore next */
|
||||||
closeWithError(err instanceof Error ? err : new Error(String(err)));
|
closeWithError(toStringifiedError(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("message", (data) => {
|
ws.on("message", (data) => {
|
||||||
@@ -798,7 +797,7 @@ export async function withCdpSocket<T>(
|
|||||||
// Error from Node's `ws` library, the latter is already an Error. The
|
// Error from Node's `ws` library, the latter is already an Error. The
|
||||||
// non-Error wrap is defensive and structurally unreachable.
|
// non-Error wrap is defensive and structurally unreachable.
|
||||||
/* c8 ignore next */
|
/* c8 ignore next */
|
||||||
closeWithError(err instanceof Error ? err : new Error(String(err)));
|
closeWithError(toStringifiedError(err));
|
||||||
// Cancellation on the final attempt must not become a handshake error.
|
// Cancellation on the final attempt must not become a handshake error.
|
||||||
opts?.signal?.throwIfAborted();
|
opts?.signal?.throwIfAborted();
|
||||||
if (attempt >= maxHandshakeRetries || !shouldRetryCdpHandshakeError(err)) {
|
if (attempt >= maxHandshakeRetries || !shouldRetryCdpHandshakeError(err)) {
|
||||||
@@ -821,7 +820,7 @@ export async function withCdpSocket<T>(
|
|||||||
try {
|
try {
|
||||||
return await fn(send);
|
return await fn(send);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
closeWithError(err instanceof Error ? err : new Error(String(err)));
|
closeWithError(toStringifiedError(err));
|
||||||
throw err;
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ import {
|
|||||||
type NormalizedChromeMcpProfileOptions,
|
type NormalizedChromeMcpProfileOptions,
|
||||||
} from "./chrome-mcp-contracts.js";
|
} from "./chrome-mcp-contracts.js";
|
||||||
|
|
||||||
function normalizeChromeMcpUserDataDir(userDataDir?: string): string | undefined {
|
|
||||||
const trimmed = userDataDir?.trim();
|
|
||||||
return trimmed ? trimmed : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeChromeMcpStringList(values?: string[]): string[] {
|
function normalizeChromeMcpStringList(values?: string[]): string[] {
|
||||||
return Array.isArray(values)
|
return Array.isArray(values)
|
||||||
? values.filter(
|
? values.filter(
|
||||||
@@ -35,7 +30,7 @@ export function normalizeChromeMcpOptions(
|
|||||||
const command = normalizeOptionalString(options.mcpCommand) ?? DEFAULT_CHROME_MCP_COMMAND;
|
const command = normalizeOptionalString(options.mcpCommand) ?? DEFAULT_CHROME_MCP_COMMAND;
|
||||||
return {
|
return {
|
||||||
command,
|
command,
|
||||||
userDataDir: normalizeChromeMcpUserDataDir(options.userDataDir),
|
userDataDir: normalizeOptionalString(options.userDataDir),
|
||||||
browserUrl: normalizeOptionalString(options.cdpUrl),
|
browserUrl: normalizeOptionalString(options.cdpUrl),
|
||||||
extraArgs: normalizeChromeMcpStringList(options.mcpArgs),
|
extraArgs: normalizeChromeMcpStringList(options.mcpArgs),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { asNullableRecord, isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
asNullableRecord,
|
||||||
|
asFiniteNumber,
|
||||||
|
filterStringEntries,
|
||||||
|
isRecord,
|
||||||
|
normalizeOptionalString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
/**
|
/**
|
||||||
* Managed Chrome graphics diagnostics.
|
* Managed Chrome graphics diagnostics.
|
||||||
*
|
*
|
||||||
@@ -24,11 +30,11 @@ type ChromeGraphicsProbeOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function readChromeString(value: unknown): string {
|
function readChromeString(value: unknown): string {
|
||||||
return typeof value === "string" ? value.trim() : "";
|
return normalizeOptionalString(value) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function readChromeNumber(value: unknown): number {
|
function readChromeNumber(value: unknown): number {
|
||||||
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
return asFiniteNumber(value) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readStringRecord(value: unknown): Record<string, string> {
|
function readStringRecord(value: unknown): Record<string, string> {
|
||||||
@@ -42,12 +48,6 @@ function readStringRecord(value: unknown): Record<string, string> {
|
|||||||
return Object.fromEntries(entries);
|
return Object.fromEntries(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readStringArray(value: unknown): string[] {
|
|
||||||
return Array.isArray(value)
|
|
||||||
? value.filter((item): item is string => typeof item === "string")
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function readSize(value: unknown): { width: number; height: number } {
|
function readSize(value: unknown): { width: number; height: number } {
|
||||||
const size = asNullableRecord(value);
|
const size = asNullableRecord(value);
|
||||||
return {
|
return {
|
||||||
@@ -162,7 +162,7 @@ function normalizeChromeGraphicsInfo(
|
|||||||
devices,
|
devices,
|
||||||
featureStatus,
|
featureStatus,
|
||||||
disabledFeatures,
|
disabledFeatures,
|
||||||
driverBugWorkarounds: readStringArray(gpu.driverBugWorkarounds),
|
driverBugWorkarounds: filterStringEntries(gpu.driverBugWorkarounds),
|
||||||
videoDecoding: readVideoDecoding(gpu.videoDecoding),
|
videoDecoding: readVideoDecoding(gpu.videoDecoding),
|
||||||
videoEncoding: readVideoEncoding(gpu.videoEncoding),
|
videoEncoding: readVideoEncoding(gpu.videoEncoding),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
ProviderToolSchemaDiagnostic,
|
ProviderToolSchemaDiagnostic,
|
||||||
} from "openclaw/plugin-sdk/plugin-entry";
|
} from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import { findUnsupportedSchemaKeywords } from "openclaw/plugin-sdk/provider-tools";
|
import { findUnsupportedSchemaKeywords } from "openclaw/plugin-sdk/provider-tools";
|
||||||
|
import { asOptionalRecord as readRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
const PERPLEXITY_UNSUPPORTED_SCHEMA_KEYWORDS = new Set([
|
const PERPLEXITY_UNSUPPORTED_SCHEMA_KEYWORDS = new Set([
|
||||||
"patternProperties",
|
"patternProperties",
|
||||||
@@ -36,12 +37,6 @@ const SCHEMA_VALUE_KEYS = new Set([
|
|||||||
"contentSchema",
|
"contentSchema",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function readRecord(value: unknown): Record<string, unknown> | undefined {
|
|
||||||
return value && typeof value === "object" && !Array.isArray(value)
|
|
||||||
? (value as Record<string, unknown>)
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON Schema allows `type` to be an array; a union containing "object" still
|
// JSON Schema allows `type` to be an array; a union containing "object" still
|
||||||
// admits objects, so it needs `properties` for Perplexity too.
|
// admits objects, so it needs `properties` for Perplexity too.
|
||||||
function isObjectType(type: unknown): boolean {
|
function isObjectType(type: unknown): boolean {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { ChannelGatewayContext } from "openclaw/plugin-sdk/channel-contract
|
|||||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
||||||
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
||||||
|
import { readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { rawDataToString } from "openclaw/plugin-sdk/webhook-ingress";
|
import { rawDataToString } from "openclaw/plugin-sdk/webhook-ingress";
|
||||||
import type { RawData } from "ws";
|
import type { RawData } from "ws";
|
||||||
import { resolveClickClackInboundAccess } from "./access.js";
|
import { resolveClickClackInboundAccess } from "./access.js";
|
||||||
@@ -28,8 +29,7 @@ import type {
|
|||||||
const CLICKCLACK_EVENT_PAGE_LIMIT = 500;
|
const CLICKCLACK_EVENT_PAGE_LIMIT = 500;
|
||||||
|
|
||||||
function payloadString(event: ClickClackEvent, key: string): string {
|
function payloadString(event: ClickClackEvent, key: string): string {
|
||||||
const value = event.payload?.[key];
|
return readStringField(event.payload, key) ?? "";
|
||||||
return typeof value === "string" ? value : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventCorrelationId(event: ClickClackEvent): string | undefined {
|
function eventCorrelationId(event: ClickClackEvent): string | undefined {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const PROVIDER_ID = "cloudflare-ai-gateway";
|
|||||||
const PROVIDER_ENV_VAR = "CLOUDFLARE_AI_GATEWAY_API_KEY";
|
const PROVIDER_ENV_VAR = "CLOUDFLARE_AI_GATEWAY_API_KEY";
|
||||||
const PROFILE_ID = "cloudflare-ai-gateway:default";
|
const PROFILE_ID = "cloudflare-ai-gateway:default";
|
||||||
function readRequiredTextInput(value: unknown): string {
|
function readRequiredTextInput(value: unknown): string {
|
||||||
return typeof value === "string" ? value.trim() : "";
|
return normalizeOptionalString(value) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveCloudflareGatewayMetadataInteractive(ctx: {
|
async function resolveCloudflareGatewayMetadataInteractive(ctx: {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
type NativeHookRelayProcessResponse,
|
type NativeHookRelayProcessResponse,
|
||||||
type NativeHookRelayRegistrationHandle,
|
type NativeHookRelayRegistrationHandle,
|
||||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||||
|
import { coerceErrorMessage as formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import {
|
import {
|
||||||
normalizeTrimmedStringList,
|
normalizeTrimmedStringList,
|
||||||
readStringField as readString,
|
readStringField as readString,
|
||||||
@@ -1332,7 +1333,4 @@ function joinDescriptionLinesWithinLimit(lines: string[], maxLength: number): st
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatErrorMessage(error: unknown): string {
|
|
||||||
return error instanceof Error ? error.message : String(error);
|
|
||||||
}
|
|
||||||
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
|
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* or whether OpenClaw must keep exec/process on a configured node host.
|
* or whether OpenClaw must keep exec/process on a configured node host.
|
||||||
*/
|
*/
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
||||||
import { resolveSandboxRuntimeStatus } from "openclaw/plugin-sdk/sandbox";
|
import { resolveSandboxRuntimeStatus } from "openclaw/plugin-sdk/sandbox";
|
||||||
import { getSessionEntry, type SessionEntry } from "openclaw/plugin-sdk/session-store-runtime";
|
import { getSessionEntry, type SessionEntry } from "openclaw/plugin-sdk/session-store-runtime";
|
||||||
|
|
||||||
@@ -16,12 +17,6 @@ type ExecHostOverride = {
|
|||||||
|
|
||||||
type AgentEntry = NonNullable<NonNullable<OpenClawConfig["agents"]>["list"]>[number];
|
type AgentEntry = NonNullable<NonNullable<OpenClawConfig["agents"]>["list"]>[number];
|
||||||
|
|
||||||
const DEFAULT_AGENT_ID = "main";
|
|
||||||
const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
||||||
const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
|
|
||||||
const LEADING_DASH_PATTERN = /^-+/;
|
|
||||||
const TRAILING_DASH_PATTERN = /-+$/;
|
|
||||||
|
|
||||||
/** Effective execution-host policy for the Codex app-server native tool surface. */
|
/** Effective execution-host policy for the Codex app-server native tool surface. */
|
||||||
export type CodexNativeExecutionPolicy = {
|
export type CodexNativeExecutionPolicy = {
|
||||||
nativeToolSurfaceAllowed: boolean;
|
nativeToolSurfaceAllowed: boolean;
|
||||||
@@ -197,25 +192,7 @@ function resolveDefaultPolicyAgentId(agents: AgentEntry[]): string {
|
|||||||
|
|
||||||
function normalizeAgentIdOrDefault(value?: string | null): string | undefined {
|
function normalizeAgentIdOrDefault(value?: string | null): string | undefined {
|
||||||
const normalized = normalizeAgentId(value);
|
const normalized = normalizeAgentId(value);
|
||||||
return normalized === DEFAULT_AGENT_ID && !(value ?? "").trim() ? undefined : normalized;
|
return normalized === "main" && !(value ?? "").trim() ? undefined : normalized;
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAgentId(value?: string | null): string {
|
|
||||||
const trimmed = (value ?? "").trim();
|
|
||||||
if (!trimmed) {
|
|
||||||
return DEFAULT_AGENT_ID;
|
|
||||||
}
|
|
||||||
const normalized = trimmed.toLowerCase();
|
|
||||||
if (VALID_AGENT_ID_PATTERN.test(trimmed)) {
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
normalized
|
|
||||||
.replace(INVALID_AGENT_ID_CHARS_PATTERN, "-")
|
|
||||||
.replace(LEADING_DASH_PATTERN, "")
|
|
||||||
.replace(TRAILING_DASH_PATTERN, "")
|
|
||||||
.slice(0, 64) || DEFAULT_AGENT_ID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeExecTarget(value?: string | null): ExecTarget | undefined {
|
function normalizeExecTarget(value?: string | null): ExecTarget | undefined {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type {
|
|||||||
PluginHookInboundClaimEvent,
|
PluginHookInboundClaimEvent,
|
||||||
} from "openclaw/plugin-sdk/plugin-entry";
|
} from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-payload";
|
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-payload";
|
||||||
|
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
||||||
import {
|
import {
|
||||||
getSessionEntry,
|
getSessionEntry,
|
||||||
resolveStorePath,
|
resolveStorePath,
|
||||||
@@ -122,11 +123,6 @@ import { isIncognitoSessionKey } from "./incognito-session.js";
|
|||||||
import { resumeCodexCliSessionOnNode } from "./node-cli-sessions.js";
|
import { resumeCodexCliSessionOnNode } from "./node-cli-sessions.js";
|
||||||
|
|
||||||
const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 60_000;
|
const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 60_000;
|
||||||
const DEFAULT_AGENT_ID = "main";
|
|
||||||
const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
||||||
const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
|
|
||||||
const LEADING_DASH_PATTERN = /^-+/;
|
|
||||||
const TRAILING_DASH_PATTERN = /-+$/;
|
|
||||||
const NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE =
|
const NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE =
|
||||||
"OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow.";
|
"OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow.";
|
||||||
|
|
||||||
@@ -1484,25 +1480,7 @@ function resolveDefaultPolicyAgentId(config: ResolvedCodexConversationConfig): s
|
|||||||
|
|
||||||
function normalizeAgentIdOrDefault(value?: string | null): string | undefined {
|
function normalizeAgentIdOrDefault(value?: string | null): string | undefined {
|
||||||
const normalized = normalizeAgentId(value);
|
const normalized = normalizeAgentId(value);
|
||||||
return normalized === DEFAULT_AGENT_ID && !(value ?? "").trim() ? undefined : normalized;
|
return normalized === "main" && !(value ?? "").trim() ? undefined : normalized;
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAgentId(value?: string | null): string {
|
|
||||||
const trimmed = (value ?? "").trim();
|
|
||||||
if (!trimmed) {
|
|
||||||
return DEFAULT_AGENT_ID;
|
|
||||||
}
|
|
||||||
const normalized = trimmed.toLowerCase();
|
|
||||||
if (VALID_AGENT_ID_PATTERN.test(trimmed)) {
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
normalized
|
|
||||||
.replace(INVALID_AGENT_ID_CHARS_PATTERN, "-")
|
|
||||||
.replace(LEADING_DASH_PATTERN, "")
|
|
||||||
.replace(TRAILING_DASH_PATTERN, "")
|
|
||||||
.slice(0, 64) || DEFAULT_AGENT_ID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCodexThreadNotFoundError(error: unknown): boolean {
|
function isCodexThreadNotFoundError(error: unknown): boolean {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Codex plugin module implements apply behavior.
|
// Codex plugin module implements apply behavior.
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { coerceErrorMessage as formatCodexMigrationError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import {
|
import {
|
||||||
applyMigrationManualItem,
|
applyMigrationManualItem,
|
||||||
markMigrationItemConflict,
|
markMigrationItemConflict,
|
||||||
@@ -293,10 +294,6 @@ function isCodexPluginInventoryLoadError(error: unknown): boolean {
|
|||||||
return message.includes("codex app-server plugin/list timed out");
|
return message.includes("codex app-server plugin/list timed out");
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCodexMigrationError(error: unknown): string {
|
|
||||||
return error instanceof Error ? error.message : String(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveTargetCodexAppServer(ctx: MigrationProviderContext) {
|
function resolveTargetCodexAppServer(ctx: MigrationProviderContext) {
|
||||||
return resolveCodexAppServerRuntimeOptions({
|
return resolveCodexAppServerRuntimeOptions({
|
||||||
pluginConfig: readCodexPluginConfig(ctx.config),
|
pluginConfig: readCodexPluginConfig(ctx.config),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { asFiniteNumber, isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import type { CodexThread, CodexThreadTurnsListResponse } from "./app-server/protocol.js";
|
import type { CodexThread, CodexThreadTurnsListResponse } from "./app-server/protocol.js";
|
||||||
@@ -305,10 +305,6 @@ export function parseJsonParams(paramsJSON?: string | null): unknown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readFiniteNumber(value: unknown): number | undefined {
|
|
||||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseOptionalCatalogString(
|
function parseOptionalCatalogString(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
field: string,
|
field: string,
|
||||||
@@ -385,9 +381,9 @@ function parseCatalogSession(
|
|||||||
const sessionKey = options.allowSessionKey
|
const sessionKey = options.allowSessionKey
|
||||||
? parseOptionalCatalogString(value.sessionKey, "OpenClaw session key", MAX_SESSION_KEY_LENGTH)
|
? parseOptionalCatalogString(value.sessionKey, "OpenClaw session key", MAX_SESSION_KEY_LENGTH)
|
||||||
: undefined;
|
: undefined;
|
||||||
const createdAt = readFiniteNumber(value.createdAt);
|
const createdAt = asFiniteNumber(value.createdAt);
|
||||||
const updatedAt = readFiniteNumber(value.updatedAt);
|
const updatedAt = asFiniteNumber(value.updatedAt);
|
||||||
const recencyAt = value.recencyAt === null ? null : readFiniteNumber(value.recencyAt);
|
const recencyAt = value.recencyAt === null ? null : asFiniteNumber(value.recencyAt);
|
||||||
return {
|
return {
|
||||||
threadId: value.threadId,
|
threadId: value.threadId,
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -124,10 +124,6 @@ if (process.env.VITEST === "true") {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function readConfigBoolean(config: ComfyProviderConfig, key: string): boolean | undefined {
|
|
||||||
return asBoolean(config[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function readConfigInteger(config: ComfyProviderConfig, key: string): number | undefined {
|
function readConfigInteger(config: ComfyProviderConfig, key: string): number | undefined {
|
||||||
const value = config[key];
|
const value = config[key];
|
||||||
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
||||||
@@ -710,8 +706,7 @@ export async function runComfyWorkflow(params: {
|
|||||||
throw new Error("Comfy Cloud API key missing");
|
throw new Error("Comfy Cloud API key missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
const explicitAllowPrivateNetwork =
|
const explicitAllowPrivateNetwork = asBoolean(capabilityConfig.allowPrivateNetwork) === true;
|
||||||
readConfigBoolean(capabilityConfig, "allowPrivateNetwork") === true;
|
|
||||||
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
||||||
resolveProviderHttpRequestConfig({
|
resolveProviderHttpRequestConfig({
|
||||||
baseUrl: normalizeOptionalString(capabilityConfig.baseUrl),
|
baseUrl: normalizeOptionalString(capabilityConfig.baseUrl),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
type AgentHarnessResetParams,
|
type AgentHarnessResetParams,
|
||||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||||
import type { PluginStateSyncKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime";
|
import type { PluginStateSyncKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime";
|
||||||
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { CopilotSessionConfig } from "./src/attempt.js";
|
import type { CopilotSessionConfig } from "./src/attempt.js";
|
||||||
import { createCopilotByokAuth, resolveCopilotAuth, tokenFingerprint } from "./src/auth-bridge.js";
|
import { createCopilotByokAuth, resolveCopilotAuth, tokenFingerprint } from "./src/auth-bridge.js";
|
||||||
import { createCopilotByokProxy } from "./src/byok-proxy.js";
|
import { createCopilotByokProxy } from "./src/byok-proxy.js";
|
||||||
@@ -454,10 +455,10 @@ function computeSessionKey(
|
|||||||
(typeof p.model === "string" ? p.model : "");
|
(typeof p.model === "string" ? p.model : "");
|
||||||
const requestTransport =
|
const requestTransport =
|
||||||
p.model && typeof p.model === "object" ? getModelProviderRequestTransport(p.model) : undefined;
|
p.model && typeof p.model === "object" ? getModelProviderRequestTransport(p.model) : undefined;
|
||||||
const requestAuthMode = readSessionString(
|
const requestAuthMode = normalizeOptionalString(
|
||||||
requestTransport?.auth?.mode ?? modelObj.request?.auth?.mode,
|
requestTransport?.auth?.mode ?? modelObj.request?.auth?.mode,
|
||||||
);
|
);
|
||||||
const azureApiVersion = readSessionString(
|
const azureApiVersion = normalizeOptionalString(
|
||||||
modelObj.azureApiVersion ?? modelObj.params?.azureApiVersion,
|
modelObj.azureApiVersion ?? modelObj.params?.azureApiVersion,
|
||||||
);
|
);
|
||||||
// resolveCopilotAuth can throw when an explicit `auth.gitHubToken`
|
// resolveCopilotAuth can throw when an explicit `auth.gitHubToken`
|
||||||
@@ -559,10 +560,6 @@ function computeSessionKey(
|
|||||||
return parts.join("|");
|
return parts.join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
function readSessionString(value: unknown): string | undefined {
|
|
||||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fingerprintSessionValue(value: unknown): string {
|
function fingerprintSessionValue(value: unknown): string {
|
||||||
return typeof value === "string" && value ? tokenFingerprint(value) : "";
|
return typeof value === "string" && value ? tokenFingerprint(value) : "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import type { MessageOptions, SessionConfig, Tool as SdkTool } from "@github/copilot-sdk";
|
import type { MessageOptions, SessionConfig, Tool as SdkTool } from "@github/copilot-sdk";
|
||||||
import type { AgentMessage, SandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
|
import type { AgentMessage, SandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||||
|
import { toStringifiedError as toCopilotError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
|
|
||||||
|
export { toCopilotError };
|
||||||
import {
|
import {
|
||||||
detectAndLoadAgentHarnessPromptImages,
|
detectAndLoadAgentHarnessPromptImages,
|
||||||
getModelProviderRequestTransport,
|
getModelProviderRequestTransport,
|
||||||
@@ -439,9 +442,6 @@ export function resolvePoolAcquire(params: AttemptParamsLike): {
|
|||||||
provider,
|
provider,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function toCopilotError(error: unknown): Error {
|
|
||||||
return error instanceof Error ? error : new Error(String(error));
|
|
||||||
}
|
|
||||||
export function isSdkSendAndWaitTimeoutError(error: unknown): boolean {
|
export function isSdkSendAndWaitTimeoutError(error: unknown): boolean {
|
||||||
if (error === null || typeof error !== "object") {
|
if (error === null || typeof error !== "object") {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
import type { ProviderConfig } from "@github/copilot-sdk";
|
import type { ProviderConfig } from "@github/copilot-sdk";
|
||||||
import { isNonSecretApiKeyMarker } from "openclaw/plugin-sdk/provider-auth";
|
import { isNonSecretApiKeyMarker } from "openclaw/plugin-sdk/provider-auth";
|
||||||
import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
filterStringRecord,
|
||||||
|
normalizeOptionalString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { tokenFingerprint } from "./auth-bridge.js";
|
import { tokenFingerprint } from "./auth-bridge.js";
|
||||||
|
|
||||||
const COPILOT_BYOK_PROVIDER_ERROR =
|
const COPILOT_BYOK_PROVIDER_ERROR =
|
||||||
@@ -96,7 +99,7 @@ export function resolveCopilotProvider(params: {
|
|||||||
const api = normalizeOptionalString(params.model.api)?.toLowerCase() ?? "openai-responses";
|
const api = normalizeOptionalString(params.model.api)?.toLowerCase() ?? "openai-responses";
|
||||||
const provider = resolveProviderType(api, baseUrl, params.model.azureApiVersion);
|
const provider = resolveProviderType(api, baseUrl, params.model.azureApiVersion);
|
||||||
const resolvedApiKey = resolveProviderCredential(params.resolvedApiKey);
|
const resolvedApiKey = resolveProviderCredential(params.resolvedApiKey);
|
||||||
const headers = resolveProviderHeaders(params.model.headers);
|
const headers = filterStringRecord(params.model.headers);
|
||||||
const requestAuthMode = normalizeOptionalString(params.model.requestAuthMode)?.toLowerCase();
|
const requestAuthMode = normalizeOptionalString(params.model.requestAuthMode)?.toLowerCase();
|
||||||
const usePreparedRequestAuth =
|
const usePreparedRequestAuth =
|
||||||
requestAuthMode !== undefined && requestAuthMode !== "provider-default";
|
requestAuthMode !== undefined && requestAuthMode !== "provider-default";
|
||||||
@@ -322,15 +325,3 @@ function resolveProviderCredential(value: string | undefined): string | undefine
|
|||||||
const credential = normalizeOptionalString(value);
|
const credential = normalizeOptionalString(value);
|
||||||
return credential && !isNonSecretApiKeyMarker(credential) ? credential : undefined;
|
return credential && !isNonSecretApiKeyMarker(credential) ? credential : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveProviderHeaders(
|
|
||||||
headers: Record<string, string | null | undefined> | undefined,
|
|
||||||
): Record<string, string> | undefined {
|
|
||||||
if (!headers) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const resolved = Object.fromEntries(
|
|
||||||
Object.entries(headers).filter(([, value]) => typeof value === "string"),
|
|
||||||
) as Record<string, string>;
|
|
||||||
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copilot plugin module implements runtime behavior.
|
// Copilot plugin module implements runtime behavior.
|
||||||
import { normalize, resolve, sep } from "node:path";
|
import { normalize, resolve, sep } from "node:path";
|
||||||
import type { CopilotClient, CopilotClientOptions } from "@github/copilot-sdk";
|
import type { CopilotClient, CopilotClientOptions } from "@github/copilot-sdk";
|
||||||
|
import { toStringifiedError as toCopilotRuntimeError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { loadCopilotSdk } from "./sdk-loader.js";
|
import { loadCopilotSdk } from "./sdk-loader.js";
|
||||||
|
|
||||||
// SAFETY: The pool reuses CopilotClient instances per normalized PoolKey and does not
|
// SAFETY: The pool reuses CopilotClient instances per normalized PoolKey and does not
|
||||||
@@ -389,10 +390,3 @@ function normalizeCopilotHome(copilotHome: string): string {
|
|||||||
}
|
}
|
||||||
return normalizedHome;
|
return normalizedHome;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toCopilotRuntimeError(error: unknown): Error {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
sanitizeToolResult,
|
sanitizeToolResult,
|
||||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||||
import { createAgentHarnessToolSurfaceRuntime } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
|
import { createAgentHarnessToolSurfaceRuntime } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
|
||||||
|
import { toStringifiedError as toCopilotToolError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
|
|
||||||
type CreateOpenClawCodingTools =
|
type CreateOpenClawCodingTools =
|
||||||
(typeof import("openclaw/plugin-sdk/agent-harness"))["createOpenClawCodingTools"];
|
(typeof import("openclaw/plugin-sdk/agent-harness"))["createOpenClawCodingTools"];
|
||||||
@@ -911,7 +912,3 @@ function findDuplicateToolNames(sourceTools: AnyAgentTool[]): string[] {
|
|||||||
.map(([name]) => name)
|
.map(([name]) => name)
|
||||||
.toSorted();
|
.toSorted();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toCopilotToolError(error: unknown): Error {
|
|
||||||
return error instanceof Error ? error : new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
resolveBootstrapContextForRun,
|
resolveBootstrapContextForRun,
|
||||||
resolveUserPath,
|
resolveUserPath,
|
||||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||||
import { hasNonEmptyString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
// Filenames the Copilot SDK already loads natively from the working
|
// Filenames the Copilot SDK already loads natively from the working
|
||||||
// directory / instructionDirectories (per
|
// directory / instructionDirectories (per
|
||||||
@@ -88,10 +88,10 @@ export async function resolveCopilotWorkspaceBootstrapContext(params: {
|
|||||||
const bootstrapContext = await resolveBootstrapContextForRun({
|
const bootstrapContext = await resolveBootstrapContextForRun({
|
||||||
workspaceDir,
|
workspaceDir,
|
||||||
config: attempt.config,
|
config: attempt.config,
|
||||||
sessionKey: readNonEmptyString((attempt as { sessionKey?: unknown }).sessionKey),
|
sessionKey: readNonBlankString((attempt as { sessionKey?: unknown }).sessionKey),
|
||||||
sessionId: readNonEmptyString(attempt.sessionId),
|
sessionId: readNonBlankString(attempt.sessionId),
|
||||||
chatType: attempt.chatType,
|
chatType: attempt.chatType,
|
||||||
agentId: readNonEmptyString(attempt.agentId),
|
agentId: readNonBlankString(attempt.agentId),
|
||||||
warn: params.warn,
|
warn: params.warn,
|
||||||
contextMode: attempt.bootstrapContextMode,
|
contextMode: attempt.bootstrapContextMode,
|
||||||
runKind: attempt.bootstrapContextRunKind,
|
runKind: attempt.bootstrapContextRunKind,
|
||||||
@@ -235,12 +235,8 @@ function getCopilotContextFileBasename(filePath: string): string {
|
|||||||
return normalizeCopilotContextFilePath(filePath).split("/").pop() ?? "";
|
return normalizeCopilotContextFilePath(filePath).split("/").pop() ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function readNonEmptyString(value: unknown): string | undefined {
|
|
||||||
return hasNonEmptyString(value) ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readResolvedWorkspacePath(value: unknown): string | undefined {
|
function readResolvedWorkspacePath(value: unknown): string | undefined {
|
||||||
const raw = readNonEmptyString(value);
|
const raw = readNonBlankString(value);
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { createHash } from "node:crypto";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { WorkerProviderError, type WorkerProfile } from "openclaw/plugin-sdk/plugin-entry";
|
import { WorkerProviderError, type WorkerProfile } from "openclaw/plugin-sdk/plugin-entry";
|
||||||
|
import { normalizeOptionalString as nonEmptyString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
|
export { nonEmptyString };
|
||||||
|
|
||||||
const PROFILE_KEYS = new Set([
|
const PROFILE_KEYS = new Set([
|
||||||
"binary",
|
"binary",
|
||||||
@@ -39,14 +42,6 @@ type CrabboxProfile = {
|
|||||||
|
|
||||||
type IsExecutable = (candidate: string) => boolean;
|
type IsExecutable = (candidate: string) => boolean;
|
||||||
|
|
||||||
export function nonEmptyString(value: unknown): string | undefined {
|
|
||||||
if (typeof value !== "string") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const trimmed = value.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function requirePositiveDuration(value: unknown, key: string): string {
|
function requirePositiveDuration(value: unknown, key: string): string {
|
||||||
const duration = nonEmptyString(value);
|
const duration = nonEmptyString(value);
|
||||||
if (!duration || !isPositiveGoDuration(duration)) {
|
if (!duration || !isPositiveGoDuration(duration)) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { SpanKind } from "@opentelemetry/api";
|
import { SpanKind } from "@opentelemetry/api";
|
||||||
import { GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT } from "@opentelemetry/semantic-conventions/incubating";
|
import { GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT } from "@opentelemetry/semantic-conventions/incubating";
|
||||||
import { normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
|
import { normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
|
||||||
|
import { asFiniteNumber, asFiniteNumberInRange } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { DiagnosticEventPayload } from "../api.js";
|
import type { DiagnosticEventPayload } from "../api.js";
|
||||||
import { redactSensitiveText } from "../api.js";
|
import { redactSensitiveText } from "../api.js";
|
||||||
import {
|
import {
|
||||||
@@ -48,11 +49,11 @@ export function genAiOperationName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function positiveFiniteNumber(value: number | undefined): number | undefined {
|
export function positiveFiniteNumber(value: number | undefined): number | undefined {
|
||||||
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
return asFiniteNumberInRange(value, { min: 0, minExclusive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function nonNegativeFiniteNumber(value: number | undefined): number | undefined {
|
function nonNegativeFiniteNumber(value: number | undefined): number | undefined {
|
||||||
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
return asFiniteNumberInRange(value, { min: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assignPositiveNumberAttr(
|
export function assignPositiveNumberAttr(
|
||||||
@@ -88,8 +89,9 @@ function assignNumberAttr(
|
|||||||
key: string,
|
key: string,
|
||||||
value: number | undefined,
|
value: number | undefined,
|
||||||
): void {
|
): void {
|
||||||
if (typeof value === "number" && Number.isFinite(value)) {
|
const normalized = asFiniteNumber(value);
|
||||||
attrs[key] = value;
|
if (normalized !== undefined) {
|
||||||
|
attrs[key] = normalized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { asBoolean } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
// Discord plugin module implements runtime.shared behavior.
|
// Discord plugin module implements runtime.shared behavior.
|
||||||
import {
|
import {
|
||||||
parseAvailableTags,
|
parseAvailableTags,
|
||||||
@@ -45,13 +46,6 @@ export function readDiscordAutoArchiveDurationParam(
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readDiscordBooleanParam(
|
|
||||||
params: Record<string, unknown>,
|
|
||||||
key: string,
|
|
||||||
): boolean | undefined {
|
|
||||||
return typeof params[key] === "boolean" ? params[key] : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDiscordActionOptions<
|
export function createDiscordActionOptions<
|
||||||
T extends Record<string, unknown> = Record<string, never>,
|
T extends Record<string, unknown> = Record<string, never>,
|
||||||
>(params: {
|
>(params: {
|
||||||
@@ -80,7 +74,7 @@ export function readDiscordChannelCreateParams(
|
|||||||
parentId: parentId ?? undefined,
|
parentId: parentId ?? undefined,
|
||||||
topic: readStringParam(params, "topic") ?? undefined,
|
topic: readStringParam(params, "topic") ?? undefined,
|
||||||
position: readNonNegativeIntegerParam(params, "position") ?? undefined,
|
position: readNonNegativeIntegerParam(params, "position") ?? undefined,
|
||||||
nsfw: readDiscordBooleanParam(params, "nsfw"),
|
nsfw: asBoolean(params.nsfw),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +86,10 @@ export function readDiscordChannelEditParams(params: Record<string, unknown>): D
|
|||||||
topic: readStringParam(params, "topic") ?? undefined,
|
topic: readStringParam(params, "topic") ?? undefined,
|
||||||
position: readNonNegativeIntegerParam(params, "position") ?? undefined,
|
position: readNonNegativeIntegerParam(params, "position") ?? undefined,
|
||||||
parentId: parentId === undefined ? undefined : parentId,
|
parentId: parentId === undefined ? undefined : parentId,
|
||||||
nsfw: readDiscordBooleanParam(params, "nsfw"),
|
nsfw: asBoolean(params.nsfw),
|
||||||
rateLimitPerUser: readNonNegativeIntegerParam(params, "rateLimitPerUser") ?? undefined,
|
rateLimitPerUser: readNonNegativeIntegerParam(params, "rateLimitPerUser") ?? undefined,
|
||||||
archived: readDiscordBooleanParam(params, "archived"),
|
archived: asBoolean(params.archived),
|
||||||
locked: readDiscordBooleanParam(params, "locked"),
|
locked: asBoolean(params.locked),
|
||||||
autoArchiveDuration: readDiscordAutoArchiveDurationParam(params, "autoArchiveDuration"),
|
autoArchiveDuration: readDiscordAutoArchiveDurationParam(params, "autoArchiveDuration"),
|
||||||
availableTags: parseAvailableTags(params.availableTags),
|
availableTags: parseAvailableTags(params.availableTags),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,11 +10,6 @@ type ActiveDiscordTurnThreadRoute = {
|
|||||||
|
|
||||||
const activeRoutes = new Map<string, Set<ActiveDiscordTurnThreadRoute>>();
|
const activeRoutes = new Map<string, Set<ActiveDiscordTurnThreadRoute>>();
|
||||||
|
|
||||||
function normalizeId(value: string | undefined): string | undefined {
|
|
||||||
const trimmed = value?.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function beginDiscordActiveTurnThreadRoute(
|
export function beginDiscordActiveTurnThreadRoute(
|
||||||
sessionKey: string | undefined,
|
sessionKey: string | undefined,
|
||||||
route: ActiveDiscordTurnThreadRoute,
|
route: ActiveDiscordTurnThreadRoute,
|
||||||
@@ -96,3 +91,4 @@ function findDiscordActiveTurnThreadReplyRoute(params: {
|
|||||||
(!route.accountId || !params.accountId || route.accountId === params.accountId),
|
(!route.accountId || !params.accountId || route.accountId === params.accountId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
import { normalizeOptionalString as normalizeId } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { DiscordAccountConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { DiscordAccountConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { normalizeOptionalString as readNonEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
type DiscordActivitiesConfigResolution =
|
type DiscordActivitiesConfigResolution =
|
||||||
| {
|
| {
|
||||||
@@ -11,11 +12,6 @@ type DiscordActivitiesConfigResolution =
|
|||||||
reason: "not-configured" | "missing-client-secret";
|
reason: "not-configured" | "missing-client-secret";
|
||||||
};
|
};
|
||||||
|
|
||||||
function readNonEmpty(value: string | undefined): string | undefined {
|
|
||||||
const trimmed = value?.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveDiscordActivitiesConfig(
|
export function resolveDiscordActivitiesConfig(
|
||||||
account: DiscordAccountConfig,
|
account: DiscordAccountConfig,
|
||||||
env: NodeJS.ProcessEnv = process.env,
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { inspect } from "node:util";
|
|||||||
import { gunzipSync } from "node:zlib";
|
import { gunzipSync } from "node:zlib";
|
||||||
import {
|
import {
|
||||||
clampTimerTimeoutMs,
|
clampTimerTimeoutMs,
|
||||||
parseFiniteNumber,
|
resolveIntegerOption as normalizeIntegerOption,
|
||||||
resolveTimerTimeoutMs,
|
resolveTimerTimeoutMs,
|
||||||
} from "openclaw/plugin-sdk/number-runtime";
|
} from "openclaw/plugin-sdk/number-runtime";
|
||||||
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
||||||
@@ -307,15 +307,6 @@ export class RequestClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeIntegerOption(
|
|
||||||
value: number | undefined,
|
|
||||||
fallback: number,
|
|
||||||
params: { min: number },
|
|
||||||
): number {
|
|
||||||
const candidate = parseFiniteNumber(value) ?? fallback;
|
|
||||||
return Math.max(params.min, Math.floor(candidate));
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeRequestClientOptions(
|
function normalizeRequestClientOptions(
|
||||||
options?: RequestClientOptions,
|
options?: RequestClientOptions,
|
||||||
): NormalizedRequestClientOptions {
|
): NormalizedRequestClientOptions {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// Discord provider module implements model/runtime integration.
|
// Discord provider module implements model/runtime integration.
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import {
|
|
||||||
parseStrictFiniteNumber,
|
|
||||||
parseStrictNonNegativeInteger,
|
|
||||||
} from "openclaw/plugin-sdk/number-runtime";
|
|
||||||
import { formatDurationSeconds } from "openclaw/plugin-sdk/runtime-env";
|
import { formatDurationSeconds } from "openclaw/plugin-sdk/runtime-env";
|
||||||
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||||
|
import {
|
||||||
|
parseFiniteNumber as readFiniteNumber,
|
||||||
|
parseStrictNonNegativeInteger as readNonNegativeInteger,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import { RateLimitError } from "../internal/discord.js";
|
import { RateLimitError } from "../internal/discord.js";
|
||||||
|
|
||||||
@@ -102,20 +102,6 @@ function readDiscordDeployObjectField(value: unknown, field: string): unknown {
|
|||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readFiniteNumber(value: unknown): number | undefined {
|
|
||||||
if (typeof value === "number" && Number.isFinite(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (typeof value === "string" && value.trim().length > 0) {
|
|
||||||
return parseStrictFiniteNumber(value);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readNonNegativeInteger(value: unknown): number | undefined {
|
|
||||||
return parseStrictNonNegativeInteger(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAbortLikeError(err: unknown): boolean {
|
function isAbortLikeError(err: unknown): boolean {
|
||||||
if (!err || typeof err !== "object") {
|
if (!err || typeof err !== "object") {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -2,22 +2,18 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
|||||||
import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
|
import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
|
||||||
import { danger } from "openclaw/plugin-sdk/runtime-env";
|
import { danger } from "openclaw/plugin-sdk/runtime-env";
|
||||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||||
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { ResolvedDiscordAccount } from "./accounts.js";
|
import type { ResolvedDiscordAccount } from "./accounts.js";
|
||||||
|
|
||||||
function resolveDiscordProxyUrl(
|
function resolveDiscordProxyUrl(
|
||||||
account: Pick<ResolvedDiscordAccount, "config">,
|
account: Pick<ResolvedDiscordAccount, "config">,
|
||||||
cfg: OpenClawConfig,
|
cfg: OpenClawConfig,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const accountProxy = account.config.proxy?.trim();
|
const accountProxy = normalizeOptionalString(account.config.proxy);
|
||||||
if (accountProxy) {
|
if (accountProxy) {
|
||||||
return accountProxy;
|
return accountProxy;
|
||||||
}
|
}
|
||||||
const channelProxy = cfg?.channels?.discord?.proxy;
|
return normalizeOptionalString(cfg?.channels?.discord?.proxy);
|
||||||
if (typeof channelProxy !== "string") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const trimmed = channelProxy.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDiscordProxyFetchByUrl(
|
function resolveDiscordProxyFetchByUrl(
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
// Duckduckgo helper module supports config behavior.
|
// Duckduckgo helper module supports config behavior.
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
normalizeLowercaseStringOrEmpty,
|
||||||
|
normalizeOptionalString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
const DEFAULT_DDG_SAFE_SEARCH = "moderate";
|
const DEFAULT_DDG_SAFE_SEARCH = "moderate";
|
||||||
|
|
||||||
@@ -25,12 +28,7 @@ function resolveDdgWebSearchConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveDdgRegion(config?: OpenClawConfig): string | undefined {
|
export function resolveDdgRegion(config?: OpenClawConfig): string | undefined {
|
||||||
const region = resolveDdgWebSearchConfig(config)?.region;
|
return normalizeOptionalString(resolveDdgWebSearchConfig(config)?.region);
|
||||||
if (typeof region !== "string") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const trimmed = region.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveDdgSafeSearch(config?: OpenClawConfig): DdgSafeSearch {
|
export function resolveDdgSafeSearch(config?: OpenClawConfig): DdgSafeSearch {
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import {
|
|||||||
} from "openclaw/plugin-sdk/realtime-transcription";
|
} from "openclaw/plugin-sdk/realtime-transcription";
|
||||||
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||||
import {
|
import {
|
||||||
|
asFiniteNumberInRange,
|
||||||
asOptionalRecord as readRecord,
|
asOptionalRecord as readRecord,
|
||||||
|
asSafeIntegerInRange,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
parseFiniteNumber as readFiniteNumber,
|
parseFiniteNumber as readFiniteNumber,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -81,19 +83,17 @@ function normalizeCommitStrategy(value: unknown): "manual" | "vad" | undefined {
|
|||||||
|
|
||||||
function normalizePositiveSafeInteger(value: unknown): number | undefined {
|
function normalizePositiveSafeInteger(value: unknown): number | undefined {
|
||||||
const parsed = readFiniteNumber(value);
|
const parsed = readFiniteNumber(value);
|
||||||
return parsed !== undefined && Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
|
return asSafeIntegerInRange(parsed, { min: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeFiniteRange(value: unknown, min: number, max: number): number | undefined {
|
function normalizeFiniteRange(value: unknown, min: number, max: number): number | undefined {
|
||||||
const parsed = readFiniteNumber(value);
|
const parsed = readFiniteNumber(value);
|
||||||
return parsed !== undefined && parsed >= min && parsed <= max ? parsed : undefined;
|
return asFiniteNumberInRange(parsed, { min, max });
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeIntegerRange(value: unknown, min: number, max: number): number | undefined {
|
function normalizeIntegerRange(value: unknown, min: number, max: number): number | undefined {
|
||||||
const parsed = readFiniteNumber(value);
|
const parsed = readFiniteNumber(value);
|
||||||
return parsed !== undefined && Number.isSafeInteger(parsed) && parsed >= min && parsed <= max
|
return asSafeIntegerInRange(parsed, { min, max });
|
||||||
? parsed
|
|
||||||
: undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeProviderConfig(
|
function normalizeProviderConfig(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
resolveChannelPreviewStreamMode,
|
resolveChannelPreviewStreamMode,
|
||||||
resolveChannelStreamingBlockEnabled,
|
resolveChannelStreamingBlockEnabled,
|
||||||
} from "openclaw/plugin-sdk/channel-outbound";
|
} from "openclaw/plugin-sdk/channel-outbound";
|
||||||
|
import { toStringifiedError as toFeishuError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
||||||
import {
|
import {
|
||||||
getReplyPayloadTtsSupplement,
|
getReplyPayloadTtsSupplement,
|
||||||
@@ -77,10 +78,6 @@ function mergeStreamingFinalText(
|
|||||||
return `${previousText}\n\n${nextText}`;
|
return `${previousText}\n\n${nextText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toFeishuError(error: unknown): Error {
|
|
||||||
return error instanceof Error ? error : new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Maximum age (ms) for a message to receive a typing indicator reaction.
|
/** Maximum age (ms) for a message to receive a typing indicator reaction.
|
||||||
* Messages older than this are likely replays after context compaction (#30418). */
|
* Messages older than this are likely replays after context compaction (#30418). */
|
||||||
const TYPING_INDICATOR_MAX_AGE_MS = 2 * 60_000;
|
const TYPING_INDICATOR_MAX_AGE_MS = 2 * 60_000;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import { formatByteSize } from "openclaw/plugin-sdk/number-runtime";
|
import { formatByteSize } from "openclaw/plugin-sdk/number-runtime";
|
||||||
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
||||||
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
type GatewayCallOptions = {
|
type GatewayCallOptions = {
|
||||||
gatewayUrl?: string;
|
gatewayUrl?: string;
|
||||||
@@ -23,8 +24,7 @@ export function readGatewayCallOptions(params: Record<string, unknown>): Gateway
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function readTrimmedString(params: Record<string, unknown>, key: string): string {
|
export function readTrimmedString(params: Record<string, unknown>, key: string): string {
|
||||||
const value = params[key];
|
return normalizeOptionalString(params[key]) ?? "";
|
||||||
return typeof value === "string" ? value.trim() : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readClampedInt(params: {
|
export function readClampedInt(params: {
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ import type {
|
|||||||
} from "openclaw/plugin-sdk/speech";
|
} from "openclaw/plugin-sdk/speech";
|
||||||
import {
|
import {
|
||||||
asBoolean,
|
asBoolean,
|
||||||
asFiniteNumber,
|
|
||||||
parseSpeechDirectiveNumberOverride,
|
parseSpeechDirectiveNumberOverride,
|
||||||
resolveSpeechProviderApiKey,
|
resolveSpeechProviderApiKey,
|
||||||
trimToUndefined,
|
trimToUndefined,
|
||||||
} from "openclaw/plugin-sdk/speech-core";
|
} from "openclaw/plugin-sdk/speech-core";
|
||||||
import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { asFiniteNumberInRange, asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import {
|
import {
|
||||||
FISH_AUDIO_STREAM_MAX_BYTES,
|
FISH_AUDIO_STREAM_MAX_BYTES,
|
||||||
type FishAudioFormat,
|
type FishAudioFormat,
|
||||||
@@ -71,8 +70,7 @@ function normalizeLatency(value: unknown): FishAudioLatency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeNumber(value: unknown, min: number, max: number): number | undefined {
|
function normalizeNumber(value: unknown, min: number, max: number): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asFiniteNumberInRange(value, { min, max });
|
||||||
return number != null && number >= min && number <= max ? number : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveReferenceId(raw: Record<string, unknown> | undefined): string | undefined {
|
function resolveReferenceId(raw: Record<string, unknown> | undefined): string | undefined {
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import {
|
|||||||
type RealtimeVoiceAgentConsultToolPolicy,
|
type RealtimeVoiceAgentConsultToolPolicy,
|
||||||
} from "openclaw/plugin-sdk/realtime-voice";
|
} from "openclaw/plugin-sdk/realtime-voice";
|
||||||
import {
|
import {
|
||||||
|
asBoolean,
|
||||||
asRecord,
|
asRecord,
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
normalizeOptionalTrimmedStringList,
|
normalizeOptionalTrimmedStringList,
|
||||||
|
parseBooleanValue,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
export type GoogleMeetTransport = "chrome" | "chrome-node" | "twilio";
|
export type GoogleMeetTransport = "chrome" | "chrome-node" | "twilio";
|
||||||
@@ -251,7 +253,7 @@ const GOOGLE_MEET_PREVIEW_ACK_KEYS = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
function resolveBoolean(value: unknown, fallback: boolean): boolean {
|
function resolveBoolean(value: unknown, fallback: boolean): boolean {
|
||||||
return typeof value === "boolean" ? value : fallback;
|
return asBoolean(value) ?? fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveNumber(value: unknown, fallback: number): number {
|
function resolveNumber(value: unknown, fallback: number): number {
|
||||||
@@ -289,17 +291,7 @@ function normalizeStringAllowEmpty(value: unknown): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readEnvBoolean(env: NodeJS.ProcessEnv, keys: readonly string[]): boolean | undefined {
|
function readEnvBoolean(env: NodeJS.ProcessEnv, keys: readonly string[]): boolean | undefined {
|
||||||
const normalized = normalizeOptionalLowercaseString(readEnvString(env, keys));
|
return parseBooleanValue(readEnvString(env, keys));
|
||||||
if (!normalized) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function readEnvNumber(env: NodeJS.ProcessEnv, keys: readonly string[]): number | undefined {
|
function readEnvNumber(env: NodeJS.ProcessEnv, keys: readonly string[]): number | undefined {
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import type {
|
|||||||
OpenClawPluginApi,
|
OpenClawPluginApi,
|
||||||
OpenClawPluginNodeInvokePolicy,
|
OpenClawPluginNodeInvokePolicy,
|
||||||
} from "openclaw/plugin-sdk/plugin-entry";
|
} from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
asNonArrayRecord as asParamRecord,
|
||||||
|
normalizeOptionalString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
|
export { asParamRecord };
|
||||||
import { isGoogleMeetBrowserManualActionError } from "./browser-manual-action-error.js";
|
import { isGoogleMeetBrowserManualActionError } from "./browser-manual-action-error.js";
|
||||||
import {
|
import {
|
||||||
resolveGoogleMeetGatewayOperationTimeoutMs,
|
resolveGoogleMeetGatewayOperationTimeoutMs,
|
||||||
@@ -44,10 +49,6 @@ type LoadGoogleMeetNodeInvokePolicy = (
|
|||||||
const loadGoogleMeetNodeInvokePolicy: LoadGoogleMeetNodeInvokePolicy = async (config) =>
|
const loadGoogleMeetNodeInvokePolicy: LoadGoogleMeetNodeInvokePolicy = async (config) =>
|
||||||
(await loadGoogleMeetNodeInvokePolicyModule()).createGoogleMeetChromeNodeInvokePolicy(config);
|
(await loadGoogleMeetNodeInvokePolicyModule()).createGoogleMeetChromeNodeInvokePolicy(config);
|
||||||
|
|
||||||
export function asParamRecord(params: unknown): Record<string, unknown> {
|
|
||||||
return isRecord(params) ? params : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeTransport(value: unknown): GoogleMeetTransport | undefined {
|
export function normalizeTransport(value: unknown): GoogleMeetTransport | undefined {
|
||||||
return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : undefined;
|
return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-i
|
|||||||
import {
|
import {
|
||||||
asBoolean,
|
asBoolean,
|
||||||
asFiniteNumber,
|
asFiniteNumber,
|
||||||
|
asSafeIntegerInRange,
|
||||||
isRecord,
|
isRecord,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -209,8 +210,7 @@ function asTurnCoverage(value: unknown): GoogleRealtimeTurnCoverage | undefined
|
|||||||
}
|
}
|
||||||
|
|
||||||
function asNonNegativeInteger(value: unknown): number | undefined {
|
function asNonNegativeInteger(value: unknown): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asSafeIntegerInRange(value, { min: 0 });
|
||||||
return number !== undefined && Number.isSafeInteger(number) && number >= 0 ? number : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function asGoogleRealtimeThinkingBudget(value: unknown): number | undefined {
|
function asGoogleRealtimeThinkingBudget(value: unknown): number | undefined {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
buildHostnameAllowlistPolicyFromSuffixAllowlist,
|
buildHostnameAllowlistPolicyFromSuffixAllowlist,
|
||||||
fetchWithSsrFGuard,
|
fetchWithSsrFGuard,
|
||||||
} from "openclaw/plugin-sdk/ssrf-runtime";
|
} from "openclaw/plugin-sdk/ssrf-runtime";
|
||||||
|
import { asNullableObjectRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import type { ResolvedGoogleChatAccount } from "./accounts.js";
|
import type { ResolvedGoogleChatAccount } from "./accounts.js";
|
||||||
import { MAX_GOOGLE_CHAT_SERVICE_ACCOUNT_FILE_BYTES } from "./google-auth-limits.js";
|
import { MAX_GOOGLE_CHAT_SERVICE_ACCOUNT_FILE_BYTES } from "./google-auth-limits.js";
|
||||||
@@ -78,10 +79,6 @@ function installGoogleAuthHeaderCompatibilityInterceptor(
|
|||||||
return transport;
|
return transport;
|
||||||
}
|
}
|
||||||
|
|
||||||
function asNullableObjectRecord(value: unknown): Record<string, unknown> | null {
|
|
||||||
return value !== null && typeof value === "object" ? (value as Record<string, unknown>) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasProxyAgentShape(value: unknown): value is ProxyAgentLike {
|
function hasProxyAgentShape(value: unknown): value is ProxyAgentLike {
|
||||||
const record = asNullableObjectRecord(value);
|
const record = asNullableObjectRecord(value);
|
||||||
return record !== null && record.proxy instanceof URL;
|
return record !== null && record.proxy instanceof URL;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Imessage plugin module implements catchup behavior.
|
// Imessage plugin module implements catchup behavior.
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
||||||
|
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
||||||
import type { PluginStateSyncKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime";
|
import type { PluginStateSyncKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime";
|
||||||
import { getIMessageRuntime } from "../runtime.js";
|
import { getIMessageRuntime } from "../runtime.js";
|
||||||
|
|
||||||
@@ -246,10 +247,7 @@ export type ResolvedCatchupConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function clampInt(value: number | undefined, min: number, max: number, fallback: number): number {
|
function clampInt(value: number | undefined, min: number, max: number, fallback: number): number {
|
||||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
return resolveIntegerOption(value, fallback, { min, max });
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
return Math.min(max, Math.max(min, Math.floor(value)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveCatchupConfig(
|
export function resolveCatchupConfig(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|||||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||||
import { detectBinary } from "openclaw/plugin-sdk/setup";
|
import { detectBinary } from "openclaw/plugin-sdk/setup";
|
||||||
import {
|
import {
|
||||||
|
filterStringEntries,
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeStringEntries,
|
normalizeStringEntries,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -187,14 +188,6 @@ function selectorsFromPayload(payload: Record<string, unknown>): Record<string,
|
|||||||
return selectors;
|
return selectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rpcMethodsFromPayload(payload: Record<string, unknown>): string[] {
|
|
||||||
const raw = payload.rpc_methods;
|
|
||||||
if (!Array.isArray(raw)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return raw.filter((entry): entry is string => typeof entry === "string");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probe whether the installed imsg CLI accepts `--file` on the `send-rich`
|
// Probe whether the installed imsg CLI accepts `--file` on the `send-rich`
|
||||||
// subcommand (added by openclaw/imsg#114, which lets a single bridge call
|
// subcommand (added by openclaw/imsg#114, which lets a single bridge call
|
||||||
// combine `--reply-to` and an attachment). We grep the help output rather
|
// combine `--reply-to` and an attachment). We grep the help output rather
|
||||||
@@ -278,7 +271,7 @@ export async function probeIMessagePrivateApi(
|
|||||||
}
|
}
|
||||||
const { payload, firstLineSnippet } = parseStatusPayload(result.stdout);
|
const { payload, firstLineSnippet } = parseStatusPayload(result.stdout);
|
||||||
const selectors = payload ? selectorsFromPayload(payload) : {};
|
const selectors = payload ? selectorsFromPayload(payload) : {};
|
||||||
const rpcMethods = payload ? rpcMethodsFromPayload(payload) : [];
|
const rpcMethods = filterStringEntries(payload?.rpc_methods);
|
||||||
const advancedFeatures = payload?.advanced_features === true;
|
const advancedFeatures = payload?.advanced_features === true;
|
||||||
const v2Ready = payload?.v2_ready === true;
|
const v2Ready = payload?.v2_ready === true;
|
||||||
// imsg explains an unavailable bridge here (SIP, library validation, macOS
|
// imsg explains an unavailable bridge here (SIP, library validation, macOS
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { OpenClawPluginNodeHostCommandAvailabilityContext } from "openclaw/plugin-sdk/plugin-entry";
|
import type { OpenClawPluginNodeHostCommandAvailabilityContext } from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import type { CommandOptions, SpawnResult } from "openclaw/plugin-sdk/process-runtime";
|
import type { CommandOptions, SpawnResult } from "openclaw/plugin-sdk/process-runtime";
|
||||||
import { asNonArrayRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { asFiniteNumber, asNonArrayRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import {
|
import {
|
||||||
resolveLinuxNodePluginConfigFromHost,
|
resolveLinuxNodePluginConfigFromHost,
|
||||||
@@ -21,9 +21,7 @@ export function parseParams(paramsJSON: string | null | undefined): Record<strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readFiniteNumber(value: unknown): number | undefined {
|
export { asFiniteNumber as readFiniteNumber };
|
||||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clamp(value: number, minimum: number, maximum: number): number {
|
export function clamp(value: number, minimum: number, maximum: number): number {
|
||||||
return Math.min(maximum, Math.max(minimum, value));
|
return Math.min(maximum, Math.max(minimum, value));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Matrix plugin module implements env auth behavior.
|
// Matrix plugin module implements env auth behavior.
|
||||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
||||||
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { getMatrixScopedEnvVarNames } from "../../env-vars.js";
|
import { getMatrixScopedEnvVarNames } from "../../env-vars.js";
|
||||||
|
|
||||||
type MatrixEnvConfig = {
|
type MatrixEnvConfig = {
|
||||||
@@ -12,7 +13,7 @@ type MatrixEnvConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function cleanEnv(value: unknown): string {
|
function cleanEnv(value: unknown): string {
|
||||||
return typeof value === "string" ? value.trim() : "";
|
return normalizeOptionalString(value) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveGlobalMatrixEnvConfig(env: NodeJS.ProcessEnv): MatrixEnvConfig {
|
export function resolveGlobalMatrixEnvConfig(env: NodeJS.ProcessEnv): MatrixEnvConfig {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Matrix plugin module implements shared behavior.
|
// Matrix plugin module implements shared behavior.
|
||||||
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
|
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
|
||||||
|
import { toStringifiedError as toRetirementError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||||
import type { CoreConfig } from "../../types.js";
|
import type { CoreConfig } from "../../types.js";
|
||||||
@@ -274,10 +275,6 @@ async function retireMonitorLeases(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRetirementError(error: unknown): Error {
|
|
||||||
return error instanceof Error ? error : new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeReleaseMode(
|
function mergeReleaseMode(
|
||||||
current: MatrixClientReleaseMode,
|
current: MatrixClientReleaseMode,
|
||||||
requested: MatrixClientReleaseMode,
|
requested: MatrixClientReleaseMode,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Matrix plugin module implements crypto bootstrap behavior.
|
// Matrix plugin module implements crypto bootstrap behavior.
|
||||||
import { setTimeout as sleep } from "node:timers/promises";
|
import { setTimeout as sleep } from "node:timers/promises";
|
||||||
import { CryptoEvent } from "matrix-js-sdk/lib/crypto-api/CryptoEvent.js";
|
import { CryptoEvent } from "matrix-js-sdk/lib/crypto-api/CryptoEvent.js";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import type { MatrixDecryptBridge } from "./decrypt-bridge.js";
|
import type { MatrixDecryptBridge } from "./decrypt-bridge.js";
|
||||||
import { LogService } from "./logger.js";
|
import { LogService } from "./logger.js";
|
||||||
import type { MatrixRecoveryKeyStore } from "./recovery-key-store.js";
|
import type { MatrixRecoveryKeyStore } from "./recovery-key-store.js";
|
||||||
@@ -236,7 +237,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
} catch (repairErr) {
|
} catch (repairErr) {
|
||||||
LogService.warn("MatrixClientLite", "Forced cross-signing reset failed:", repairErr);
|
LogService.warn("MatrixClientLite", "Forced cross-signing reset failed:", repairErr);
|
||||||
if (options.strict) {
|
if (options.strict) {
|
||||||
throw repairErr instanceof Error ? repairErr : new Error(String(repairErr));
|
throw toStringifiedError(repairErr);
|
||||||
}
|
}
|
||||||
return { ready: false, published: false };
|
return { ready: false, published: false };
|
||||||
}
|
}
|
||||||
@@ -250,7 +251,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
{ cause: err },
|
{ cause: err },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw err instanceof Error ? err : new Error(String(err));
|
throw toStringifiedError(err);
|
||||||
}
|
}
|
||||||
return { ready: false, published: false };
|
return { ready: false, published: false };
|
||||||
}
|
}
|
||||||
@@ -300,7 +301,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
} catch (resetErr) {
|
} catch (resetErr) {
|
||||||
LogService.warn("MatrixClientLite", "Failed to bootstrap cross-signing:", resetErr);
|
LogService.warn("MatrixClientLite", "Failed to bootstrap cross-signing:", resetErr);
|
||||||
if (options.strict) {
|
if (options.strict) {
|
||||||
throw resetErr instanceof Error ? resetErr : new Error(String(resetErr));
|
throw toStringifiedError(resetErr);
|
||||||
}
|
}
|
||||||
return { ready: false, published: false };
|
return { ready: false, published: false };
|
||||||
}
|
}
|
||||||
@@ -328,7 +329,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
LogService.warn("MatrixClientLite", "Fallback cross-signing bootstrap failed:", err);
|
LogService.warn("MatrixClientLite", "Fallback cross-signing bootstrap failed:", err);
|
||||||
if (options.strict) {
|
if (options.strict) {
|
||||||
throw err instanceof Error ? err : new Error(String(err));
|
throw toStringifiedError(err);
|
||||||
}
|
}
|
||||||
return { ready: false, published: false };
|
return { ready: false, published: false };
|
||||||
}
|
}
|
||||||
@@ -371,7 +372,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
LogService.warn("MatrixClientLite", "Failed to bootstrap secret storage:", err);
|
LogService.warn("MatrixClientLite", "Failed to bootstrap secret storage:", err);
|
||||||
if (options.strict) {
|
if (options.strict) {
|
||||||
throw err instanceof Error ? err : new Error(String(err));
|
throw toStringifiedError(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import { parseStrictInteger } from "openclaw/plugin-sdk/number-runtime";
|
|||||||
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
||||||
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
||||||
import {
|
import {
|
||||||
|
asFiniteNumber,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
normalizeStringifiedOptionalString,
|
normalizeStringifiedOptionalString,
|
||||||
|
readStringField,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { MattermostInteractiveButtonInput } from "./interactions.js";
|
import type { MattermostInteractiveButtonInput } from "./interactions.js";
|
||||||
|
|
||||||
@@ -59,19 +61,12 @@ function splitModelRef(modelRef?: string | null): { provider: string; model: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readContextString(context: Record<string, unknown>, key: string, fallback = ""): string {
|
function readContextString(context: Record<string, unknown>, key: string, fallback = ""): string {
|
||||||
const value = context[key];
|
return readStringField(context, key) ?? fallback;
|
||||||
return typeof value === "string" ? value : fallback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function readContextNumber(context: Record<string, unknown>, key: string): number | undefined {
|
function readContextNumber(context: Record<string, unknown>, key: string): number | undefined {
|
||||||
const value = context[key];
|
const value = context[key];
|
||||||
if (typeof value === "number" && Number.isFinite(value)) {
|
return asFiniteNumber(value) ?? parseStrictInteger(value);
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return parseStrictInteger(value);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePage(value: number | undefined): number {
|
function normalizePage(value: number | undefined): number {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { resolveNonNegativeIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
||||||
import type {
|
import type {
|
||||||
OpenKeyedStoreOptions,
|
OpenKeyedStoreOptions,
|
||||||
PluginStateKeyedStore,
|
PluginStateKeyedStore,
|
||||||
@@ -149,10 +150,6 @@ function normalizeImportRunEntries(value: unknown): ChatGptImportRunEntry[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function asNonNegativeInteger(value: unknown): number {
|
|
||||||
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeMemoryWikiImportRunRecord(raw: unknown): ChatGptImportRunRecord | null {
|
function normalizeMemoryWikiImportRunRecord(raw: unknown): ChatGptImportRunRecord | null {
|
||||||
const record = asNullableRecord(raw);
|
const record = asNullableRecord(raw);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
@@ -182,10 +179,10 @@ function normalizeMemoryWikiImportRunRecord(raw: unknown): ChatGptImportRunRecor
|
|||||||
exportPath,
|
exportPath,
|
||||||
sourcePath,
|
sourcePath,
|
||||||
appliedAt,
|
appliedAt,
|
||||||
conversationCount: asNonNegativeInteger(record.conversationCount),
|
conversationCount: resolveNonNegativeIntegerOption(record.conversationCount, 0),
|
||||||
createdCount: asNonNegativeInteger(record.createdCount),
|
createdCount: resolveNonNegativeIntegerOption(record.createdCount, 0),
|
||||||
updatedCount: asNonNegativeInteger(record.updatedCount),
|
updatedCount: resolveNonNegativeIntegerOption(record.updatedCount, 0),
|
||||||
skippedCount: asNonNegativeInteger(record.skippedCount),
|
skippedCount: resolveNonNegativeIntegerOption(record.skippedCount, 0),
|
||||||
createdPaths: normalizeImportRunEntries(record.createdPaths),
|
createdPaths: normalizeImportRunEntries(record.createdPaths),
|
||||||
updatedPaths: normalizeImportRunEntries(record.updatedPaths),
|
updatedPaths: normalizeImportRunEntries(record.updatedPaths),
|
||||||
...(rollbackStartedAt ? { rollbackStartedAt } : {}),
|
...(rollbackStartedAt ? { rollbackStartedAt } : {}),
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
||||||
import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry";
|
import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import { createPayloadPatchStreamWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
|
import { createPayloadPatchStreamWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
|
||||||
|
import { filterStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
const META_REASONING_ENCRYPTED_CONTENT_INCLUDE = "reasoning.encrypted_content";
|
const META_REASONING_ENCRYPTED_CONTENT_INCLUDE = "reasoning.encrypted_content";
|
||||||
|
|
||||||
function ensureMetaResponsesReplayFields(payloadObj: Record<string, unknown>): void {
|
function ensureMetaResponsesReplayFields(payloadObj: Record<string, unknown>): void {
|
||||||
const existing = payloadObj.include;
|
const existing = payloadObj.include;
|
||||||
const include = Array.isArray(existing)
|
const include = filterStringEntries(existing);
|
||||||
? existing.filter((entry): entry is string => typeof entry === "string")
|
|
||||||
: [];
|
|
||||||
if (!include.includes(META_REASONING_ENCRYPTED_CONTENT_INCLUDE)) {
|
if (!include.includes(META_REASONING_ENCRYPTED_CONTENT_INCLUDE)) {
|
||||||
include.push(META_REASONING_ENCRYPTED_CONTENT_INCLUDE);
|
include.push(META_REASONING_ENCRYPTED_CONTENT_INCLUDE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ function readOptionalTrimmedString(
|
|||||||
params: Record<string, unknown>,
|
params: Record<string, unknown>,
|
||||||
key: string,
|
key: string,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
return typeof params[key] === "string" ? params[key].trim() || undefined : undefined;
|
return normalizeOptionalString(params[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveActionUploadFilePath(params: Record<string, unknown>): string | undefined {
|
function resolveActionUploadFilePath(params: Record<string, unknown>): string | undefined {
|
||||||
|
|||||||
@@ -31,13 +31,6 @@ function cacheRoomInfo(
|
|||||||
pruneMapToMaxSize(roomCache, ROOM_CACHE_MAX_ENTRIES);
|
pruneMapToMaxSize(roomCache, ROOM_CACHE_MAX_ENTRIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
function coerceRoomType(value: unknown): number | undefined {
|
|
||||||
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return parseStrictPositiveInteger(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveRoomKindFromType(type: number | undefined): "direct" | "group" | undefined {
|
function resolveRoomKindFromType(type: number | undefined): "direct" | "group" | undefined {
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -117,7 +110,7 @@ export async function resolveNextcloudTalkRoomKind(params: {
|
|||||||
const payload = await readProviderJsonResponse<{
|
const payload = await readProviderJsonResponse<{
|
||||||
ocs?: { data?: { type?: number | string } };
|
ocs?: { data?: { type?: number | string } };
|
||||||
}>(response, "Nextcloud Talk room info failed");
|
}>(response, "Nextcloud Talk room info failed");
|
||||||
const type = coerceRoomType(payload.ocs?.data?.type);
|
const type = parseStrictPositiveInteger(payload.ocs?.data?.type);
|
||||||
const kind = resolveRoomKindFromType(type);
|
const kind = resolveRoomKindFromType(type);
|
||||||
cacheRoomInfo(key, { fetchedAt: Date.now(), kind });
|
cacheRoomInfo(key, { fetchedAt: Date.now(), kind });
|
||||||
return kind;
|
return kind;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
||||||
import {
|
import {
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
readStringValue,
|
readNonEmptyStringPreservingWhitespace as readNonEmptyUntrimmedString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { resolveDefaultNextcloudTalkAccountId, resolveNextcloudTalkAccount } from "./accounts.js";
|
import { resolveDefaultNextcloudTalkAccountId, resolveNextcloudTalkAccount } from "./accounts.js";
|
||||||
import type { CoreConfig } from "./types.js";
|
import type { CoreConfig } from "./types.js";
|
||||||
@@ -39,11 +39,6 @@ type NextcloudSetupInput = ChannelSetupInput & {
|
|||||||
password?: string;
|
password?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function readNonEmptyUntrimmedString(value: unknown): string | undefined {
|
|
||||||
const text = readStringValue(value);
|
|
||||||
return text ? text : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeNextcloudTalkBaseUrl(value: string | undefined): string {
|
export function normalizeNextcloudTalkBaseUrl(value: string | undefined): string {
|
||||||
return value?.trim().replace(/\/+$/, "") ?? "";
|
return value?.trim().replace(/\/+$/, "") ?? "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// Nostr plugin module owns durable ingress identity and legacy-state migration.
|
// Nostr plugin module owns durable ingress identity and legacy-state migration.
|
||||||
import type { ChannelIngressQueue } from "openclaw/plugin-sdk/channel-outbound";
|
import type { ChannelIngressQueue } from "openclaw/plugin-sdk/channel-outbound";
|
||||||
|
import { isRecord as isNostrIngressRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
|
export { isNostrIngressRecord };
|
||||||
|
|
||||||
export const NOSTR_INGRESS_PAYLOAD_VERSION = 1;
|
export const NOSTR_INGRESS_PAYLOAD_VERSION = 1;
|
||||||
|
|
||||||
@@ -20,10 +23,6 @@ export class NostrIngressPermanentError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNostrIngressRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function requiredString(value: unknown, field: string): string {
|
function requiredString(value: unknown, field: string): string {
|
||||||
if (typeof value === "string" && value.trim()) {
|
if (typeof value === "string" && value.trim()) {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { DEFAULT_SECRET_FILE_MAX_BYTES, tryReadSecretFileSync } from "@openclaw/fs-safe/secret";
|
import { DEFAULT_SECRET_FILE_MAX_BYTES, tryReadSecretFileSync } from "@openclaw/fs-safe/secret";
|
||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
|
import { coerceErrorMessage as errorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { resolveTrustedOnePasswordCli } from "./onepassword-op-path.js";
|
import { resolveTrustedOnePasswordCli } from "./onepassword-op-path.js";
|
||||||
import { resolveOnePasswordSecretReference } from "./onepassword-secret-id.js";
|
import { resolveOnePasswordSecretReference } from "./onepassword-secret-id.js";
|
||||||
|
|
||||||
@@ -65,10 +66,6 @@ function opMissingMessage(command) {
|
|||||||
return `1Password CLI "${command}" is not installed or cannot be executed. Install the official 1Password CLI v2, and set CLAW_1PASSWORD_OP to its absolute path.`;
|
return `1Password CLI "${command}" is not installed or cannot be executed. Install the official 1Password CLI v2, and set CLAW_1PASSWORD_OP to its absolute path.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function errorMessage(error) {
|
|
||||||
return error instanceof Error ? error.message : String(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveOsHome() {
|
function resolveOsHome() {
|
||||||
const home =
|
const home =
|
||||||
process.platform === "win32"
|
process.platform === "win32"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { coerceErrorMessage as formatOpenAiBatchError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
// Openai plugin module implements embedding batch behavior.
|
// Openai plugin module implements embedding batch behavior.
|
||||||
import {
|
import {
|
||||||
applyEmbeddingBatchOutputLine,
|
applyEmbeddingBatchOutputLine,
|
||||||
@@ -158,10 +159,6 @@ async function fetchOpenAiBatchResource<T>(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatOpenAiBatchError(error: unknown): string {
|
|
||||||
return error instanceof Error ? error.message : String(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatOpenAiBatchDiagnostic(error: unknown): string {
|
function formatOpenAiBatchDiagnostic(error: unknown): string {
|
||||||
return formatBatchErrorDetail(formatOpenAiBatchError(error)) ?? "unknown error";
|
return formatBatchErrorDetail(formatOpenAiBatchError(error)) ?? "unknown error";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
sanitizeConfiguredModelProviderRequest,
|
sanitizeConfiguredModelProviderRequest,
|
||||||
} from "openclaw/plugin-sdk/provider-http";
|
} from "openclaw/plugin-sdk/provider-http";
|
||||||
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
|
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||||
|
import { filterStringRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import {
|
import {
|
||||||
canonicalizeCodexResponsesBaseUrl,
|
canonicalizeCodexResponsesBaseUrl,
|
||||||
@@ -447,16 +448,7 @@ function hasChatGPTImageRouteConfig(cfg: OpenClawConfig | undefined): boolean {
|
|||||||
function resolveConfiguredOpenAIImageHeaders(
|
function resolveConfiguredOpenAIImageHeaders(
|
||||||
cfg: OpenClawConfig | undefined,
|
cfg: OpenClawConfig | undefined,
|
||||||
): Record<string, string> | undefined {
|
): Record<string, string> | undefined {
|
||||||
const headers = cfg?.models?.providers?.openai?.headers;
|
return filterStringRecord(cfg?.models?.providers?.openai?.headers);
|
||||||
if (!headers) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const stringHeaders = Object.fromEntries(
|
|
||||||
Object.entries(headers).filter(
|
|
||||||
(entry): entry is [string, string] => typeof entry[1] === "string",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return Object.keys(stringHeaders).length > 0 ? stringHeaders : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function forceOpenAIImageApiKeyAuth(cfg: OpenClawConfig | undefined): OpenClawConfig | undefined {
|
function forceOpenAIImageApiKeyAuth(cfg: OpenClawConfig | undefined): OpenClawConfig | undefined {
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import {
|
|||||||
type RealtimeTranscriptionWebSocketTransport,
|
type RealtimeTranscriptionWebSocketTransport,
|
||||||
} from "openclaw/plugin-sdk/realtime-transcription";
|
} from "openclaw/plugin-sdk/realtime-transcription";
|
||||||
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||||
import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
asFiniteNumberInRange,
|
||||||
|
asSafeIntegerInRange,
|
||||||
|
normalizeOptionalString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import {
|
import {
|
||||||
createOpenAIRealtimeTranscriptionClientSecret,
|
createOpenAIRealtimeTranscriptionClientSecret,
|
||||||
readRealtimeErrorDetail,
|
readRealtimeErrorDetail,
|
||||||
@@ -133,19 +137,11 @@ function normalizeProviderConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeNonNegativeInteger(value: unknown): number | undefined {
|
function normalizeNonNegativeInteger(value: unknown): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asSafeIntegerInRange(value, { min: 0 });
|
||||||
if (number === undefined || !Number.isSafeInteger(number) || number < 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeVadThreshold(value: unknown): number | undefined {
|
function normalizeVadThreshold(value: unknown): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asFiniteNumberInRange(value, { min: 0, max: 1 });
|
||||||
if (number === undefined || number < 0 || number > 1) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildOpenAIRealtimeTranscriptionSessionPayload(
|
function buildOpenAIRealtimeTranscriptionSessionPayload(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { canonicalizeBase64 } from "openclaw/plugin-sdk/media-runtime";
|
import { canonicalizeBase64 } from "openclaw/plugin-sdk/media-runtime";
|
||||||
import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
|
import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
|
||||||
import {
|
import {
|
||||||
@@ -41,6 +42,8 @@ import {
|
|||||||
} from "openclaw/plugin-sdk/secret-input";
|
} from "openclaw/plugin-sdk/secret-input";
|
||||||
import {
|
import {
|
||||||
asFiniteNumber,
|
asFiniteNumber,
|
||||||
|
asFiniteNumberInRange,
|
||||||
|
asSafeIntegerInRange,
|
||||||
isRecord,
|
isRecord,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -294,13 +297,11 @@ function normalizeProviderConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function asNonNegativeInteger(value: unknown): number | undefined {
|
function asNonNegativeInteger(value: unknown): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asSafeIntegerInRange(value, { min: 0 });
|
||||||
return number !== undefined && Number.isSafeInteger(number) && number >= 0 ? number : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function asUnitInterval(value: unknown): number | undefined {
|
function asUnitInterval(value: unknown): number | undefined {
|
||||||
const number = asFiniteNumber(value);
|
return asFiniteNumberInRange(value, { min: 0, max: 1 });
|
||||||
return number !== undefined && number >= 0 && number <= 1 ? number : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenAIRealtimeApiKeyResolution =
|
type OpenAIRealtimeApiKeyResolution =
|
||||||
@@ -995,7 +996,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
try {
|
try {
|
||||||
this.handleEvent(event, lifecycleConnection);
|
this.handleEvent(event, lifecycleConnection);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const readyError = error instanceof Error ? error : new Error(String(error));
|
const readyError = toStringifiedError(error);
|
||||||
attempt.reject(readyError);
|
attempt.reject(readyError);
|
||||||
this.failConnection(readyError, ws, lifecycleConnection, {
|
this.failConnection(readyError, ws, lifecycleConnection, {
|
||||||
code: 1011,
|
code: 1011,
|
||||||
@@ -1036,7 +1037,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!attempt.ready) {
|
if (!attempt.ready) {
|
||||||
const startupError = error instanceof Error ? error : new Error(String(error));
|
const startupError = toStringifiedError(error);
|
||||||
rejectStartup(
|
rejectStartup(
|
||||||
isDirectOpenAIRealtimeWebSocketUrl(url) &&
|
isDirectOpenAIRealtimeWebSocketUrl(url) &&
|
||||||
isOpenAIRealtimeStartupAuthFailure(startupError)
|
isOpenAIRealtimeStartupAuthFailure(startupError)
|
||||||
@@ -1045,7 +1046,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
this.config.onError?.(toStringifiedError(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", (code, reasonBuffer) => {
|
ws.on("close", (code, reasonBuffer) => {
|
||||||
@@ -1091,7 +1092,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
try {
|
try {
|
||||||
connectionOrPromise = this.resolveConnectionParams();
|
connectionOrPromise = this.resolveConnectionParams();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
attempt.reject(error instanceof Error ? error : new Error(String(error)));
|
attempt.reject(toStringifiedError(error));
|
||||||
return attempt.promise;
|
return attempt.promise;
|
||||||
}
|
}
|
||||||
if (connectionOrPromise instanceof Promise) {
|
if (connectionOrPromise instanceof Promise) {
|
||||||
@@ -1103,13 +1104,13 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
attempt.resolve();
|
attempt.resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
attempt.reject(error instanceof Error ? error : new Error(String(error)));
|
attempt.reject(toStringifiedError(error));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
openWebSocket(connectionOrPromise);
|
openWebSocket(connectionOrPromise);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
attempt.reject(error instanceof Error ? error : new Error(String(error)));
|
attempt.reject(toStringifiedError(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await attempt.promise;
|
await attempt.promise;
|
||||||
@@ -1277,7 +1278,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
|
|||||||
if (!this.lifecycle.acceptsEvents(nextConnection)) {
|
if (!this.lifecycle.acceptsEvents(nextConnection)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
this.config.onError?.(toStringifiedError(error));
|
||||||
await this.attemptReconnect(reason, nextConnection);
|
await this.attemptReconnect(reason, nextConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
toImageDataUrl,
|
toImageDataUrl,
|
||||||
} from "openclaw/plugin-sdk/image-generation";
|
} from "openclaw/plugin-sdk/image-generation";
|
||||||
import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
|
import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
|
||||||
|
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
||||||
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
||||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||||
import {
|
import {
|
||||||
@@ -171,10 +172,7 @@ function extractOpenRouterImagesFromResponse(body: unknown): GeneratedImageAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveImageCount(count: number | undefined): number {
|
function resolveImageCount(count: number | undefined): number {
|
||||||
if (typeof count !== "number" || !Number.isFinite(count)) {
|
return resolveIntegerOption(count, 1, { min: 1, max: MAX_IMAGE_RESULTS });
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return Math.max(1, Math.min(MAX_IMAGE_RESULTS, Math.trunc(count)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGeminiImageModel(model: string): boolean {
|
function isGeminiImageModel(model: string): boolean {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
asOptionalRecord,
|
asOptionalRecord,
|
||||||
asPositiveSafeInteger,
|
asPositiveSafeInteger,
|
||||||
|
filterStringEntries,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
@@ -140,10 +141,7 @@ export function buildOpenrouterProvider(): ModelProviderConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readStringArray(record: Record<string, unknown> | undefined, key: string): string[] {
|
function readStringArray(record: Record<string, unknown> | undefined, key: string): string[] {
|
||||||
const value = record?.[key];
|
return filterStringEntries(record?.[key]);
|
||||||
return Array.isArray(value)
|
|
||||||
? value.filter((entry): entry is string => typeof entry === "string")
|
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function readTokenPrice(record: Record<string, unknown> | undefined, key: string): number {
|
function readTokenPrice(record: Record<string, unknown> | undefined, key: string): number {
|
||||||
|
|||||||
@@ -57,10 +57,6 @@ type OpenRouterVideoRequestPolicyCacheKey = ReturnType<
|
|||||||
|
|
||||||
type OpenRouterVideoRequestConfig = Parameters<typeof sanitizeConfiguredModelProviderRequest>[0];
|
type OpenRouterVideoRequestConfig = Parameters<typeof sanitizeConfiguredModelProviderRequest>[0];
|
||||||
|
|
||||||
function normalizeStringArray(value: unknown): string[] {
|
|
||||||
return normalizeTrimmedStringList(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeNumberArray(value: unknown): number[] {
|
function normalizeNumberArray(value: unknown): number[] {
|
||||||
return Array.isArray(value)
|
return Array.isArray(value)
|
||||||
? value.filter((entry): entry is number => typeof entry === "number" && Number.isFinite(entry))
|
? value.filter((entry): entry is number => typeof entry === "number" && Number.isFinite(entry))
|
||||||
@@ -68,14 +64,14 @@ function normalizeNumberArray(value: unknown): number[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeResolutionArray(value: unknown): VideoGenerationResolution[] {
|
function normalizeResolutionArray(value: unknown): VideoGenerationResolution[] {
|
||||||
return normalizeStringArray(value).map(
|
return normalizeTrimmedStringList(value).map(
|
||||||
(entry) => entry.toUpperCase() as VideoGenerationResolution,
|
(entry) => entry.toUpperCase() as VideoGenerationResolution,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeFrameImageRoles(value: unknown): Array<"first_frame" | "last_frame"> {
|
function normalizeFrameImageRoles(value: unknown): Array<"first_frame" | "last_frame"> {
|
||||||
const seen = new Set<"first_frame" | "last_frame">();
|
const seen = new Set<"first_frame" | "last_frame">();
|
||||||
for (const entry of normalizeStringArray(value)) {
|
for (const entry of normalizeTrimmedStringList(value)) {
|
||||||
if (entry === "first_frame" || entry === "last_frame") {
|
if (entry === "first_frame" || entry === "last_frame") {
|
||||||
seen.add(entry);
|
seen.add(entry);
|
||||||
}
|
}
|
||||||
@@ -136,12 +132,14 @@ function buildOpenRouterVideoModeCapabilities(params: {
|
|||||||
function buildOpenRouterVideoModelCapabilities(
|
function buildOpenRouterVideoModelCapabilities(
|
||||||
model: OpenRouterVideoModel,
|
model: OpenRouterVideoModel,
|
||||||
): OpenRouterVideoModelCatalogCapabilities {
|
): OpenRouterVideoModelCatalogCapabilities {
|
||||||
const aspectRatios = normalizeStringArray(model.supported_aspect_ratios);
|
const aspectRatios = normalizeTrimmedStringList(model.supported_aspect_ratios);
|
||||||
const durations = normalizeNumberArray(model.supported_durations);
|
const durations = normalizeNumberArray(model.supported_durations);
|
||||||
const frameImages = normalizeFrameImageRoles(model.supported_frame_images);
|
const frameImages = normalizeFrameImageRoles(model.supported_frame_images);
|
||||||
const resolutions = normalizeResolutionArray(model.supported_resolutions);
|
const resolutions = normalizeResolutionArray(model.supported_resolutions);
|
||||||
const sizes = normalizeStringArray(model.supported_sizes);
|
const sizes = normalizeTrimmedStringList(model.supported_sizes);
|
||||||
const allowedPassthroughParameters = normalizeStringArray(model.allowed_passthrough_parameters);
|
const allowedPassthroughParameters = normalizeTrimmedStringList(
|
||||||
|
model.allowed_passthrough_parameters,
|
||||||
|
);
|
||||||
const supportsAudio =
|
const supportsAudio =
|
||||||
typeof model.generate_audio === "boolean" ? model.generate_audio : undefined;
|
typeof model.generate_audio === "boolean" ? model.generate_audio : undefined;
|
||||||
const modeCapabilities = buildOpenRouterVideoModeCapabilities({
|
const modeCapabilities = buildOpenRouterVideoModeCapabilities({
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
||||||
// Transport-agnostic Parallel search normalization shared by the paid REST
|
// Transport-agnostic Parallel search normalization shared by the paid REST
|
||||||
// provider (`parallel`) and the free Search MCP provider (`parallel-free`).
|
// provider (`parallel`) and the free Search MCP provider (`parallel-free`).
|
||||||
// Both transports return the same v1 result shape, so query/result handling
|
// Both transports return the same v1 result shape, so query/result handling
|
||||||
@@ -53,7 +54,10 @@ export function resolveParallelSearchCount(
|
|||||||
const value =
|
const value =
|
||||||
requestedCount ??
|
requestedCount ??
|
||||||
(typeof configuredCount === "number" ? configuredCount : DEFAULT_SEARCH_COUNT);
|
(typeof configuredCount === "number" ? configuredCount : DEFAULT_SEARCH_COUNT);
|
||||||
return Math.max(1, Math.min(PARALLEL_MAX_SEARCH_COUNT, Math.floor(value)));
|
return resolveIntegerOption(value, DEFAULT_SEARCH_COUNT, {
|
||||||
|
min: 1,
|
||||||
|
max: PARALLEL_MAX_SEARCH_COUNT,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeParallelSessionId(
|
export function normalizeParallelSessionId(
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import path from "node:path";
|
|||||||
import { StringDecoder } from "node:string_decoder";
|
import { StringDecoder } from "node:string_decoder";
|
||||||
import { pathToFileURL } from "node:url";
|
import { pathToFileURL } from "node:url";
|
||||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
asNullableRecord as readRecord,
|
||||||
|
readStringValue,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import pLimit from "p-limit";
|
import pLimit from "p-limit";
|
||||||
import type {
|
import type {
|
||||||
QaEvidenceArtifactView,
|
QaEvidenceArtifactView,
|
||||||
@@ -565,12 +568,6 @@ async function buildArtifactView(params: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function readRecord(value: unknown): Record<string, unknown> | null {
|
|
||||||
return value && typeof value === "object" && !Array.isArray(value)
|
|
||||||
? (value as Record<string, unknown>)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readCountRecord(value: unknown): Record<string, number> {
|
function readCountRecord(value: unknown): Record<string, number> {
|
||||||
const record = readRecord(value);
|
const record = readRecord(value);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||||
import {
|
import {
|
||||||
acquireQaCredentialLease,
|
acquireQaCredentialLease,
|
||||||
@@ -96,7 +97,7 @@ export async function createDiscordQaTransportAdapter(
|
|||||||
}
|
}
|
||||||
})().catch((error: unknown) => {
|
})().catch((error: unknown) => {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
pollingError = error instanceof Error ? error : new Error(String(error));
|
pollingError = toStringifiedError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const scenarioEnvironment = createDiscordQaScenarioEnvironment({
|
const scenarioEnvironment = createDiscordQaScenarioEnvironment({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { setTimeout as sleep } from "node:timers/promises";
|
import { setTimeout as sleep } from "node:timers/promises";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { buildQaTarget } from "openclaw/plugin-sdk/qa-channel-protocol";
|
import { buildQaTarget } from "openclaw/plugin-sdk/qa-channel-protocol";
|
||||||
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||||
import { readQaScenarioExecutionConfig } from "../../scenario-catalog.js";
|
import { readQaScenarioExecutionConfig } from "../../scenario-catalog.js";
|
||||||
@@ -324,7 +325,7 @@ export async function createMatrixQaTransportAdapter(
|
|||||||
}),
|
}),
|
||||||
).catch((error: unknown) => {
|
).catch((error: unknown) => {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
pollingError = error instanceof Error ? error : new Error(String(error));
|
pollingError = toStringifiedError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "@openclaw/slack/api.js";
|
} from "@openclaw/slack/api.js";
|
||||||
import type { FetchFunction } from "@slack/web-api";
|
import type { FetchFunction } from "@slack/web-api";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { acquireDebugProxyCaptureStore } from "openclaw/plugin-sdk/proxy-capture";
|
import { acquireDebugProxyCaptureStore } from "openclaw/plugin-sdk/proxy-capture";
|
||||||
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||||
import {
|
import {
|
||||||
@@ -222,7 +223,7 @@ export async function createSlackQaTransportAdapter(
|
|||||||
}
|
}
|
||||||
})().catch((error: unknown) => {
|
})().catch((error: unknown) => {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
pollingError = error instanceof Error ? error : new Error(String(error));
|
pollingError = toStringifiedError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { WhatsAppQaDriverSession } from "@openclaw/whatsapp/api.js";
|
import type { WhatsAppQaDriverSession } from "@openclaw/whatsapp/api.js";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||||
|
import { toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { buildQaTarget } from "openclaw/plugin-sdk/qa-channel-protocol";
|
import { buildQaTarget } from "openclaw/plugin-sdk/qa-channel-protocol";
|
||||||
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
import type { QaRunnerCliRegistration } from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||||
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
||||||
@@ -130,7 +131,7 @@ export async function createWhatsAppQaTransportAdapter(
|
|||||||
}
|
}
|
||||||
})().catch((error: unknown) => {
|
})().catch((error: unknown) => {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
pollingError = error instanceof Error ? error : new Error(String(error));
|
pollingError = toStringifiedError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Qa Lab plugin module implements process tree cpu behavior.
|
// Qa Lab plugin module implements process tree cpu behavior.
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
import { parseStrictFiniteNumber, parseStrictInteger } from "openclaw/plugin-sdk/number-runtime";
|
import { parseStrictFiniteNumber, parseStrictInteger } from "openclaw/plugin-sdk/number-runtime";
|
||||||
|
import { isRecord as isPlainObject } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { resolveQaWindowsPowerShellExePath } from "./windows-system-tools.js";
|
import { resolveQaWindowsPowerShellExePath } from "./windows-system-tools.js";
|
||||||
|
|
||||||
type ProcessTreeSnapshot = {
|
type ProcessTreeSnapshot = {
|
||||||
@@ -11,10 +12,6 @@ type ProcessTreeSnapshot = {
|
|||||||
|
|
||||||
const PROCESS_TREE_SNAPSHOT_TIMEOUT_MS = 5_000;
|
const PROCESS_TREE_SNAPSHOT_TIMEOUT_MS = 5_000;
|
||||||
|
|
||||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePositiveInteger(value: unknown): number | null {
|
function parsePositiveInteger(value: unknown): number | null {
|
||||||
const parsed = parseStrictInteger(value);
|
const parsed = parseStrictInteger(value);
|
||||||
if (parsed === undefined || parsed <= 0) {
|
if (parsed === undefined || parsed <= 0) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:child_process";
|
import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:child_process";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
||||||
|
import { isRecord as isJsonRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
import {
|
import {
|
||||||
appendQaChildOutput,
|
appendQaChildOutput,
|
||||||
@@ -83,10 +84,6 @@ function parseBalancedJsonPayloadStart(text: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJsonRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStructuredDiagnosticJson(value: unknown) {
|
function isStructuredDiagnosticJson(value: unknown) {
|
||||||
if (!isJsonRecord(value)) {
|
if (!isJsonRecord(value)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -394,12 +394,8 @@ async function formatRuntimePatchMutationDiagnostics(params: {
|
|||||||
].join("; ");
|
].join("; ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function readNonEmptyString(value: unknown) {
|
|
||||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeToolCallId(value: unknown) {
|
function normalizeToolCallId(value: unknown) {
|
||||||
return readNonEmptyString(value);
|
return normalizeOptionalString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringifyTranscriptToolResult(value: unknown): string {
|
function stringifyTranscriptToolResult(value: unknown): string {
|
||||||
@@ -436,10 +432,10 @@ function extractTranscriptText(value: unknown): string {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const text =
|
const text =
|
||||||
readNonEmptyString(block.text) ??
|
normalizeOptionalString(block.text) ??
|
||||||
readNonEmptyString(block.content) ??
|
normalizeOptionalString(block.content) ??
|
||||||
readNonEmptyString(block.message) ??
|
normalizeOptionalString(block.message) ??
|
||||||
readNonEmptyString(block.error);
|
normalizeOptionalString(block.error);
|
||||||
if (text) {
|
if (text) {
|
||||||
parts.push(text);
|
parts.push(text);
|
||||||
}
|
}
|
||||||
@@ -457,11 +453,11 @@ function extractTranscriptToolCalls(
|
|||||||
if (!isRecord(block)) {
|
if (!isRecord(block)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const type = readNonEmptyString(block.type)?.toLowerCase();
|
const type = normalizeOptionalString(block.type)?.toLowerCase();
|
||||||
if (type !== "tool_use" && type !== "toolcall" && type !== "tool_call") {
|
if (type !== "tool_use" && type !== "toolcall" && type !== "tool_call") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const tool = readNonEmptyString(block.name);
|
const tool = normalizeOptionalString(block.name);
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -486,7 +482,8 @@ function extractTranscriptToolCalls(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const functionRecord = isRecord(call.function) ? call.function : undefined;
|
const functionRecord = isRecord(call.function) ? call.function : undefined;
|
||||||
const tool = readNonEmptyString(call.name) ?? readNonEmptyString(functionRecord?.name);
|
const tool =
|
||||||
|
normalizeOptionalString(call.name) ?? normalizeOptionalString(functionRecord?.name);
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -558,10 +555,10 @@ function extractTranscriptToolResults(
|
|||||||
): QaRuntimeToolFixtureTranscriptToolResult[] {
|
): QaRuntimeToolFixtureTranscriptToolResult[] {
|
||||||
const results: QaRuntimeToolFixtureTranscriptToolResult[] = [];
|
const results: QaRuntimeToolFixtureTranscriptToolResult[] = [];
|
||||||
const tool =
|
const tool =
|
||||||
readNonEmptyString(message.toolName) ??
|
normalizeOptionalString(message.toolName) ??
|
||||||
readNonEmptyString(message.tool_name) ??
|
normalizeOptionalString(message.tool_name) ??
|
||||||
readNonEmptyString(message.name) ??
|
normalizeOptionalString(message.name) ??
|
||||||
readNonEmptyString(message.tool);
|
normalizeOptionalString(message.tool);
|
||||||
if ((message.role === "tool" || message.role === "toolResult") && message.content !== undefined) {
|
if ((message.role === "tool" || message.role === "toolResult") && message.content !== undefined) {
|
||||||
const text = extractTranscriptText(message.content);
|
const text = extractTranscriptText(message.content);
|
||||||
const structuredFailure = isStructuredFailureToolResult({
|
const structuredFailure = isStructuredFailureToolResult({
|
||||||
@@ -598,7 +595,7 @@ function extractTranscriptToolResults(
|
|||||||
if (!isRecord(block)) {
|
if (!isRecord(block)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const type = readNonEmptyString(block.type)?.toLowerCase();
|
const type = normalizeOptionalString(block.type)?.toLowerCase();
|
||||||
if (type !== "tool_result" && type !== "toolresult" && type !== "tool_result_error") {
|
if (type !== "tool_result" && type !== "toolresult" && type !== "tool_result_error") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -611,10 +608,10 @@ function extractTranscriptToolResults(
|
|||||||
is_error: block.is_error,
|
is_error: block.is_error,
|
||||||
});
|
});
|
||||||
const blockTool =
|
const blockTool =
|
||||||
readNonEmptyString(block.toolName) ??
|
normalizeOptionalString(block.toolName) ??
|
||||||
readNonEmptyString(block.tool_name) ??
|
normalizeOptionalString(block.tool_name) ??
|
||||||
readNonEmptyString(block.name) ??
|
normalizeOptionalString(block.name) ??
|
||||||
readNonEmptyString(block.tool);
|
normalizeOptionalString(block.tool);
|
||||||
results.push({
|
results.push({
|
||||||
id:
|
id:
|
||||||
normalizeToolCallId(block.tool_use_id) ??
|
normalizeToolCallId(block.tool_use_id) ??
|
||||||
@@ -703,7 +700,7 @@ async function readSessionTranscriptBytes(
|
|||||||
) {
|
) {
|
||||||
const store = await readRawQaSessionStore(env);
|
const store = await readRawQaSessionStore(env);
|
||||||
const entry = store[sessionKey];
|
const entry = store[sessionKey];
|
||||||
const sessionId = readNonEmptyString(entry?.sessionId);
|
const sessionId = normalizeOptionalString(entry?.sessionId);
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
throw new Error(`session transcript entry not found for ${sessionKey}`);
|
throw new Error(`session transcript entry not found for ${sessionKey}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { normalizeOptionalString as normalizeQaConfigString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { QaCliBackendAuthMode } from "./gateway-child.js";
|
import type { QaCliBackendAuthMode } from "./gateway-child.js";
|
||||||
import { splitQaModelRef, type QaProviderMode } from "./model-selection.js";
|
import { splitQaModelRef, type QaProviderMode } from "./model-selection.js";
|
||||||
import {
|
import {
|
||||||
@@ -14,10 +15,6 @@ export type QaScenarioExecutionCell = {
|
|||||||
channel: string | null;
|
channel: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizeQaConfigString(value: unknown): string | undefined {
|
|
||||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveQaScenarioLaneChannels(params: {
|
function resolveQaScenarioLaneChannels(params: {
|
||||||
scenario: QaSeedScenario;
|
scenario: QaSeedScenario;
|
||||||
channelDriver: QaScorecardChannelDriver;
|
channelDriver: QaScorecardChannelDriver;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { toStringifiedError as asError } from "openclaw/plugin-sdk/error-runtime";
|
||||||
import { buildTimeoutAbortSignal } from "openclaw/plugin-sdk/extension-shared";
|
import { buildTimeoutAbortSignal } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
@@ -590,10 +591,6 @@ export class ReefInboxConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function asError(error: unknown): Error {
|
|
||||||
return error instanceof Error ? error : new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
function reefInboxCloseError(event: { code?: number; reason?: string }): Error {
|
function reefInboxCloseError(event: { code?: number; reason?: string }): Error {
|
||||||
const code = Number.isInteger(event.code) ? ` code=${event.code}` : "";
|
const code = Number.isInteger(event.code) ? ` code=${event.code}` : "";
|
||||||
const reason = event.reason?.trim() ? ` reason=${event.reason.trim()}` : "";
|
const reason = event.reason?.trim() ? ` reason=${event.reason.trim()}` : "";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
normalizeResolvedSecretInputString,
|
normalizeResolvedSecretInputString,
|
||||||
normalizeSecretInput,
|
normalizeSecretInput,
|
||||||
} from "openclaw/plugin-sdk/secret-input";
|
} from "openclaw/plugin-sdk/secret-input";
|
||||||
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
type SearxngPluginConfig = {
|
type SearxngPluginConfig = {
|
||||||
webSearch?: {
|
webSearch?: {
|
||||||
@@ -37,14 +38,6 @@ function readInlineEnvSecretRefValue(value: unknown, env: NodeJS.ProcessEnv): st
|
|||||||
return normalizeSecretInput(env[record.id]);
|
return normalizeSecretInput(env[record.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTrimmedString(value: unknown): string | undefined {
|
|
||||||
if (typeof value !== "string") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const trimmed = value.trim();
|
|
||||||
return trimmed || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeBaseUrl(value: string | undefined): string | undefined {
|
function normalizeBaseUrl(value: string | undefined): string | undefined {
|
||||||
return value?.replace(/\/+$/u, "") || undefined;
|
return value?.replace(/\/+$/u, "") || undefined;
|
||||||
}
|
}
|
||||||
@@ -78,9 +71,9 @@ export function resolveSearxngBaseUrl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveSearxngCategories(config?: OpenClawConfig): string | undefined {
|
export function resolveSearxngCategories(config?: OpenClawConfig): string | undefined {
|
||||||
return normalizeTrimmedString(resolveSearxngWebSearchConfig(config)?.categories);
|
return normalizeOptionalString(resolveSearxngWebSearchConfig(config)?.categories);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveSearxngLanguage(config?: OpenClawConfig): string | undefined {
|
export function resolveSearxngLanguage(config?: OpenClawConfig): string | undefined {
|
||||||
return normalizeTrimmedString(resolveSearxngWebSearchConfig(config)?.language);
|
return normalizeOptionalString(resolveSearxngWebSearchConfig(config)?.language);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import {
|
|||||||
renderMessagePresentationTableFallbackText,
|
renderMessagePresentationTableFallbackText,
|
||||||
type MessagePresentationTableBlock,
|
type MessagePresentationTableBlock,
|
||||||
} from "openclaw/plugin-sdk/interactive-runtime";
|
} from "openclaw/plugin-sdk/interactive-runtime";
|
||||||
import { asOptionalRecord, hasNonEmptyString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
asOptionalRecord,
|
||||||
|
readNonBlankString as readNonEmptyString,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { escapeSlackMrkdwn } from "./monitor/mrkdwn.js";
|
import { escapeSlackMrkdwn } from "./monitor/mrkdwn.js";
|
||||||
import { renderSlackMessagePresentationTableFallbackText } from "./presentation-fallback.js";
|
import { renderSlackMessagePresentationTableFallbackText } from "./presentation-fallback.js";
|
||||||
|
|
||||||
@@ -44,10 +47,6 @@ type ParsedSlackDataTable = {
|
|||||||
cellCharacterCount: number;
|
cellCharacterCount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function readNonEmptyString(value: unknown): string | undefined {
|
|
||||||
return hasNonEmptyString(value) ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function countCharacters(value: string): number {
|
function countCharacters(value: string): number {
|
||||||
return Array.from(value).length;
|
return Array.from(value).length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "openclaw/plugin-sdk/number-runtime";
|
} from "openclaw/plugin-sdk/number-runtime";
|
||||||
import { classifyTransientNetworkErrorCode } from "openclaw/plugin-sdk/retry-runtime";
|
import { classifyTransientNetworkErrorCode } from "openclaw/plugin-sdk/retry-runtime";
|
||||||
import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||||
|
import { normalizeOptionalString as normalizeThreadTs } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { formatSlackError } from "../errors.js";
|
import { formatSlackError } from "../errors.js";
|
||||||
import type { SlackMessageEvent } from "../types.js";
|
import type { SlackMessageEvent } from "../types.js";
|
||||||
import type { SlackIngressTurnLifecycle } from "./ingress.js";
|
import type { SlackIngressTurnLifecycle } from "./ingress.js";
|
||||||
@@ -30,11 +31,6 @@ type ThreadTsCacheEntry = {
|
|||||||
const DEFAULT_THREAD_TS_CACHE_TTL_MS = 60_000;
|
const DEFAULT_THREAD_TS_CACHE_TTL_MS = 60_000;
|
||||||
const DEFAULT_THREAD_TS_CACHE_MAX = 500;
|
const DEFAULT_THREAD_TS_CACHE_MAX = 500;
|
||||||
|
|
||||||
const normalizeThreadTs = (threadTs?: string | null) => {
|
|
||||||
const trimmed = threadTs?.trim();
|
|
||||||
return trimmed ? trimmed : undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const markAmbiguousThreadReply = (message: SlackMessageEvent): SlackMessageEvent => ({
|
const markAmbiguousThreadReply = (message: SlackMessageEvent): SlackMessageEvent => ({
|
||||||
...message,
|
...message,
|
||||||
_ambiguousThreadReply: true,
|
_ambiguousThreadReply: true,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { SpeechVoiceOption } from "openclaw/plugin-sdk/speech";
|
|||||||
import {
|
import {
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { resolveActiveTalkProviderConfig } from "openclaw/plugin-sdk/talk-config-runtime";
|
import { resolveActiveTalkProviderConfig } from "openclaw/plugin-sdk/talk-config-runtime";
|
||||||
import { definePluginEntry, type OpenClawPluginApi } from "./api.js";
|
import { definePluginEntry, type OpenClawPluginApi } from "./api.js";
|
||||||
@@ -95,11 +96,7 @@ function findVoice(voices: SpeechVoiceOption[], query: string): SpeechVoiceOptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
function asTrimmedString(value: unknown): string {
|
function asTrimmedString(value: unknown): string {
|
||||||
return typeof value === "string" ? value.trim() : "";
|
return normalizeOptionalString(value) ?? "";
|
||||||
}
|
|
||||||
|
|
||||||
function parsePositiveIntegerToken(value: unknown): number | undefined {
|
|
||||||
return parseStrictPositiveInteger(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveCommandLabel(channel: string): string {
|
function resolveCommandLabel(channel: string): string {
|
||||||
@@ -171,7 +168,7 @@ export default definePluginEntry({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action === "list") {
|
if (action === "list") {
|
||||||
const limit = parsePositiveIntegerToken(tokens[1]) ?? 12;
|
const limit = parseStrictPositiveInteger(tokens[1]) ?? 12;
|
||||||
try {
|
try {
|
||||||
const voices = await api.runtime.tts.listVoices({
|
const voices = await api.runtime.tts.listVoices({
|
||||||
provider: providerId,
|
provider: providerId,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
* without requiring a gateway restart.
|
* without requiring a gateway restart.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { filterStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import type { UrbitSSEClient } from "./urbit/sse-client.js";
|
import type { UrbitSSEClient } from "./urbit/sse-client.js";
|
||||||
|
|
||||||
/** Pending approval request stored for persistence */
|
/** Pending approval request stored for persistence */
|
||||||
@@ -112,10 +113,10 @@ function parseSettingsResponse(raw: unknown): TlonSettingsStore {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
groupChannels: Array.isArray(settings.groupChannels)
|
groupChannels: Array.isArray(settings.groupChannels)
|
||||||
? settings.groupChannels.filter((x): x is string => typeof x === "string")
|
? filterStringEntries(settings.groupChannels)
|
||||||
: undefined,
|
: undefined,
|
||||||
dmAllowlist: Array.isArray(settings.dmAllowlist)
|
dmAllowlist: Array.isArray(settings.dmAllowlist)
|
||||||
? settings.dmAllowlist.filter((x): x is string => typeof x === "string")
|
? filterStringEntries(settings.dmAllowlist)
|
||||||
: undefined,
|
: undefined,
|
||||||
autoDiscoverChannels:
|
autoDiscoverChannels:
|
||||||
typeof settings.autoDiscoverChannels === "boolean"
|
typeof settings.autoDiscoverChannels === "boolean"
|
||||||
@@ -129,11 +130,11 @@ function parseSettingsResponse(raw: unknown): TlonSettingsStore {
|
|||||||
? settings.autoAcceptGroupInvites
|
? settings.autoAcceptGroupInvites
|
||||||
: undefined,
|
: undefined,
|
||||||
groupInviteAllowlist: Array.isArray(settings.groupInviteAllowlist)
|
groupInviteAllowlist: Array.isArray(settings.groupInviteAllowlist)
|
||||||
? settings.groupInviteAllowlist.filter((x): x is string => typeof x === "string")
|
? filterStringEntries(settings.groupInviteAllowlist)
|
||||||
: undefined,
|
: undefined,
|
||||||
channelRules: parseChannelRules(settings.channelRules),
|
channelRules: parseChannelRules(settings.channelRules),
|
||||||
defaultAuthorizedShips: Array.isArray(settings.defaultAuthorizedShips)
|
defaultAuthorizedShips: Array.isArray(settings.defaultAuthorizedShips)
|
||||||
? settings.defaultAuthorizedShips.filter((x): x is string => typeof x === "string")
|
? filterStringEntries(settings.defaultAuthorizedShips)
|
||||||
: undefined,
|
: undefined,
|
||||||
ownerShip: typeof settings.ownerShip === "string" ? settings.ownerShip : undefined,
|
ownerShip: typeof settings.ownerShip === "string" ? settings.ownerShip : undefined,
|
||||||
pendingApprovals: parsePendingApprovals(settings.pendingApprovals),
|
pendingApprovals: parsePendingApprovals(settings.pendingApprovals),
|
||||||
@@ -242,14 +243,10 @@ function applySettingsUpdate(
|
|||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "groupChannels":
|
case "groupChannels":
|
||||||
next.groupChannels = Array.isArray(value)
|
next.groupChannels = Array.isArray(value) ? filterStringEntries(value) : undefined;
|
||||||
? value.filter((x): x is string => typeof x === "string")
|
|
||||||
: undefined;
|
|
||||||
break;
|
break;
|
||||||
case "dmAllowlist":
|
case "dmAllowlist":
|
||||||
next.dmAllowlist = Array.isArray(value)
|
next.dmAllowlist = Array.isArray(value) ? filterStringEntries(value) : undefined;
|
||||||
? value.filter((x): x is string => typeof x === "string")
|
|
||||||
: undefined;
|
|
||||||
break;
|
break;
|
||||||
case "autoDiscoverChannels":
|
case "autoDiscoverChannels":
|
||||||
next.autoDiscoverChannels = typeof value === "boolean" ? value : undefined;
|
next.autoDiscoverChannels = typeof value === "boolean" ? value : undefined;
|
||||||
@@ -264,17 +261,13 @@ function applySettingsUpdate(
|
|||||||
next.autoAcceptGroupInvites = typeof value === "boolean" ? value : undefined;
|
next.autoAcceptGroupInvites = typeof value === "boolean" ? value : undefined;
|
||||||
break;
|
break;
|
||||||
case "groupInviteAllowlist":
|
case "groupInviteAllowlist":
|
||||||
next.groupInviteAllowlist = Array.isArray(value)
|
next.groupInviteAllowlist = Array.isArray(value) ? filterStringEntries(value) : undefined;
|
||||||
? value.filter((x): x is string => typeof x === "string")
|
|
||||||
: undefined;
|
|
||||||
break;
|
break;
|
||||||
case "channelRules":
|
case "channelRules":
|
||||||
next.channelRules = parseChannelRules(value);
|
next.channelRules = parseChannelRules(value);
|
||||||
break;
|
break;
|
||||||
case "defaultAuthorizedShips":
|
case "defaultAuthorizedShips":
|
||||||
next.defaultAuthorizedShips = Array.isArray(value)
|
next.defaultAuthorizedShips = Array.isArray(value) ? filterStringEntries(value) : undefined;
|
||||||
? value.filter((x): x is string => typeof x === "string")
|
|
||||||
: undefined;
|
|
||||||
break;
|
break;
|
||||||
case "ownerShip":
|
case "ownerShip":
|
||||||
next.ownerShip = typeof value === "string" ? value : undefined;
|
next.ownerShip = typeof value === "string" ? value : undefined;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type {
|
|||||||
SpeechSynthesisRequest,
|
SpeechSynthesisRequest,
|
||||||
SpeechTelephonySynthesisRequest,
|
SpeechTelephonySynthesisRequest,
|
||||||
} from "openclaw/plugin-sdk/speech-core";
|
} from "openclaw/plugin-sdk/speech-core";
|
||||||
import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import { asOptionalRecord, filterStringRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { tempWorkspace, resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
import { tempWorkspace, resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
||||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||||
|
|
||||||
@@ -41,20 +41,6 @@ function asStringArray(value: unknown): string[] | undefined {
|
|||||||
return Array.isArray(value) && value.every((v) => typeof v === "string") ? value : undefined;
|
return Array.isArray(value) && value.every((v) => typeof v === "string") ? value : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readStringRecord(value: unknown): Record<string, string> | undefined {
|
|
||||||
const obj = asOptionalRecord(value);
|
|
||||||
if (!obj) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const result: Record<string, string> = {};
|
|
||||||
for (const [k, v] of Object.entries(obj)) {
|
|
||||||
if (typeof v === "string") {
|
|
||||||
result[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Object.keys(result).length > 0 ? result : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeOutputFormat(value: unknown): OutputFormat {
|
function normalizeOutputFormat(value: unknown): OutputFormat {
|
||||||
if (typeof value !== "string") {
|
if (typeof value !== "string") {
|
||||||
return "mp3";
|
return "mp3";
|
||||||
@@ -82,7 +68,7 @@ function getConfig(cfg: SpeechProviderConfig): CliConfig | null {
|
|||||||
outputFormat: normalizeOutputFormat(cfg.outputFormat),
|
outputFormat: normalizeOutputFormat(cfg.outputFormat),
|
||||||
timeoutMs: typeof cfg.timeoutMs === "number" ? cfg.timeoutMs : DEFAULT_TIMEOUT_MS,
|
timeoutMs: typeof cfg.timeoutMs === "number" ? cfg.timeoutMs : DEFAULT_TIMEOUT_MS,
|
||||||
cwd: typeof cfg.cwd === "string" ? cfg.cwd : undefined,
|
cwd: typeof cfg.cwd === "string" ? cfg.cwd : undefined,
|
||||||
env: readStringRecord(cfg.env),
|
env: filterStringRecord(cfg.env),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ErrorCodes, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
|
|||||||
import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
|
import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
|
||||||
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
||||||
import {
|
import {
|
||||||
asNonArrayRecord,
|
asNonArrayRecord as asParamRecord,
|
||||||
asOptionalRecord,
|
asOptionalRecord,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -88,10 +88,6 @@ const VoiceCallToolSchema = Type.Union([
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function asParamRecord(params: unknown): Record<string, unknown> {
|
|
||||||
return asNonArrayRecord(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCliOnlyProcess(): boolean {
|
function isCliOnlyProcess(): boolean {
|
||||||
return process.env.OPENCLAW_CLI === "1" && !process.argv.slice(2).includes("gateway");
|
return process.env.OPENCLAW_CLI === "1" && !process.argv.slice(2).includes("gateway");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ import {
|
|||||||
readStringField,
|
readStringField,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
|
|
||||||
/** Read finite numeric config values. */
|
|
||||||
function getNumber(obj: Record<string, unknown> | undefined, key: string): number | undefined {
|
|
||||||
return asFiniteNumber(obj?.[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Merge legacy provider-specific values into the canonical providers map. */
|
/** Merge legacy provider-specific values into the canonical providers map. */
|
||||||
function mergeProviderConfig(
|
function mergeProviderConfig(
|
||||||
providersValue: unknown,
|
providersValue: unknown,
|
||||||
@@ -55,11 +50,11 @@ export function migrateVoiceCallLegacyConfigInput(params: {
|
|||||||
if (streamingSttModel) {
|
if (streamingSttModel) {
|
||||||
legacyStreamingOpenAICompat.model = streamingSttModel;
|
legacyStreamingOpenAICompat.model = streamingSttModel;
|
||||||
}
|
}
|
||||||
const streamingSilenceDurationMs = getNumber(streaming, "silenceDurationMs");
|
const streamingSilenceDurationMs = asFiniteNumber(streaming?.silenceDurationMs);
|
||||||
if (streamingSilenceDurationMs !== undefined) {
|
if (streamingSilenceDurationMs !== undefined) {
|
||||||
legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
|
legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
|
||||||
}
|
}
|
||||||
const streamingVadThreshold = getNumber(streaming, "vadThreshold");
|
const streamingVadThreshold = asFiniteNumber(streaming?.vadThreshold);
|
||||||
if (streamingVadThreshold !== undefined) {
|
if (streamingVadThreshold !== undefined) {
|
||||||
legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
|
legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
|
||||||
}
|
}
|
||||||
@@ -138,14 +133,14 @@ export function migrateVoiceCallLegacyConfigInput(params: {
|
|||||||
`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`,
|
`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (getNumber(streaming, "silenceDurationMs") !== undefined) {
|
if (asFiniteNumber(streaming?.silenceDurationMs) !== undefined) {
|
||||||
changes.push(
|
changes.push(
|
||||||
`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`,
|
`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`,
|
||||||
);
|
);
|
||||||
} else if (typeof streaming?.silenceDurationMs === "number") {
|
} else if (typeof streaming?.silenceDurationMs === "number") {
|
||||||
changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
|
changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
|
||||||
}
|
}
|
||||||
if (getNumber(streaming, "vadThreshold") !== undefined) {
|
if (asFiniteNumber(streaming?.vadThreshold) !== undefined) {
|
||||||
changes.push(
|
changes.push(
|
||||||
`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`,
|
`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "openclaw/plugin-sdk/model-session-runtime";
|
} from "openclaw/plugin-sdk/model-session-runtime";
|
||||||
import {
|
import {
|
||||||
isRecord,
|
isRecord,
|
||||||
|
filterStringEntries,
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeStringEntries,
|
normalizeStringEntries,
|
||||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
@@ -66,7 +67,7 @@ function readExplicitToolsAllow(value: unknown): string[] | undefined {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return allow.filter((entry): entry is string => typeof entry === "string");
|
return filterStringEntries(allow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveVoiceAgentToolsAllow(config: CoreConfig, agentId: string): string[] | undefined {
|
function resolveVoiceAgentToolsAllow(config: CoreConfig, agentId: string): string[] | undefined {
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import {
|
|||||||
} from "openclaw/plugin-sdk/channel-outbound";
|
} from "openclaw/plugin-sdk/channel-outbound";
|
||||||
import { buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
|
import { buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
|
||||||
import type { FinalizedMsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
import type { FinalizedMsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
||||||
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
import {
|
||||||
|
normalizeOptionalString,
|
||||||
|
normalizeStringEntries,
|
||||||
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||||
import { requireWhatsAppInboundAdmission } from "../../inbound/admission.js";
|
import { requireWhatsAppInboundAdmission } from "../../inbound/admission.js";
|
||||||
import type { AdmittedWebInboundMessage } from "../../inbound/types.js";
|
import type { AdmittedWebInboundMessage } from "../../inbound/types.js";
|
||||||
import {
|
import {
|
||||||
@@ -147,7 +150,7 @@ function isWhatsAppVisibleDeliveryError(error: unknown): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readTrimmedString(value: unknown): string {
|
function readTrimmedString(value: unknown): string {
|
||||||
return typeof value === "string" ? value.trim() : "";
|
return normalizeOptionalString(value) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(
|
function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user