diff --git a/extensions/acpx/src/runtime-internals/process.test.ts b/extensions/acpx/src/runtime-internals/process.test.ts index 90b7560c47e..5768f90117a 100644 --- a/extensions/acpx/src/runtime-internals/process.test.ts +++ b/extensions/acpx/src/runtime-internals/process.test.ts @@ -2,8 +2,8 @@ import { spawn } from "node:child_process"; import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; +import { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { createWindowsCmdShimFixture } from "../../../shared/windows-cmd-shim-test-fixtures.js"; import { resolveSpawnCommand, spawnAndCollect, diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index 29dfeae6ac0..fc4cf489928 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -19,8 +19,8 @@ import { listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom, } from "openclaw/plugin-sdk/directory-runtime"; +import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared"; import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime"; -import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { buildComputedAccountStatusSnapshot, buildChannelConfigSchema, diff --git a/extensions/googlechat/src/resolve-target.test.ts b/extensions/googlechat/src/resolve-target.test.ts index e2e382af056..85dfb8c005c 100644 --- a/extensions/googlechat/src/resolve-target.test.ts +++ b/extensions/googlechat/src/resolve-target.test.ts @@ -1,5 +1,5 @@ +import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { installCommonResolveTargetErrorCases } from "../../shared/resolve-target-test-helpers.js"; const runtimeMocks = vi.hoisted(() => ({ chunkMarkdownText: vi.fn((text: string) => [text]), diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 514b798b7df..d084ee92a15 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -4,9 +4,9 @@ import { resolveOutboundSendDep, } from "openclaw/plugin-sdk/channel-runtime"; import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/core"; +import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared"; import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime"; import { type RoutePeer } from "openclaw/plugin-sdk/routing"; -import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { collectStatusIssuesFromLastError, DEFAULT_ACCOUNT_ID, diff --git a/extensions/irc/src/channel.ts b/extensions/irc/src/channel.ts index a4e75f72af5..27571c92d35 100644 --- a/extensions/irc/src/channel.ts +++ b/extensions/irc/src/channel.ts @@ -14,7 +14,7 @@ import { createTextPairingAdapter, listResolvedDirectoryEntriesFromSources, } from "openclaw/plugin-sdk/channel-runtime"; -import { runStoppablePassiveMonitor } from "../../shared/passive-monitor.js"; +import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared"; import { listIrcAccountIds, resolveDefaultIrcAccountId, diff --git a/extensions/irc/src/config-schema.ts b/extensions/irc/src/config-schema.ts index d1af189484b..5534e0098c5 100644 --- a/extensions/irc/src/config-schema.ts +++ b/extensions/irc/src/config-schema.ts @@ -1,5 +1,5 @@ +import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared"; import { z } from "zod"; -import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js"; import { BlockStreamingCoalesceSchema, DmConfigSchema, diff --git a/extensions/irc/src/monitor.ts b/extensions/irc/src/monitor.ts index 072c5a91081..2a75b76ee08 100644 --- a/extensions/irc/src/monitor.ts +++ b/extensions/irc/src/monitor.ts @@ -1,4 +1,4 @@ -import { resolveLoggerBackedRuntime } from "../../shared/runtime.js"; +import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared"; import { resolveIrcAccount } from "./accounts.js"; import { connectIrcClient, type IrcClient } from "./client.js"; import { buildIrcConnectOptions } from "./connect-options.js"; diff --git a/extensions/lobster/src/test-helpers.ts b/extensions/lobster/src/test-helpers.ts index 19609c0c11b..52db2fad942 100644 --- a/extensions/lobster/src/test-helpers.ts +++ b/extensions/lobster/src/test-helpers.ts @@ -1,5 +1,7 @@ type PathEnvKey = "PATH" | "Path" | "PATHEXT" | "Pathext"; +export { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing"; + const PATH_ENV_KEYS = ["PATH", "Path", "PATHEXT", "Pathext"] as const; export type PlatformPathEnvSnapshot = { @@ -40,4 +42,3 @@ export function restorePlatformPathEnv(snapshot: PlatformPathEnvSnapshot): void process.env[key] = value; } } -export { createWindowsCmdShimFixture } from "../../shared/windows-cmd-shim-test-fixtures.js"; diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index 4c83f627261..894488da567 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -15,8 +15,8 @@ import { createTextPairingAdapter, listResolvedDirectoryEntriesFromSources, } from "openclaw/plugin-sdk/channel-runtime"; +import { buildTrafficStatusSummary } from "openclaw/plugin-sdk/extension-shared"; import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime"; -import { buildTrafficStatusSummary } from "../../shared/channel-status-summary.js"; import { buildChannelConfigSchema, buildProbeChannelStatusSummary, diff --git a/extensions/matrix/src/matrix/send-queue.test.ts b/extensions/matrix/src/matrix/send-queue.test.ts index 240dd8ee71d..c85981697a0 100644 --- a/extensions/matrix/src/matrix/send-queue.test.ts +++ b/extensions/matrix/src/matrix/send-queue.test.ts @@ -1,5 +1,5 @@ +import { createDeferred } from "openclaw/plugin-sdk/extension-shared"; 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"; describe("enqueueSend", () => { diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index cf8f51c245c..94c5bbff092 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -12,7 +12,7 @@ import { createScopedAccountReplyToModeResolver, type ChannelMessageToolDiscovery, } 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 { resolveMattermostGroupRequireMention } from "./group-mentions.js"; import { diff --git a/extensions/mattermost/src/config-schema.ts b/extensions/mattermost/src/config-schema.ts index e8e50371bd4..1c2f48ed405 100644 --- a/extensions/mattermost/src/config-schema.ts +++ b/extensions/mattermost/src/config-schema.ts @@ -1,5 +1,5 @@ +import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared"; import { z } from "zod"; -import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js"; import { BlockStreamingCoalesceSchema, DmPolicySchema, diff --git a/extensions/nextcloud-talk/src/channel.ts b/extensions/nextcloud-talk/src/channel.ts index d24822efb26..ff316e3a533 100644 --- a/extensions/nextcloud-talk/src/channel.ts +++ b/extensions/nextcloud-talk/src/channel.ts @@ -10,7 +10,7 @@ import { createLoggedPairingApprovalNotifier, createPairingPrefixStripper, } from "openclaw/plugin-sdk/channel-runtime"; -import { runStoppablePassiveMonitor } from "../../shared/passive-monitor.js"; +import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared"; import { buildBaseChannelStatusSummary, buildChannelConfigSchema, diff --git a/extensions/nextcloud-talk/src/config-schema.ts b/extensions/nextcloud-talk/src/config-schema.ts index 020a69d7992..685ac0fe525 100644 --- a/extensions/nextcloud-talk/src/config-schema.ts +++ b/extensions/nextcloud-talk/src/config-schema.ts @@ -1,5 +1,5 @@ +import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared"; import { z } from "zod"; -import { requireChannelOpenAllowFrom } from "../../shared/config-schema-helpers.js"; import { BlockStreamingCoalesceSchema, DmConfigSchema, diff --git a/extensions/nextcloud-talk/src/monitor.ts b/extensions/nextcloud-talk/src/monitor.ts index 8721ff5fe6b..b40024e5eb0 100644 --- a/extensions/nextcloud-talk/src/monitor.ts +++ b/extensions/nextcloud-talk/src/monitor.ts @@ -1,6 +1,6 @@ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; import os from "node:os"; -import { resolveLoggerBackedRuntime } from "../../shared/runtime.js"; +import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared"; import { type RuntimeEnv, isRequestBodyLimitError, diff --git a/extensions/nostr/src/channel.ts b/extensions/nostr/src/channel.ts index a11a882b81e..a047cbd2a97 100644 --- a/extensions/nostr/src/channel.ts +++ b/extensions/nostr/src/channel.ts @@ -6,7 +6,7 @@ import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result"; import { buildPassiveChannelStatusSummary, buildTrafficStatusSummary, -} from "../../shared/channel-status-summary.js"; +} from "openclaw/plugin-sdk/extension-shared"; import { buildChannelConfigSchema, collectStatusIssuesFromLastError, diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 379d0537e2b..fe28054c380 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -16,8 +16,8 @@ import { resolveTargetsWithOptionalToken, } from "openclaw/plugin-sdk/channel-runtime"; 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 { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { listEnabledSlackAccounts, resolveSlackAccount, diff --git a/extensions/twitch/src/plugin.ts b/extensions/twitch/src/plugin.ts index 59e016d4473..eb2513ca69e 100644 --- a/extensions/twitch/src/plugin.ts +++ b/extensions/twitch/src/plugin.ts @@ -5,7 +5,7 @@ * 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 { buildChannelConfigSchema } from "../api.js"; import { twitchMessageActions } from "./actions.js"; diff --git a/extensions/whatsapp/src/resolve-target.test.ts b/extensions/whatsapp/src/resolve-target.test.ts index b0ed25e4dc9..fb6da25a659 100644 --- a/extensions/whatsapp/src/resolve-target.test.ts +++ b/extensions/whatsapp/src/resolve-target.test.ts @@ -1,5 +1,5 @@ +import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing"; import { describe, expect, it, vi } from "vitest"; -import { installCommonResolveTargetErrorCases } from "../../shared/resolve-target-test-helpers.js"; vi.mock("openclaw/plugin-sdk/whatsapp", async () => { const actual = await vi.importActual( diff --git a/extensions/zalo/src/status-issues.ts b/extensions/zalo/src/status-issues.ts index 28e2f333c80..ebb24ad7e18 100644 --- a/extensions/zalo/src/status-issues.ts +++ b/extensions/zalo/src/status-issues.ts @@ -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"; const ZALO_STATUS_FIELDS = ["accountId", "enabled", "configured", "dmPolicy"] as const; diff --git a/extensions/zalouser/src/channel.ts b/extensions/zalouser/src/channel.ts index b6cf6111580..24e46323a8d 100644 --- a/extensions/zalouser/src/channel.ts +++ b/extensions/zalouser/src/channel.ts @@ -7,7 +7,7 @@ import { createStaticReplyToModeResolver, createTextPairingAdapter, } from "openclaw/plugin-sdk/channel-runtime"; -import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; +import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared"; import type { ChannelAccountSnapshot, ChannelDirectoryEntry, diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index 1a807a1a1b9..31853fb207f 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -2,6 +2,7 @@ import { DM_GROUP_ACCESS_REASON, resolveDmGroupAccessWithLists, } from "openclaw/plugin-sdk/channel-policy"; +import { createDeferred } from "openclaw/plugin-sdk/extension-shared"; import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue"; import { DEFAULT_GROUP_HISTORY_LIMIT, @@ -10,7 +11,6 @@ import { clearHistoryEntriesIfEnabled, recordPendingHistoryEntryIfEnabled, } from "openclaw/plugin-sdk/reply-history"; -import { createDeferred } from "../../shared/deferred.js"; import type { MarkdownTableMode, OpenClawConfig, diff --git a/extensions/zalouser/src/status-issues.ts b/extensions/zalouser/src/status-issues.ts index ca324f6d169..6e43bf0ec3d 100644 --- a/extensions/zalouser/src/status-issues.ts +++ b/extensions/zalouser/src/status-issues.ts @@ -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"; const ZALOUSER_STATUS_FIELDS = [ diff --git a/package.json b/package.json index 17f04666edd..797142fc574 100644 --- a/package.json +++ b/package.json @@ -333,6 +333,10 @@ "types": "./dist/plugin-sdk/diffs.d.ts", "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": { "types": "./dist/plugin-sdk/channel-config-helpers.d.ts", "default": "./dist/plugin-sdk/channel-config-helpers.js" diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index 6373432652b..d889433dae8 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -73,6 +73,7 @@ "device-pair", "diagnostics-otel", "diffs", + "extension-shared", "channel-config-helpers", "channel-config-schema", "channel-lifecycle", diff --git a/src/plugin-sdk/extension-shared.ts b/src/plugin-sdk/extension-shared.ts new file mode 100644 index 00000000000..43c11f7c09d --- /dev/null +++ b/src/plugin-sdk/extension-shared.ts @@ -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; + ctx: z.RefinementCtx; + path: Array; + message: string; +}) => void; + +export function buildPassiveChannelStatusSummary( + 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( + snapshot: PassiveChannelStatusSnapshot, + extra?: TExtra, +) { + return { + ...buildPassiveChannelStatusSummary(snapshot, extra), + probe: snapshot.probe, + lastProbeAt: snapshot.lastProbeAt ?? null, + }; +} + +export function buildTrafficStatusSummary( + snapshot?: TSnapshot | null, +) { + return { + lastInboundAt: snapshot?.lastInboundAt ?? null, + lastOutboundAt: snapshot?.lastOutboundAt ?? null, + }; +} + +export async function runStoppablePassiveMonitor(params: { + abortSignal: AbortSignal; + start: () => Promise; +}): Promise { + await runPassiveAccountLifecycle({ + abortSignal: params.abortSignal, + start: params.start, + stop: async (monitor) => { + monitor.stop(); + }, + }); +} + +export function resolveLoggerBackedRuntime( + runtime: TRuntime | undefined, + logger: Parameters[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; + 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( + value: unknown, + fields: readonly TField[], +): Record | null { + if (!value || typeof value !== "object") { + return null; + } + const record = value as Record; + const result = {} as Record; + 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() { + let resolve!: (value: T | PromiseLike) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} diff --git a/src/plugin-sdk/testing.ts b/src/plugin-sdk/testing.ts index e8a7e89f646..ebb931df1bb 100644 --- a/src/plugin-sdk/testing.ts +++ b/src/plugin-sdk/testing.ts @@ -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. // 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 { RuntimeEnv } from "../runtime.js"; export type { MockFn } from "../test-utils/vitest-mock-fn.js"; + +export async function createWindowsCmdShimFixture(params: { + shimPath: string; + scriptPath: string; + shimLine: string; +}): Promise { + 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(); + }); +} diff --git a/test/fixtures/extension-relative-outside-package-inventory.json b/test/fixtures/extension-relative-outside-package-inventory.json index 222840d1304..fe51488c706 100644 --- a/test/fixtures/extension-relative-outside-package-inventory.json +++ b/test/fixtures/extension-relative-outside-package-inventory.json @@ -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" - } -] +[]