2026-02-09 17:49:10 +08:00
|
|
|
import chalk from 'chalk'
|
|
|
|
|
|
2026-03-05 16:54:41 +08:00
|
|
|
export * from './autoFixer'
|
2026-01-13 13:49:19 +08:00
|
|
|
|
2025-12-05 16:18:01 +08:00
|
|
|
export async function waitFor(seconds: number): Promise<void> {
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-29 16:33:15 +08:00
|
|
|
//
|
|
|
|
|
|
|
|
|
|
export function truncate(text: string, maxLength: number): string {
|
|
|
|
|
if (text.length > maxLength) {
|
|
|
|
|
return text.substring(0, maxLength) + '...'
|
|
|
|
|
}
|
|
|
|
|
return text
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
export function randomID(existingIDs?: string[]): string {
|
|
|
|
|
let id = Math.random().toString(36).substring(2, 11)
|
|
|
|
|
|
|
|
|
|
if (!existingIDs) {
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MAX_TRY = 1000
|
|
|
|
|
let tryCount = 0
|
|
|
|
|
|
|
|
|
|
while (existingIDs.includes(id)) {
|
|
|
|
|
id = Math.random().toString(36).substring(2, 11)
|
|
|
|
|
tryCount++
|
|
|
|
|
if (tryCount > MAX_TRY) {
|
|
|
|
|
throw new Error('randomID: too many try')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2026-01-19 16:06:07 +08:00
|
|
|
const _global = globalThis as any
|
2025-09-29 16:33:15 +08:00
|
|
|
|
2026-01-19 16:06:07 +08:00
|
|
|
if (!_global.__PAGE_AGENT_IDS__) {
|
|
|
|
|
_global.__PAGE_AGENT_IDS__ = []
|
2025-09-29 16:33:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-19 16:06:07 +08:00
|
|
|
const ids = _global.__PAGE_AGENT_IDS__
|
2025-09-29 16:33:15 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a random ID.
|
|
|
|
|
* @note Unique within this window.
|
|
|
|
|
*/
|
|
|
|
|
export function uid() {
|
|
|
|
|
const id = randomID(ids)
|
|
|
|
|
ids.push(id)
|
|
|
|
|
return id
|
|
|
|
|
}
|
2026-02-09 17:49:10 +08:00
|
|
|
|
2026-03-04 18:53:24 +08:00
|
|
|
const llmsTxtCache = new Map<string, string | null>()
|
|
|
|
|
|
|
|
|
|
/** Fetch /llms.txt for a URL's origin. Cached per origin, `null` = tried and not found. */
|
|
|
|
|
export async function fetchLlmsTxt(url: string): Promise<string | null> {
|
|
|
|
|
const origin = new URL(url).origin
|
|
|
|
|
if (llmsTxtCache.has(origin)) return llmsTxtCache.get(origin)!
|
|
|
|
|
|
|
|
|
|
const endpoint = `${origin}/llms.txt`
|
|
|
|
|
let result: string | null = null
|
|
|
|
|
try {
|
|
|
|
|
console.log(chalk.gray(`[llms.txt] Fetching ${endpoint}`))
|
|
|
|
|
const res = await fetch(endpoint, { signal: AbortSignal.timeout(3000) })
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
result = await res.text()
|
|
|
|
|
console.log(chalk.green(`[llms.txt] Found (${result.length} chars)`))
|
2026-03-05 16:59:08 +08:00
|
|
|
if (result.length > 1000) {
|
|
|
|
|
console.log(chalk.yellow(`[llms.txt] Truncating to 1000 chars`))
|
|
|
|
|
result = truncate(result, 1000)
|
|
|
|
|
}
|
2026-03-04 18:53:24 +08:00
|
|
|
} else {
|
2026-03-05 16:59:08 +08:00
|
|
|
console.debug(chalk.gray(`[llms.txt] ${res.status} for ${endpoint}`))
|
2026-03-04 18:53:24 +08:00
|
|
|
}
|
|
|
|
|
} catch (e) {
|
2026-03-05 16:59:08 +08:00
|
|
|
console.debug(chalk.gray(`[llms.txt] not found for ${endpoint}`), e)
|
2026-03-04 18:53:24 +08:00
|
|
|
}
|
|
|
|
|
llmsTxtCache.set(origin, result)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 17:49:10 +08:00
|
|
|
/**
|
|
|
|
|
* Simple assertion function that throws an error if the condition is falsy
|
|
|
|
|
* @param condition - The condition to assert
|
|
|
|
|
* @param message - Optional error message
|
|
|
|
|
* @throws Error if condition is falsy
|
|
|
|
|
*/
|
|
|
|
|
export function assert(condition: unknown, message?: string, silent?: boolean): asserts condition {
|
|
|
|
|
if (!condition) {
|
|
|
|
|
const errorMessage = message ?? 'Assertion failed'
|
|
|
|
|
|
|
|
|
|
if (!silent) console.error(chalk.red(`❌ assert: ${errorMessage}`))
|
|
|
|
|
|
|
|
|
|
throw new Error(errorMessage)
|
|
|
|
|
}
|
|
|
|
|
}
|