Plugins: remove shared extension boundary debt
This commit is contained in:
parent
126839380c
commit
5b7b5529f1
@ -2,8 +2,8 @@ import { spawn } from "node:child_process";
|
|||||||
import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { createWindowsCmdShimFixture } from "../../../shared/windows-cmd-shim-test-fixtures.js";
|
|
||||||
import {
|
import {
|
||||||
resolveSpawnCommand,
|
resolveSpawnCommand,
|
||||||
spawnAndCollect,
|
spawnAndCollect,
|
||||||
|
|||||||
@ -19,8 +19,8 @@ import {
|
|||||||
listResolvedDirectoryGroupEntriesFromMapKeys,
|
listResolvedDirectoryGroupEntriesFromMapKeys,
|
||||||
listResolvedDirectoryUserEntriesFromAllowFrom,
|
listResolvedDirectoryUserEntriesFromAllowFrom,
|
||||||
} from "openclaw/plugin-sdk/directory-runtime";
|
} from "openclaw/plugin-sdk/directory-runtime";
|
||||||
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
|
||||||
import {
|
import {
|
||||||
buildComputedAccountStatusSnapshot,
|
buildComputedAccountStatusSnapshot,
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { installCommonResolveTargetErrorCases } from "../../shared/resolve-target-test-helpers.js";
|
|
||||||
|
|
||||||
const runtimeMocks = vi.hoisted(() => ({
|
const runtimeMocks = vi.hoisted(() => ({
|
||||||
chunkMarkdownText: vi.fn((text: string) => [text]),
|
chunkMarkdownText: vi.fn((text: string) => [text]),
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import {
|
|||||||
resolveOutboundSendDep,
|
resolveOutboundSendDep,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/core";
|
import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/core";
|
||||||
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||||
import { type RoutePeer } from "openclaw/plugin-sdk/routing";
|
import { type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
|
||||||
import {
|
import {
|
||||||
collectStatusIssuesFromLastError,
|
collectStatusIssuesFromLastError,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
createTextPairingAdapter,
|
createTextPairingAdapter,
|
||||||
listResolvedDirectoryEntriesFromSources,
|
listResolvedDirectoryEntriesFromSources,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { runStoppablePassiveMonitor } from "../../shared/passive-monitor.js";
|
import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import {
|
import {
|
||||||
listIrcAccountIds,
|
listIrcAccountIds,
|
||||||
resolveDefaultIrcAccountId,
|
resolveDefaultIrcAccountId,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js";
|
|
||||||
import {
|
import {
|
||||||
BlockStreamingCoalesceSchema,
|
BlockStreamingCoalesceSchema,
|
||||||
DmConfigSchema,
|
DmConfigSchema,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { resolveLoggerBackedRuntime } from "../../shared/runtime.js";
|
import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { resolveIrcAccount } from "./accounts.js";
|
import { resolveIrcAccount } from "./accounts.js";
|
||||||
import { connectIrcClient, type IrcClient } from "./client.js";
|
import { connectIrcClient, type IrcClient } from "./client.js";
|
||||||
import { buildIrcConnectOptions } from "./connect-options.js";
|
import { buildIrcConnectOptions } from "./connect-options.js";
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
type PathEnvKey = "PATH" | "Path" | "PATHEXT" | "Pathext";
|
type PathEnvKey = "PATH" | "Path" | "PATHEXT" | "Pathext";
|
||||||
|
|
||||||
|
export { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing";
|
||||||
|
|
||||||
const PATH_ENV_KEYS = ["PATH", "Path", "PATHEXT", "Pathext"] as const;
|
const PATH_ENV_KEYS = ["PATH", "Path", "PATHEXT", "Pathext"] as const;
|
||||||
|
|
||||||
export type PlatformPathEnvSnapshot = {
|
export type PlatformPathEnvSnapshot = {
|
||||||
@ -40,4 +42,3 @@ export function restorePlatformPathEnv(snapshot: PlatformPathEnvSnapshot): void
|
|||||||
process.env[key] = value;
|
process.env[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export { createWindowsCmdShimFixture } from "../../shared/windows-cmd-shim-test-fixtures.js";
|
|
||||||
|
|||||||
@ -15,8 +15,8 @@ import {
|
|||||||
createTextPairingAdapter,
|
createTextPairingAdapter,
|
||||||
listResolvedDirectoryEntriesFromSources,
|
listResolvedDirectoryEntriesFromSources,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
|
import { buildTrafficStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
||||||
import { buildTrafficStatusSummary } from "../../shared/channel-status-summary.js";
|
|
||||||
import {
|
import {
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
buildProbeChannelStatusSummary,
|
buildProbeChannelStatusSummary,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { createDeferred } from "../../../shared/deferred.js";
|
|
||||||
import { DEFAULT_SEND_GAP_MS, enqueueSend } from "./send-queue.js";
|
import { DEFAULT_SEND_GAP_MS, enqueueSend } from "./send-queue.js";
|
||||||
|
|
||||||
describe("enqueueSend", () => {
|
describe("enqueueSend", () => {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
createScopedAccountReplyToModeResolver,
|
createScopedAccountReplyToModeResolver,
|
||||||
type ChannelMessageToolDiscovery,
|
type ChannelMessageToolDiscovery,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { MattermostConfigSchema } from "./config-schema.js";
|
import { MattermostConfigSchema } from "./config-schema.js";
|
||||||
import { resolveMattermostGroupRequireMention } from "./group-mentions.js";
|
import { resolveMattermostGroupRequireMention } from "./group-mentions.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js";
|
|
||||||
import {
|
import {
|
||||||
BlockStreamingCoalesceSchema,
|
BlockStreamingCoalesceSchema,
|
||||||
DmPolicySchema,
|
DmPolicySchema,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
createLoggedPairingApprovalNotifier,
|
createLoggedPairingApprovalNotifier,
|
||||||
createPairingPrefixStripper,
|
createPairingPrefixStripper,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { runStoppablePassiveMonitor } from "../../shared/passive-monitor.js";
|
import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import {
|
import {
|
||||||
buildBaseChannelStatusSummary,
|
buildBaseChannelStatusSummary,
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js";
|
|
||||||
import {
|
import {
|
||||||
BlockStreamingCoalesceSchema,
|
BlockStreamingCoalesceSchema,
|
||||||
DmConfigSchema,
|
DmConfigSchema,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import { resolveLoggerBackedRuntime } from "../../shared/runtime.js";
|
import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import {
|
import {
|
||||||
type RuntimeEnv,
|
type RuntimeEnv,
|
||||||
isRequestBodyLimitError,
|
isRequestBodyLimitError,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
|
|||||||
import {
|
import {
|
||||||
buildPassiveChannelStatusSummary,
|
buildPassiveChannelStatusSummary,
|
||||||
buildTrafficStatusSummary,
|
buildTrafficStatusSummary,
|
||||||
} from "../../shared/channel-status-summary.js";
|
} from "openclaw/plugin-sdk/extension-shared";
|
||||||
import {
|
import {
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
collectStatusIssuesFromLastError,
|
collectStatusIssuesFromLastError,
|
||||||
|
|||||||
@ -16,8 +16,8 @@ import {
|
|||||||
resolveTargetsWithOptionalToken,
|
resolveTargetsWithOptionalToken,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core";
|
import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core";
|
||||||
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
|
||||||
import {
|
import {
|
||||||
listEnabledSlackAccounts,
|
listEnabledSlackAccounts,
|
||||||
resolveSlackAccount,
|
resolveSlackAccount,
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* This is the primary entry point for the Twitch channel integration.
|
* This is the primary entry point for the Twitch channel integration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import type { OpenClawConfig } from "../api.js";
|
import type { OpenClawConfig } from "../api.js";
|
||||||
import { buildChannelConfigSchema } from "../api.js";
|
import { buildChannelConfigSchema } from "../api.js";
|
||||||
import { twitchMessageActions } from "./actions.js";
|
import { twitchMessageActions } from "./actions.js";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import { installCommonResolveTargetErrorCases } from "../../shared/resolve-target-test-helpers.js";
|
|
||||||
|
|
||||||
vi.mock("openclaw/plugin-sdk/whatsapp", async () => {
|
vi.mock("openclaw/plugin-sdk/whatsapp", async () => {
|
||||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/whatsapp")>(
|
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/whatsapp")>(
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { coerceStatusIssueAccountId, readStatusIssueFields } from "../../shared/status-issues.js";
|
import {
|
||||||
|
coerceStatusIssueAccountId,
|
||||||
|
readStatusIssueFields,
|
||||||
|
} from "openclaw/plugin-sdk/extension-shared";
|
||||||
import type { ChannelAccountSnapshot, ChannelStatusIssue } from "./runtime-api.js";
|
import type { ChannelAccountSnapshot, ChannelStatusIssue } from "./runtime-api.js";
|
||||||
|
|
||||||
const ZALO_STATUS_FIELDS = ["accountId", "enabled", "configured", "dmPolicy"] as const;
|
const ZALO_STATUS_FIELDS = ["accountId", "enabled", "configured", "dmPolicy"] as const;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
createStaticReplyToModeResolver,
|
createStaticReplyToModeResolver,
|
||||||
createTextPairingAdapter,
|
createTextPairingAdapter,
|
||||||
} from "openclaw/plugin-sdk/channel-runtime";
|
} from "openclaw/plugin-sdk/channel-runtime";
|
||||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import type {
|
import type {
|
||||||
ChannelAccountSnapshot,
|
ChannelAccountSnapshot,
|
||||||
ChannelDirectoryEntry,
|
ChannelDirectoryEntry,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {
|
|||||||
DM_GROUP_ACCESS_REASON,
|
DM_GROUP_ACCESS_REASON,
|
||||||
resolveDmGroupAccessWithLists,
|
resolveDmGroupAccessWithLists,
|
||||||
} from "openclaw/plugin-sdk/channel-policy";
|
} from "openclaw/plugin-sdk/channel-policy";
|
||||||
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
||||||
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
||||||
import {
|
import {
|
||||||
DEFAULT_GROUP_HISTORY_LIMIT,
|
DEFAULT_GROUP_HISTORY_LIMIT,
|
||||||
@ -10,7 +11,6 @@ import {
|
|||||||
clearHistoryEntriesIfEnabled,
|
clearHistoryEntriesIfEnabled,
|
||||||
recordPendingHistoryEntryIfEnabled,
|
recordPendingHistoryEntryIfEnabled,
|
||||||
} from "openclaw/plugin-sdk/reply-history";
|
} from "openclaw/plugin-sdk/reply-history";
|
||||||
import { createDeferred } from "../../shared/deferred.js";
|
|
||||||
import type {
|
import type {
|
||||||
MarkdownTableMode,
|
MarkdownTableMode,
|
||||||
OpenClawConfig,
|
OpenClawConfig,
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { coerceStatusIssueAccountId, readStatusIssueFields } from "../../shared/status-issues.js";
|
import {
|
||||||
|
coerceStatusIssueAccountId,
|
||||||
|
readStatusIssueFields,
|
||||||
|
} from "openclaw/plugin-sdk/extension-shared";
|
||||||
import type { ChannelAccountSnapshot, ChannelStatusIssue } from "../runtime-api.js";
|
import type { ChannelAccountSnapshot, ChannelStatusIssue } from "../runtime-api.js";
|
||||||
|
|
||||||
const ZALOUSER_STATUS_FIELDS = [
|
const ZALOUSER_STATUS_FIELDS = [
|
||||||
|
|||||||
@ -333,6 +333,10 @@
|
|||||||
"types": "./dist/plugin-sdk/diffs.d.ts",
|
"types": "./dist/plugin-sdk/diffs.d.ts",
|
||||||
"default": "./dist/plugin-sdk/diffs.js"
|
"default": "./dist/plugin-sdk/diffs.js"
|
||||||
},
|
},
|
||||||
|
"./plugin-sdk/extension-shared": {
|
||||||
|
"types": "./dist/plugin-sdk/extension-shared.d.ts",
|
||||||
|
"default": "./dist/plugin-sdk/extension-shared.js"
|
||||||
|
},
|
||||||
"./plugin-sdk/channel-config-helpers": {
|
"./plugin-sdk/channel-config-helpers": {
|
||||||
"types": "./dist/plugin-sdk/channel-config-helpers.d.ts",
|
"types": "./dist/plugin-sdk/channel-config-helpers.d.ts",
|
||||||
"default": "./dist/plugin-sdk/channel-config-helpers.js"
|
"default": "./dist/plugin-sdk/channel-config-helpers.js"
|
||||||
|
|||||||
@ -73,6 +73,7 @@
|
|||||||
"device-pair",
|
"device-pair",
|
||||||
"diagnostics-otel",
|
"diagnostics-otel",
|
||||||
"diffs",
|
"diffs",
|
||||||
|
"extension-shared",
|
||||||
"channel-config-helpers",
|
"channel-config-helpers",
|
||||||
"channel-config-schema",
|
"channel-config-schema",
|
||||||
"channel-lifecycle",
|
"channel-lifecycle",
|
||||||
|
|||||||
135
src/plugin-sdk/extension-shared.ts
Normal file
135
src/plugin-sdk/extension-shared.ts
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import type { z } from "zod";
|
||||||
|
import { runPassiveAccountLifecycle } from "./channel-runtime.js";
|
||||||
|
import { createLoggerBackedRuntime } from "./runtime.js";
|
||||||
|
|
||||||
|
type PassiveChannelStatusSnapshot = {
|
||||||
|
configured?: boolean;
|
||||||
|
running?: boolean;
|
||||||
|
lastStartAt?: number | null;
|
||||||
|
lastStopAt?: number | null;
|
||||||
|
lastError?: string | null;
|
||||||
|
probe?: unknown;
|
||||||
|
lastProbeAt?: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TrafficStatusSnapshot = {
|
||||||
|
lastInboundAt?: number | null;
|
||||||
|
lastOutboundAt?: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StoppableMonitor = {
|
||||||
|
stop: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequireOpenAllowFromFn = (params: {
|
||||||
|
policy?: string;
|
||||||
|
allowFrom?: Array<string | number>;
|
||||||
|
ctx: z.RefinementCtx;
|
||||||
|
path: Array<string | number>;
|
||||||
|
message: string;
|
||||||
|
}) => void;
|
||||||
|
|
||||||
|
export function buildPassiveChannelStatusSummary<TExtra extends object>(
|
||||||
|
snapshot: PassiveChannelStatusSnapshot,
|
||||||
|
extra?: TExtra,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
configured: snapshot.configured ?? false,
|
||||||
|
...(extra ?? ({} as TExtra)),
|
||||||
|
running: snapshot.running ?? false,
|
||||||
|
lastStartAt: snapshot.lastStartAt ?? null,
|
||||||
|
lastStopAt: snapshot.lastStopAt ?? null,
|
||||||
|
lastError: snapshot.lastError ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildPassiveProbedChannelStatusSummary<TExtra extends object>(
|
||||||
|
snapshot: PassiveChannelStatusSnapshot,
|
||||||
|
extra?: TExtra,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...buildPassiveChannelStatusSummary(snapshot, extra),
|
||||||
|
probe: snapshot.probe,
|
||||||
|
lastProbeAt: snapshot.lastProbeAt ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTrafficStatusSummary<TSnapshot extends TrafficStatusSnapshot>(
|
||||||
|
snapshot?: TSnapshot | null,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
lastInboundAt: snapshot?.lastInboundAt ?? null,
|
||||||
|
lastOutboundAt: snapshot?.lastOutboundAt ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runStoppablePassiveMonitor<TMonitor extends StoppableMonitor>(params: {
|
||||||
|
abortSignal: AbortSignal;
|
||||||
|
start: () => Promise<TMonitor>;
|
||||||
|
}): Promise<void> {
|
||||||
|
await runPassiveAccountLifecycle({
|
||||||
|
abortSignal: params.abortSignal,
|
||||||
|
start: params.start,
|
||||||
|
stop: async (monitor) => {
|
||||||
|
monitor.stop();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveLoggerBackedRuntime<TRuntime>(
|
||||||
|
runtime: TRuntime | undefined,
|
||||||
|
logger: Parameters<typeof createLoggerBackedRuntime>[0]["logger"],
|
||||||
|
): TRuntime {
|
||||||
|
return (
|
||||||
|
runtime ??
|
||||||
|
(createLoggerBackedRuntime({
|
||||||
|
logger,
|
||||||
|
exitError: () => new Error("Runtime exit not available"),
|
||||||
|
}) as TRuntime)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requireChannelOpenAllowFrom(params: {
|
||||||
|
channel: string;
|
||||||
|
policy?: string;
|
||||||
|
allowFrom?: Array<string | number>;
|
||||||
|
ctx: z.RefinementCtx;
|
||||||
|
requireOpenAllowFrom: RequireOpenAllowFromFn;
|
||||||
|
}) {
|
||||||
|
params.requireOpenAllowFrom({
|
||||||
|
policy: params.policy,
|
||||||
|
allowFrom: params.allowFrom,
|
||||||
|
ctx: params.ctx,
|
||||||
|
path: ["allowFrom"],
|
||||||
|
message: `channels.${params.channel}.dmPolicy="open" requires channels.${params.channel}.allowFrom to include "*"`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readStatusIssueFields<TField extends string>(
|
||||||
|
value: unknown,
|
||||||
|
fields: readonly TField[],
|
||||||
|
): Record<TField, unknown> | null {
|
||||||
|
if (!value || typeof value !== "object") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const record = value as Record<string, unknown>;
|
||||||
|
const result = {} as Record<TField, unknown>;
|
||||||
|
for (const field of fields) {
|
||||||
|
result[field] = record[field];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function coerceStatusIssueAccountId(value: unknown): string | undefined {
|
||||||
|
return typeof value === "string" ? value : typeof value === "number" ? String(value) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDeferred<T>() {
|
||||||
|
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||||
|
let reject!: (reason?: unknown) => void;
|
||||||
|
const promise = new Promise<T>((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
return { promise, resolve, reject };
|
||||||
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { expect, it } from "vitest";
|
||||||
|
|
||||||
// Narrow public testing surface for plugin authors.
|
// Narrow public testing surface for plugin authors.
|
||||||
// Keep this list additive and limited to helpers we are willing to support.
|
// Keep this list additive and limited to helpers we are willing to support.
|
||||||
|
|
||||||
@ -7,3 +11,79 @@ export type { OpenClawConfig } from "../config/config.js";
|
|||||||
export type { PluginRuntime } from "../plugins/runtime/types.js";
|
export type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||||
export type { RuntimeEnv } from "../runtime.js";
|
export type { RuntimeEnv } from "../runtime.js";
|
||||||
export type { MockFn } from "../test-utils/vitest-mock-fn.js";
|
export type { MockFn } from "../test-utils/vitest-mock-fn.js";
|
||||||
|
|
||||||
|
export async function createWindowsCmdShimFixture(params: {
|
||||||
|
shimPath: string;
|
||||||
|
scriptPath: string;
|
||||||
|
shimLine: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
await fs.mkdir(path.dirname(params.scriptPath), { recursive: true });
|
||||||
|
await fs.mkdir(path.dirname(params.shimPath), { recursive: true });
|
||||||
|
await fs.writeFile(params.scriptPath, "module.exports = {};\n", "utf8");
|
||||||
|
await fs.writeFile(params.shimPath, `@echo off\r\n${params.shimLine}\r\n`, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResolveTargetMode = "explicit" | "implicit" | "heartbeat";
|
||||||
|
|
||||||
|
type ResolveTargetResult = {
|
||||||
|
ok: boolean;
|
||||||
|
to?: string;
|
||||||
|
error?: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ResolveTargetFn = (params: {
|
||||||
|
to?: string;
|
||||||
|
mode: ResolveTargetMode;
|
||||||
|
allowFrom: string[];
|
||||||
|
}) => ResolveTargetResult;
|
||||||
|
|
||||||
|
export function installCommonResolveTargetErrorCases(params: {
|
||||||
|
resolveTarget: ResolveTargetFn;
|
||||||
|
implicitAllowFrom: string[];
|
||||||
|
}) {
|
||||||
|
const { resolveTarget, implicitAllowFrom } = params;
|
||||||
|
|
||||||
|
it("should error on normalization failure with allowlist (implicit mode)", () => {
|
||||||
|
const result = resolveTarget({
|
||||||
|
to: "invalid-target",
|
||||||
|
mode: "implicit",
|
||||||
|
allowFrom: implicitAllowFrom,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
expect(result.error).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should error when no target provided with allowlist", () => {
|
||||||
|
const result = resolveTarget({
|
||||||
|
to: undefined,
|
||||||
|
mode: "implicit",
|
||||||
|
allowFrom: implicitAllowFrom,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
expect(result.error).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should error when no target and no allowlist", () => {
|
||||||
|
const result = resolveTarget({
|
||||||
|
to: undefined,
|
||||||
|
mode: "explicit",
|
||||||
|
allowFrom: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
expect(result.error).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle whitespace-only target", () => {
|
||||||
|
const result = resolveTarget({
|
||||||
|
to: " ",
|
||||||
|
mode: "explicit",
|
||||||
|
allowFrom: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
expect(result.error).toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,146 +1 @@
|
|||||||
[
|
[]
|
||||||
{
|
|
||||||
"file": "extensions/googlechat/src/channel.ts",
|
|
||||||
"line": 23,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/imessage/src/channel.ts",
|
|
||||||
"line": 9,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/irc/src/channel.ts",
|
|
||||||
"line": 17,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/passive-monitor.js",
|
|
||||||
"resolvedPath": "extensions/shared/passive-monitor.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/irc/src/config-schema.ts",
|
|
||||||
"line": 2,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/config-schema-helpers.js",
|
|
||||||
"resolvedPath": "extensions/shared/config-schema-helpers.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/irc/src/monitor.ts",
|
|
||||||
"line": 1,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/runtime.js",
|
|
||||||
"resolvedPath": "extensions/shared/runtime.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/matrix/src/channel.ts",
|
|
||||||
"line": 19,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/mattermost/src/channel.ts",
|
|
||||||
"line": 15,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/mattermost/src/config-schema.ts",
|
|
||||||
"line": 2,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/config-schema-helpers.js",
|
|
||||||
"resolvedPath": "extensions/shared/config-schema-helpers.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/nextcloud-talk/src/channel.ts",
|
|
||||||
"line": 13,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/passive-monitor.js",
|
|
||||||
"resolvedPath": "extensions/shared/passive-monitor.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/nextcloud-talk/src/config-schema.ts",
|
|
||||||
"line": 2,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/config-schema-helpers.js",
|
|
||||||
"resolvedPath": "extensions/shared/config-schema-helpers.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/nextcloud-talk/src/monitor.ts",
|
|
||||||
"line": 3,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/runtime.js",
|
|
||||||
"resolvedPath": "extensions/shared/runtime.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/nostr/src/channel.ts",
|
|
||||||
"line": 9,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/slack/src/channel.ts",
|
|
||||||
"line": 20,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/twitch/src/plugin.ts",
|
|
||||||
"line": 8,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/zalo/src/status-issues.ts",
|
|
||||||
"line": 1,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/status-issues.js",
|
|
||||||
"resolvedPath": "extensions/shared/status-issues.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/zalouser/src/channel.ts",
|
|
||||||
"line": 10,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/channel-status-summary.js",
|
|
||||||
"resolvedPath": "extensions/shared/channel-status-summary.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/zalouser/src/monitor.ts",
|
|
||||||
"line": 13,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/deferred.js",
|
|
||||||
"resolvedPath": "extensions/shared/deferred.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "extensions/zalouser/src/status-issues.ts",
|
|
||||||
"line": 1,
|
|
||||||
"kind": "import",
|
|
||||||
"specifier": "../../shared/status-issues.js",
|
|
||||||
"resolvedPath": "extensions/shared/status-issues.js",
|
|
||||||
"reason": "imports another extension via relative path outside the extension package"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user