@remnic/connector-x 9.69.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/chunk-JR2ZNAYD.js +1482 -0
- package/dist/chunk-JR2ZNAYD.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +129 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +467 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/file-sink.ts","../src/guards.ts","../src/normalize.ts","../src/mcp-client.ts","../src/token-store.ts","../src/sources.ts","../src/sync.ts"],"sourcesContent":["/**\n * Strict parser for the `xConnector` config block (issue #2009).\n *\n * Invalid values are rejected, never silently reinterpreted: unknown\n * source kinds, duplicate source ids, priorities naming unknown sources,\n * non-finite numbers, and unrecognized enum values all throw.\n */\n\nimport type { XMemoryMode, XSourceKind } from \"./types.js\";\n\nexport const X_SOURCE_KINDS: readonly XSourceKind[] = [\"mcp\", \"corpusDir\", \"cli\"];\nexport const X_MEMORY_MODES: readonly XMemoryMode[] = [\"suggest\", \"store\"];\nexport const X_SYNC_SCHEDULES: readonly string[] = [\"hourly\", \"4x-daily\", \"3x-daily\", \"2x-daily\", \"daily\", \"weekly\"];\n\nexport const X_DEFAULT_MCP_URL = \"https://api.x.com/mcp\";\nexport const X_DEFAULT_TOKEN_FILE = \"~/.openclaw/secrets/x-tokens.json\";\nexport const X_DEFAULT_STATE_DIR = \"~/.remnic/x-connector\";\n/** Pay-per-use reference rate: ~1 credit per read at ~$0.01/credit. */\nexport const X_DEFAULT_COST_PER_READ_USD = 0.01;\n\nexport interface XBudgetConfig {\n maxPagesPerSync: number;\n maxCostUsdPerMonth: number;\n costPerReadUsd: number;\n}\n\ninterface XBudgetInput {\n maxPagesPerSync?: unknown;\n maxCostUsdPerMonth?: unknown;\n costPerReadUsd?: unknown;\n}\n\nexport interface XMcpSourceConfig {\n id: string;\n kind: \"mcp\";\n url: string;\n tokenFile: string;\n bookmarksTool: string;\n timelineTool: string;\n maxResults: number;\n budget: XBudgetConfig;\n}\n\nexport interface XCorpusSourceConfig {\n id: string;\n kind: \"corpusDir\";\n path: string;\n}\n\nexport interface XCliSourceConfig {\n id: string;\n kind: \"cli\";\n bin: string;\n bookmarksArgs: string[];\n /** When unset, this source contributes bookmarks only. */\n postsArgs?: string[];\n}\n\nexport type XSourceConfig = XMcpSourceConfig | XCorpusSourceConfig | XCliSourceConfig;\n\nexport interface XConnectorConfig {\n enabled: boolean;\n userId?: string;\n sources: XSourceConfig[];\n sourcePriority: string[];\n syncSchedule: string;\n memoryMode: XMemoryMode;\n stateDir: string;\n}\n\nexport class XConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"XConfigError\";\n }\n}\n\n/** Coerces boolean-like strings at the config boundary; anything else is invalid. */\nexport function coerceXBool(value: unknown, field: string): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new XConfigError(`${field} must be a boolean (got ${JSON.stringify(value)})`);\n}\n\nfunction requiredString(value: unknown, field: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new XConfigError(`${field} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction optionalString(value: unknown, field: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n return requiredString(value, field);\n}\n\nfunction positiveInt(value: unknown, field: string, fallback: number): number {\n if (value === undefined || value === null) return fallback;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new XConfigError(`${field} must be an integer >= 1 (got ${JSON.stringify(value)})`);\n }\n return value;\n}\n\nfunction nonNegativeNumber(value: unknown, field: string, fallback: number): number {\n if (value === undefined || value === null) return fallback;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n throw new XConfigError(`${field} must be a finite number >= 0 (got ${JSON.stringify(value)})`);\n }\n return value;\n}\n\nfunction parseBudget(raw: unknown, sourceId: string): XBudgetConfig {\n const input: XBudgetInput =\n raw === undefined || raw === null ? {} : objectOrThrow(raw, `sources[${sourceId}].budget`);\n return {\n maxPagesPerSync: positiveInt(input.maxPagesPerSync, `sources[${sourceId}].budget.maxPagesPerSync`, 2),\n maxCostUsdPerMonth: nonNegativeNumber(\n input.maxCostUsdPerMonth,\n `sources[${sourceId}].budget.maxCostUsdPerMonth`,\n 1.0\n ),\n costPerReadUsd: nonNegativeNumber(\n input.costPerReadUsd,\n `sources[${sourceId}].budget.costPerReadUsd`,\n X_DEFAULT_COST_PER_READ_USD\n ),\n };\n}\n\nfunction objectOrThrow(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new XConfigError(`${field} must be an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction parseMcpSource(raw: Record<string, unknown>): XMcpSourceConfig {\n const id = requiredString(raw.id, \"source.id\");\n const auth = objectOrThrow(raw.auth ?? {}, `sources[${id}].auth`);\n return {\n id,\n kind: \"mcp\",\n url: optionalString(raw.url, `sources[${id}].url`) ?? X_DEFAULT_MCP_URL,\n tokenFile: optionalString(auth.tokenFile ?? raw.tokenFile, `sources[${id}].auth.tokenFile`) ?? X_DEFAULT_TOKEN_FILE,\n bookmarksTool: optionalString(raw.bookmarksTool, `sources[${id}].bookmarksTool`) ?? \"get_users_bookmarks\",\n timelineTool: optionalString(raw.timelineTool, `sources[${id}].timelineTool`) ?? \"get_users_tweets\",\n maxResults: positiveInt(raw.maxResults, `sources[${id}].maxResults`, 20),\n budget: parseBudget(raw.budget, id),\n };\n}\n\nfunction stringArray(value: unknown, field: string): string[] {\n if (value === undefined || value === null) return [];\n if (!Array.isArray(value)) {\n throw new XConfigError(`${field} must be an array of strings`);\n }\n return value.map((entry, index) => requiredString(entry, `${field}[${index}]`));\n}\n\nfunction parseCorpusSource(raw: Record<string, unknown>): XCorpusSourceConfig {\n const id = requiredString(raw.id, \"source.id\");\n return {\n id,\n kind: \"corpusDir\",\n path: requiredString(raw.path, `sources[${id}].path`),\n };\n}\n\nfunction parseCliSource(raw: Record<string, unknown>): XCliSourceConfig {\n const id = requiredString(raw.id, \"source.id\");\n const bookmarksArgs = stringArray(raw.bookmarksArgs, `sources[${id}].bookmarksArgs`);\n return {\n id,\n kind: \"cli\",\n bin: optionalString(raw.bin, `sources[${id}].bin`) ?? \"bird\",\n bookmarksArgs: bookmarksArgs.length > 0 ? bookmarksArgs : [\"bookmarks\", \"--json\"],\n postsArgs: (() => {\n const postsArgs = stringArray(raw.postsArgs, `sources[${id}].postsArgs`);\n return postsArgs.length > 0 ? postsArgs : undefined;\n })(),\n };\n}\n\nexport function parseXConnectorConfig(raw: unknown): XConnectorConfig {\n const input = objectOrThrow(raw, \"xConnector\");\n\n const enabled = coerceXBool(input.enabled ?? true, \"xConnector.enabled\");\n\n const userId = optionalString(input.userId, \"xConnector.userId\");\n if (userId !== undefined && !/^\\d+$/.test(userId)) {\n throw new XConfigError(\"xConnector.userId must be the numeric X user id\");\n }\n\n const sourcesRaw = input.sources;\n if (!Array.isArray(sourcesRaw) || sourcesRaw.length === 0) {\n throw new XConfigError(\"xConnector.sources must be a non-empty array\");\n }\n const sources: XSourceConfig[] = [];\n const seenIds = new Set<string>();\n for (let index = 0; index < sourcesRaw.length; index++) {\n const entry = objectOrThrow(sourcesRaw[index], `sources[${index}]`);\n const kind = requiredString(entry.kind, `sources[${index}].kind`);\n if (!(X_SOURCE_KINDS as readonly string[]).includes(kind)) {\n throw new XConfigError(\n `sources[${index}].kind must be one of ${X_SOURCE_KINDS.join(\", \")} (got ${JSON.stringify(kind)})`\n );\n }\n const source =\n kind === \"mcp\" ? parseMcpSource(entry) : kind === \"corpusDir\" ? parseCorpusSource(entry) : parseCliSource(entry);\n if (seenIds.has(source.id)) {\n throw new XConfigError(`duplicate source id ${JSON.stringify(source.id)} in xConnector.sources`);\n }\n seenIds.add(source.id);\n sources.push(source);\n }\n\n const sourcePriority = stringArray(input.sourcePriority, \"xConnector.sourcePriority\");\n for (const id of sourcePriority) {\n if (!seenIds.has(id)) {\n throw new XConfigError(`xConnector.sourcePriority references unknown source id ${JSON.stringify(id)}`);\n }\n }\n const orderedPriority = sourcePriority.length > 0 ? sourcePriority : sources.map((source) => source.id);\n\n const memoryModeRaw = optionalString(input.memoryMode, \"xConnector.memoryMode\") ?? \"suggest\";\n if (!(X_MEMORY_MODES as readonly string[]).includes(memoryModeRaw)) {\n throw new XConfigError(\n `xConnector.memoryMode must be one of ${X_MEMORY_MODES.join(\", \")} (got ${JSON.stringify(memoryModeRaw)})`\n );\n }\n\n const syncSchedule = optionalString(input.syncSchedule, \"xConnector.syncSchedule\") ?? \"3x-daily\";\n if (!X_SYNC_SCHEDULES.includes(syncSchedule)) {\n throw new XConfigError(\n `xConnector.syncSchedule must be one of ${X_SYNC_SCHEDULES.join(\", \")} (got ${JSON.stringify(syncSchedule)})`\n );\n }\n\n return {\n enabled,\n userId,\n sources,\n sourcePriority: orderedPriority,\n syncSchedule,\n memoryMode: memoryModeRaw as XMemoryMode,\n stateDir: optionalString(input.stateDir, \"xConnector.stateDir\") ?? X_DEFAULT_STATE_DIR,\n };\n}\n\n/** OAuth2 client credentials for the MCP source, with env fallbacks. */\nexport function resolveMcpClientCredentials(\n source: XMcpSourceConfig,\n env: NodeJS.ProcessEnv = process.env\n): { clientId?: string; clientSecret?: string; tokenFile: string } {\n const clientId = env.REMNIC_X_CLIENT_ID ?? env.X_CLIENT_ID;\n const clientSecret = env.REMNIC_X_CLIENT_SECRET ?? env.X_CLIENT_SECRET;\n return {\n clientId: typeof clientId === \"string\" && clientId.trim().length > 0 ? clientId.trim() : undefined,\n clientSecret: typeof clientSecret === \"string\" && clientSecret.trim().length > 0 ? clientSecret.trim() : undefined,\n tokenFile: source.tokenFile,\n };\n}\n\n/** The effective monthly cost cap across all paid sources (max of per-source caps). */\nexport function monthlyCostCapUsd(config: XConnectorConfig): number {\n let cap = 0;\n for (const source of config.sources) {\n if (source.kind === \"mcp\") cap = Math.max(cap, source.budget.maxCostUsdPerMonth);\n }\n return cap;\n}\n","/**\n * Default on-disk memory sink: `suggest` mode writes review-queue\n * files under `<stateDir>/suggestions/`, `store` mode writes directly\n * under `<stateDir>/records/`. Hosts with a live Remnic daemon pass\n * their own XMemorySink instead.\n */\n\nimport { mkdir, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport type { XMemorySink, XMemorySuggestion } from \"./types.js\";\n\nexport interface FileSinkOptions {\n stateDir: string;\n mode: \"suggest\" | \"store\";\n}\n\n/** On-disk sink honoring the memoryMode trust gate by directory. */\nexport function createFileSink(options: FileSinkOptions): XMemorySink {\n const root = path.join(expandTildePath(options.stateDir), options.mode === \"store\" ? \"records\" : \"suggestions\");\n const write = async (suggestion: XMemorySuggestion): Promise<void> => {\n await mkdir(root, { recursive: true });\n const safeName = suggestion.record.postId.replace(/[^0-9A-Za-z._-]/g, \"_\");\n const target = path.join(root, `${safeName}.json`);\n const tmp = `${target}.tmp`;\n await writeFile(tmp, `${JSON.stringify(suggestion, null, 2)}\\n`, { mode: 0o600 });\n await rename(tmp, target);\n };\n return {\n submitSuggestion: write,\n storeMemory: write,\n };\n}\n","/**\n * Canonical unknown-payload guard for @remnic/connector-x.\n *\n * Fields stay `unknown` after narrowing; every field read is checked at\n * its use site with `typeof` / `in` / `Array.isArray`.\n */\n\nexport function isXObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * Payload normalization for all three X sources plus the shared\n * dedupe fingerprint and the record → memory mapping.\n *\n * All parsers are shape-tolerant: X MCP payloads follow the v2 API\n * expansions shape (`data[]` + `includes.users`), local corpora and\n * CLI tools use a variety of field aliases. Every field read is\n * checked; unrecognized shapes yield empty results, never crashes.\n */\n\nimport { createHash } from \"node:crypto\";\n\nimport { isXObject } from \"./guards.js\";\nimport type { XMemorySuggestion, XPostRecord, XRecordKind } from \"./types.js\";\n\n/** Sorts keys recursively so key order never changes the fingerprint. */\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, (_key, entry) => {\n if (isXObject(entry)) {\n return Object.fromEntries(\n Object.keys(entry)\n .sort()\n .map((key) => [key, entry[key]])\n );\n }\n return entry;\n });\n}\n\n/** Identity fingerprint: same post + same content = same memory, regardless of source key order. */\nexport function recordFingerprint(record: XPostRecord): string {\n return createHash(\"sha256\")\n .update(\n stableStringify({\n postId: record.postId,\n kind: record.kind,\n text: record.text,\n urls: [...record.urls].sort(),\n authorUsername: record.author?.username ?? null,\n })\n )\n .digest(\"hex\");\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n }\n return undefined;\n}\n\nfunction asStringArray(value: unknown): string[] {\n if (typeof value === \"string\") return [value];\n if (!Array.isArray(value)) return [];\n const urls: string[] = [];\n for (const entry of value) {\n if (typeof entry === \"string\" && entry.trim().length > 0) urls.push(entry.trim());\n else if (isXObject(entry)) {\n const expanded = firstString(entry.expanded_url, entry.url, entry.href);\n if (expanded !== undefined) urls.push(expanded);\n }\n }\n return urls;\n}\n\nfunction kindFrom(value: unknown, fallback: XRecordKind): XRecordKind {\n const raw = typeof value === \"string\" ? value.trim().toLowerCase() : \"\";\n if (raw === \"bookmark\" || raw === \"bookmarks\") return \"bookmark\";\n if (raw === \"own_post\" || raw === \"post\" || raw === \"tweet\" || raw === \"own-post\") {\n return \"own_post\";\n }\n return fallback;\n}\n\n/**\n * Normalizes an MCP tool-result payload (v2 expansions shape) into\n * records. Accepts `{data: [...]}` with `includes.users`, a bare\n * array, or `{bookmarks: [...]}`.\n */\nexport function normalizeMcpPayload(payload: unknown, kind: XRecordKind, ownUsername?: string): XPostRecord[] {\n const container = isXObject(payload) ? payload : {};\n const includesUsers = isXObject(container.includes)\n ? Array.isArray(container.includes.users)\n ? container.includes.users\n : []\n : [];\n const rows = Array.isArray(container.data)\n ? container.data\n : Array.isArray(payload)\n ? payload\n : isXObject(container.bookmarks) && Array.isArray(container.bookmarks.data)\n ? container.bookmarks.data\n : Array.isArray(container.bookmarks)\n ? container.bookmarks\n : [];\n const records: XPostRecord[] = [];\n for (const row of rows) {\n const record = normalizeEntry(row, kind, includesUsers, ownUsername);\n if (record !== null) records.push(record);\n }\n return records;\n}\n\n/** Normalizes one corpus/CLI entry (tolerant field aliases). */\nexport function normalizeCorpusEntry(\n entry: unknown,\n fallbackKind: XRecordKind,\n ownUsername?: string\n): XPostRecord | null {\n return normalizeEntry(entry, fallbackKind, [], ownUsername);\n}\n\nfunction normalizeEntry(\n entry: unknown,\n fallbackKind: XRecordKind,\n includesUsers: unknown[],\n ownUsername?: string\n): XPostRecord | null {\n if (!isXObject(entry)) return null;\n const postId = firstString(entry.post_id, entry.id, entry.tweet_id, entry.postId);\n const text = firstString(entry.text, entry.full_text, entry.content, entry.note) ?? \"\";\n if (postId === undefined || (text.length === 0 && !hasUrls(entry))) return null;\n\n const kind = kindFrom(entry.kind ?? entry.type, fallbackKind);\n const authorRaw = isXObject(entry.author) ? entry.author : entry;\n const authorId = firstString(entry.author_id, entry.authorId, authorRaw.id);\n const authorUsername =\n firstString(authorRaw.username, authorRaw.handle, authorRaw.screen_name) ??\n lookupIncludedUsername(includesUsers, authorId) ??\n ownUsername;\n const authorName = firstString(authorRaw.name, authorRaw.display_name);\n const author =\n authorUsername !== undefined || authorId !== undefined || authorName !== undefined\n ? {\n ...(authorId !== undefined ? { id: authorId } : {}),\n ...(authorUsername !== undefined ? { username: authorUsername } : {}),\n ...(authorName !== undefined ? { name: authorName } : {}),\n }\n : undefined;\n\n const urls = collectUrls(entry);\n const createdAt = firstString(entry.created_at, entry.createdAt, entry.created_at_iso);\n const bookmarkedAt = firstString(entry.bookmarked_at, entry.bookmarkedAt, entry.saved_at);\n const mediaCount = countMedia(entry);\n const enrichment = isXObject(entry.enrichment) ? entry.enrichment : undefined;\n\n return {\n postId,\n kind,\n ...(author !== undefined ? { author } : {}),\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(bookmarkedAt !== undefined ? { bookmarkedAt } : {}),\n text: trimTcoSuffix(text, entry),\n urls,\n mediaCount,\n ...(enrichment !== undefined ? { enrichment } : {}),\n };\n}\n\nfunction hasUrls(entry: Record<string, unknown>): boolean {\n return collectUrls(entry).length > 0;\n}\n\nfunction collectUrls(entry: Record<string, unknown>): string[] {\n const urls = [...asStringArray(entry.urls), ...asStringArray(entry.url), ...asStringArray(entry.links)];\n if (isXObject(entry.entities) && Array.isArray(entry.entities.urls)) {\n urls.push(...asStringArray(entry.entities.urls));\n }\n return [...new Set(urls)];\n}\n\nfunction countMedia(entry: Record<string, unknown>): number {\n if (isXObject(entry.attachments) && Array.isArray(entry.attachments.media_keys)) {\n return entry.attachments.media_keys.length;\n }\n if (Array.isArray(entry.media)) return entry.media.length;\n if (isXObject(entry.media) && Array.isArray(entry.media.media_keys)) {\n return entry.media.media_keys.length;\n }\n return 0;\n}\n\nfunction lookupIncludedUsername(includesUsers: unknown[], authorId: string | undefined): string | undefined {\n if (authorId === undefined) return undefined;\n for (const user of includesUsers) {\n if (isXObject(user) && user.id === authorId) {\n return firstString(user.username, user.screen_name);\n }\n }\n return undefined;\n}\n\n/**\n * X appends the share URL (a t.co short link) to tweet text; when the\n * text ends with exactly that short link, drop it — the expanded URL\n * is already in `urls`.\n */\nfunction trimTcoSuffix(text: string, entry: Record<string, unknown>): string {\n const entities = isXObject(entry.entities) && Array.isArray(entry.entities.urls) ? entry.entities.urls : [];\n for (const raw of entities) {\n if (!isXObject(raw) || typeof raw.url !== \"string\") continue;\n if (raw.url.includes(\"://t.co/\") && text.endsWith(raw.url)) {\n return text.slice(0, text.length - raw.url.length).trimEnd();\n }\n }\n return text;\n}\n\nfunction postUrl(record: XPostRecord): string {\n const username = record.author?.username;\n return username !== undefined\n ? `https://x.com/${username}/status/${record.postId}`\n : `https://x.com/i/status/${record.postId}`;\n}\n\nconst QUOTE = '\"';\n\n/**\n * Record → memory mapping (issue #2009 §2):\n * - bookmarks → tag `x/bookmark`, category `reference` (carries a URL) or `interest`\n * - own posts → tag `x/post`, category `expression`, higher confidence\n */\nexport function suggestionForRecord(record: XPostRecord): XMemorySuggestion {\n const quoted = `${QUOTE}${record.text.slice(0, 280)}${record.text.length > 280 ? \"…\" : \"\"}${QUOTE}`;\n const from = record.author?.username !== undefined ? ` from @${record.author.username}` : \"\";\n const firstUrl = record.urls[0];\n const title = enrichmentTitle(record);\n const isOwnPost = record.kind === \"own_post\";\n const content = isOwnPost\n ? `Posted on X: ${quoted}${firstUrl !== undefined ? ` ${firstUrl}` : \"\"}${title !== undefined ? ` (${title})` : \"\"}`\n : `Bookmarked on X${from}: ${quoted}${firstUrl !== undefined ? ` ${firstUrl}` : \"\"}${\n title !== undefined ? ` (${title})` : \"\"\n }`;\n return {\n record,\n tags: [isOwnPost ? \"x/post\" : \"x/bookmark\"],\n category: isOwnPost ? \"expression\" : firstUrl !== undefined ? \"reference\" : \"interest\",\n ...(record.author?.username !== undefined ? { entityRef: `person-${record.author.username.toLowerCase()}` } : {}),\n confidence: isOwnPost ? 0.9 : 0.7,\n postUrl: postUrl(record),\n content,\n };\n}\n\nfunction enrichmentTitle(record: XPostRecord): string | undefined {\n if (record.enrichment === undefined) return undefined;\n const title = isXObject(record.enrichment) ? record.enrichment.title : undefined;\n return typeof title === \"string\" && title.trim().length > 0 ? title.trim() : undefined;\n}\n","/**\n * Official X MCP client (Streamable HTTP, protocol 2025-06-18).\n *\n * Contract per https://docs.x.com (MCP, launched 2026-06-30): JSON-RPC\n * over HTTP POST; `initialize` hands back an `Mcp-Session-Id` response\n * header; response bodies may be plain JSON or SSE (`text/event-stream`,\n * `data:` lines). Reads bill against X API credits — `credits depleted`\n * (HTTP 402 or a 402-in-tool-result payload) maps to\n * XCreditsDepletedError so callers can skip the cycle cleanly instead\n * of erroring. Session `initialize`/`tools/list` are free.\n *\n * The API token is never logged and never included in thrown error\n * messages.\n */\n\nimport { setTimeout as sleepMs } from \"node:timers/promises\";\n\nimport {\n ConnectorApiError,\n describeNetworkError,\n retryingFetch,\n stripTrailingSlashes,\n} from \"@remnic/core/http-retry\";\n\nimport { isXObject } from \"./guards.js\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_RETRIES = 2;\nconst MAX_RETRY_DELAY_MS = 8_000;\nexport const X_MCP_PROTOCOL_VERSION = \"2025-06-18\";\nexport const X_MCP_DEFAULT_URL = \"https://api.x.com/mcp\";\n\nexport class XMcpError extends ConnectorApiError {\n constructor(\n message: string,\n status?: number\n ) {\n super(message, status);\n this.name = \"XMcpError\";\n }\n}\n\n/** Clean-skip signal: the account's X API credits are exhausted. */\nexport class XCreditsDepletedError extends Error {\n constructor() {\n super(\"X API credits depleted — skipping this sync cycle\");\n this.name = \"XCreditsDepletedError\";\n }\n}\n\nexport interface XMcpToolCallResult {\n isError: boolean;\n /** text blocks of result.content, in order. */\n texts: string[];\n raw: unknown;\n}\n\nexport interface XMcpClientOptions {\n url?: string;\n /** Lazily supplies a valid bearer token (user-context OAuth2). */\n tokenProvider: () => Promise<string>;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n protocolVersion?: string;\n clientName?: string;\n clientVersion?: string;\n}\n\ninterface JsonRpcMessage {\n jsonrpc: \"2.0\";\n id?: number;\n method: string;\n params?: unknown;\n}\n\ninterface RpcResponse {\n result: unknown;\n headers: Headers | null;\n}\n\n/** Parses an SSE body into decoded `data:` JSON values, in order. */\nexport function parseSseData(body: string): unknown[] {\n const values: unknown[] = [];\n for (const line of body.split(/\\r?\\n/)) {\n if (!line.startsWith(\"data:\")) continue;\n const payload = line.slice(\"data:\".length).trim();\n if (payload.length === 0) continue;\n try {\n values.push(JSON.parse(payload));\n } catch {\n // Ignore keep-alives and non-JSON comments.\n }\n }\n return values;\n}\n\n/** True when a tool-result body signals exhausted credits (docs + observed shape). */\nexport function looksLikeCreditsDepleted(text: string): boolean {\n return text.includes(\"credits depleted\") || text.includes('\"status\":402');\n}\n\n/** Extracts text blocks from an MCP tool-result content array. */\nexport function toolResultTexts(payload: Record<string, unknown>): string[] {\n const content = Array.isArray(payload.content) ? payload.content : [];\n const texts: string[] = [];\n for (const block of content) {\n if (isXObject(block) && block.type === \"text\" && typeof block.text === \"string\") {\n texts.push(block.text);\n }\n }\n return texts;\n}\n\nexport class XMcpClient {\n private readonly url: string;\n private readonly tokenProvider: () => Promise<string>;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly protocolVersion: string;\n private readonly clientName: string;\n private readonly clientVersion: string;\n private sessionId: string | null = null;\n private nextMessageId = 1;\n\n constructor(options: XMcpClientOptions) {\n if (typeof options.tokenProvider !== \"function\") {\n throw new XMcpError(\"XMcpClient requires a tokenProvider function\");\n }\n this.url = stripTrailingSlashes(options.url ?? X_MCP_DEFAULT_URL);\n this.tokenProvider = options.tokenProvider;\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep = options.sleep ?? sleepMs;\n this.protocolVersion = options.protocolVersion ?? X_MCP_PROTOCOL_VERSION;\n this.clientName = options.clientName ?? \"remnic-connector-x\";\n this.clientVersion = options.clientVersion ?? \"1.0.0\";\n }\n\n /**\n * Calls an MCP tool. Re-initializes once when the server rejects the\n * session id (e.g. expired session), then retries the call.\n */\n async callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<XMcpToolCallResult> {\n return this.withSessionRetry(async () => {\n const { result } = await this.rpcMessage(\n {\n jsonrpc: \"2.0\",\n id: this.allocateId(),\n method: \"tools/call\",\n params: { name, arguments: args },\n },\n signal,\n true\n );\n if (!isXObject(result)) {\n throw new XMcpError(`tool ${name} returned a non-object result`);\n }\n const texts = toolResultTexts(result);\n const isError = result.isError === true;\n if (isError && looksLikeCreditsDepleted(texts.join(\"\\n\"))) {\n throw new XCreditsDepletedError();\n }\n return { isError, texts, raw: result };\n });\n }\n\n /** Best-effort session shutdown (MCP `DELETE`). */\n async close(): Promise<void> {\n if (this.sessionId === null) return;\n const sessionId = this.sessionId;\n this.sessionId = null;\n try {\n await this.fetchImpl(this.url, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${await this.tokenProvider()}`,\n \"Mcp-Session-Id\": sessionId,\n },\n });\n } catch {\n // Shutdown is advisory.\n }\n }\n\n private async withSessionRetry<T>(operation: () => Promise<T>): Promise<T> {\n await this.ensureSession();\n try {\n return await operation();\n } catch (err) {\n if (err instanceof XMcpError && err.status === 404) {\n // Session expired server-side: drop it and retry once on a fresh session.\n this.sessionId = null;\n await this.ensureSession();\n return operation();\n }\n throw err;\n }\n }\n\n private async ensureSession(signal?: AbortSignal): Promise<void> {\n if (this.sessionId !== null) return;\n const initialized = await this.rpcMessage(\n {\n jsonrpc: \"2.0\",\n id: this.allocateId(),\n method: \"initialize\",\n params: {\n protocolVersion: this.protocolVersion,\n capabilities: {},\n clientInfo: { name: this.clientName, version: this.clientVersion },\n },\n },\n signal,\n true\n );\n const sessionId = initialized.headers?.get(\"mcp-session-id\");\n if (typeof sessionId === \"string\" && sessionId.length > 0) {\n this.sessionId = sessionId;\n }\n // Initialized notification: no id, no response body expected.\n await this.rpcMessage({ jsonrpc: \"2.0\", method: \"notifications/initialized\" }, signal, false);\n }\n\n private allocateId(): number {\n const id = this.nextMessageId;\n this.nextMessageId += 1;\n return id;\n }\n\n private async rpcMessage(\n message: JsonRpcMessage,\n signal: AbortSignal | undefined,\n expectBody: boolean\n ): Promise<RpcResponse> {\n const response = await retryingFetch(this.url, {\n buildInit: async () => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n Authorization: `Bearer ${await this.tokenProvider()}`,\n };\n if (this.sessionId !== null) headers[\"Mcp-Session-Id\"] = this.sessionId;\n return {\n method: \"POST\",\n headers,\n body: JSON.stringify(message),\n };\n },\n fetchImpl: this.fetchImpl,\n sleep: this.sleep,\n signal,\n timeoutMs: this.timeoutMs,\n maxRetries: MAX_RETRIES,\n maxRetryDelayMs: MAX_RETRY_DELAY_MS,\n backoffBaseMs: 500,\n networkError: (err, attempts) =>\n new XMcpError(`X MCP request failed after ${attempts} attempts: ${describeNetworkError(err)}`),\n retryableError: (retryable) => new XMcpError(`X MCP responded ${retryable.status}`, retryable.status),\n });\n if (response.status === 402) throw new XCreditsDepletedError();\n if (response.status === 401) {\n throw new XMcpError(\n \"X MCP rejected the bearer token (401) — the OAuth2 token or refresh chain is broken; re-authorize\",\n 401\n );\n }\n if (response.status === 404 && this.sessionId !== null) {\n throw new XMcpError(\"X MCP session expired\", 404);\n }\n if (!response.ok) {\n throw new XMcpError(`X MCP responded ${response.status}`, response.status);\n }\n if (!expectBody || response.status === 202) {\n return { result: null, headers: response.headers };\n }\n const body = await response.text();\n return {\n result: this.decodeBody(body, response.headers, message.id),\n headers: response.headers,\n };\n }\n\n private decodeBody(body: string, headers: Headers, messageId: number | undefined): unknown {\n const contentType = headers.get(\"content-type\") ?? \"\";\n let messages: unknown[];\n if (contentType.includes(\"text/event-stream\")) {\n messages = parseSseData(body);\n } else {\n try {\n messages = [JSON.parse(body)];\n } catch {\n throw new XMcpError(\"X MCP returned a non-JSON body\");\n }\n }\n const match = messages.find((entry) => isXObject(entry) && entry.id === messageId && entry.error === undefined);\n if (match === undefined) {\n const errorEntry = messages.find(\n (entry) => isXObject(entry) && entry.id === messageId && entry.error !== undefined\n );\n if (errorEntry !== undefined && isXObject(errorEntry)) {\n const rpcError = errorEntry.error;\n const detail =\n isXObject(rpcError) && typeof rpcError.message === \"string\"\n ? `${String(rpcError.code)}: ${rpcError.message}`\n : \"unknown JSON-RPC error\";\n if (looksLikeCreditsDepleted(detail)) throw new XCreditsDepletedError();\n throw new XMcpError(`X MCP tool call failed: ${detail}`);\n }\n throw new XMcpError(\"X MCP response carried no message for this request id\");\n }\n if (isXObject(match) && \"result\" in match) return match.result;\n return match;\n }\n}\n\n\n","/**\n * OAuth2 user-token store with single-owner refresh for the X MCP\n * source.\n *\n * X rotates the refresh token on EVERY refresh. Two independent\n * refreshers fork the chain and kill one of them (observed failure:\n * HTTP 401 on refresh after two tools both rotated the same grant).\n * This store is therefore the single owner of the refresh chain: the\n * refresh runs only while holding `${tokenFile}.lock`; a concurrent\n * refresher waits, then adopts the rotated pair from the file — it\n * never refreshes in parallel.\n *\n * Token files are written 0600, atomic (tmp + rename), and unknown\n * top-level fields are preserved on rotation.\n */\n\nimport { open, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { setTimeout as sleepMs } from \"node:timers/promises\";\n\nimport { isXObject } from \"./guards.js\";\n\nexport const X_TOKEN_REFRESH_URL = \"https://api.x.com/2/oauth2/token\";\nconst EXPIRY_MARGIN_MS = 60_000;\nconst DEFAULT_LOCK_STALE_MS = 60_000;\nconst DEFAULT_LOCK_WAIT_MS = 15_000;\nconst LOCK_POLL_MS = 200;\n\nexport class XTokenError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"XTokenError\";\n }\n}\n\n/** The refresh chain forked or was revoked — re-authorization is required. */\nexport class XRefreshChainBrokenError extends XTokenError {\n constructor(detail: string) {\n super(\n `X OAuth2 refresh failed (${detail}). The refresh chain was rotated by another refresher or the grant was revoked. Ensure only one refresher owns the chain, then re-authorize to write a fresh token file.`\n );\n this.name = \"XRefreshChainBrokenError\";\n }\n}\n\nexport interface XTokenPair {\n accessToken: string;\n refreshToken: string;\n /** Epoch ms when the access token expires. */\n expiresAt: number;\n}\n\nexport interface XTokenStoreOptions {\n tokenFile: string;\n clientId: string;\n clientSecret: string;\n /** OAuth2 token endpoint. */\n refreshUrl?: string;\n fetchImpl?: typeof fetch;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n lockStaleMs?: number;\n lockWaitMs?: number;\n}\n\ninterface LockHandle {\n release(): Promise<void>;\n}\n\nclass LockUnavailableError extends Error {}\n\nexport class XTokenStore {\n private readonly tokenFile: string;\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly refreshUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly lockStaleMs: number;\n private readonly lockWaitMs: number;\n private cached: XTokenPair | null = null;\n\n constructor(options: XTokenStoreOptions) {\n if (typeof options.tokenFile !== \"string\" || options.tokenFile.length === 0) {\n throw new XTokenError(\"XTokenStore requires tokenFile\");\n }\n for (const [field, value] of [\n [\"clientId\", options.clientId],\n [\"clientSecret\", options.clientSecret],\n ] as const) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new XTokenError(\n `XTokenStore requires ${field} (pre-registered confidential client; set xConnector source auth or the REMNIC_X_CLIENT_ID/REMNIC_X_CLIENT_SECRET env vars)`\n );\n }\n }\n this.tokenFile = options.tokenFile;\n this.clientId = options.clientId.trim();\n this.clientSecret = options.clientSecret.trim();\n this.refreshUrl = options.refreshUrl ?? X_TOKEN_REFRESH_URL;\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.now = options.now ?? (() => Date.now());\n this.lockStaleMs = options.lockStaleMs ?? DEFAULT_LOCK_STALE_MS;\n this.lockWaitMs = options.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS;\n this.sleep = options.sleep ?? sleepMs;\n }\n\n /** Returns a valid access token, refreshing under the lock when expired. */\n async getAccessToken(): Promise<string> {\n return (await this.getValidPair()).accessToken;\n }\n\n private async getValidPair(): Promise<XTokenPair> {\n const current = this.cached ?? (await this.readTokenFile());\n if (current !== null && current.expiresAt - EXPIRY_MARGIN_MS > this.now()) {\n this.cached = current;\n return current;\n }\n const refreshed = await this.refreshWithLock();\n this.cached = refreshed;\n return refreshed;\n }\n\n /**\n * Refreshes the token pair under the file lock. When another owner\n * holds the lock, waits for it, then adopts the pair it wrote.\n */\n async refresh(): Promise<XTokenPair> {\n const pair = await this.refreshWithLock();\n this.cached = pair;\n return pair;\n }\n\n private async refreshWithLock(): Promise<XTokenPair> {\n let lock: LockHandle | null = null;\n try {\n lock = await this.acquireLock();\n } catch (err) {\n if (err instanceof LockUnavailableError) {\n return this.waitForOtherOwner();\n }\n throw err;\n }\n try {\n // Re-read under the lock: another owner may have rotated while we waited.\n const underLock = await this.readTokenFile();\n if (underLock !== null && underLock.expiresAt - EXPIRY_MARGIN_MS > this.now()) {\n return underLock;\n }\n const refreshToken =\n underLock?.refreshToken ??\n (() => {\n throw new XTokenError(\n `X token file ${this.tokenFile} is missing or carries no refresh_token — run the OAuth2 user-code flow once to seed it`\n );\n })();\n const rotated = await this.requestRefresh(refreshToken);\n await this.writeTokenFile(rotated);\n return rotated;\n } finally {\n await lock.release();\n }\n }\n\n private async waitForOtherOwner(): Promise<XTokenPair> {\n const deadline = this.now() + this.lockWaitMs;\n while (this.now() < deadline) {\n await this.sleep(LOCK_POLL_MS);\n const pair = await this.readTokenFile();\n if (pair !== null && pair.expiresAt - EXPIRY_MARGIN_MS > this.now()) {\n return pair;\n }\n }\n throw new XTokenError(\n `another refresher has held ${this.tokenFile}.lock for over ` +\n `${Math.round(this.lockWaitMs / 1000)}s — investigate the competing owner`\n );\n }\n\n private async acquireLock(): Promise<LockHandle> {\n const lockPath = `${this.tokenFile}.lock`;\n const deadline = this.now() + this.lockWaitMs;\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n await handle.write(`${process.pid}\\n`);\n await handle.close();\n return {\n release: async () => {\n try {\n await unlink(lockPath);\n } catch {\n // Already gone — nothing to release.\n }\n },\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") throw err;\n if (await this.stealStaleLock(lockPath)) continue;\n if (this.now() >= deadline) throw new LockUnavailableError(\"lock wait timeout\");\n await this.sleep(LOCK_POLL_MS);\n }\n }\n }\n\n /** Steals the lock when its mtime is older than lockStaleMs. */\n private async stealStaleLock(lockPath: string): Promise<boolean> {\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(lockPath)).mtimeMs;\n } catch {\n // Lock vanished between open(EEXIST) and stat — retry create.\n return true;\n }\n if (this.now() - mtimeMs < this.lockStaleMs) return false;\n try {\n await unlink(lockPath);\n } catch {\n // Another stealer won the race — the next create attempt decides.\n }\n return true;\n }\n\n private async requestRefresh(refreshToken: string): Promise<XTokenPair> {\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: this.clientId,\n }).toString();\n let response: Response;\n try {\n response = await this.fetchImpl(this.refreshUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Authorization: `Basic ${Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\")}`,\n },\n body,\n });\n } catch (err) {\n throw new XTokenError(`X OAuth2 refresh request failed: ${err instanceof Error ? err.name : \"network error\"}`);\n }\n if (response.status === 400 || response.status === 401 || response.status === 403) {\n throw new XRefreshChainBrokenError(`HTTP ${response.status}`);\n }\n if (!response.ok) {\n throw new XTokenError(`X OAuth2 refresh responded HTTP ${response.status}`);\n }\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new XTokenError(\"X OAuth2 refresh returned a non-JSON body\");\n }\n if (!isTokenResponse(payload)) {\n throw new XTokenError(\"X OAuth2 refresh returned an unexpected payload shape\");\n }\n return {\n accessToken: payload.access_token,\n // X rotates refresh tokens; keep the old one when the response omits it.\n refreshToken: typeof payload.refresh_token === \"string\" ? payload.refresh_token : refreshToken,\n expiresAt: this.now() + payload.expires_in * 1_000,\n };\n }\n\n private async readTokenFile(): Promise<XTokenPair | null> {\n let raw: string;\n try {\n raw = await readFile(this.tokenFile, \"utf8\");\n } catch {\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new XTokenError(`X token file ${this.tokenFile} is not valid JSON`);\n }\n if (!isXObject(parsed)) return null;\n const accessToken = firstString(parsed.access_token, parsed.accessToken);\n if (accessToken === undefined) {\n throw new XTokenError(`X token file ${this.tokenFile} carries no access token`);\n }\n const refreshToken = firstString(parsed.refresh_token, parsed.refreshToken) ?? \"\";\n const expiresAtRaw = parsed.expires_at ?? parsed.expiresAt;\n const expiresAt =\n typeof expiresAtRaw === \"number\" && Number.isFinite(expiresAtRaw)\n ? expiresAtRaw\n : // Files without expiry force one refresh on first use.\n 0;\n return { accessToken, refreshToken, expiresAt };\n }\n\n /** Atomic 0600 write; preserves unknown top-level fields from the prior file. */\n private async writeTokenFile(pair: XTokenPair): Promise<void> {\n let previous: Record<string, unknown> = {};\n try {\n const priorRaw: unknown = JSON.parse(await readFile(this.tokenFile, \"utf8\"));\n if (isXObject(priorRaw)) previous = priorRaw;\n } catch {\n previous = {};\n }\n const next: Record<string, unknown> = {\n ...previous,\n access_token: pair.accessToken,\n refresh_token: pair.refreshToken,\n expires_at: pair.expiresAt,\n };\n const dir = path.dirname(this.tokenFile);\n const tmpPath = path.join(dir, `.${path.basename(this.tokenFile)}.${process.pid}.${Date.now()}.tmp`);\n await writeFile(tmpPath, `${JSON.stringify(next, null, 2)}\\n`, { mode: 0o600 });\n try {\n await rename(tmpPath, this.tokenFile);\n } catch (err) {\n try {\n await unlink(tmpPath);\n } catch {\n // Best-effort cleanup.\n }\n throw new XTokenError(\n `failed to persist rotated X tokens to ${this.tokenFile}: ${err instanceof Error ? err.name : \"write error\"}`\n );\n }\n }\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n }\n return undefined;\n}\n\nfunction isTokenResponse(value: unknown): value is {\n access_token: string;\n refresh_token?: unknown;\n expires_in: number;\n} {\n return isXObject(value) && typeof value.access_token === \"string\";\n}\n","/**\n * Pluggable X sources (issue #2009): official X MCP (paid, budget-\n * capped), a local corpus directory (zero credits), and a cookie-CLI\n * such as `bird` (zero credits). All three emit the same normalized\n * XPostRecord currency and degrade with a `skipped` reason instead of\n * throwing, except auth/config breakage which surfaces to the sync\n * report's `error` field.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { lstat, readFile, readdir, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport {\n type XBudgetConfig,\n type XCliSourceConfig,\n type XCorpusSourceConfig,\n type XMcpSourceConfig,\n resolveMcpClientCredentials,\n} from \"./config.js\";\nimport { isXObject } from \"./guards.js\";\nimport { XCreditsDepletedError, XMcpClient, XMcpError } from \"./mcp-client.js\";\nimport { normalizeCorpusEntry, normalizeMcpPayload } from \"./normalize.js\";\nimport { XTokenStore } from \"./token-store.js\";\nimport type {\n XBudgetRuntime,\n XPostRecord,\n XRecordKind,\n XSource,\n XSourceFetchContext,\n XSourceFetchOutcome,\n} from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type XExecFn = (bin: string, args: string[]) => Promise<{ stdout: string; stderr: string }>;\n\nexport interface XSourceDeps {\n /** Owning X user id — gates the own-posts timeline reads for MCP sources. */\n userId?: string;\n fetchImpl?: typeof fetch;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n env?: NodeJS.ProcessEnv;\n execImpl?: XExecFn;\n}\n\n/** Budget enforcement for paid (MCP) sources. */\nexport class XBudgetTracker implements XBudgetRuntime {\n pagesUsed = 0;\n reads = 0;\n readonly maxPages: number;\n\n constructor(\n private readonly budget: XBudgetConfig,\n private readonly monthSpendUsd: number\n ) {\n this.maxPages = budget.maxPagesPerSync;\n }\n\n canRead(): { ok: true } | { ok: false; reason: string; detail?: string } {\n if (this.pagesUsed >= this.budget.maxPagesPerSync) {\n return { ok: false, reason: \"page-cap\", detail: `${this.budget.maxPagesPerSync} pages/sync` };\n }\n const projected = this.monthSpendUsd + (this.reads + 1) * this.budget.costPerReadUsd;\n if (projected > this.budget.maxCostUsdPerMonth + 1e-9) {\n return {\n ok: false,\n reason: \"monthly-cost-cap\",\n detail: `$${projected.toFixed(2)} projected vs $${this.budget.maxCostUsdPerMonth.toFixed(2)} cap`,\n };\n }\n return { ok: true };\n }\n\n noteRead(): void {\n this.reads += 1;\n }\n}\n\n/** Zero-credit sources never consume budget. */\nexport const unlimitedBudget: XBudgetRuntime = {\n pagesUsed: 0,\n maxPages: Number.POSITIVE_INFINITY,\n canRead: () => ({ ok: true }),\n noteRead: () => {},\n};\n\n/** Builds the source adapter for a parsed source config entry. */\nexport function createXSource(\n config: XMcpSourceConfig | XCorpusSourceConfig | XCliSourceConfig,\n deps: XSourceDeps = {}\n): XSource {\n if (config.kind === \"mcp\") return createMcpSource(config, deps);\n if (config.kind === \"corpusDir\") return createCorpusSource(config);\n return createCliSource(config, deps);\n}\n\n// ── MCP source ──────────────────────────────────────────────────────────────\n\nfunction createMcpSource(config: XMcpSourceConfig, deps: XSourceDeps): XSource {\n const credentials = resolveMcpClientCredentials(config, deps.env ?? process.env);\n let client: XMcpClient | null = null;\n const getClient = (): XMcpClient => {\n if (client === null) {\n if (credentials.clientId === undefined || credentials.clientSecret === undefined) {\n throw new XMcpError(\n `source ${config.id}: OAuth2 client credentials missing — set auth on the source or REMNIC_X_CLIENT_ID/REMNIC_X_CLIENT_SECRET`\n );\n }\n const store = new XTokenStore({\n tokenFile: expandTildePath(credentials.tokenFile),\n clientId: credentials.clientId,\n clientSecret: credentials.clientSecret,\n ...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),\n ...(deps.sleep !== undefined ? { sleep: deps.sleep } : {}),\n ...(deps.now !== undefined ? { now: deps.now } : {}),\n });\n client = new XMcpClient({\n url: config.url,\n tokenProvider: () => store.getAccessToken(),\n ...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),\n ...(deps.sleep !== undefined ? { sleep: deps.sleep } : {}),\n });\n }\n return client;\n };\n\n return {\n id: config.id,\n kind: \"mcp\",\n async fetch(ctx: XSourceFetchContext): Promise<XSourceFetchOutcome> {\n const records: XPostRecord[] = [];\n let reads = 0;\n let pages = 0;\n let skipped: XSourceFetchOutcome[\"skipped\"];\n try {\n getClient();\n } catch (err) {\n return {\n records,\n reads,\n pages,\n skipped: {\n reason: \"auth-not-configured\",\n ...(err instanceof Error ? { detail: err.message } : {}),\n },\n };\n }\n\n const runKind = async (kind: XRecordKind, toolName: string, args: Record<string, unknown>) => {\n let nextToken: string | undefined;\n for (;;) {\n const gate = ctx.budget.canRead();\n if (!gate.ok) {\n skipped ??= {\n reason: gate.reason,\n ...(gate.detail !== undefined ? { detail: gate.detail } : {}),\n };\n return;\n }\n const page = await getClient().callTool(toolName, {\n ...args,\n ...(nextToken !== undefined ? { pagination_token: nextToken } : {}),\n });\n ctx.budget.noteRead();\n reads += 1;\n pages += 1;\n ctx.budget.pagesUsed = pages;\n const payload = parseToolJson(page.texts);\n if (payload === null) {\n skipped ??= { reason: \"unexpected-payload\", detail: `tool ${toolName}` };\n return;\n }\n const pageRecords = normalizeMcpPayload(payload, kind);\n records.push(...pageRecords);\n nextToken = nextPageToken(payload);\n if (nextToken === undefined || pageRecords.length === 0) return;\n // Stop-on-known: a page of entirely known posts means everything\n // deeper is older state we already ingested.\n if (pageRecords.every((record) => ctx.knownIds.has(record.postId))) return;\n }\n };\n\n try {\n await runKind(\"bookmark\", config.bookmarksTool, { max_results: config.maxResults });\n if (deps.userId !== undefined) {\n await runKind(\"own_post\", config.timelineTool, {\n id: deps.userId,\n max_results: config.maxResults,\n });\n }\n } catch (err) {\n if (err instanceof XCreditsDepletedError) {\n return { records, reads, pages, skipped: { reason: \"credits-depleted\" } };\n }\n throw err;\n }\n return { records, reads, pages, ...(skipped !== undefined ? { skipped } : {}) };\n },\n };\n}\n\nfunction parseToolJson(texts: string[]): unknown {\n const candidates = [texts.join(\"\\n\"), ...texts];\n for (const candidate of candidates) {\n try {\n return JSON.parse(candidate);\n } catch {\n // Try the next candidate.\n }\n }\n return null;\n}\n\nfunction nextPageToken(payload: unknown): string | undefined {\n if (!isXObject(payload)) return undefined;\n if (isXObject(payload.meta)) {\n for (const key of [\"next_token\", \"next_cursor\", \"nextToken\"] as const) {\n const value = payload.meta[key];\n if (typeof value === \"string\" && value.length > 0) return value;\n }\n }\n for (const key of [\"next_token\", \"next_cursor\", \"nextToken\"] as const) {\n const value = payload[key];\n if (typeof value === \"string\" && value.length > 0) return value;\n }\n return undefined;\n}\n\n// ── Corpus directory source ─────────────────────────────────────────────────\n\nfunction createCorpusSource(config: XCorpusSourceConfig): XSource {\n return {\n id: config.id,\n kind: \"corpusDir\",\n async fetch(): Promise<XSourceFetchOutcome> {\n const root = expandTildePath(config.path);\n let entries: string[];\n try {\n const rootStat = await stat(root);\n if (!rootStat.isDirectory()) {\n return { records: [], reads: 0, pages: 0, skipped: { reason: \"corpus-dir-missing\" } };\n }\n entries = (await readdir(root)).sort();\n } catch {\n return { records: [], reads: 0, pages: 0, skipped: { reason: \"corpus-dir-missing\" } };\n }\n const records: XPostRecord[] = [];\n let parseFailures = 0;\n let skippedFiles = 0;\n const rootReal = await realpath(root);\n for (const name of entries) {\n if (!name.endsWith(\".json\")) continue;\n const filePath = path.join(root, name);\n try {\n const info = await lstat(filePath);\n if (info.isSymbolicLink()) {\n // Containment: a symlink may not point outside the corpus root.\n const target = await realpath(filePath);\n if (!(target === rootReal || target.startsWith(`${rootReal}${path.sep}`))) {\n skippedFiles += 1;\n continue;\n }\n } else if (!info.isFile()) {\n continue;\n }\n const parsed: unknown = JSON.parse(await readFile(filePath, \"utf8\"));\n for (const entry of asEntryList(parsed)) {\n const record = normalizeCorpusEntry(entry, \"bookmark\");\n if (record !== null) records.push(record);\n }\n } catch {\n parseFailures += 1;\n }\n }\n const degraded =\n records.length === 0 && (parseFailures > 0 || skippedFiles > 0)\n ? {\n skipped: {\n reason: \"corpus-empty\",\n detail: `${parseFailures} unparseable, ${skippedFiles} out-of-root files skipped`,\n },\n }\n : {};\n return { records, reads: 0, pages: 1, ...degraded };\n },\n };\n}\n\nfunction asEntryList(parsed: unknown): unknown[] {\n if (Array.isArray(parsed)) return parsed;\n if (isXObject(parsed) && Array.isArray(parsed.data)) return parsed.data;\n return [parsed];\n}\n\n// ── CLI source ──────────────────────────────────────────────────────────────\n\nfunction createCliSource(config: XCliSourceConfig, deps: XSourceDeps): XSource {\n const exec = deps.execImpl ?? defaultExec;\n return {\n id: config.id,\n kind: \"cli\",\n async fetch(): Promise<XSourceFetchOutcome> {\n const records: XPostRecord[] = [];\n const commands: Array<{ args: string[]; kind: XRecordKind }> = [\n { args: config.bookmarksArgs, kind: \"bookmark\" },\n ...(config.postsArgs !== undefined ? [{ args: config.postsArgs, kind: \"own_post\" as const }] : []),\n ];\n let skipped: XSourceFetchOutcome[\"skipped\"];\n for (const command of commands) {\n let stdout: string;\n try {\n ({ stdout } = await exec(config.bin, command.args));\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n skipped = {\n reason: code === \"ENOENT\" ? \"cli-not-installed\" : \"cli-failed\",\n detail: code === \"ENOENT\" ? config.bin : exitDetail(err),\n };\n continue;\n }\n const parsed = parseStdout(stdout);\n if (parsed === null) {\n skipped = {\n reason: \"cli-output-unparseable\",\n detail: `${config.bin} ${command.args.join(\" \")}`,\n };\n continue;\n }\n for (const entry of asEntryList(parsed)) {\n const record = normalizeCorpusEntry(entry, command.kind);\n if (record !== null) records.push(record);\n }\n }\n return { records, reads: 0, pages: commands.length, ...(skipped !== undefined ? { skipped } : {}) };\n },\n };\n}\n\nfunction exitDetail(err: unknown): string {\n if (isXObject(err) && typeof err.code === \"number\") return `exit ${err.code}`;\n return \"non-zero exit\";\n}\n\nfunction parseStdout(stdout: string): unknown {\n try {\n return JSON.parse(stdout);\n } catch {\n return null;\n }\n}\n\nasync function defaultExec(bin: string, args: string[]): Promise<{ stdout: string; stderr: string }> {\n const { stdout, stderr } = await execFileAsync(bin, args, {\n timeout: 60_000,\n maxBuffer: 10 * 1024 * 1024,\n });\n return { stdout, stderr };\n}\n","/**\n * Sync orchestration (issue #2009): run sources in configured priority\n * order (cheapest first is the recommended arrangement), dedupe by\n * post_id + content fingerprint, stamp provenance, and route the mapped\n * memory through the trust gate (`suggest` → review queue, `store` →\n * direct write). Persisted state lives in `<stateDir>/state.json`\n * (dedupe map, per-source last sync + new counts, monthly cost ledger);\n * every ingested record is materialized under `<stateDir>/records/`.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport {\n monthlyCostCapUsd,\n resolveMcpClientCredentials,\n type XConnectorConfig,\n type XSourceConfig,\n} from \"./config.js\";\nimport { isXObject } from \"./guards.js\";\nimport { recordFingerprint, suggestionForRecord } from \"./normalize.js\";\nimport { createXSource, unlimitedBudget, XBudgetTracker, type XSourceDeps } from \"./sources.js\";\nimport type {\n XMemorySink,\n XPostRecord,\n XSourceFetchOutcome,\n XSourceStatus,\n XSourceSyncSummary,\n XStatusReport,\n XSyncReport,\n} from \"./types.js\";\n\nconst SEEN_CAP = 20_000;\n\ninterface SeenEntry {\n fingerprint: string;\n firstSeenAt: string;\n lastSeenAt: string;\n kind: string;\n}\n\ninterface XSyncState {\n version: 1;\n seen: Record<string, SeenEntry>;\n lastSyncAt: Record<string, string>;\n /** new records recorded by the latest sync, per source id */\n lastNewCount: Record<string, number>;\n /** usd spent per \"YYYY-MM\", paid sources only */\n costLedger: Record<string, number>;\n}\n\nexport interface XSyncDeps extends XSourceDeps {\n sink: XMemorySink;\n}\n\nfunction freshState(): XSyncState {\n return { version: 1, seen: {}, lastSyncAt: {}, lastNewCount: {}, costLedger: {} };\n}\n\nfunction monthKeyOf(nowMs: number): string {\n return new Date(nowMs).toISOString().slice(0, 7);\n}\n\nfunction resolveStateDir(config: XConnectorConfig): string {\n return expandTildePath(config.stateDir);\n}\n\nfunction stringRecord(raw: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (typeof value === \"string\") out[key] = value;\n }\n return out;\n}\n\nasync function loadState(stateDir: string): Promise<{ state: XSyncState; warning?: string }> {\n const statePath = path.join(stateDir, \"state.json\");\n let raw: string;\n try {\n raw = await readFile(statePath, \"utf8\");\n } catch {\n return { state: freshState() };\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!isXObject(parsed) || !isXObject(parsed.seen)) throw new Error(\"bad shape\");\n const costLedger: Record<string, number> = {};\n if (isXObject(parsed.costLedger)) {\n for (const [key, value] of Object.entries(parsed.costLedger)) {\n if (typeof value === \"number\" && Number.isFinite(value)) costLedger[key] = value;\n }\n }\n const seen: Record<string, SeenEntry> = {};\n for (const [postId, entry] of Object.entries(parsed.seen)) {\n if (isXObject(entry) && typeof entry.fingerprint === \"string\") {\n seen[postId] = {\n fingerprint: entry.fingerprint,\n firstSeenAt: typeof entry.firstSeenAt === \"string\" ? entry.firstSeenAt : \"\",\n lastSeenAt: typeof entry.lastSeenAt === \"string\" ? entry.lastSeenAt : \"\",\n kind: typeof entry.kind === \"string\" ? entry.kind : \"\",\n };\n }\n }\n const lastNewCountRaw = isXObject(parsed.lastNewCount) ? parsed.lastNewCount : {};\n const lastNewCount: Record<string, number> = {};\n for (const [key, value] of Object.entries(lastNewCountRaw)) {\n if (typeof value === \"number\" && Number.isFinite(value)) lastNewCount[key] = value;\n }\n return {\n state: {\n version: 1,\n seen,\n lastSyncAt: stringRecord(isXObject(parsed.lastSyncAt) ? parsed.lastSyncAt : {}),\n lastNewCount,\n costLedger,\n },\n };\n } catch {\n // A corrupt state file only costs dedupe history (re-fetch, re-dedupe):\n // quarantine it and start fresh rather than failing the sync.\n const quarantine = `${statePath}.corrupt`;\n try {\n await rename(statePath, quarantine);\n } catch {\n // Leave it; the save below overwrites.\n }\n return {\n state: freshState(),\n warning: `state.json was unreadable and has been quarantined at ${quarantine}`,\n };\n }\n}\n\nasync function saveState(stateDir: string, state: XSyncState): Promise<void> {\n const entries = Object.entries(state.seen);\n if (entries.length > SEEN_CAP) {\n // ponytail: FIFO prune by firstSeenAt; switch to an LRU store if a\n // single principal ever exceeds 20k live records.\n entries.sort((a, b) => compareIso(a[1].firstSeenAt, b[1].firstSeenAt, a[0], b[0]));\n for (const [postId] of entries.slice(0, entries.length - SEEN_CAP)) {\n delete state.seen[postId];\n }\n }\n const statePath = path.join(stateDir, \"state.json\");\n const tmpPath = `${statePath}.${process.pid}.tmp`;\n await writeFile(tmpPath, `${JSON.stringify(state, null, 2)}\\n`, { mode: 0o600 });\n await rename(tmpPath, statePath);\n}\n\nfunction compareIso(a: string, b: string, idA: string, idB: string): number {\n if (a < b) return -1;\n if (a > b) return 1;\n // Total comparator: fall back to id so equal keys keep a stable order.\n return idA < idB ? -1 : idA > idB ? 1 : 0;\n}\n\nfunction orderedSources(config: XConnectorConfig): XSourceConfig[] {\n const byId = new Map(config.sources.map((source) => [source.id, source]));\n const ordered: XSourceConfig[] = [];\n for (const id of config.sourcePriority) {\n const source = byId.get(id);\n if (source !== undefined) ordered.push(source);\n }\n for (const source of config.sources) {\n if (!config.sourcePriority.includes(source.id)) ordered.push(source);\n }\n return ordered;\n}\n\nasync function writeRecordFile(stateDir: string, record: XPostRecord): Promise<void> {\n const recordsDir = path.join(stateDir, \"records\");\n await mkdir(recordsDir, { recursive: true });\n const safeName = record.postId.replace(/[^0-9A-Za-z._-]/g, \"_\");\n const recordPath = path.join(recordsDir, `${safeName}.json`);\n const tmpPath = `${recordPath}.tmp`;\n await writeFile(tmpPath, `${JSON.stringify(record, null, 2)}\\n`, { mode: 0o600 });\n await rename(tmpPath, recordPath);\n}\n\n/** Runs one sync cycle. Source-level degradation lands in the report, never a throw. */\nexport async function runXSync(config: XConnectorConfig, deps: XSyncDeps): Promise<XSyncReport> {\n const stateDir = resolveStateDir(config);\n await mkdir(path.join(stateDir, \"records\"), { recursive: true });\n const { state, warning } = await loadState(stateDir);\n if (warning !== undefined) {\n process.stderr.write(`[remnic-x] warning: ${warning}\\n`);\n }\n const now = deps.now ?? (() => Date.now());\n const startedMs = now();\n const runId = randomUUID();\n const monthKey = monthKeyOf(startedMs);\n const knownIds = new Set(Object.keys(state.seen));\n\n const summaries: XSourceSyncSummary[] = [];\n let suggestionsSubmitted = 0;\n let memoriesStored = 0;\n let sinkFailures = 0;\n\n for (const sourceConfig of orderedSources(config)) {\n const summary: XSourceSyncSummary = {\n sourceId: sourceConfig.id,\n kind: sourceConfig.kind,\n recordsNew: 0,\n recordsKnown: 0,\n reads: 0,\n pages: 0,\n };\n const source = createXSource(sourceConfig, { ...deps, userId: config.userId });\n const budget =\n sourceConfig.kind === \"mcp\"\n ? new XBudgetTracker(sourceConfig.budget, state.costLedger[monthKey] ?? 0)\n : unlimitedBudget;\n let outcome: XSourceFetchOutcome;\n try {\n outcome = await source.fetch({ knownIds, budget });\n } catch (err) {\n summary.error = err instanceof Error ? err.message : String(err);\n summaries.push(summary);\n continue;\n }\n summary.reads = outcome.reads;\n summary.pages = outcome.pages;\n summary.skipped = outcome.skipped;\n\n for (const raw of outcome.records) {\n const record: XPostRecord = {\n ...raw,\n provenance: {\n sourceId: sourceConfig.id,\n sourceKind: sourceConfig.kind,\n syncRunId: runId,\n fetchedAt: new Date(startedMs).toISOString(),\n },\n };\n const fingerprint = recordFingerprint(record);\n const seenEntry = state.seen[record.postId];\n if (seenEntry !== undefined && seenEntry.fingerprint === fingerprint) {\n seenEntry.lastSeenAt = new Date(startedMs).toISOString();\n summary.recordsKnown += 1;\n continue;\n }\n knownIds.add(record.postId);\n state.seen[record.postId] = {\n fingerprint,\n firstSeenAt:\n seenEntry !== undefined && seenEntry.firstSeenAt.length > 0\n ? seenEntry.firstSeenAt\n : new Date(startedMs).toISOString(),\n lastSeenAt: new Date(startedMs).toISOString(),\n kind: record.kind,\n };\n summary.recordsNew += 1;\n try {\n await writeRecordFile(stateDir, record);\n } catch (err) {\n sinkFailures += 1;\n summary.error = `record write failed: ${err instanceof Error ? err.name : \"write error\"}`;\n continue;\n }\n try {\n const suggestion = suggestionForRecord(record);\n if (config.memoryMode === \"store\") {\n await deps.sink.storeMemory(suggestion);\n memoriesStored += 1;\n } else {\n await deps.sink.submitSuggestion(suggestion);\n suggestionsSubmitted += 1;\n }\n } catch {\n sinkFailures += 1;\n }\n }\n\n if (sourceConfig.kind === \"mcp\" && outcome.reads > 0) {\n state.costLedger[monthKey] =\n (state.costLedger[monthKey] ?? 0) + outcome.reads * sourceConfig.budget.costPerReadUsd;\n }\n state.lastSyncAt[sourceConfig.id] = new Date(startedMs).toISOString();\n state.lastNewCount[sourceConfig.id] = summary.recordsNew;\n summaries.push(summary);\n }\n\n const finishedMs = now();\n await saveState(stateDir, state);\n\n return {\n runId,\n startedAt: new Date(startedMs).toISOString(),\n finishedAt: new Date(finishedMs).toISOString(),\n memoryMode: config.memoryMode,\n sources: summaries,\n suggestionsSubmitted,\n memoriesStored,\n sinkFailures,\n monthKey,\n monthSpendUsd: state.costLedger[monthKey] ?? 0,\n };\n}\n\n/**\n * Offline status snapshot: config sources, availability, spend vs cap.\n * No network calls, no credit use.\n */\nexport async function getXStatus(\n config: XConnectorConfig,\n deps: Pick<XSyncDeps, \"execImpl\" | \"env\"> = {}\n): Promise<XStatusReport> {\n const stateDir = resolveStateDir(config);\n const { state } = await loadState(stateDir);\n const monthKey = monthKeyOf(Date.now());\n const sources: XSourceStatus[] = [];\n for (let index = 0; index < config.sourcePriority.length; index++) {\n const id = config.sourcePriority[index];\n const sourceConfig = config.sources.find((entry) => entry.id === id);\n if (sourceConfig === undefined) continue;\n sources.push({\n sourceId: id,\n kind: sourceConfig.kind,\n priority: index,\n lastSyncAt: state.lastSyncAt[id] ?? null,\n lastRecordsNew: state.lastNewCount[id] ?? 0,\n ...(await probeAvailability(sourceConfig, deps)),\n });\n }\n const lastSyncValues = Object.values(state.lastSyncAt).sort();\n return {\n enabled: config.enabled,\n memoryMode: config.memoryMode,\n syncSchedule: config.syncSchedule,\n sources,\n seenCount: Object.keys(state.seen).length,\n monthKey,\n monthSpendUsd: state.costLedger[monthKey] ?? 0,\n monthlyCostCapUsd: monthlyCostCapUsd(config),\n lastSyncAt: lastSyncValues.length > 0 ? lastSyncValues[lastSyncValues.length - 1] : null,\n };\n}\n\nasync function probeAvailability(\n sourceConfig: XSourceConfig,\n deps: Pick<XSyncDeps, \"execImpl\" | \"env\">\n): Promise<{ available: boolean; availabilityDetail?: string }> {\n if (sourceConfig.kind === \"corpusDir\") {\n const dir = expandTildePath(sourceConfig.path);\n try {\n const info = await stat(dir);\n return info.isDirectory()\n ? { available: true }\n : { available: false, availabilityDetail: `${sourceConfig.path} is not a directory` };\n } catch {\n return { available: false, availabilityDetail: `${sourceConfig.path} not found` };\n }\n }\n if (sourceConfig.kind === \"cli\") {\n return { available: true, availabilityDetail: `assumed present (${sourceConfig.bin})` };\n }\n const credentials = resolveMcpClientCredentials(sourceConfig, deps.env ?? process.env);\n return credentials.clientId !== undefined && credentials.clientSecret !== undefined\n ? { available: true }\n : { available: false, availabilityDetail: \"OAuth2 client credentials missing\" };\n}\n"],"mappings":"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","names":["rename","writeFile","path","sleepMs","sleepMs","firstString","path","writeFile","rename","readFile","stat","path","expandTildePath","expandTildePath","stat","path","readFile","mkdir","readFile","rename","stat","writeFile","path","expandTildePath","expandTildePath","path","readFile","rename","writeFile","mkdir","stat"]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// openclaw-engram: Local-first memory plugin
|
|
3
|
+
import {
|
|
4
|
+
createFileSink,
|
|
5
|
+
getXStatus,
|
|
6
|
+
parseXConnectorConfig,
|
|
7
|
+
runXSync
|
|
8
|
+
} from "./chunk-JR2ZNAYD.js";
|
|
9
|
+
|
|
10
|
+
// src/cli.ts
|
|
11
|
+
import { readFile } from "fs/promises";
|
|
12
|
+
import { exit } from "process";
|
|
13
|
+
import { expandTildePath } from "@remnic/core";
|
|
14
|
+
var USAGE = [
|
|
15
|
+
"usage: remnic-x <status|sync> [--config <path>] [--json]",
|
|
16
|
+
"",
|
|
17
|
+
" status offline snapshot: sources, availability, spend vs cap",
|
|
18
|
+
" sync run one sync cycle per configured source priority",
|
|
19
|
+
" --config path to the xConnector JSON (default: $REMNIC_X_CONFIG",
|
|
20
|
+
" or ~/.config/remnic/x-connector.json)",
|
|
21
|
+
" --json machine-readable output"
|
|
22
|
+
].join("\n");
|
|
23
|
+
function parseArgs(argv) {
|
|
24
|
+
let command = null;
|
|
25
|
+
let configPath = null;
|
|
26
|
+
let json = false;
|
|
27
|
+
for (let index = 0; index < argv.length; index++) {
|
|
28
|
+
const arg = argv[index];
|
|
29
|
+
if (arg === "status" || arg === "sync") {
|
|
30
|
+
if (command !== null) return null;
|
|
31
|
+
command = arg;
|
|
32
|
+
} else if (arg === "--config") {
|
|
33
|
+
const value = argv[index + 1];
|
|
34
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
35
|
+
configPath = value;
|
|
36
|
+
index += 1;
|
|
37
|
+
} else if (arg === "--json") {
|
|
38
|
+
json = true;
|
|
39
|
+
} else {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (command === null) return null;
|
|
44
|
+
return {
|
|
45
|
+
command,
|
|
46
|
+
configPath: configPath ?? process.env.REMNIC_X_CONFIG ?? "~/.config/remnic/x-connector.json",
|
|
47
|
+
json
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function loadConfig(configPath) {
|
|
51
|
+
const resolved = expandTildePath(configPath);
|
|
52
|
+
let raw;
|
|
53
|
+
try {
|
|
54
|
+
raw = await readFile(resolved, "utf8");
|
|
55
|
+
} catch {
|
|
56
|
+
throw new Error(`config file not found: ${resolved} (pass --config or set REMNIC_X_CONFIG)`);
|
|
57
|
+
}
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(raw);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
throw new Error(`config file ${resolved} is not valid JSON (${err instanceof Error ? err.name : "parse error"})`);
|
|
63
|
+
}
|
|
64
|
+
const block = typeof parsed === "object" && parsed !== null && "xConnector" in parsed && typeof parsed.xConnector === "object" ? parsed.xConnector : parsed;
|
|
65
|
+
return parseXConnectorConfig(block);
|
|
66
|
+
}
|
|
67
|
+
function printHuman(text) {
|
|
68
|
+
process.stdout.write(`${text}
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
async function main() {
|
|
72
|
+
const args = parseArgs(process.argv.slice(2));
|
|
73
|
+
if (args === null) {
|
|
74
|
+
process.stderr.write(`${USAGE}
|
|
75
|
+
`);
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
let config;
|
|
79
|
+
try {
|
|
80
|
+
config = await loadConfig(args.configPath);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
process.stderr.write(`remnic-x: ${err instanceof Error ? err.message : String(err)}
|
|
83
|
+
`);
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
if (!config.enabled) {
|
|
87
|
+
printHuman(args.json ? JSON.stringify({ enabled: false }) : "xConnector is disabled.");
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
if (args.command === "status") {
|
|
91
|
+
const status = await getXStatus(config);
|
|
92
|
+
printHuman(args.json ? JSON.stringify(status, null, 2) : renderStatus(status));
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
const report = await runXSync(config, {
|
|
96
|
+
sink: createFileSink({ stateDir: config.stateDir, mode: config.memoryMode })
|
|
97
|
+
});
|
|
98
|
+
printHuman(args.json ? JSON.stringify(report, null, 2) : renderReport(report));
|
|
99
|
+
return report.sinkFailures > 0 ? 1 : 0;
|
|
100
|
+
}
|
|
101
|
+
function renderStatus(status) {
|
|
102
|
+
const lines = [
|
|
103
|
+
`xConnector ${status.enabled ? "enabled" : "disabled"} \xB7 memoryMode=${status.memoryMode} \xB7 schedule=${status.syncSchedule}`,
|
|
104
|
+
`seen records: ${status.seenCount} \xB7 spend ${status.monthKey}: $${status.monthSpendUsd.toFixed(2)} of $${status.monthlyCostCapUsd.toFixed(2)} cap`,
|
|
105
|
+
`last sync: ${status.lastSyncAt ?? "never"}`,
|
|
106
|
+
"sources (priority order):"
|
|
107
|
+
];
|
|
108
|
+
for (const source of status.sources) {
|
|
109
|
+
const flag = source.available ? "ok " : "MISS";
|
|
110
|
+
lines.push(
|
|
111
|
+
` ${source.priority}. [${flag}] ${source.sourceId} (${source.kind}) last=${source.lastSyncAt ?? "never"} new=${source.lastRecordsNew}${source.availabilityDetail !== void 0 ? ` \u2014 ${source.availabilityDetail}` : ""}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
function renderReport(report) {
|
|
117
|
+
const lines = [
|
|
118
|
+
`sync ${report.runId} \xB7 mode=${report.memoryMode} \xB7 suggested=${report.suggestionsSubmitted} stored=${report.memoriesStored} failures=${report.sinkFailures} \xB7 month spend $${report.monthSpendUsd.toFixed(2)}`
|
|
119
|
+
];
|
|
120
|
+
for (const source of report.sources) {
|
|
121
|
+
const note = source.error !== void 0 ? ` error=${source.error}` : source.skipped !== void 0 ? ` skipped=${source.skipped.reason}${source.skipped.detail !== void 0 ? ` (${source.skipped.detail})` : ""}` : "";
|
|
122
|
+
lines.push(
|
|
123
|
+
` ${source.sourceId} (${source.kind}): new=${source.recordsNew} known=${source.recordsKnown} reads=${source.reads} pages=${source.pages}${note}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n");
|
|
127
|
+
}
|
|
128
|
+
main().then((code) => exit(code));
|
|
129
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `remnic-x` — standalone CLI for the X connector.\n *\n * remnic-x status [--config <path>] [--json]\n * remnic-x sync [--config <path>] [--json]\n *\n * The config file is JSON: either the `xConnector` block itself or a\n * document with an `xConnector` key. Default path: $REMNIC_X_CONFIG or\n * ~/.config/remnic/x-connector.json.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { exit } from \"node:process\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport { type XConnectorConfig, parseXConnectorConfig } from \"./config.js\";\nimport { createFileSink } from \"./file-sink.js\";\nimport { getXStatus, runXSync } from \"./sync.js\";\nimport type { XStatusReport, XSyncReport } from \"./types.js\";\n\nconst USAGE = [\n \"usage: remnic-x <status|sync> [--config <path>] [--json]\",\n \"\",\n \" status offline snapshot: sources, availability, spend vs cap\",\n \" sync run one sync cycle per configured source priority\",\n \" --config path to the xConnector JSON (default: $REMNIC_X_CONFIG\",\n \" or ~/.config/remnic/x-connector.json)\",\n \" --json machine-readable output\",\n].join(\"\\n\");\n\ninterface CliArgs {\n command: \"status\" | \"sync\";\n configPath: string;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): CliArgs | null {\n let command: \"status\" | \"sync\" | null = null;\n let configPath: string | null = null;\n let json = false;\n for (let index = 0; index < argv.length; index++) {\n const arg = argv[index];\n if (arg === \"status\" || arg === \"sync\") {\n if (command !== null) return null;\n command = arg;\n } else if (arg === \"--config\") {\n const value = argv[index + 1];\n if (typeof value !== \"string\" || value.length === 0) return null;\n configPath = value;\n index += 1;\n } else if (arg === \"--json\") {\n json = true;\n } else {\n return null;\n }\n }\n if (command === null) return null;\n return {\n command,\n configPath: configPath ?? process.env.REMNIC_X_CONFIG ?? \"~/.config/remnic/x-connector.json\",\n json,\n };\n}\n\nasync function loadConfig(configPath: string): Promise<XConnectorConfig> {\n const resolved = expandTildePath(configPath);\n let raw: string;\n try {\n raw = await readFile(resolved, \"utf8\");\n } catch {\n throw new Error(`config file not found: ${resolved} (pass --config or set REMNIC_X_CONFIG)`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(`config file ${resolved} is not valid JSON (${err instanceof Error ? err.name : \"parse error\"})`);\n }\n const block =\n typeof parsed === \"object\" && parsed !== null && \"xConnector\" in parsed && typeof parsed.xConnector === \"object\"\n ? parsed.xConnector\n : parsed;\n return parseXConnectorConfig(block);\n}\n\nfunction printHuman(text: string): void {\n process.stdout.write(`${text}\\n`);\n}\n\nasync function main(): Promise<number> {\n const args = parseArgs(process.argv.slice(2));\n if (args === null) {\n process.stderr.write(`${USAGE}\\n`);\n return 2;\n }\n let config: XConnectorConfig;\n try {\n config = await loadConfig(args.configPath);\n } catch (err) {\n process.stderr.write(`remnic-x: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 2;\n }\n if (!config.enabled) {\n printHuman(args.json ? JSON.stringify({ enabled: false }) : \"xConnector is disabled.\");\n return 0;\n }\n if (args.command === \"status\") {\n const status = await getXStatus(config);\n printHuman(args.json ? JSON.stringify(status, null, 2) : renderStatus(status));\n return 0;\n }\n const report = await runXSync(config, {\n sink: createFileSink({ stateDir: config.stateDir, mode: config.memoryMode }),\n });\n printHuman(args.json ? JSON.stringify(report, null, 2) : renderReport(report));\n // Skips are expected degradation (credits, caps), not failures.\n return report.sinkFailures > 0 ? 1 : 0;\n}\n\nfunction renderStatus(status: XStatusReport): string {\n const lines = [\n `xConnector ${status.enabled ? \"enabled\" : \"disabled\"} · memoryMode=${status.memoryMode} · schedule=${status.syncSchedule}`,\n `seen records: ${status.seenCount} · spend ${status.monthKey}: $${status.monthSpendUsd.toFixed(2)} of $${status.monthlyCostCapUsd.toFixed(2)} cap`,\n `last sync: ${status.lastSyncAt ?? \"never\"}`,\n \"sources (priority order):\",\n ];\n for (const source of status.sources) {\n const flag = source.available ? \"ok \" : \"MISS\";\n lines.push(\n ` ${source.priority}. [${flag}] ${source.sourceId} (${source.kind}) last=${source.lastSyncAt ?? \"never\"} new=${source.lastRecordsNew}${source.availabilityDetail !== undefined ? ` — ${source.availabilityDetail}` : \"\"}`\n );\n }\n return lines.join(\"\\n\");\n}\n\nfunction renderReport(report: XSyncReport): string {\n const lines = [\n `sync ${report.runId} · mode=${report.memoryMode} · suggested=${report.suggestionsSubmitted} stored=${report.memoriesStored} failures=${report.sinkFailures} · month spend $${report.monthSpendUsd.toFixed(2)}`,\n ];\n for (const source of report.sources) {\n const note =\n source.error !== undefined\n ? ` error=${source.error}`\n : source.skipped !== undefined\n ? ` skipped=${source.skipped.reason}${source.skipped.detail !== undefined ? ` (${source.skipped.detail})` : \"\"}`\n : \"\";\n lines.push(\n ` ${source.sourceId} (${source.kind}): new=${source.recordsNew} known=${source.recordsKnown} reads=${source.reads} pages=${source.pages}${note}`\n );\n }\n return lines.join(\"\\n\");\n}\n\nmain().then((code) => exit(code));\n"],"mappings":"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","names":[]}
|