mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-02 02:57:51 +00:00
style: fix extension lint violations
This commit is contained in:
@@ -3,7 +3,6 @@ import type { ChannelAccountSnapshot } from "openclaw/plugin-sdk/channel-contrac
|
||||
import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk/channel-send-result";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { createLoggerBackedRuntime } from "openclaw/plugin-sdk/runtime";
|
||||
import { monitorTlonProvider } from "./monitor/index.js";
|
||||
import { tlonSetupWizard } from "./setup-surface.js";
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
||||
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
||||
import { createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import {
|
||||
@@ -12,20 +11,14 @@ import {
|
||||
import { tlonChannelConfigSchema } from "./config-schema.js";
|
||||
import { tlonDoctor } from "./doctor.js";
|
||||
import { resolveTlonOutboundSessionRoute } from "./session-route.js";
|
||||
import {
|
||||
applyTlonSetupConfig,
|
||||
createTlonSetupWizardBase,
|
||||
resolveTlonSetupConfigured,
|
||||
tlonSetupAdapter,
|
||||
} from "./setup-core.js";
|
||||
import { createTlonSetupWizardBase, tlonSetupAdapter } from "./setup-core.js";
|
||||
import {
|
||||
formatTargetHint,
|
||||
normalizeShip,
|
||||
parseTlonTarget,
|
||||
resolveTlonOutboundTarget,
|
||||
} from "./targets.js";
|
||||
import { resolveTlonAccount, listTlonAccountIds } from "./types.js";
|
||||
import { validateUrbitBaseUrl } from "./urbit/base-url.js";
|
||||
import { listTlonAccountIds, resolveTlonAccount } from "./types.js";
|
||||
|
||||
const TLON_CHANNEL_ID = "tlon" as const;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export function createTlonCitationResolver(params: { api: TlonScryApi; runtime:
|
||||
const scryPath = `/channels/v4/${cite.nest}/posts/post/${cite.postId}.json`;
|
||||
runtime.log?.(`[tlon] Fetching cited post: ${scryPath}`);
|
||||
|
||||
const data: any = await api.scry(scryPath);
|
||||
const data: unknown = await api.scry(scryPath);
|
||||
if (data?.essay?.content) {
|
||||
return extractMessageText(data.essay.content) || null;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function fetchGroupChanges(
|
||||
return changes;
|
||||
}
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.log?.(
|
||||
`[tlon] Failed to fetch changes (falling back to full init): ${error?.message ?? String(error)}`,
|
||||
);
|
||||
@@ -39,11 +39,11 @@ export async function fetchInitData(
|
||||
): Promise<InitData> {
|
||||
try {
|
||||
runtime.log?.("[tlon] Fetching groups-ui init data...");
|
||||
const initData = (await api.scry("/groups-ui/v6/init.json")) as any;
|
||||
const initData = await api.scry("/groups-ui/v6/init.json");
|
||||
|
||||
const channels: string[] = [];
|
||||
if (initData?.groups) {
|
||||
for (const groupData of Object.values(initData.groups as Record<string, any>)) {
|
||||
for (const groupData of Object.values(initData.groups as Record<string, unknown>)) {
|
||||
if (groupData && typeof groupData === "object" && groupData.channels) {
|
||||
for (const channelNest of Object.keys(groupData.channels)) {
|
||||
if (channelNest.startsWith("chat/")) {
|
||||
@@ -71,7 +71,7 @@ export async function fetchInitData(
|
||||
}
|
||||
|
||||
return { channels, foreigns };
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.log?.(`[tlon] Init data fetch failed: ${error?.message ?? String(error)}`);
|
||||
return { channels: [], foreigns: null };
|
||||
}
|
||||
|
||||
@@ -54,12 +54,12 @@ export async function fetchChannelHistory(
|
||||
const scryPath = `/channels/v4/${channelNest}/posts/newest/${count}/outline.json`;
|
||||
runtime?.log?.(`[tlon] Fetching history: ${scryPath}`);
|
||||
|
||||
const data: any = await api.scry(scryPath);
|
||||
const data: unknown = await api.scry(scryPath);
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let posts: any[] = [];
|
||||
let posts: unknown[] = [];
|
||||
if (Array.isArray(data)) {
|
||||
posts = data;
|
||||
} else if (data.posts && typeof data.posts === "object") {
|
||||
@@ -84,7 +84,7 @@ export async function fetchChannelHistory(
|
||||
|
||||
runtime?.log?.(`[tlon] Extracted ${messages.length} messages from history`);
|
||||
return messages;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime?.log?.(`[tlon] Error fetching channel history: ${error?.message ?? String(error)}`);
|
||||
return [];
|
||||
}
|
||||
@@ -129,13 +129,13 @@ export async function fetchThreadHistory(
|
||||
const scryPath = `/channels/v4/${channelNest}/posts/post/id/${formattedParentId}/replies/newest/${count}.json`;
|
||||
runtime?.log?.(`[tlon] Fetching thread history: ${scryPath}`);
|
||||
|
||||
const data: any = await api.scry(scryPath);
|
||||
const data: unknown = await api.scry(scryPath);
|
||||
if (!data) {
|
||||
runtime?.log?.(`[tlon] No thread history data returned`);
|
||||
return [];
|
||||
}
|
||||
|
||||
let replies: any[] = [];
|
||||
let replies: unknown[] = [];
|
||||
if (Array.isArray(data)) {
|
||||
replies = data;
|
||||
} else if (data.replies && Array.isArray(data.replies)) {
|
||||
@@ -161,18 +161,18 @@ export async function fetchThreadHistory(
|
||||
|
||||
runtime?.log?.(`[tlon] Extracted ${messages.length} thread replies from history`);
|
||||
return messages;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime?.log?.(`[tlon] Error fetching thread history: ${error?.message ?? String(error)}`);
|
||||
// Fall back to trying alternate path structure
|
||||
try {
|
||||
const altPath = `/channels/v4/${channelNest}/posts/post/id/${formatUd(parentId)}.json`;
|
||||
runtime?.log?.(`[tlon] Trying alternate path: ${altPath}`);
|
||||
const data: any = await api.scry(altPath);
|
||||
const data: unknown = await api.scry(altPath);
|
||||
|
||||
if (data?.seal?.meta?.replyCount > 0 && data?.replies) {
|
||||
const replies = Array.isArray(data.replies) ? data.replies : Object.values(data.replies);
|
||||
const messages = replies
|
||||
.map((reply: any) => ({
|
||||
.map((reply: unknown) => ({
|
||||
author: reply.memo?.author || "unknown",
|
||||
content: extractMessageText(reply.memo?.content || []),
|
||||
timestamp: reply.memo?.sent || Date.now(),
|
||||
@@ -183,7 +183,7 @@ export async function fetchThreadHistory(
|
||||
runtime?.log?.(`[tlon] Extracted ${messages.length} replies from post data`);
|
||||
return messages;
|
||||
}
|
||||
} catch (altError: any) {
|
||||
} catch (altError: unknown) {
|
||||
runtime?.log?.(`[tlon] Alternate path also failed: ${altError?.message ?? String(altError)}`);
|
||||
}
|
||||
return [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RuntimeEnv, ReplyPayload, OpenClawConfig } from "../../api.js";
|
||||
import type { ReplyPayload, RuntimeEnv } from "../../api.js";
|
||||
import { createLoggerBackedRuntime } from "../../api.js";
|
||||
import { getTlonRuntime } from "../runtime.js";
|
||||
import { createSettingsManager, type TlonSettingsStore } from "../settings.js";
|
||||
@@ -6,21 +6,20 @@ import { normalizeShip, parseChannelNest } from "../targets.js";
|
||||
import { resolveTlonAccount } from "../types.js";
|
||||
import { authenticate } from "../urbit/auth.js";
|
||||
import { ssrfPolicyFromDangerouslyAllowPrivateNetwork } from "../urbit/context.js";
|
||||
import type { Foreigns, DmInvite } from "../urbit/foreigns.js";
|
||||
import type { DmInvite, Foreigns } from "../urbit/foreigns.js";
|
||||
import { sendDm, sendGroupMessage } from "../urbit/send.js";
|
||||
import { UrbitSSEClient } from "../urbit/sse-client.js";
|
||||
import { createTlonApprovalRuntime } from "./approval-runtime.js";
|
||||
import {
|
||||
type PendingApproval,
|
||||
type AdminCommand,
|
||||
createPendingApproval,
|
||||
isApprovalResponse,
|
||||
isAdminCommand,
|
||||
isApprovalResponse,
|
||||
type PendingApproval,
|
||||
} from "./approval.js";
|
||||
import { resolveChannelAuthorization } from "./authorization.js";
|
||||
import { createTlonCitationResolver } from "./cites.js";
|
||||
import { fetchAllChannels, fetchInitData } from "./discovery.js";
|
||||
import { cacheMessage, getChannelHistory, fetchThreadHistory } from "./history.js";
|
||||
import { cacheMessage, fetchThreadHistory, getChannelHistory } from "./history.js";
|
||||
import { downloadMessageImages } from "./media.js";
|
||||
import { createProcessedMessageTracker } from "./processed-messages.js";
|
||||
import {
|
||||
@@ -31,15 +30,13 @@ import {
|
||||
} from "./settings-helpers.js";
|
||||
import {
|
||||
extractMessageText,
|
||||
extractCites,
|
||||
formatModelName,
|
||||
isBotMentioned,
|
||||
stripBotMention,
|
||||
isDmAllowed,
|
||||
isGroupInviteAllowed,
|
||||
isSummarizationRequest,
|
||||
resolveAuthorizedMessageText,
|
||||
type ParsedCite,
|
||||
stripBotMention,
|
||||
} from "./utils.js";
|
||||
|
||||
export type MonitorTlonOpts = {
|
||||
@@ -50,7 +47,7 @@ export type MonitorTlonOpts = {
|
||||
|
||||
export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<void> {
|
||||
const core = getTlonRuntime();
|
||||
const cfg = core.config.loadConfig() as OpenClawConfig;
|
||||
const cfg = core.config.loadConfig();
|
||||
if (cfg.channels?.tlon?.enabled === false) {
|
||||
return;
|
||||
}
|
||||
@@ -90,7 +87,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
try {
|
||||
runtime.log?.(`[tlon] Attempting authentication to ${accountUrl}...`);
|
||||
return await authenticate(accountUrl, accountCode, { ssrfPolicy });
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.error?.(
|
||||
`[tlon] Failed to authenticate (attempt ${attempt}): ${error?.message ?? String(error)}`,
|
||||
);
|
||||
@@ -171,7 +168,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
runtime.log?.(`[tlon] Bot nickname: ${botNickname}`);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.log?.(`[tlon] Could not fetch nickname: ${error?.message ?? String(error)}`);
|
||||
}
|
||||
|
||||
@@ -238,7 +235,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
groupChannels = initData.channels;
|
||||
}
|
||||
initForeigns = initData.foreigns;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.error?.(`[tlon] Auto-discovery failed: ${error?.message ?? String(error)}`);
|
||||
}
|
||||
}
|
||||
@@ -305,8 +302,8 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
senderShip,
|
||||
isGroup,
|
||||
channelNest,
|
||||
hostShip,
|
||||
channelName,
|
||||
_hostShip,
|
||||
_channelName,
|
||||
timestamp,
|
||||
parentId,
|
||||
isThreadReply,
|
||||
@@ -323,7 +320,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
if (attachments.length > 0) {
|
||||
runtime.log?.(`[tlon] Downloaded ${attachments.length} image(s) from message`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime.log?.(`[tlon] Failed to download images: ${error?.message ?? String(error)}`);
|
||||
}
|
||||
}
|
||||
@@ -346,7 +343,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
`[tlon] Added thread context (${threadHistory.length} replies) to message`,
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
runtime?.log?.(`[tlon] Could not fetch thread context: ${error?.message ?? String(error)}`);
|
||||
// Continue without thread context - not critical
|
||||
}
|
||||
@@ -393,7 +390,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
"2. Key decisions or conclusions\n" +
|
||||
"3. Action items if any\n" +
|
||||
"4. Notable participants";
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
const errorMsg = `Sorry, I encountered an error while fetching the channel history: ${error?.message ?? String(error)}`;
|
||||
if (isGroup && groupChannel) {
|
||||
const parsed = parseChannelNest(groupChannel);
|
||||
@@ -569,10 +566,6 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
extPayload.model ||
|
||||
extRoute.model ||
|
||||
(typeof defaultModel === "string" ? defaultModel : defaultModel?.primary);
|
||||
extPayload.metadata?.model ||
|
||||
extPayload.model ||
|
||||
extRoute.model ||
|
||||
(typeof defaultModel === "string" ? defaultModel : defaultModel?.primary);
|
||||
replyText = `${replyText}\n\n_[Generated by ${formatModelName(modelInfo)}]_`;
|
||||
}
|
||||
|
||||
@@ -613,7 +606,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
const _watchedDMs = new Set<string>();
|
||||
|
||||
const refreshWatchedChannels = async (): Promise<number> => {
|
||||
const discoveredChannels = await fetchAllChannels(api!, runtime);
|
||||
const discoveredChannels = await fetchAllChannels(api, runtime);
|
||||
let newCount = 0;
|
||||
for (const channelNest of discoveredChannels) {
|
||||
if (!watchedChannels.has(channelNest)) {
|
||||
@@ -625,15 +618,15 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
|
||||
};
|
||||
|
||||
const { resolveAllCites } = createTlonCitationResolver({
|
||||
api: { scry: (path) => api!.scry(path) },
|
||||
api: { scry: (path) => api.scry(path) },
|
||||
runtime,
|
||||
});
|
||||
|
||||
const { queueApprovalRequest, handleApprovalResponse, handleAdminCommand } =
|
||||
createTlonApprovalRuntime({
|
||||
api: {
|
||||
poke: (payload) => api!.poke(payload),
|
||||
scry: (path) => api!.scry(path),
|
||||
poke: (payload) => api.poke(payload),
|
||||
scry: (path) => api.scry(path),
|
||||
},
|
||||
runtime,
|
||||
botShipName,
|
||||
|
||||
@@ -130,7 +130,9 @@ export function isBotMentioned(
|
||||
* "~bot-ship /status" → "/status"
|
||||
*/
|
||||
export function stripBotMention(messageText: string, botShipName: string): string {
|
||||
if (!messageText || !botShipName) return messageText;
|
||||
if (!messageText || !botShipName) {
|
||||
return messageText;
|
||||
}
|
||||
return messageText.replace(normalizeShip(botShipName), "").trim();
|
||||
}
|
||||
|
||||
|
||||
@@ -533,7 +533,9 @@ describe("Security: Sender Role Identification", () => {
|
||||
|
||||
// Helper to compute sender role (mirrors logic in monitor/index.ts)
|
||||
function getSenderRole(senderShip: string, ownerShip: string | null): "owner" | "user" {
|
||||
if (!ownerShip) return "user";
|
||||
if (!ownerShip) {
|
||||
return "user";
|
||||
}
|
||||
return normalizeShip(senderShip) === normalizeShip(ownerShip) ? "owner" : "user";
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ function parseSettingsEvent(event: unknown): { key: string; value: unknown } | n
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
key: String(put["entry-key"] ?? ""),
|
||||
key: typeof put["entry-key"] === "string" ? put["entry-key"] : "",
|
||||
value: put.value,
|
||||
};
|
||||
}
|
||||
@@ -220,7 +220,7 @@ function parseSettingsEvent(event: unknown): { key: string; value: unknown } | n
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
key: String(del["entry-key"] ?? ""),
|
||||
key: typeof del["entry-key"] === "string" ? del["entry-key"] : "",
|
||||
value: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
||||
import {
|
||||
applyTlonSetupConfig,
|
||||
createTlonSetupWizardBase,
|
||||
resolveTlonSetupConfigured,
|
||||
resolveTlonSetupStatusLines,
|
||||
type TlonSetupInput,
|
||||
tlonSetupAdapter,
|
||||
} from "./setup-core.js";
|
||||
import { normalizeShip } from "./targets.js";
|
||||
import { listTlonAccountIds, resolveTlonAccount, type TlonResolvedAccount } from "./types.js";
|
||||
import { resolveTlonAccount, type TlonResolvedAccount } from "./types.js";
|
||||
import { isBlockedUrbitHostname, validateUrbitBaseUrl } from "./urbit/base-url.js";
|
||||
|
||||
const channel = "tlon" as const;
|
||||
const _channel = "tlon" as const;
|
||||
|
||||
function isConfigured(account: TlonResolvedAccount): boolean {
|
||||
function _isConfigured(account: TlonResolvedAccount): boolean {
|
||||
return Boolean(account.ship && account.url && account.code);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,9 @@ export function resolveTlonAccount(
|
||||
}
|
||||
|
||||
const merged = resolveMergedTlonAccountConfig(cfg, resolvedAccountId);
|
||||
const ship = (merged.ship ?? null) as string | null;
|
||||
const url = (merged.url ?? null) as string | null;
|
||||
const code = (merged.code ?? null) as string | null;
|
||||
const ship = merged.ship ?? null;
|
||||
const url = merged.url ?? null;
|
||||
const code = merged.code ?? null;
|
||||
const dangerouslyAllowPrivateNetwork = isPrivateNetworkOptInEnabled(merged)
|
||||
? true
|
||||
: typeof merged.network?.dangerouslyAllowPrivateNetwork === "boolean"
|
||||
@@ -116,20 +116,20 @@ export function resolveTlonAccount(
|
||||
typeof merged.allowPrivateNetwork === "boolean"
|
||||
? merged.allowPrivateNetwork
|
||||
: null;
|
||||
const groupChannels = (merged.groupChannels ?? []) as string[];
|
||||
const dmAllowlist = (merged.dmAllowlist ?? []) as string[];
|
||||
const groupInviteAllowlist = (merged.groupInviteAllowlist ?? []) as string[];
|
||||
const autoDiscoverChannels = (merged.autoDiscoverChannels ?? null) as boolean | null;
|
||||
const showModelSignature = (merged.showModelSignature ?? null) as boolean | null;
|
||||
const autoAcceptDmInvites = (merged.autoAcceptDmInvites ?? null) as boolean | null;
|
||||
const autoAcceptGroupInvites = (merged.autoAcceptGroupInvites ?? null) as boolean | null;
|
||||
const ownerShip = (merged.ownerShip ?? null) as string | null;
|
||||
const defaultAuthorizedShips = (merged.defaultAuthorizedShips ?? []) as string[];
|
||||
const groupChannels = merged.groupChannels ?? [];
|
||||
const dmAllowlist = merged.dmAllowlist ?? [];
|
||||
const groupInviteAllowlist = merged.groupInviteAllowlist ?? [];
|
||||
const autoDiscoverChannels = merged.autoDiscoverChannels ?? null;
|
||||
const showModelSignature = merged.showModelSignature ?? null;
|
||||
const autoAcceptDmInvites = merged.autoAcceptDmInvites ?? null;
|
||||
const autoAcceptGroupInvites = merged.autoAcceptGroupInvites ?? null;
|
||||
const ownerShip = merged.ownerShip ?? null;
|
||||
const defaultAuthorizedShips = merged.defaultAuthorizedShips ?? [];
|
||||
const configured = Boolean(ship && url && code);
|
||||
|
||||
return {
|
||||
accountId: resolvedAccountId,
|
||||
name: (merged.name ?? null) as string | null,
|
||||
name: merged.name ?? null,
|
||||
enabled: merged.enabled !== false,
|
||||
configured,
|
||||
ship,
|
||||
|
||||
@@ -5,7 +5,9 @@ describe("validateUrbitBaseUrl", () => {
|
||||
it("adds https:// when scheme is missing and strips path/query fragments", () => {
|
||||
const result = validateUrbitBaseUrl("example.com/foo?bar=baz");
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.baseUrl).toBe("https://example.com");
|
||||
expect(result.hostname).toBe("example.com");
|
||||
});
|
||||
@@ -13,21 +15,27 @@ describe("validateUrbitBaseUrl", () => {
|
||||
it("rejects non-http schemes", () => {
|
||||
const result = validateUrbitBaseUrl("file:///etc/passwd");
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
if (result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.error).toContain("http:// or https://");
|
||||
});
|
||||
|
||||
it("rejects embedded credentials", () => {
|
||||
const result = validateUrbitBaseUrl("https://user:pass@example.com");
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
if (result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.error).toContain("credentials");
|
||||
});
|
||||
|
||||
it("normalizes a trailing dot in the hostname for origin construction", () => {
|
||||
const result = validateUrbitBaseUrl("https://example.com./foo");
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.baseUrl).toBe("https://example.com");
|
||||
expect(result.hostname).toBe("example.com");
|
||||
});
|
||||
@@ -35,7 +43,9 @@ describe("validateUrbitBaseUrl", () => {
|
||||
it("preserves port in the normalized origin", () => {
|
||||
const result = validateUrbitBaseUrl("http://example.com:8080/~/login");
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
expect(result.baseUrl).toBe("http://example.com:8080");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -242,7 +242,7 @@ export function markdownToStory(markdown: string): Story {
|
||||
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
||||
if (headerMatch) {
|
||||
const level = headerMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6;
|
||||
const tag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
||||
const tag = `h${level}`;
|
||||
story.push({
|
||||
block: {
|
||||
header: {
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function uploadImageFromUrl(imageUrl: string): Promise<string> {
|
||||
await release();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[tlon] Failed to upload image, using original URL: ${err}`);
|
||||
console.warn(`[tlon] Failed to upload image, using original URL: ${String(err)}`);
|
||||
return imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user