#!/usr/bin/env nodeimport React, { useEffect, useRef, useState } from "react";import { Box, render, Text, useApp, useInput } from "ink";import { marked, type Token, type Tokens } from "marked";import { configureAuthProvider, listAuthProviderTools, shouldDiscoverToolsAfterAuth,} from "./auth/configure.js";import { startNgrokTunnel } from "./auth/ngrok.js";import { formatAuthProviderList, runOAuthAuth } from "./auth/oauth.js";import { ensureCodeModeRepoSetup, runCodeModeConnectors } from "./code-mode.js";import { commandEmitsTelemetry, helpContent, isDevelopmentMode, parseCommand, shouldRunNonInteractively, type CliCommand, type HelpRow, type OpenWikiRunMode,} from "./commands.js";import { resolveStartupCommand } from "./startup.js";import { InitSetup, needsCredentialSetup, type InitSetupResult,} from "./credentials.js";import { getCredentialDiagnostics, getShellEnvValue, loadOpenWikiEnv, saveOpenWikiEnv, type CredentialDiagnostic,} from "./env.js";import { createOpenWikiThreadId, runOpenWikiAgent } from "./agent/index.js";import { formatChatGptAccountFromEnv } from "./agent/openai-chatgpt-oauth.js";import { getErrorMessage, isAuthError, isSecretLikeKey, sanitizeDiagnosticText,} from "./diagnostics.js";import { stripHtmlTags } from "./utils.js";import { type OpenWikiRunEvent, type OpenWikiRunResult,} from "./agent/types.js";import { runOpenWikiIngestion, type OpenWikiIngestionResult,} from "./ingestion.js";import { readOpenWikiOnboardingConfig, saveOpenWikiOnboardingConfig,} from "./onboarding.js";import { openWikiLocalWikiDir } from "./openwiki-home.js";import { deleteConnectorSchedules, getSavedPowerScheduleStatus, listConnectorSchedules, pauseConnectorSchedules, resumeConnectorSchedules, type ConnectorScheduleStatus, type PowerScheduleStatus, type ScheduleMutationResult,} from "./schedules.js";import { getDefaultModelId, getMissingProviderEnvKey, getProviderApiKeyEnvKey, getProviderCredentialHint, getProviderLabel, getProviderModelOptions, getProviderProjectEnvKey, isValidModelId, normalizeModelId, normalizeProvider, OPENWIKI_PROVIDER_ENV_KEY, OPENWIKI_MODEL_ID_ENV_KEY, providerUsesAwsSdkCredentials, resolveConfiguredProvider, SELECTABLE_OPENWIKI_PROVIDERS, OPENWIKI_VERSION, type OpenWikiProvider,} from "./constants.js";import type { OpenWikiCommand, OpenWikiOutputMode } from "./agent/types.js";import { firstRunNoticePending, FIRST_RUN_NOTICE_BODY, FIRST_RUN_NOTICE_OPT_OUT, FIRST_RUN_NOTICE_VERIFY,} from "./telemetry/index.js"; type RunState = | { status: "idle" } | { status: "setup-complete-exit"; result: InitSetupResult } | { status: "init-setup-saved"; result: InitSetupResult } | { status: "ingestion-running"; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "ingestion-success"; result: OpenWikiIngestionResult; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "running"; command: OpenWikiCommand; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "success"; result: OpenWikiRunResult; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "error"; message: string; credentialDiagnostics?: CredentialDiagnostic[]; errorDiagnostics?: ErrorDiagnostic[]; authFix?: AuthFix; }; type RunLogItem = { actionCount?: number; activeToolCallIds?: string[]; call?: string; doneContent?: string; errorCount?: number; id: number; latestDoneContent?: string; status?: "done" | "error" | "running"; toolCallId?: string; toolName?: string; type: "debug" | "text" | "tool"; content: string;}; type CompletedRun = { id: number; command: OpenWikiCommand; credentialDiagnostics?: CredentialDiagnostic[]; log: RunLogItem[]; message: string | null; result: OpenWikiRunResult;}; type ErrorDiagnostic = { label: string; value: string;}; /** * What the "How to fix" panel needs after an auth failure. Key names only, no * secret values: {@link AuthFix.keyFromShell} reflects only whether the failing * provider's API key was sourced from a shell export (which shadows saved * config), so the panel can tell the user to unset it. */type AuthFix = { apiKeyEnvKey: string | undefined; keyFromShell: boolean; provider: OpenWikiProvider;}; type AppProps = { command: CliCommand;}; const OPENWIKI_LOGO_LINES = [ " ___ __ ___ _ _ ", " / _ \\ _ __ ___ _ __ \\ \\ / (_) | _(_)", "| | | | '_ \\ / _ \\ '_ \\ \\ \\ /\\ / /| | |/ / |", "| |_| | |_) | __/ | | | \\ V V / | | <| |", " \\___/| .__/ \\___|_| |_| \\_/\\_/ |_|_|\\_\\_|", " |_|",];const OPENWIKI_LOGO_WIDTH = Math.max( ...OPENWIKI_LOGO_LINES.map((line) => line.length),); /** Frame/wrap width for the plain-text (print/non-TTY) first-run disclosure. */const FIRST_RUN_NOTICE_WIDTH = 64; /** Greedy word-wrap to `width` columns. Input carries no existing newlines. */function wrapText(text: string, width: number): string[] { const lines: string[] = []; let line = ""; for (const word of text.split(/\s+/)) { if (line.length === 0) { line = word; } else if (line.length + 1 + word.length <= width) { line += ` ${word}`; } else { lines.push(line); line = word; } } if (line.length > 0) { lines.push(line); } return lines;} /** * The plain-text first-run disclosure for print/non-TTY output: the same copy as * the interactive box (single-sourced in telemetry/config.ts), framed with light * rules and wrapped to a fixed width. Rendered gray when stderr is a TTY, plain * when redirected so a captured log stays free of escape codes. */function renderFirstRunNoticeText(color: boolean): string { const label = "OpenWiki telemetry"; const width = FIRST_RUN_NOTICE_WIDTH; const topRule = `─── ${label} ${"─".repeat(Math.max(3, width - label.length - 5))}`; const block = [ "", topRule, "", ...wrapText(FIRST_RUN_NOTICE_BODY, width), "", ...wrapText(FIRST_RUN_NOTICE_OPT_OUT, width), "", ...wrapText(FIRST_RUN_NOTICE_VERIFY, width), "─".repeat(width), "", ].join("\n"); return color ? `\u001b[90m${block}\u001b[39m` : block;} /** * The one-time telemetry disclosure, rendered as a box so it sits inline with * the rest of the TUI (mirrors SetupHeader's rounded style). The copy is * single-sourced in telemetry/config.ts; the print/non-TTY path renders the * same wording as plain text via renderFirstRunNoticeText. */function FirstRunNotice() { return ( <Box borderStyle="round" borderColor="cyan" flexDirection="column" marginBottom={1} paddingX={1} > <Text> <Text bold color="cyan"> OpenWiki </Text>{" "} <Text color="gray">telemetry</Text> </Text> <Text color="white">{FIRST_RUN_NOTICE_BODY}</Text> <Text color="white">{FIRST_RUN_NOTICE_OPT_OUT}</Text> <Text color="white">{FIRST_RUN_NOTICE_VERIFY}</Text> </Box> );} function App({ command }: AppProps) { const app = useApp(); const startupModelId = command.kind === "run" ? command.modelId : null; const startupRunMode = command.kind === "run" ? command.mode : "personal"; const [runMode, setRunMode] = useState<OpenWikiRunMode>(startupRunMode); const [codeRuntimeCwd, setCodeRuntimeCwd] = useState(process.cwd()); const runtimeCwd = getRunModeCwd(runMode, codeRuntimeCwd); const runtimeOutputMode = getRunModeOutputMode(runMode); const startupProvider = resolveConfiguredProvider(); const autoExitOnSuccess = shouldAutoExitStartupRun(command); const [sessionProvider, setSessionProvider] = useState<OpenWikiProvider>(startupProvider); const [sessionModelId, setSessionModelId] = useState<string | null>( startupModelId, ); const activeRunId = useRef(0); const sessionThreadId = useRef(createOpenWikiThreadId(runtimeCwd)); const sessionThreadMode = useRef<OpenWikiRunMode>(runMode); const mountedRef = useRef(false); const nextLogId = useRef(1); const nextCompletedRunId = useRef(1); const activeRunCredentialDiagnostics = useRef< CredentialDiagnostic[] | undefined >(undefined); const activeRunLog = useRef<RunLogItem[]>([]); const [runState, setRunState] = useState<RunState>({ status: "idle" }); const [completedRuns, setCompletedRuns] = useState<CompletedRun[]>([]); const [activeUserMessage, setActiveUserMessage] = useState<string | null>( command.kind === "run" ? command.userMessage : null, ); const [activeMessageIsFollowup, setActiveMessageIsFollowup] = useState( command.kind === "run" && command.command === "chat", ); const shouldOpenSetupForExplicitModeChat = command.kind === "run" && !command.dryRun && !command.shouldStart && command.modeSource !== "default" && process.stdin.isTTY && needsCredentialSetup(sessionModelId, runMode); const [resolvedCommand, setResolvedCommand] = useState<OpenWikiCommand | null>( command.kind === "run" && (command.shouldStart || shouldOpenSetupForExplicitModeChat) ? command.command : null, ); // `--init` always opens the full setup walk, even when everything is already // configured, so you can review or change any step. Consumed once the walk // finishes so it does not re-open when the run later returns to idle. const [initWizardConsumed, setInitWizardConsumed] = useState(false); const isInitCommand = command.kind === "run" && command.command === "init"; const shouldRunInteractiveCredentialSetup = command.kind === "run" && resolvedCommand !== null && !command.dryRun && process.stdin.isTTY && runState.status === "idle" && (needsCredentialSetup(sessionModelId, runMode) || (isInitCommand && !initWizardConsumed)); const displayModelId = sessionModelId ?? startupModelId; function submitChatMessage(message: string) { if (isExitMessage(message)) { process.exitCode = 0; app.exit(); return; } setActiveUserMessage(message); setActiveMessageIsFollowup(true); setResolvedCommand("chat"); setRunState({ status: "idle" }); } function submitCommandRun( nextCommand: Extract<OpenWikiCommand, "init" | "update">, message: string | null, ) { setActiveUserMessage(message); setActiveMessageIsFollowup(false); setResolvedCommand(nextCommand); setRunState({ status: "idle" }); } function startIngestionRun(modelId: string | null) { const runId = activeRunId.current + 1; activeRunId.current = runId; activeRunCredentialDiagnostics.current = undefined; activeRunLog.current = []; setResolvedCommand(null); setActiveUserMessage( "Run source-specific OpenWiki ingestion for configured sources.", ); setActiveMessageIsFollowup(false); setRunState({ status: "ingestion-running", log: [], }); void runOpenWikiIngestion(process.cwd(), { debug: isDebugMode(), modelId, target: "all", onEvent: (event) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } activeRunLog.current = appendRunLogEvent( activeRunLog.current, event, nextLogId, ); setRunState((currentState) => currentState.status === "ingestion-running" ? { ...currentState, log: activeRunLog.current, } : currentState, ); }, }) .then((result) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } if ( result.results.some((sourceResult) => sourceResult.status === "error") ) { process.exitCode = 1; } setRunState({ status: "ingestion-success", result, log: activeRunLog.current, credentialDiagnostics: activeRunCredentialDiagnostics.current, }); }) .catch((error: unknown) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } const errorDiagnostics = getErrorDiagnostics(error); const message = getErrorMessage(error); const authFix = getAuthFix(error, message, sessionProvider); // The full credential dump is opt-in (--debug); by default show only the // concise message, allowlisted error fields, and the how-to-fix panel. if (!shouldShowCredentialDiagnostics()) { setRunState({ status: "error", message, errorDiagnostics, authFix, }); return; } void getCredentialDiagnostics() .catch(() => undefined) .then((credentialDiagnostics) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } setRunState({ status: "error", message, credentialDiagnostics, errorDiagnostics, authFix, }); }); }); } function clearSession() { activeRunId.current += 1; sessionThreadId.current = createOpenWikiThreadId(runtimeCwd); activeRunCredentialDiagnostics.current = undefined; activeRunLog.current = []; nextLogId.current = 1; nextCompletedRunId.current = 1; setCompletedRuns([]); setActiveUserMessage(null); setActiveMessageIsFollowup(false); setResolvedCommand(null); setRunState({ status: "idle" }); } async function selectModel(modelId: string) { await saveOpenWikiEnv({ [OPENWIKI_MODEL_ID_ENV_KEY]: modelId, }); setSessionModelId(modelId); } async function selectProvider(provider: OpenWikiProvider) { const modelId = getProviderModelOptions(provider).length > 0 ? getDefaultModelId(provider) : null; await saveOpenWikiEnv({ [OPENWIKI_PROVIDER_ENV_KEY]: provider, ...(modelId ? { [OPENWIKI_MODEL_ID_ENV_KEY]: modelId } : {}), }); setSessionProvider(provider); setSessionModelId(modelId); } useEffect(() => { mountedRef.current = true; return () => { mountedRef.current = false; }; }, []); useEffect(() => { if (sessionThreadMode.current === runMode) { return; } sessionThreadId.current = createOpenWikiThreadId(runtimeCwd); sessionThreadMode.current = runMode; }, [runMode, runtimeCwd]); useEffect(() => { if (command.kind === "help" || command.kind === "error") {Lines 1–500 of 4222. Load more
#!/usr/bin/env node import React, { useEffect, useRef, useState } from "react"; import { Box, render, Text, useApp, useInput } from "ink"; import { marked, type Token, type Tokens } from "marked"; import { configureAuthProvider, listAuthProviderTools, shouldDiscoverToolsAfterAuth, } from "./auth/configure.js"; import { startNgrokTunnel } from "./auth/ngrok.js"; import { formatAuthProviderList, runOAuthAuth } from "./auth/oauth.js"; import { ensureCodeModeRepoSetup, runCodeModeConnectors } from "./code-mode.js"; import { commandEmitsTelemetry, helpContent, isDevelopmentMode, parseCommand, shouldRunNonInteractively, type CliCommand, type HelpRow, type OpenWikiRunMode, } from "./commands.js"; import { resolveStartupCommand } from "./startup.js"; import { InitSetup, needsCredentialSetup, type InitSetupResult, } from "./credentials.js"; import { getCredentialDiagnostics, getShellEnvValue, loadOpenWikiEnv, saveOpenWikiEnv, type CredentialDiagnostic, } from "./env.js"; import { createOpenWikiThreadId, runOpenWikiAgent } from "./agent/index.js"; import { formatChatGptAccountFromEnv } from "./agent/openai-chatgpt-oauth.js"; import { getErrorMessage, isAuthError, isSecretLikeKey, sanitizeDiagnosticText, } from "./diagnostics.js"; import { stripHtmlTags } from "./utils.js"; import { type OpenWikiRunEvent, type OpenWikiRunResult, } from "./agent/types.js"; import { runOpenWikiIngestion, type OpenWikiIngestionResult, } from "./ingestion.js"; import { readOpenWikiOnboardingConfig, saveOpenWikiOnboardingConfig, } from "./onboarding.js"; import { openWikiLocalWikiDir } from "./openwiki-home.js"; import { deleteConnectorSchedules, getSavedPowerScheduleStatus, listConnectorSchedules, pauseConnectorSchedules, resumeConnectorSchedules, type ConnectorScheduleStatus, type PowerScheduleStatus, type ScheduleMutationResult, } from "./schedules.js"; import { getDefaultModelId, getMissingProviderEnvKey, getProviderApiKeyEnvKey, getProviderCredentialHint, getProviderLabel, getProviderModelOptions, getProviderProjectEnvKey, isValidModelId, normalizeModelId, normalizeProvider, OPENWIKI_PROVIDER_ENV_KEY, OPENWIKI_MODEL_ID_ENV_KEY, providerUsesAwsSdkCredentials, resolveConfiguredProvider, SELECTABLE_OPENWIKI_PROVIDERS, OPENWIKI_VERSION, type OpenWikiProvider, } from "./constants.js"; import type { OpenWikiCommand, OpenWikiOutputMode } from "./agent/types.js"; import { firstRunNoticePending, FIRST_RUN_NOTICE_BODY, FIRST_RUN_NOTICE_OPT_OUT, FIRST_RUN_NOTICE_VERIFY, } from "./telemetry/index.js"; type RunState = | { status: "idle" } | { status: "setup-complete-exit"; result: InitSetupResult } | { status: "init-setup-saved"; result: InitSetupResult } | { status: "ingestion-running"; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "ingestion-success"; result: OpenWikiIngestionResult; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "running"; command: OpenWikiCommand; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "success"; result: OpenWikiRunResult; log: RunLogItem[]; credentialDiagnostics?: CredentialDiagnostic[]; } | { status: "error"; message: string; credentialDiagnostics?: CredentialDiagnostic[]; errorDiagnostics?: ErrorDiagnostic[]; authFix?: AuthFix; }; type RunLogItem = { actionCount?: number; activeToolCallIds?: string[]; call?: string; doneContent?: string; errorCount?: number; id: number; latestDoneContent?: string; status?: "done" | "error" | "running"; toolCallId?: string; toolName?: string; type: "debug" | "text" | "tool"; content: string; }; type CompletedRun = { id: number; command: OpenWikiCommand; credentialDiagnostics?: CredentialDiagnostic[]; log: RunLogItem[]; message: string | null; result: OpenWikiRunResult; }; type ErrorDiagnostic = { label: string; value: string; }; /** * What the "How to fix" panel needs after an auth failure. Key names only, no * secret values: {@link AuthFix.keyFromShell} reflects only whether the failing * provider's API key was sourced from a shell export (which shadows saved * config), so the panel can tell the user to unset it. */ type AuthFix = { apiKeyEnvKey: string | undefined; keyFromShell: boolean; provider: OpenWikiProvider; }; type AppProps = { command: CliCommand; }; const OPENWIKI_LOGO_LINES = [ " ___ __ ___ _ _ ", " / _ \\ _ __ ___ _ __ \\ \\ / (_) | _(_)", "| | | | '_ \\ / _ \\ '_ \\ \\ \\ /\\ / /| | |/ / |", "| |_| | |_) | __/ | | | \\ V V / | | <| |", " \\___/| .__/ \\___|_| |_| \\_/\\_/ |_|_|\\_\\_|", " |_|", ]; const OPENWIKI_LOGO_WIDTH = Math.max( ...OPENWIKI_LOGO_LINES.map((line) => line.length), ); /** Frame/wrap width for the plain-text (print/non-TTY) first-run disclosure. */ const FIRST_RUN_NOTICE_WIDTH = 64; /** Greedy word-wrap to `width` columns. Input carries no existing newlines. */ function wrapText(text: string, width: number): string[] { const lines: string[] = []; let line = ""; for (const word of text.split(/\s+/)) { if (line.length === 0) { line = word; } else if (line.length + 1 + word.length <= width) { line += ` ${word}`; } else { lines.push(line); line = word; } } if (line.length > 0) { lines.push(line); } return lines; } /** * The plain-text first-run disclosure for print/non-TTY output: the same copy as * the interactive box (single-sourced in telemetry/config.ts), framed with light * rules and wrapped to a fixed width. Rendered gray when stderr is a TTY, plain * when redirected so a captured log stays free of escape codes. */ function renderFirstRunNoticeText(color: boolean): string { const label = "OpenWiki telemetry"; const width = FIRST_RUN_NOTICE_WIDTH; const topRule = `─── ${label} ${"─".repeat(Math.max(3, width - label.length - 5))}`; const block = [ "", topRule, "", ...wrapText(FIRST_RUN_NOTICE_BODY, width), "", ...wrapText(FIRST_RUN_NOTICE_OPT_OUT, width), "", ...wrapText(FIRST_RUN_NOTICE_VERIFY, width), "─".repeat(width), "", ].join("\n"); return color ? `\u001b[90m${block}\u001b[39m` : block; } /** * The one-time telemetry disclosure, rendered as a box so it sits inline with * the rest of the TUI (mirrors SetupHeader's rounded style). The copy is * single-sourced in telemetry/config.ts; the print/non-TTY path renders the * same wording as plain text via renderFirstRunNoticeText. */ function FirstRunNotice() { return ( <Box borderStyle="round" borderColor="cyan" flexDirection="column" marginBottom={1} paddingX={1} > <Text> <Text bold color="cyan"> OpenWiki </Text>{" "} <Text color="gray">telemetry</Text> </Text> <Text color="white">{FIRST_RUN_NOTICE_BODY}</Text> <Text color="white">{FIRST_RUN_NOTICE_OPT_OUT}</Text> <Text color="white">{FIRST_RUN_NOTICE_VERIFY}</Text> </Box> ); } function App({ command }: AppProps) { const app = useApp(); const startupModelId = command.kind === "run" ? command.modelId : null; const startupRunMode = command.kind === "run" ? command.mode : "personal"; const [runMode, setRunMode] = useState<OpenWikiRunMode>(startupRunMode); const [codeRuntimeCwd, setCodeRuntimeCwd] = useState(process.cwd()); const runtimeCwd = getRunModeCwd(runMode, codeRuntimeCwd); const runtimeOutputMode = getRunModeOutputMode(runMode); const startupProvider = resolveConfiguredProvider(); const autoExitOnSuccess = shouldAutoExitStartupRun(command); const [sessionProvider, setSessionProvider] = useState<OpenWikiProvider>(startupProvider); const [sessionModelId, setSessionModelId] = useState<string | null>( startupModelId, ); const activeRunId = useRef(0); const sessionThreadId = useRef(createOpenWikiThreadId(runtimeCwd)); const sessionThreadMode = useRef<OpenWikiRunMode>(runMode); const mountedRef = useRef(false); const nextLogId = useRef(1); const nextCompletedRunId = useRef(1); const activeRunCredentialDiagnostics = useRef< CredentialDiagnostic[] | undefined >(undefined); const activeRunLog = useRef<RunLogItem[]>([]); const [runState, setRunState] = useState<RunState>({ status: "idle" }); const [completedRuns, setCompletedRuns] = useState<CompletedRun[]>([]); const [activeUserMessage, setActiveUserMessage] = useState<string | null>( command.kind === "run" ? command.userMessage : null, ); const [activeMessageIsFollowup, setActiveMessageIsFollowup] = useState( command.kind === "run" && command.command === "chat", ); const shouldOpenSetupForExplicitModeChat = command.kind === "run" && !command.dryRun && !command.shouldStart && command.modeSource !== "default" && process.stdin.isTTY && needsCredentialSetup(sessionModelId, runMode); const [resolvedCommand, setResolvedCommand] = useState<OpenWikiCommand | null>( command.kind === "run" && (command.shouldStart || shouldOpenSetupForExplicitModeChat) ? command.command : null, ); // `--init` always opens the full setup walk, even when everything is already // configured, so you can review or change any step. Consumed once the walk // finishes so it does not re-open when the run later returns to idle. const [initWizardConsumed, setInitWizardConsumed] = useState(false); const isInitCommand = command.kind === "run" && command.command === "init"; const shouldRunInteractiveCredentialSetup = command.kind === "run" && resolvedCommand !== null && !command.dryRun && process.stdin.isTTY && runState.status === "idle" && (needsCredentialSetup(sessionModelId, runMode) || (isInitCommand && !initWizardConsumed)); const displayModelId = sessionModelId ?? startupModelId; function submitChatMessage(message: string) { if (isExitMessage(message)) { process.exitCode = 0; app.exit(); return; } setActiveUserMessage(message); setActiveMessageIsFollowup(true); setResolvedCommand("chat"); setRunState({ status: "idle" }); } function submitCommandRun( nextCommand: Extract<OpenWikiCommand, "init" | "update">, message: string | null, ) { setActiveUserMessage(message); setActiveMessageIsFollowup(false); setResolvedCommand(nextCommand); setRunState({ status: "idle" }); } function startIngestionRun(modelId: string | null) { const runId = activeRunId.current + 1; activeRunId.current = runId; activeRunCredentialDiagnostics.current = undefined; activeRunLog.current = []; setResolvedCommand(null); setActiveUserMessage( "Run source-specific OpenWiki ingestion for configured sources.", ); setActiveMessageIsFollowup(false); setRunState({ status: "ingestion-running", log: [], }); void runOpenWikiIngestion(process.cwd(), { debug: isDebugMode(), modelId, target: "all", onEvent: (event) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } activeRunLog.current = appendRunLogEvent( activeRunLog.current, event, nextLogId, ); setRunState((currentState) => currentState.status === "ingestion-running" ? { ...currentState, log: activeRunLog.current, } : currentState, ); }, }) .then((result) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } if ( result.results.some((sourceResult) => sourceResult.status === "error") ) { process.exitCode = 1; } setRunState({ status: "ingestion-success", result, log: activeRunLog.current, credentialDiagnostics: activeRunCredentialDiagnostics.current, }); }) .catch((error: unknown) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } const errorDiagnostics = getErrorDiagnostics(error); const message = getErrorMessage(error); const authFix = getAuthFix(error, message, sessionProvider); // The full credential dump is opt-in (--debug); by default show only the // concise message, allowlisted error fields, and the how-to-fix panel. if (!shouldShowCredentialDiagnostics()) { setRunState({ status: "error", message, errorDiagnostics, authFix, }); return; } void getCredentialDiagnostics() .catch(() => undefined) .then((credentialDiagnostics) => { if (!mountedRef.current || activeRunId.current !== runId) { return; } setRunState({ status: "error", message, credentialDiagnostics, errorDiagnostics, authFix, }); }); }); } function clearSession() { activeRunId.current += 1; sessionThreadId.current = createOpenWikiThreadId(runtimeCwd); activeRunCredentialDiagnostics.current = undefined; activeRunLog.current = []; nextLogId.current = 1; nextCompletedRunId.current = 1; setCompletedRuns([]); setActiveUserMessage(null); setActiveMessageIsFollowup(false); setResolvedCommand(null); setRunState({ status: "idle" }); } async function selectModel(modelId: string) { await saveOpenWikiEnv({ [OPENWIKI_MODEL_ID_ENV_KEY]: modelId, }); setSessionModelId(modelId); } async function selectProvider(provider: OpenWikiProvider) { const modelId = getProviderModelOptions(provider).length > 0 ? getDefaultModelId(provider) : null; await saveOpenWikiEnv({ [OPENWIKI_PROVIDER_ENV_KEY]: provider, ...(modelId ? { [OPENWIKI_MODEL_ID_ENV_KEY]: modelId } : {}), }); setSessionProvider(provider); setSessionModelId(modelId); } useEffect(() => { mountedRef.current = true; return () => { mountedRef.current = false; }; }, []); useEffect(() => { if (sessionThreadMode.current === runMode) { return; } sessionThreadId.current = createOpenWikiThreadId(runtimeCwd); sessionThreadMode.current = runMode; }, [runMode, runtimeCwd]); useEffect(() => { if (command.kind === "help" || command.kind === "error") {