@moikapy/lich 0.4.0 → 0.5.0

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/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/cli_config.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Thin zero-dependency CLI for lich: one-shot tasks, interactive chat,\n * config files, and environment-based provider resolution.\n */\nimport { readFileSync } from \"node:fs\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { create_agent, type Agent, type AgentRunResult } from \"./agent/agent.js\";\nimport { parse_agent_config, type AgentConfig } from \"./agent/config.js\";\nimport { AgentEmitter } from \"./agent/events.js\";\nimport { LICH_VERSION } from \"./index.js\";\nimport { load_config, config_template } from \"./cli_config.js\";\n\ntype ProviderKind = \"openai_compat\" | \"anthropic\" | \"ollama\";\n\ninterface CliOptions {\n config_path?: string;\n help?: boolean;\n version?: boolean;\n overrides: Record<string, string>;\n positionals: string[];\n}\n\nconst FLAG_KEYS: Record<string, string> = {\n \"--work-dir\": \"work_dir\",\n \"--max-turns\": \"max_turns\",\n \"--model\": \"provider_model\",\n \"--provider-kind\": \"provider_kind\",\n \"--base-url\": \"provider_base_url\",\n \"--api-key-env\": \"provider_api_key_env\",\n \"--system-prompt\": \"system_prompt\",\n \"--session-dir\": \"session_dir\",\n \"--log-level\": \"log_level\",\n};\n\nconst DEFAULT_BASE_URLS: Record<ProviderKind, string> = {\n openai_compat: \"https://api.openai.com/v1\",\n anthropic: \"https://api.anthropic.com\",\n ollama: \"http://localhost:11434\",\n};\n\nconst DEFAULT_ENV_API_KEYS: Record<ProviderKind, string | undefined> = {\n openai_compat: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n ollama: undefined, // ollama needs no api key\n};\n\nfunction usage_text(): string {\n return [\n \"lich — a TypeScript AI agent harness\",\n \"\",\n \"Usage:\",\n ' lich \"one shot task\" run a single task and print the reply',\n \" lich chat interactive chat (commands: /exit, /quit)\",\n \" lich tui interactive terminal UI (ink)\",\n \" lich gateway <plat..> messaging gateway (webhook|telegram|discord|twitch)\",\n \" lich config print a starter config template (save as .lich/config.json)\",\n \" lich --help show this help\",\n \" lich --version print version\",\n \"\",\n \"Flags (before or after the subcommand):\",\n \" --config <path> JSON config file parsed by parse_agent_config\",\n \" --work-dir <path> working directory for tools\",\n \" --max-turns <n> loop turn budget (default 25)\",\n \" --model <m> model name (default from LICH_MODEL)\",\n \" --provider-kind <k> openai_compat | anthropic | ollama (default LICH_PROVIDER_KIND)\",\n \" --base-url <u> provider base url (default LICH_BASE_URL)\",\n \" --api-key-env <NAME> env var holding the api key (default LICH_API_KEY_ENV; unused by ollama)\",\n \" --system-prompt <s> system prompt override\",\n \" --session-dir <path> session transcript directory\",\n \" --log-level <level> debug | info | warn | error\",\n ].join(\"\\n\");\n}\n\nfunction error_message(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Mode-aware config failure message; one-shot keeps the generic variant. */\nfunction error_for_mode(mode: string, base_message: string): string {\n if (mode === \"tui\") {\n return \"lich tui: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n if (mode === \"gateway\") {\n return \"lich gateway: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n if (mode === \"chat\") {\n return \"lich chat: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n return base_message;\n}\n\nfunction parse_args(argv: string[]): CliOptions {\n const options: CliOptions = { overrides: {}, positionals: [] };\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (arg === undefined) {\n break;\n }\n if (arg === \"--help\") {\n options.help = true;\n continue;\n }\n if (arg === \"--version\") {\n options.version = true;\n continue;\n }\n if (arg === \"--config\") {\n const value = argv[index + 1];\n if (value === undefined) {\n throw new Error(\"--config requires a path\");\n }\n options.config_path = value;\n index += 1;\n continue;\n }\n const override_key = FLAG_KEYS[arg];\n if (override_key === undefined) {\n if (arg.startsWith(\"--\") === true) {\n throw new Error(`unknown flag: ${arg}`);\n }\n options.positionals.push(arg);\n continue;\n }\n const value = argv[index + 1];\n if (value === undefined || value.startsWith(\"--\") === true) {\n throw new Error(`${arg} requires a value`);\n }\n options.overrides[override_key] = value;\n index += 1;\n }\n return options;\n}\n\nfunction env_provider(overrides: Record<string, string>): Record<string, unknown> {\n const kind_value = overrides[\"provider_kind\"] ?? process.env.LICH_PROVIDER_KIND ?? \"openai_compat\";\n if (kind_value !== \"openai_compat\" && kind_value !== \"anthropic\" && kind_value !== \"ollama\") {\n throw new Error(`unknown provider kind \"${kind_value}\" (expected openai_compat, anthropic, or ollama)`);\n }\n const kind: ProviderKind = kind_value;\n const model = overrides[\"provider_model\"] ?? process.env.LICH_MODEL;\n if (model === undefined || model.length === 0) {\n throw new Error(\"no model configured: set LICH_MODEL, pass --model, or create .lich/config.json (`lich config` prints a template)\");\n }\n return {\n kind,\n name: \"default\",\n model,\n base_url: overrides[\"provider_base_url\"] ?? process.env.LICH_BASE_URL ?? DEFAULT_BASE_URLS[kind],\n api_key_env: overrides[\"provider_api_key_env\"] ?? process.env.LICH_API_KEY_ENV ?? DEFAULT_ENV_API_KEYS[kind],\n };\n}\n\nfunction load_config_file(config_path: string): Record<string, unknown> {\n try {\n const raw = readFileSync(config_path, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(\"config root must be a JSON object\");\n }\n return parsed as Record<string, unknown>;\n } catch (error) {\n throw new Error(`cannot use config file ${config_path}: ${error_message(error)}`);\n }\n}\n\nfunction apply_provider_override(config: Record<string, unknown>, overrides: Record<string, string>): void {\n const keys = [\"provider_kind\", \"provider_model\", \"provider_base_url\", \"provider_api_key_env\"] as const;\n const any_present = keys.some((key) => overrides[key] !== undefined);\n if (any_present === false) {\n return;\n }\n const providers = config[\"providers\"];\n if (Array.isArray(providers) === false || providers.length === 0) {\n config[\"providers\"] = [env_provider(overrides)];\n return;\n }\n const first = providers[0];\n if (typeof first !== \"object\" || first === null) {\n throw new Error(\"config providers[0] must be an object\");\n }\n const provider = first as Record<string, unknown>;\n for (const key of keys) {\n const value = overrides[key];\n if (value !== undefined) {\n provider[key.replace(\"provider_\", \"\")] = value;\n }\n }\n}\n\nfunction apply_overrides(config: Record<string, unknown>, overrides: Record<string, string>): void {\n for (const key of [\"work_dir\", \"system_prompt\", \"session_dir\", \"log_level\"]) {\n if (overrides[key] !== undefined) {\n config[key] = overrides[key];\n }\n }\n if (overrides[\"max_turns\"] !== undefined) {\n const turns = Number(overrides[\"max_turns\"]);\n if (Number.isInteger(turns) === false || turns < 1) {\n throw new Error(\"--max-turns must be a positive integer\");\n }\n config[\"max_turns\"] = turns;\n }\n apply_provider_override(config, overrides);\n}\n\nfunction build_config(options: CliOptions): AgentConfig {\n const base =\n options.config_path === undefined ? load_config() ?? { providers: [env_provider(options.overrides)] } : load_config_file(options.config_path);\n apply_overrides(base, options.overrides);\n const providers = base[\"providers\"];\n if (Array.isArray(providers) === false || providers.length === 0) {\n throw new Error(\"no model configured: set LICH_MODEL, pass --model, or create .lich/config.json (`lich config` prints a template)\");\n }\n return parse_agent_config(base);\n}\n\n/** build_config with the failure message prefixed for the invoking subcommand. */\nfunction build_config_for(options: CliOptions, mode: string): AgentConfig {\n try {\n return build_config(options);\n } catch (error) {\n throw new Error(error_for_mode(mode, error_message(error)));\n }\n}\n\nfunction attach_progress(emitter: AgentEmitter): () => void {\n return emitter.on((event) => {\n if (event.type === \"turn_start\") {\n process.stderr.write(`\\n[lich] turn ${event.turn}`);\n return;\n }\n if (event.type === \"tool_call_end\") {\n const status = event.result.ok === true ? \"ok\" : `error: ${event.result.error ?? \"\"}`;\n process.stderr.write(`\\n[lich] ${event.call.name}: ${status}`);\n return;\n }\n if (event.type === \"final\") {\n process.stderr.write(\"\\n\");\n }\n });\n}\n\nasync function run_one_shot(config: unknown, input: string): Promise<number> {\n const agent = create_agent(config);\n const stop_progress = attach_progress(agent.events);\n let result: AgentRunResult;\n try {\n result = await agent.run({ input });\n } finally {\n stop_progress();\n }\n const final = result.outcome.final;\n if (final !== undefined && final.content.length > 0) {\n process.stdout.write(`${final.content}\\n`);\n }\n if (result.outcome.stopped_reason === \"budget\") {\n process.stderr.write(`[lich] budget exhausted after ${result.outcome.turns_used} turns\\n`);\n return 1;\n }\n if (result.outcome.stopped_reason === \"aborted\") {\n process.stderr.write(\"[lich] run aborted\\n\");\n return 1;\n }\n return 0;\n}\n\nasync function run_chat_turn(agent: Agent, input: string): Promise<void> {\n const stop_progress = attach_progress(agent.events);\n try {\n const result = await agent.run({ input });\n const final = result.outcome.final;\n if (final !== undefined && final.content.length > 0) {\n process.stdout.write(`${final.content}\\n`);\n }\n process.stdout.write(`[turns ${result.outcome.turns_used} | tokens ${result.usage_total.total_tokens}]\\n`);\n } catch (error) {\n process.stderr.write(`[lich] ${error_message(error)}\\n`);\n } finally {\n stop_progress();\n }\n}\n\nfunction ask_line(rl: Interface): Promise<string> {\n return new Promise((resolve) => {\n const on_close = (): void => resolve(\"\");\n rl.question(\"> \", (answer) => {\n rl.removeListener(\"close\", on_close);\n resolve(answer);\n });\n rl.once(\"close\", on_close);\n });\n}\n\nasync function run_chat(config: unknown): Promise<number> {\n const agent = create_agent(config);\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n for (;;) {\n const line = await ask_line(rl);\n if (line.trim() === \"\") {\n return 0;\n }\n const command = line.trim();\n if (command === \"/exit\" || command === \"/quit\") {\n return 0;\n }\n await run_chat_turn(agent, command);\n }\n } finally {\n rl.close();\n }\n}\n\nasync function run_tui_entry(config: ReturnType<typeof build_config>): Promise<number> {\n const { run_tui } = await import(\"./tui.js\");\n return run_tui(config);\n}\n\nasync function run_gateway_entry(\n config: ReturnType<typeof build_config>,\n platforms: string[],\n): Promise<number> {\n const { run_gateway } = await import(\"./gateway.js\");\n return run_gateway(config, platforms.length === 0 ? [\"webhook\"] : platforms);\n}\n\nfunction is_main_module(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) {\n return false;\n }\n return import.meta.url === pathToFileURL(path.resolve(entry)).href;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const options = parse_args(argv);\n if (options.help === true) {\n process.stdout.write(`${usage_text()}\\n`);\n return 0;\n }\n if (options.version === true) {\n process.stdout.write(`${LICH_VERSION}\\n`);\n return 0;\n }\n const [first] = options.positionals;\n if (first === undefined) {\n process.stderr.write(`${usage_text()}\\n`);\n return 1;\n }\n if (first === \"config\") {\n process.stdout.write(`${config_template()}\\n`);\n return 0;\n }\n if (first === \"chat\") {\n if (options.positionals.length > 1) {\n throw new Error(\"chat mode takes no task argument\");\n }\n return run_chat(build_config_for(options, first));\n }\n if (first === \"tui\") {\n return run_tui_entry(build_config_for(options, first));\n }\n if (first === \"gateway\") {\n return run_gateway_entry(build_config_for(options, first), options.positionals.slice(1));\n }\n return run_one_shot(build_config_for(options, \"one-shot\"), options.positionals.join(\" \"));\n}\n\nif (is_main_module() === true) {\n main(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error) => {\n process.stderr.write(`lich: ${error_message(error)}\\n`);\n process.stderr.write(\"run `lich --help` for usage\\n\");\n process.exitCode = 1;\n });\n}","/**\n * Config-file discovery for the CLI: an ordered search chain, safe loading,\n * and a starter template so `lich tui` works with zero environment setup.\n */\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { safe_json_parse } from \"./util/json.js\";\n\nconst LICH_DIRNAME = \".lich\";\nconst CONFIG_RELPATH = `${LICH_DIRNAME}/config.json`;\nconst DEFAULT_USER_CONFIG = \".config/lich/config.json\";\n\n/** Ordered absolute chain: work_dir (default cwd) first, then the user config home. */\nexport function config_search_paths(work_dir?: string): string[] {\n const root = work_dir ?? process.cwd();\n const first = path.resolve(root, CONFIG_RELPATH);\n const second = path.resolve(homedir(), DEFAULT_USER_CONFIG);\n return first === second ? [first] : [first, second];\n}\n\n/** Explicit path must exist (else throw); otherwise walk the chain, undefined when absent. */\nexport function find_config_file(explicit?: string): string | undefined {\n if (explicit !== undefined) {\n if (existsSync(explicit) === false) {\n throw new Error(`config not found: ${explicit}`);\n }\n return explicit;\n }\n for (const candidate of config_search_paths()) {\n if (existsSync(candidate) === true) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/** Load the first config found as an object; undefined when absent, throw on bad json. */\nexport function load_config(explicit?: string): Record<string, unknown> | undefined {\n const found = find_config_file(explicit);\n if (found === undefined) {\n return undefined;\n }\n const raw = readFileSync(found, \"utf8\");\n const parsed = safe_json_parse<unknown>(raw);\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(`invalid config json: ${found}`);\n }\n return parsed as Record<string, unknown>;\n}\n\n/** Starter config honoring LICH_* env hints; `lich config` prints it. */\nexport function config_template(): string {\n const kind = process.env.LICH_PROVIDER_KIND ?? \"ollama\";\n const provider: Record<string, unknown> = {\n kind,\n name: \"main\",\n model: process.env.LICH_MODEL ?? \"<model-name>\",\n };\n if (kind === \"ollama\") {\n provider[\"base_url\"] = \"http://localhost:11434\";\n }\n return JSON.stringify({ providers: [provider], max_turns: 25 }, null, 2);\n}\n\n/** mkdir -p `${work_dir}/.lich` and return the directory path. */\nexport function ensure_lich_config_dir(work_dir: string): string {\n const dir = path.resolve(work_dir, LICH_DIRNAME);\n mkdirSync(dir, { recursive: true });\n return dir;\n}"],"mappings":"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","names":["readFileSync","path","value","readFileSync","path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/cli_config.ts","../src/cli_update.ts","../src/setup_wizard.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Thin zero-dependency CLI for lich: one-shot tasks, interactive chat,\n * config files, and environment-based provider resolution.\n */\nimport { readFileSync } from \"node:fs\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { create_agent_with_plugins, type Agent, type AgentRunResult } from \"./agent/agent.js\";\nimport { parse_agent_config, type AgentConfig } from \"./agent/config.js\";\nimport { AgentEmitter } from \"./agent/events.js\";\nimport { LICH_VERSION } from \"./index.js\";\nimport { load_config, config_template, existing_config_path, starter_config_object, write_lich_config } from \"./cli_config.js\";\nimport { run_update } from \"./cli_update.js\";\nimport { ask_line as ask_wizard_line, build_setup_config, collect_setup_answers } from \"./setup_wizard.js\";\n\ntype ProviderKind = \"openai_compat\" | \"anthropic\" | \"ollama\";\n\ninterface CliOptions {\n config_path?: string;\n help?: boolean;\n version?: boolean;\n overrides: Record<string, string>;\n positionals: string[];\n}\n\nconst FLAG_KEYS: Record<string, string> = {\n \"--work-dir\": \"work_dir\",\n \"--max-turns\": \"max_turns\",\n \"--model\": \"provider_model\",\n \"--provider-kind\": \"provider_kind\",\n \"--base-url\": \"provider_base_url\",\n \"--api-key-env\": \"provider_api_key_env\",\n \"--system-prompt\": \"system_prompt\",\n \"--session-dir\": \"session_dir\",\n \"--log-level\": \"log_level\",\n};\n\nconst DEFAULT_BASE_URLS: Record<ProviderKind, string> = {\n openai_compat: \"https://api.openai.com/v1\",\n anthropic: \"https://api.anthropic.com\",\n ollama: \"http://localhost:11434\",\n};\n\nconst DEFAULT_ENV_API_KEYS: Record<ProviderKind, string | undefined> = {\n openai_compat: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n ollama: undefined, // ollama needs no api key\n};\n\nfunction usage_text(): string {\n return [\n \"lich — a TypeScript AI agent harness\",\n \"\",\n \"Usage:\",\n \" lich open the TUI (first run: setup wizard, then TUI)\",\n \" lich init write .lich/config.json without the wizard (flags apply; never overwrites)\",\n ' lich \"one shot task\" run a single task and print the reply',\n \" lich chat interactive chat (commands: /exit, /quit)\",\n \" lich tui interactive terminal UI (ink)\",\n \" lich gateway <plat..> messaging gateway (webhook|telegram|discord|twitch)\",\n \" lich config print a starter config template (save as .lich/config.json)\",\n \" lich update install a newer @moikapy/lich from npm, if one exists\",\n \" lich --help show this help\",\n \" lich --version print version\",\n \"\",\n \"Flags (before or after the subcommand):\",\n \" --config <path> JSON config file parsed by parse_agent_config\",\n \" --work-dir <path> working directory for tools\",\n \" --max-turns <n> loop turn budget (default 25)\",\n \" --model <m> model name (default from LICH_MODEL)\",\n \" --provider-kind <k> openai_compat | anthropic | ollama (default LICH_PROVIDER_KIND)\",\n \" --base-url <u> provider base url (default LICH_BASE_URL)\",\n \" --api-key-env <NAME> env var holding the api key (default LICH_API_KEY_ENV; unused by ollama)\",\n \" --system-prompt <s> system prompt override\",\n \" --session-dir <path> session transcript directory\",\n \" --log-level <level> debug | info | warn | error\",\n ].join(\"\\n\");\n}\n\nfunction error_message(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Mode-aware config failure message; one-shot keeps the generic variant. */\nfunction error_for_mode(mode: string, base_message: string): string {\n if (mode === \"tui\") {\n return \"lich tui: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n if (mode === \"gateway\") {\n return \"lich gateway: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n if (mode === \"chat\") {\n return \"lich chat: no model configured — set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)\";\n }\n return base_message;\n}\n\nfunction parse_args(argv: string[]): CliOptions {\n const options: CliOptions = { overrides: {}, positionals: [] };\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (arg === undefined) {\n break;\n }\n if (arg === \"--help\") {\n options.help = true;\n continue;\n }\n if (arg === \"--version\") {\n options.version = true;\n continue;\n }\n if (arg === \"--config\") {\n const value = argv[index + 1];\n if (value === undefined) {\n throw new Error(\"--config requires a path\");\n }\n options.config_path = value;\n index += 1;\n continue;\n }\n const override_key = FLAG_KEYS[arg];\n if (override_key === undefined) {\n if (arg.startsWith(\"--\") === true) {\n throw new Error(`unknown flag: ${arg}`);\n }\n options.positionals.push(arg);\n continue;\n }\n const value = argv[index + 1];\n if (value === undefined || value.startsWith(\"--\") === true) {\n throw new Error(`${arg} requires a value`);\n }\n options.overrides[override_key] = value;\n index += 1;\n }\n return options;\n}\n\nfunction env_provider(overrides: Record<string, string>): Record<string, unknown> {\n const kind_value = overrides[\"provider_kind\"] ?? process.env.LICH_PROVIDER_KIND ?? \"openai_compat\";\n if (kind_value !== \"openai_compat\" && kind_value !== \"anthropic\" && kind_value !== \"ollama\") {\n throw new Error(`unknown provider kind \"${kind_value}\" (expected openai_compat, anthropic, or ollama)`);\n }\n const kind: ProviderKind = kind_value;\n const model = overrides[\"provider_model\"] ?? process.env.LICH_MODEL;\n if (model === undefined || model.length === 0) {\n throw new Error(\"no model configured: set LICH_MODEL, pass --model, or create .lich/config.json (`lich config` prints a template)\");\n }\n return {\n kind,\n name: \"default\",\n model,\n base_url: overrides[\"provider_base_url\"] ?? process.env.LICH_BASE_URL ?? DEFAULT_BASE_URLS[kind],\n api_key_env: overrides[\"provider_api_key_env\"] ?? process.env.LICH_API_KEY_ENV ?? DEFAULT_ENV_API_KEYS[kind],\n };\n}\n\nfunction load_config_file(config_path: string): Record<string, unknown> {\n try {\n const raw = readFileSync(config_path, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(\"config root must be a JSON object\");\n }\n return parsed as Record<string, unknown>;\n } catch (error) {\n throw new Error(`cannot use config file ${config_path}: ${error_message(error)}`);\n }\n}\n\nfunction apply_provider_override(config: Record<string, unknown>, overrides: Record<string, string>): void {\n const keys = [\"provider_kind\", \"provider_model\", \"provider_base_url\", \"provider_api_key_env\"] as const;\n const any_present = keys.some((key) => overrides[key] !== undefined);\n if (any_present === false) {\n return;\n }\n const providers = config[\"providers\"];\n if (Array.isArray(providers) === false || providers.length === 0) {\n config[\"providers\"] = [env_provider(overrides)];\n return;\n }\n const first = providers[0];\n if (typeof first !== \"object\" || first === null) {\n throw new Error(\"config providers[0] must be an object\");\n }\n const provider = first as Record<string, unknown>;\n for (const key of keys) {\n const value = overrides[key];\n if (value !== undefined) {\n provider[key.replace(\"provider_\", \"\")] = value;\n }\n }\n}\n\nfunction apply_overrides(config: Record<string, unknown>, overrides: Record<string, string>): void {\n for (const key of [\"work_dir\", \"system_prompt\", \"session_dir\", \"log_level\"]) {\n if (overrides[key] !== undefined) {\n config[key] = overrides[key];\n }\n }\n if (overrides[\"max_turns\"] !== undefined) {\n const turns = Number(overrides[\"max_turns\"]);\n if (Number.isInteger(turns) === false || turns < 1) {\n throw new Error(\"--max-turns must be a positive integer\");\n }\n config[\"max_turns\"] = turns;\n }\n apply_provider_override(config, overrides);\n}\n\n/** Same search as the wizard: `--work-dir` (else cwd), then the user-home file. */\nfunction load_discovered_config(work_dir: string): Record<string, unknown> | undefined {\n const found = existing_config_path(work_dir);\n if (found === undefined) {\n return undefined;\n }\n return load_config(found);\n}\n\nfunction build_config(options: CliOptions): AgentConfig {\n const base =\n options.config_path === undefined\n ? load_discovered_config(work_dir_of(options)) ?? { providers: [env_provider(options.overrides)] }\n : load_config_file(options.config_path);\n apply_overrides(base, options.overrides);\n const providers = base[\"providers\"];\n if (Array.isArray(providers) === false || providers.length === 0) {\n throw new Error(\"no model configured: set LICH_MODEL, pass --model, or create .lich/config.json (`lich config` prints a template)\");\n }\n return parse_agent_config(base);\n}\n\n/** build_config with the failure message prefixed for the invoking subcommand. */\nfunction build_config_for(options: CliOptions, mode: string): AgentConfig {\n try {\n return build_config(options);\n } catch (error) {\n throw new Error(error_for_mode(mode, error_message(error)));\n }\n}\n\nfunction attach_progress(emitter: AgentEmitter): () => void {\n return emitter.on((event) => {\n if (event.type === \"turn_start\") {\n process.stderr.write(`\\n[lich] turn ${event.turn}`);\n return;\n }\n if (event.type === \"tool_call_end\") {\n const status = event.result.ok === true ? \"ok\" : `error: ${event.result.error ?? \"\"}`;\n process.stderr.write(`\\n[lich] ${event.call.name}: ${status}`);\n return;\n }\n if (event.type === \"final\") {\n process.stderr.write(\"\\n\");\n }\n });\n}\n\nexport async function run_one_shot(config: unknown, input: string): Promise<number> {\n const agent = await create_agent_with_plugins(config);\n const stop_progress = attach_progress(agent.events);\n let result: AgentRunResult;\n try {\n result = await agent.run({ input });\n } finally {\n stop_progress();\n }\n const final = result.outcome.final;\n if (final !== undefined && final.content.length > 0) {\n process.stdout.write(`${final.content}\\n`);\n }\n if (result.outcome.stopped_reason === \"budget\") {\n process.stderr.write(`[lich] budget exhausted after ${result.outcome.turns_used} turns\\n`);\n return 1;\n }\n if (result.outcome.stopped_reason === \"aborted\") {\n process.stderr.write(\"[lich] run aborted\\n\");\n return 1;\n }\n return 0;\n}\n\nasync function run_chat_turn(agent: Agent, input: string): Promise<void> {\n const stop_progress = attach_progress(agent.events);\n try {\n const result = await agent.run({ input });\n const final = result.outcome.final;\n if (final !== undefined && final.content.length > 0) {\n process.stdout.write(`${final.content}\\n`);\n }\n process.stdout.write(`[turns ${result.outcome.turns_used} | tokens ${result.usage_total.total_tokens}]\\n`);\n } catch (error) {\n process.stderr.write(`[lich] ${error_message(error)}\\n`);\n } finally {\n stop_progress();\n }\n}\n\nfunction ask_line(rl: Interface): Promise<string> {\n return new Promise((resolve) => {\n const on_close = (): void => resolve(\"\");\n rl.question(\"> \", (answer) => {\n rl.removeListener(\"close\", on_close);\n resolve(answer);\n });\n rl.once(\"close\", on_close);\n });\n}\n\nexport async function run_chat(config: unknown): Promise<number> {\n const agent = await create_agent_with_plugins(config);\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n for (;;) {\n const line = await ask_line(rl);\n if (line.trim() === \"\") {\n return 0;\n }\n const command = line.trim();\n if (command === \"/exit\" || command === \"/quit\") {\n return 0;\n }\n await run_chat_turn(agent, command);\n }\n } finally {\n rl.close();\n }\n}\n\nfunction model_hint(options: CliOptions): string | undefined {\n const model = options.overrides[\"provider_model\"] ?? process.env.LICH_MODEL;\n if (model === undefined || model.length === 0) {\n return undefined;\n }\n return model;\n}\n\nfunction skip_setup_message(existing: string): string {\n if (existing.endsWith(`${path.sep}.lich${path.sep}config.json`) === true) {\n return `lich: .lich/config.json already exists at ${existing}; skipping setup`;\n }\n return `lich: config already exists at ${existing}; skipping setup`;\n}\n\nfunction work_dir_of(options: CliOptions): string {\n return options.overrides[\"work_dir\"] ?? process.cwd();\n}\n\nasync function offer_wizard(work_dir: string, hint?: string): Promise<string | \"cancelled\"> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answers = await collect_setup_answers(work_dir, (prompt) => ask_wizard_line(rl, prompt), hint);\n if (answers === undefined) {\n return \"cancelled\";\n }\n const result = write_lich_config(work_dir, build_setup_config(answers));\n process.stdout.write(`${result.message}\\n`);\n return result.path;\n } finally {\n rl.close();\n }\n}\n\nasync function maybe_first_run(options: CliOptions, work_dir: string): Promise<number | undefined> {\n if (options.config_path !== undefined) {\n return undefined;\n }\n const existing = existing_config_path(work_dir);\n if (existing !== undefined) {\n process.stdout.write(`${skip_setup_message(existing)}\\n`);\n options.config_path = existing;\n return undefined;\n }\n const wrote = await offer_wizard(work_dir, model_hint(options));\n if (wrote === \"cancelled\") {\n process.stderr.write(\"lich: setup cancelled; nothing written\\n\");\n return 1;\n }\n options.config_path = wrote;\n return undefined;\n}\n\nasync function run_bare(options: CliOptions): Promise<number> {\n if (process.stdin.isTTY !== true) {\n process.stderr.write(\"lich: stdin is not a TTY; skipping setup wizard. Run `lich init` to write .lich/config.json, or `lich --help` for usage.\\n\");\n return 1;\n }\n const work_dir = work_dir_of(options);\n const stopped = await maybe_first_run(options, work_dir);\n if (stopped !== undefined) {\n return stopped;\n }\n return run_tui_entry(build_config_for(options, \"tui\"));\n}\n\nfunction model_still_placeholder(config: Record<string, unknown>): boolean {\n const providers = config[\"providers\"];\n const first = Array.isArray(providers) === true ? providers[0] : undefined;\n if (typeof first !== \"object\" || first === null) {\n return false;\n }\n return (first as Record<string, unknown>)[\"model\"] === \"<model-name>\";\n}\n\nfunction run_init(options: CliOptions): number {\n if (options.positionals.length > 1) {\n throw new Error(\"init takes no extra arguments\");\n }\n const config = starter_config_object();\n apply_overrides(config, options.overrides);\n const result = write_lich_config(work_dir_of(options), config);\n process.stdout.write(`${result.message}\\n`);\n if (result.written === true && model_still_placeholder(config) === true) {\n process.stdout.write(\"next: edit the model in .lich/config.json if needed, then run `lich`\\n\");\n }\n return 0;\n}\n\nasync function run_tui_entry(config: ReturnType<typeof build_config>): Promise<number> {\n const { run_tui } = await import(\"./tui.js\");\n return run_tui(config);\n}\n\nfunction gateway_platforms(config: AgentConfig, cli_platforms: readonly string[]): readonly string[] {\n if (cli_platforms.length > 0) {\n return cli_platforms;\n }\n const configured = config.gateway?.platforms ?? [];\n return configured.length === 0 ? [\"webhook\"] : configured;\n}\n\nasync function run_gateway_entry(config: AgentConfig, platforms: string[]): Promise<number> {\n const { run_gateway } = await import(\"./gateway.js\");\n return run_gateway(config, gateway_platforms(config, platforms));\n}\n\nfunction is_main_module(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) {\n return false;\n }\n return import.meta.url === pathToFileURL(path.resolve(entry)).href;\n}\n\nexport async function run_cli(argv: string[]): Promise<number> {\n const options = parse_args(argv);\n if (options.help === true) {\n process.stdout.write(`${usage_text()}\\n`);\n return 0;\n }\n if (options.version === true) {\n process.stdout.write(`${LICH_VERSION}\\n`);\n return 0;\n }\n const [first] = options.positionals;\n if (first === undefined) {\n return run_bare(options);\n }\n if (first === \"init\") {\n return run_init(options);\n }\n if (first === \"config\") {\n process.stdout.write(`${config_template()}\\n`);\n return 0;\n }\n if (first === \"update\") {\n if (options.positionals.length > 1) {\n throw new Error(\"update takes no arguments\");\n }\n return run_update(fileURLToPath(import.meta.url));\n }\n if (first === \"chat\") {\n if (options.positionals.length > 1) {\n throw new Error(\"chat mode takes no task argument\");\n }\n return run_chat(build_config_for(options, first));\n }\n if (first === \"tui\") {\n return run_tui_entry(build_config_for(options, first));\n }\n if (first === \"gateway\") {\n return run_gateway_entry(build_config_for(options, first), options.positionals.slice(1));\n }\n return run_one_shot(build_config_for(options, \"one-shot\"), options.positionals.join(\" \"));\n}\n\nif (is_main_module() === true) {\n run_cli(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error) => {\n process.stderr.write(`lich: ${error_message(error)}\\n`);\n process.stderr.write(\"run `lich --help` for usage\\n\");\n process.exitCode = 1;\n });\n}","/**\n * Config-file discovery for the CLI: an ordered search chain, safe loading,\n * and a starter template so `lich tui` works with zero environment setup.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { safe_json_parse } from \"./util/json.js\";\n\nconst LICH_DIRNAME = \".lich\";\nconst CONFIG_RELPATH = `${LICH_DIRNAME}/config.json`;\nconst DEFAULT_USER_CONFIG = \".config/lich/config.json\";\n\n/** Ordered absolute chain: work_dir (default cwd) first, then the user config home. */\nexport function config_search_paths(work_dir?: string): string[] {\n const root = work_dir ?? process.cwd();\n const first = path.resolve(root, CONFIG_RELPATH);\n const second = path.resolve(homedir(), DEFAULT_USER_CONFIG);\n return first === second ? [first] : [first, second];\n}\n\n/** Explicit path must exist (else throw); otherwise walk the chain, undefined when absent. */\nexport function find_config_file(explicit?: string): string | undefined {\n if (explicit !== undefined) {\n if (existsSync(explicit) === false) {\n throw new Error(`config not found: ${explicit}`);\n }\n return explicit;\n }\n return existing_config_path(process.cwd());\n}\n\n/** Load the first config found as an object; undefined when absent, throw on bad json. */\nexport function load_config(explicit?: string): Record<string, unknown> | undefined {\n const found = find_config_file(explicit);\n if (found === undefined) {\n return undefined;\n }\n const raw = readFileSync(found, \"utf8\");\n const parsed = safe_json_parse<unknown>(raw);\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(`invalid config json: ${found}`);\n }\n return parsed as Record<string, unknown>;\n}\n\n/** Starter config honoring LICH_* env hints; `lich config` prints it. */\nexport function config_template(): string {\n const kind = process.env.LICH_PROVIDER_KIND ?? \"ollama\";\n const provider: Record<string, unknown> = {\n kind,\n name: \"main\",\n model: process.env.LICH_MODEL ?? \"<model-name>\",\n };\n if (kind === \"ollama\") {\n provider[\"base_url\"] = \"http://localhost:11434\";\n }\n return JSON.stringify({ providers: [provider], max_turns: 25 }, null, 2);\n}\n\n/** mkdir -p `${work_dir}/.lich` and return the directory path. */\nexport function ensure_lich_config_dir(work_dir: string): string {\n const dir = path.resolve(work_dir, LICH_DIRNAME);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\n/** Project config path: the first entry in the search chain. */\nexport function project_config_path(work_dir: string): string {\n return path.resolve(work_dir, CONFIG_RELPATH);\n}\n\n/** First existing file on `config_search_paths(work_dir)`. */\nexport function existing_config_path(work_dir: string): string | undefined {\n for (const candidate of config_search_paths(work_dir)) {\n if (existsSync(candidate) === true) {\n return candidate;\n }\n }\n return undefined;\n}\n\nexport interface LichConfigWriteResult {\n path: string;\n written: boolean;\n message: string;\n}\n\n/** Parsed `config_template()` object. Does not write. */\nexport function starter_config_object(): Record<string, unknown> {\n const parsed: unknown = JSON.parse(config_template());\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(\"config template must be a JSON object\");\n }\n return parsed as Record<string, unknown>;\n}\n\n/**\n * Create `.lich/` and write `config.json` once. The only writer of that file.\n * Existing files are left untouched.\n */\nexport function write_lich_config(work_dir: string, config: Record<string, unknown>): LichConfigWriteResult {\n ensure_lich_config_dir(work_dir);\n const file = project_config_path(work_dir);\n if (existsSync(file) === true) {\n return already_exists(file);\n }\n try {\n writeFileSync(file, `${JSON.stringify(config, null, 2)}\\n`, { encoding: \"utf8\", flag: \"wx\" });\n } catch (error) {\n if (is_eexist(error) === true) {\n return already_exists(file);\n }\n throw error;\n }\n return { path: file, written: true, message: `wrote ${file}` };\n}\n\nfunction already_exists(file: string): LichConfigWriteResult {\n return {\n path: file,\n written: false,\n message: `lich: .lich/config.json already exists at ${file}; not overwriting`,\n };\n}\n\nfunction is_eexist(error: unknown): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error && (error as { code?: unknown }).code === \"EEXIST\";\n}\n","/**\n * `lich update`: compare the installed version to the npm registry and,\n * when newer, shell out to `npm install -g @moikapy/lich@latest`.\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { LICH_VERSION } from \"./index.js\";\n\nexport const PACKAGE_NAME = \"@moikapy/lich\";\nexport const VIEW_ARGS = [\"view\", PACKAGE_NAME, \"version\"] as const;\nexport const INSTALL_ARGS = [\"install\", \"-g\", `${PACKAGE_NAME}@latest`] as const;\n\nexport type InstallKind = \"npm\" | \"git\" | \"npx\";\n\nexport interface CommandResult {\n exit_code: number;\n stdout: string;\n stderr: string;\n error_code?: string;\n}\n\nexport type CommandRunner = (command: string, args: readonly string[]) => Promise<CommandResult>;\n\nexport interface UpdateIo {\n write_stdout: (text: string) => void;\n write_stderr: (text: string) => void;\n}\n\nexport interface UpdateRequest extends UpdateIo {\n installed_version: string;\n install_kind: InstallKind;\n run_command: CommandRunner;\n}\n\ntype Semver = [number, number, number, string];\ntype VersionRelation = \"update\" | \"current\" | \"invalid\";\n\nconst NPM_MISSING =\n \"lich: npm is not on PATH. Install Node.js, or run `npm install -g @moikapy/lich@latest` once npm is available.\\n\";\nconst EXIT_FIRST_HINT =\n \"Exit any running `lich tui` or `lich gateway` first — npm cannot replace the package while those processes are running.\\n\";\n\nfunction parse_semver(version: string): Semver | null {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(version.trim());\n if (match === null || match[1] === undefined || match[2] === undefined || match[3] === undefined) {\n return null;\n }\n return [Number(match[1]), Number(match[2]), Number(match[3]), match[4] ?? \"\"];\n}\n\n/** `update` when the registry version is strictly newer than the installed one. */\nexport function version_relation(installed: string, registry: string): VersionRelation {\n const left = parse_semver(installed);\n const right = parse_semver(registry);\n if (left === null || right === null) {\n return \"invalid\";\n }\n for (let index = 0; index < 3; index += 1) {\n const installed_part = left[index];\n const registry_part = right[index];\n if (typeof installed_part !== \"number\" || typeof registry_part !== \"number\") {\n return \"invalid\";\n }\n if (installed_part < registry_part) {\n return \"update\";\n }\n if (installed_part > registry_part) {\n return \"current\";\n }\n }\n if (left[3] !== \"\" && right[3] === \"\") {\n return \"update\";\n }\n return \"current\";\n}\n\n/** npm global, a git clone (`git pull`), or an npx temporary copy. */\nexport function detect_install_kind(\n module_path: string,\n env: NodeJS.ProcessEnv,\n has_git: (dir: string) => boolean,\n): InstallKind {\n if (env.npm_command === \"exec\" || module_path.includes(`${path.sep}_npx${path.sep}`)) {\n return \"npx\";\n }\n let dir = path.dirname(path.resolve(module_path));\n const root = path.parse(dir).root;\n while (dir !== root) {\n if (path.basename(dir) === \"node_modules\") {\n return \"npm\";\n }\n if (has_git(dir) === true) {\n return \"git\";\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n break;\n }\n dir = parent;\n }\n return \"npm\";\n}\n\nfunction blocked_install_message(kind: InstallKind): string | null {\n if (kind === \"git\") {\n return \"lich: this copy is a git clone, not an npm install. Update with `git pull` in the repository.\\n\";\n }\n if (kind === \"npx\") {\n return \"lich: npx runs a temporary copy and cannot persist an update. Install with `npm install -g @moikapy/lich`.\\n\";\n }\n return null;\n}\n\nfunction registry_version(viewed: CommandResult, request: UpdateRequest): string | null {\n if (viewed.error_code === \"ENOENT\") {\n request.write_stderr(NPM_MISSING);\n return null;\n }\n if (viewed.exit_code !== 0) {\n const detail = viewed.stderr.trim() || viewed.stdout.trim() || `exit ${viewed.exit_code}`;\n request.write_stderr(`lich: could not read the registry version of ${PACKAGE_NAME}: ${detail}\\n`);\n return null;\n }\n const registry = viewed.stdout.trim();\n if (parse_semver(registry) === null) {\n request.write_stderr(`lich: could not read the registry version of ${PACKAGE_NAME}: unexpected version \"${registry}\"\\n`);\n return null;\n }\n return registry;\n}\n\nfunction is_permission_failure(installed: CommandResult): boolean {\n if (installed.error_code === \"EACCES\" || installed.error_code === \"EPERM\") {\n return true;\n }\n const text = `${installed.stderr}\\n${installed.stdout}`.toLowerCase();\n return text.includes(\"eacces\") || text.includes(\"eperm\") || text.includes(\"permission denied\");\n}\n\nfunction report_install(request: UpdateRequest, installed: CommandResult): number {\n if (installed.exit_code === 0) {\n request.write_stdout(`updated ${PACKAGE_NAME}. Run \\`lich --version\\` in a new shell to confirm.\\n`);\n return 0;\n }\n if (installed.error_code === \"ENOENT\") {\n request.write_stderr(NPM_MISSING);\n return 1;\n }\n if (is_permission_failure(installed) === true) {\n request.write_stderr(\"lich: npm install failed (permission denied). Use a writable npm prefix, or run `npm install -g @moikapy/lich@latest` yourself.\\n\");\n return 1;\n }\n const detail = installed.stderr.trim() || `exit ${installed.exit_code}`;\n request.write_stderr(`lich: npm install failed: ${detail}\\n`);\n return 1;\n}\n\nasync function install_if_newer(request: UpdateRequest, registry: string): Promise<number> {\n const relation = version_relation(request.installed_version, registry);\n if (relation === \"invalid\") {\n request.write_stderr(`lich: could not compare versions (installed ${request.installed_version}, registry ${registry})\\n`);\n return 1;\n }\n if (relation !== \"update\") {\n request.write_stdout(`lich ${request.installed_version} is up to date\\n`);\n return 0;\n }\n request.write_stdout(`lich ${request.installed_version} -> ${registry}\\n`);\n request.write_stdout(EXIT_FIRST_HINT);\n const installed = await request.run_command(\"npm\", INSTALL_ARGS);\n return report_install(request, installed);\n}\n\n/** Decide whether to install. Tests inject `run_command` so neither lookup nor install hits the network. */\nexport async function run_lich_update(request: UpdateRequest): Promise<number> {\n const blocked = blocked_install_message(request.install_kind);\n if (blocked !== null) {\n request.write_stderr(blocked);\n return 1;\n }\n const viewed = await request.run_command(\"npm\", VIEW_ARGS);\n const registry = registry_version(viewed, request);\n if (registry === null) {\n return 1;\n }\n return install_if_newer(request, registry);\n}\n\n/** Default spawn. Forwards only `npm install` output; `npm view` stays quiet. */\nexport function default_command_runner(command: string, args: readonly string[]): Promise<CommandResult> {\n return new Promise((resolve) => {\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n const finish = (result: CommandResult): void => {\n if (settled === true) {\n return;\n }\n settled = true;\n resolve(result);\n };\n const forward = args[0] === \"install\";\n const child = spawn(command, [...args], { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n stdout += text;\n if (forward === true) {\n process.stdout.write(text);\n }\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n stderr += text;\n if (forward === true) {\n process.stderr.write(text);\n }\n });\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n finish({ exit_code: 127, stdout, stderr: error.message, error_code: error.code });\n });\n child.on(\"close\", (code) => {\n finish({ exit_code: code ?? 1, stdout, stderr });\n });\n });\n}\n\nexport async function run_update(module_path: string): Promise<number> {\n return run_lich_update({\n installed_version: LICH_VERSION,\n install_kind: detect_install_kind(module_path, process.env, (dir) => existsSync(path.join(dir, \".git\"))),\n run_command: default_command_runner,\n write_stdout: (text) => {\n process.stdout.write(text);\n },\n write_stderr: (text) => {\n process.stderr.write(text);\n },\n });\n}\n","/**\n * First-run setup wizard. Builds an AgentConfig object; does not write the file.\n * Callers pass the object to write_lich_config.\n */\nimport { readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Interface } from \"node:readline\";\nimport { DEFAULT_GATEWAY_TOKEN_ENVS, is_env_var_name } from \"./gateway/token_env.js\";\n\nexport type ProviderKind = \"ollama\" | \"openai_compat\" | \"anthropic\";\n\nconst PLATFORMS = [\"webhook\", \"telegram\", \"discord\", \"twitch\"] as const;\nconst PLUGIN_EXT = /\\.(mjs|js|ts|mts|cts|jsx|tsx)$/;\n\nconst PROVIDER_DEFAULTS: Record<ProviderKind, { model: string; base_url: string; api_key_env?: string }> = {\n ollama: { model: \"llama3.2\", base_url: \"http://localhost:11434\" },\n openai_compat: { model: \"gpt-4.1-mini\", base_url: \"https://api.openai.com/v1\", api_key_env: \"OPENAI_API_KEY\" },\n anthropic: { model: \"claude-sonnet-4\", base_url: \"https://api.anthropic.com\", api_key_env: \"ANTHROPIC_API_KEY\" },\n};\n\nexport interface SetupAnswers {\n agent_name: string;\n provider_kind: ProviderKind;\n model: string;\n base_url: string;\n api_key_env?: string;\n platforms: string[];\n token_envs: Record<string, string>;\n plugins: string[];\n}\n\nexport type AskLine = (prompt: string) => Promise<string | undefined>;\n\n/** Plugin entry files under `.lich/plugins`, as work_dir-relative specifiers. */\nfunction discover_plugin_entries(work_dir: string): string[] {\n const dir = path.resolve(work_dir, \".lich\", \"plugins\");\n let names: string[];\n try {\n names = readdirSync(dir);\n } catch {\n return [];\n }\n const entries: string[] = [];\n for (const name of names) {\n if (PLUGIN_EXT.test(name) === true) {\n entries.push(path.posix.join(\".lich/plugins\", name));\n }\n }\n return entries;\n}\n\n/** Object the agent schema already accepts. Omits gateway when no platforms were chosen. */\nexport function build_setup_config(answers: SetupAnswers): Record<string, unknown> {\n const provider: Record<string, unknown> = {\n kind: answers.provider_kind,\n name: \"main\",\n model: answers.model,\n base_url: answers.base_url,\n };\n if (answers.api_key_env !== undefined && answers.api_key_env.length > 0) {\n provider[\"api_key_env\"] = answers.api_key_env;\n }\n const config: Record<string, unknown> = {\n agent_name: answers.agent_name,\n providers: [provider],\n max_turns: 25,\n plugins: answers.plugins,\n };\n if (answers.platforms.length > 0) {\n config[\"gateway\"] = { platforms: answers.platforms, token_envs: answers.token_envs };\n }\n return config;\n}\n\nfunction blank_as_default(raw: string, fallback: string): string {\n const trimmed = raw.trim();\n return trimmed.length === 0 ? fallback : trimmed;\n}\n\nfunction parse_kind(raw: string): ProviderKind | undefined {\n const trimmed = raw.trim();\n const kind = trimmed.length === 0 ? \"ollama\" : trimmed;\n if (kind === \"ollama\" || kind === \"openai_compat\" || kind === \"anthropic\") {\n return kind;\n }\n return undefined;\n}\n\nfunction parse_platforms(raw: string): string[] {\n const seen = new Set<string>();\n const platforms: string[] = [];\n for (const part of raw.split(\",\")) {\n const name = part.trim().toLowerCase();\n if ((PLATFORMS as readonly string[]).includes(name) === false || seen.has(name) === true) {\n continue;\n }\n seen.add(name);\n platforms.push(name);\n }\n return platforms;\n}\n\n/** One prompt. Resolves undefined on Ctrl+C or a closed stream so nothing is written. */\nexport function ask_line(rl: Interface, prompt: string): Promise<string | undefined> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (value: string | undefined): void => {\n if (settled === true) {\n return;\n }\n settled = true;\n rl.removeListener(\"SIGINT\", on_sigint);\n rl.removeListener(\"close\", on_close);\n resolve(value);\n };\n const on_sigint = (): void => finish(undefined);\n const on_close = (): void => finish(undefined);\n rl.once(\"SIGINT\", on_sigint);\n rl.once(\"close\", on_close);\n rl.question(prompt, (answer) => finish(answer));\n });\n}\n\nasync function ask_name(ask: AskLine): Promise<string | undefined> {\n const raw = await ask(\"Agent name [lich]: \");\n if (raw === undefined) {\n return undefined;\n }\n return blank_as_default(raw, \"lich\");\n}\n\nasync function ask_provider(\n ask: AskLine,\n model_hint?: string,\n): Promise<Pick<SetupAnswers, \"provider_kind\" | \"model\" | \"base_url\" | \"api_key_env\"> | undefined> {\n const kind_raw = await ask(\"Provider kind (ollama|openai_compat|anthropic) [ollama]: \");\n if (kind_raw === undefined) {\n return undefined;\n }\n let kind = parse_kind(kind_raw);\n if (kind === undefined) {\n const retry = await ask(\"Unknown kind. Provider kind (ollama|openai_compat|anthropic) [ollama]: \");\n if (retry === undefined) {\n return undefined;\n }\n kind = parse_kind(retry) ?? \"ollama\";\n }\n const defaults = PROVIDER_DEFAULTS[kind];\n const model_default = model_hint !== undefined && model_hint.length > 0 ? model_hint : defaults.model;\n const model_raw = await ask(`Model [${model_default}]: `);\n if (model_raw === undefined) {\n return undefined;\n }\n const url_raw = await ask(`Base URL [${defaults.base_url}]: `);\n if (url_raw === undefined) {\n return undefined;\n }\n const key_default = defaults.api_key_env ?? \"\";\n const key_prompt = key_default.length === 0 ? \"API key env var name (empty to skip): \" : `API key env var name [${key_default}]: `;\n const key_raw = await ask(key_prompt);\n if (key_raw === undefined) {\n return undefined;\n }\n const api_key_env = blank_as_default(key_raw, key_default);\n return {\n provider_kind: kind,\n model: blank_as_default(model_raw, model_default),\n base_url: blank_as_default(url_raw, defaults.base_url),\n api_key_env: api_key_env.length === 0 ? undefined : api_key_env,\n };\n}\n\nfunction token_env_name(raw: string, fallback: string): string | undefined {\n const name = blank_as_default(raw, fallback);\n return is_env_var_name(name) === true ? name : undefined;\n}\n\nasync function ask_token_env(ask: AskLine, platform: string, fallback: string): Promise<string | undefined> {\n const first = await ask(`${platform} token env var [${fallback}] (name only, not the secret): `);\n if (first === undefined) {\n return undefined;\n }\n const named = token_env_name(first, fallback);\n if (named !== undefined) {\n return named;\n }\n const retry = await ask(`Env var name must match [A-Za-z_][A-Za-z0-9_]* [${fallback}]: `);\n if (retry === undefined) {\n return undefined;\n }\n return token_env_name(retry, fallback);\n}\n\nasync function ask_gateway(ask: AskLine): Promise<{ platforms: string[]; token_envs: Record<string, string> } | undefined> {\n const raw = await ask(\"Gateway platforms (webhook,telegram,discord,twitch; empty to skip): \");\n if (raw === undefined) {\n return undefined;\n }\n const platforms = parse_platforms(raw);\n const token_envs: Record<string, string> = {};\n for (const platform of platforms) {\n const name = await ask_token_env(ask, platform, DEFAULT_GATEWAY_TOKEN_ENVS[platform] ?? \"\");\n if (name === undefined) {\n return undefined;\n }\n token_envs[platform] = name;\n }\n return { platforms, token_envs };\n}\n\nasync function ask_plugins(work_dir: string, ask: AskLine): Promise<string[] | undefined> {\n const found = discover_plugin_entries(work_dir);\n if (found.length === 0) {\n return [];\n }\n const enabled: string[] = [];\n for (const entry of found) {\n const answer = await ask(`Enable plugin ${entry}? [y/N]: `);\n if (answer === undefined) {\n return undefined;\n }\n const yes = answer.trim().toLowerCase();\n if (yes === \"y\" || yes === \"yes\") {\n enabled.push(entry);\n }\n }\n return enabled;\n}\n\n/** All steps skippable. Undefined means Ctrl+C or a rejected env name: caller must not write. */\nexport async function collect_setup_answers(work_dir: string, ask: AskLine, model_hint?: string): Promise<SetupAnswers | undefined> {\n const agent_name = await ask_name(ask);\n if (agent_name === undefined) {\n return undefined;\n }\n const provider = await ask_provider(ask, model_hint);\n if (provider === undefined) {\n return undefined;\n }\n const gateway = await ask_gateway(ask);\n if (gateway === undefined) {\n return undefined;\n }\n const plugins = await ask_plugins(work_dir, ask);\n if (plugins === undefined) {\n return undefined;\n }\n return { agent_name, ...provider, platforms: gateway.platforms, token_envs: gateway.token_envs, plugins };\n}\n"],"mappings":"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","names":["readFileSync","path","existsSync","path","path","existsSync","path","path","model_hint","value","readFileSync","ask_line","path"]}
@@ -1,8 +1,10 @@
1
1
  import {
2
- create_agent,
2
+ create_agent_with_plugins,
3
3
  logger,
4
+ platform_token_env,
5
+ read_platform_token,
4
6
  sleep
5
- } from "./chunk-MLFJW4JU.js";
7
+ } from "./chunk-CV2YH3FH.js";
6
8
 
7
9
  // src/gateway/types.ts
8
10
  var ERROR_SNIPPET_CHARS = 300;
@@ -141,9 +143,9 @@ var GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json";
141
143
  var INTENTS = 512 | 32768;
142
144
  var heartbeat_timers = /* @__PURE__ */ new WeakMap();
143
145
  function create_discord_adapter(params) {
144
- const token = process.env.LICH_DISCORD_BOT_TOKEN;
145
- if (token === void 0 || token.length === 0) {
146
- return create_idle_adapter("discord", "LICH_DISCORD_BOT_TOKEN not set");
146
+ const token = read_platform_token(params.config, "discord");
147
+ if (token === void 0) {
148
+ return create_idle_adapter("discord", `${platform_token_env(params.config, "discord")} not set`);
147
149
  }
148
150
  let running = false;
149
151
  let socket;
@@ -225,10 +227,10 @@ async function on_message_create(params, data) {
225
227
  message.author_id,
226
228
  message.content
227
229
  );
228
- await rest_send_message(message.channel_id, reply);
230
+ await rest_send_message(params, message.channel_id, reply);
229
231
  }
230
- async function rest_send_message(channel_id, text) {
231
- const token = process.env.LICH_DISCORD_BOT_TOKEN;
232
+ async function rest_send_message(params, channel_id, text) {
233
+ const token = read_platform_token(params.config, "discord");
232
234
  if (token === void 0 || channel_id === "") {
233
235
  return;
234
236
  }
@@ -298,9 +300,9 @@ function split_chunks(text, limit) {
298
300
  var TELEGRAM_MAX_MESSAGE_CHARS = 4096;
299
301
  var TELEGRAM_BACKOFF_MS = [2e3, 4e3, 8e3, 16e3, 3e4];
300
302
  function create_telegram_adapter(params) {
301
- const token = process.env.LICH_TELEGRAM_BOT_TOKEN;
302
- if (token === void 0 || token.length === 0) {
303
- return create_idle_adapter("telegram", "LICH_TELEGRAM_BOT_TOKEN not set");
303
+ const token = read_platform_token(params.config, "telegram");
304
+ if (token === void 0) {
305
+ return create_idle_adapter("telegram", `${platform_token_env(params.config, "telegram")} not set`);
304
306
  }
305
307
  let running = false;
306
308
  return {
@@ -400,9 +402,11 @@ function split_text(text, limit) {
400
402
  var TWITCH_IRC_URL = "wss://irc-ws.chat.twitch.tv:443";
401
403
  var TWITCH_MESSAGE_CAP = 512;
402
404
  function create_twitch_adapter(params) {
403
- const twitch = read_twitch_env();
405
+ const twitch = read_twitch_env(params.config);
404
406
  if (twitch === void 0) {
405
- return create_idle_adapter("twitch", "LICH_TWITCH_OAUTH_TOKEN / NICK not set");
407
+ const token_env = platform_token_env(params.config, "twitch");
408
+ const reason = token_env === "LICH_TWITCH_OAUTH_TOKEN" ? "LICH_TWITCH_OAUTH_TOKEN / NICK not set" : `${token_env} / LICH_TWITCH_NICK not set`;
409
+ return create_idle_adapter("twitch", reason);
406
410
  }
407
411
  let running = false;
408
412
  let socket;
@@ -421,8 +425,8 @@ function create_twitch_adapter(params) {
421
425
  }
422
426
  };
423
427
  }
424
- function read_twitch_env() {
425
- const raw_token = process.env.LICH_TWITCH_OAUTH_TOKEN;
428
+ function read_twitch_env(config) {
429
+ const raw_token = read_platform_token(config, "twitch");
426
430
  const nick = process.env.LICH_TWITCH_NICK;
427
431
  const channels_env = process.env.LICH_TWITCH_CHANNELS;
428
432
  if (raw_token === void 0 || raw_token.length === 0 || nick === void 0 || nick.length === 0) {
@@ -546,7 +550,7 @@ function format_agent_reply(text, usage, platform) {
546
550
  var DEFAULT_GATEWAY_PORT = 8089;
547
551
  function create_webhook_adapter(params) {
548
552
  const port = params.port ?? read_port_env() ?? DEFAULT_GATEWAY_PORT;
549
- const token = process.env.LICH_GATEWAY_TOKEN;
553
+ const token = read_platform_token(params.config, "webhook");
550
554
  let server;
551
555
  return {
552
556
  name: "webhook",
@@ -661,6 +665,11 @@ function read_port_env() {
661
665
  }
662
666
 
663
667
  // src/gateway/runner.ts
668
+ async function create_gateway_bus(config) {
669
+ const agent = await create_agent_with_plugins(config);
670
+ const bus = new GatewayBus({ config, agent_factory: () => agent, wire_tool_logging: true });
671
+ return { agent, bus };
672
+ }
664
673
  async function run_gateway(config, platforms) {
665
674
  const valid = [];
666
675
  for (const platform of platforms) {
@@ -675,7 +684,7 @@ async function run_gateway(config, platforms) {
675
684
  process.stderr.write("lich: gateway needs at least one valid platform (webhook|telegram|discord|twitch)\n");
676
685
  return 1;
677
686
  }
678
- const bus = new GatewayBus({ config, agent_factory: () => create_agent(config), wire_tool_logging: true });
687
+ const { bus } = await create_gateway_bus(config);
679
688
  const adapters = build_adapters(config, bus, valid);
680
689
  install_signal_handlers(bus, adapters);
681
690
  logger.info(`gateway starting: platforms=${valid.join(",")}, port=${process.env.LICH_GATEWAY_PORT ?? "8089"}, pid=${process.pid}`);
@@ -749,4 +758,4 @@ function install_signal_handlers(bus, adapters) {
749
758
  export {
750
759
  run_gateway
751
760
  };
752
- //# sourceMappingURL=gateway-XTYDYT67.js.map
761
+ //# sourceMappingURL=gateway-W6S43ETE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/gateway/types.ts","../src/gateway/bus.ts","../src/gateway/discord.ts","../src/gateway/telegram.ts","../src/gateway/twitch.ts","../src/gateway/webhook.ts","../src/gateway/format.ts","../src/gateway/runner.ts"],"sourcesContent":["/**\n * Shared contracts for the messaging gateway: platform message shapes,\n * reply sinks, adapter lifecycle, and the small runtime helpers every\n * platform adapter shares (idle fallback, raw WebSocket, inbound dispatch).\n */\nimport type { Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\n\nexport type PlatformName = \"webhook\" | \"telegram\" | \"discord\" | \"twitch\";\n\n/** Normalized inbound message from any supported platform. */\nexport interface PlatformMessage {\n platform: PlatformName;\n chat_id: string;\n user_id: string;\n text: string;\n}\n\n/** Outbound-only view used to deliver a reply to a conversation. */\nexport interface ReplySink {\n send(text: string): Promise<void>;\n}\n\n/** Lifecycle contract implemented by every platform adapter. */\nexport interface PlatformAdapter {\n readonly name: string;\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/** Handles one inbound message and resolves to the reply text. */\nexport type InboundHandler = (\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n) => Promise<string | undefined>;\n\n/** Dependencies handed to every adapter factory. */\nexport interface AdapterParams {\n config: AgentConfig;\n handle_message: InboundHandler;\n get_agent: () => Agent;\n reply_router: (platform: string, chat_id: string) => ReplySink | undefined;\n}\n\n/** Minimal structural WebSocket surface shared by bun and node runtimes. */\nexport interface RawSocket {\n onopen: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(): void;\n}\n\nconst ERROR_SNIPPET_CHARS = 300;\n\n/** Flattens a failure into a single-line, bounded, safe reply string. */\nexport function sanitize_agent_error(error: unknown): string {\n const raw = error instanceof Error ? error.message : String(error);\n const flat = raw.replace(/\\s+/g, \" \").trim();\n return `agent error: ${flat.slice(0, ERROR_SNIPPET_CHARS) || \"unknown\"}`;\n}\n\n/** Adapter that logs why it is idle once and otherwise does nothing. */\nexport function create_idle_adapter(name: string, reason: string): PlatformAdapter {\n logger.warn(`gateway ${name} adapter idle: ${reason}`);\n return {\n name,\n start: async () => undefined,\n stop: async () => undefined,\n };\n}\n\n/** Opens a WebSocket and resolves once connected; rejects on open error. */\nexport function open_socket(url: string): Promise<RawSocket> {\n return new Promise((resolve, reject) => {\n const ctor = (globalThis as { WebSocket?: new (url: string) => unknown }).WebSocket;\n if (ctor === undefined) {\n reject(new Error(\"runtime does not expose a WebSocket constructor\"));\n return;\n }\n const socket = new ctor(url) as unknown as RawSocket;\n socket.onopen = () => resolve(socket);\n socket.onerror = () => reject(new Error(`websocket connect failed: ${url}`));\n });\n}\n\n/** Runs one inbound message, converting failures into a safe reply string. */\nexport async function run_inbound_message(\n handle: InboundHandler,\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n): Promise<string> {\n try {\n return (await handle(platform, chat_id, user_id, text)) ?? \"\";\n } catch (error) {\n logger.error(`gateway ${platform} message handling failed`, error);\n return sanitize_agent_error(error);\n }\n}","/**\n * GatewayBus: conversation-keyed runner over one shared Agent.\n *\n * Per-conversation history lives in a bounded Map; concurrent messages for\n * the same conversation are serialized through a promise chain so history\n * never interleaves. Agent failures become sanitized reply strings.\n */\nimport type { Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport type { Message } from \"../providers/types.js\";\nimport { logger } from \"../util/log.js\";\nimport { sanitize_agent_error } from \"./types.js\";\n\nexport interface GatewayBusOptions {\n history_cap?: number;\n max_conversations?: number;\n}\n\nexport interface BusParams {\n config: AgentConfig;\n agent_factory: () => Agent;\n /** Subscribe to agent tool events for debug logging (creates the agent). */\n wire_tool_logging?: boolean;\n}\n\nconst DEFAULT_HISTORY_CAP = 40;\nconst DEFAULT_MAX_CONVERSATIONS = 200;\n\nexport class GatewayBus {\n private readonly config: AgentConfig;\n private readonly agent_factory: () => Agent;\n private agent: Agent | undefined;\n private readonly histories: Map<string, Message[]> = new Map();\n private readonly chains: Map<string, Promise<void>> = new Map();\n private readonly history_cap: number;\n private readonly max_conversations: number;\n private stop_logging: (() => void) | undefined;\n\n constructor(params: BusParams, options?: GatewayBusOptions) {\n this.config = params.config;\n this.agent_factory = params.agent_factory;\n this.history_cap = options?.history_cap ?? DEFAULT_HISTORY_CAP;\n this.max_conversations = options?.max_conversations ?? DEFAULT_MAX_CONVERSATIONS;\n if (params.wire_tool_logging === true) {\n this.wire_tool_logging();\n }\n }\n\n /** Serializes runs per conversation and resolves to the reply text. */\n async handle(platform: string, chat_id: string, user_id: string, text: string): Promise<string | undefined> {\n const key = conversation_key(platform, chat_id);\n const previous = this.chains.get(key) ?? Promise.resolve();\n const run = previous.then(() => this.run_once(key, platform, chat_id, user_id, text));\n this.chains.set(\n key,\n run.then(\n () => undefined,\n () => undefined,\n ),\n );\n return run;\n }\n\n /** Unsubscribes the debug tool logger (bus owns no other resources). */\n stop(): void {\n this.stop_logging?.();\n this.stop_logging = undefined;\n }\n\n private async run_once(\n key: string,\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n ): Promise<string | undefined> {\n const input = text.startsWith(\"/start\") === true ? \"hello\" : text;\n const history = this.history_for(key);\n const agent = this.ensure_agent();\n try {\n const result = await agent.run({ input, history, label: `gw:${platform}:${chat_id}` });\n this.histories.set(key, cap_history(result.messages, this.history_cap));\n return final_reply_text(result.outcome.final?.content);\n } catch (error) {\n logger.error(`gateway bus run failed for ${key} (user ${user_id})`, error);\n return sanitize_agent_error(error);\n }\n }\n\n private ensure_agent(): Agent {\n if (this.agent === undefined) {\n this.agent = this.agent_factory();\n }\n return this.agent;\n }\n\n /** Oldest-first eviction keeps the conversation map bounded. */\n private history_for(key: string): Message[] {\n while (this.histories.size >= this.max_conversations && this.histories.has(key) === false) {\n const oldest = this.histories.keys().next();\n if (oldest.done === true) {\n break;\n }\n this.histories.delete(oldest.value);\n }\n return this.histories.get(key) ?? [];\n }\n\n /** Logs completed tool calls at debug level for gateway observability. */\n private wire_tool_logging(): void {\n const agent = this.agent_factory();\n this.agent = agent;\n this.stop_logging = agent.events.on((event) => {\n if (event.type === \"tool_call_end\") {\n logger.debug(`tool ${event.call.name} ${event.result.ok === true ? \"ok\" : \"failed\"}`);\n }\n });\n }\n}\n\nfunction conversation_key(platform: string, chat_id: string): string {\n return `${platform}:${chat_id}`;\n}\n\nfunction cap_history(messages: Message[], cap: number): Message[] {\n const overflow = messages.length - cap;\n if (overflow <= 0) {\n return messages;\n }\n return messages.slice(overflow);\n}\n\nfunction final_reply_text(content: string | undefined): string | undefined {\n return content === undefined || content.length === 0 ? undefined : content;\n}","/**\n * Discord adapter: gateway WebSocket (op-code switch, heartbeat, fresh\n * reconnect) plus REST replies. Token optional; absent token degrades to\n * an idle adapter. No resume support — reconnects are fresh by design.\n */\nimport { logger } from \"../util/log.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter, RawSocket } from \"./types.js\";\nimport { create_idle_adapter, open_socket, run_inbound_message } from \"./types.js\";\n\nconst DISCORD_API = \"https://discord.com/api/v10\";\nconst GATEWAY_URL = \"wss://gateway.discord.gg/?v=10&encoding=json\";\nconst INTENTS = 512 | 32768;\n/** Live heartbeat timers keyed by socket, cleared when the session ends. */\nconst heartbeat_timers = new WeakMap<RawSocket, ReturnType<typeof setInterval>>();\n\ntype DiscordPayload = {\n op?: number;\n t?: string;\n s?: number | null;\n d?: Record<string, unknown>;\n};\n\ninterface DiscordMessageData {\n content: string;\n channel_id: string;\n author_id: string;\n}\n\nexport function create_discord_adapter(params: AdapterParams): PlatformAdapter {\n const token = read_platform_token(params.config, \"discord\");\n if (token === undefined) {\n return create_idle_adapter(\"discord\", `${platform_token_env(params.config, \"discord\")} not set`);\n }\n let running = false;\n let socket: RawSocket | undefined;\n return {\n name: \"discord\",\n start: async () => {\n running = true;\n void connect_loop(params, token, () => running, (opened) => {\n socket = opened;\n });\n },\n stop: async () => {\n running = false;\n socket?.close();\n socket = undefined;\n },\n };\n}\n\nasync function connect_loop(\n params: AdapterParams,\n token: string,\n keep_running: () => boolean,\n set_socket: (socket: RawSocket) => void,\n): Promise<void> {\n while (keep_running()) {\n try {\n const socket = await open_socket(GATEWAY_URL);\n set_socket(socket);\n await socket_session(socket, token, params);\n } catch (error) {\n logger.warn(\"gateway discord connection failed; reconnecting in 5s\", error);\n await new Promise((resolve) => setTimeout(resolve, 5000));\n }\n }\n}\n\nasync function socket_session(socket: RawSocket, token: string, params: AdapterParams): Promise<void> {\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n const done = new Promise<void>((resolve) => {\n socket.onclose = () => resolve();\n });\n socket.onmessage = (event) => {\n const payload = parse_payload(event.data);\n if (payload === undefined) {\n return;\n }\n handle_discord_payload(socket, token, params, payload);\n };\n await done;\n heartbeat = heartbeat_timers.get(socket);\n if (heartbeat !== undefined) {\n clearInterval(heartbeat);\n heartbeat_timers.delete(socket);\n }\n}\n\nfunction handle_discord_payload(\n socket: RawSocket,\n token: string,\n params: AdapterParams,\n payload: DiscordPayload,\n): void {\n if (payload.op === 10 && is_hello(payload.d)) {\n socket.send(JSON.stringify({ op: 2, d: identify_body(token) }));\n schedule_heartbeat(socket, payload.d.heartbeat_interval);\n return;\n }\n if (payload.t === \"MESSAGE_CREATE\") {\n void on_message_create(params, payload.d);\n }\n}\n\nfunction schedule_heartbeat(\n socket: RawSocket,\n interval_ms: unknown,\n): void {\n const interval = typeof interval_ms === \"number\" ? interval_ms : 45000;\n const timer = setInterval(() => {\n socket.send(JSON.stringify({ op: 1, d: null }));\n }, Math.max(1000, interval - 1000));\n heartbeat_timers.set(socket, timer);\n}\n\nfunction identify_body(token: string): Record<string, unknown> {\n return { token, intents: INTENTS, properties: { os: \"linux\", browser: \"lich\", device: \"lich\" } };\n}\n\nasync function on_message_create(params: AdapterParams, data: Record<string, unknown> | undefined): Promise<void> {\n const message = normalize_message(data);\n if (message === undefined) {\n return;\n }\n const reply = await run_inbound_message(\n params.handle_message,\n \"discord\",\n message.channel_id,\n message.author_id,\n message.content,\n );\n await rest_send_message(params, message.channel_id, reply);\n}\n\nasync function rest_send_message(params: AdapterParams, channel_id: string, text: string): Promise<void> {\n const token = read_platform_token(params.config, \"discord\");\n if (token === undefined || channel_id === \"\") {\n return;\n }\n for (const chunk of split_chunks(text, 2000)) {\n const response = await fetch(`${DISCORD_API}/channels/${channel_id}/messages`, {\n method: \"POST\",\n headers: { authorization: `Bot ${token}`, \"content-type\": \"application/json\" },\n body: JSON.stringify({ content: chunk }),\n signal: AbortSignal.timeout(30_000),\n });\n if (response.ok === false) {\n logger.warn(`gateway discord sendMessage failed with http ${response.status}`);\n }\n }\n}\n\nfunction normalize_message(data: Record<string, unknown> | undefined): DiscordMessageData | undefined {\n if (data === undefined) {\n return undefined;\n }\n const author = data.author;\n if (typeof author !== \"object\" || author === null) {\n return undefined;\n }\n const info = author as { id?: unknown; bot?: unknown };\n if (info.bot === true) {\n return undefined;\n }\n const channel_id = typeof data.channel_id === \"string\" ? data.channel_id : \"\";\n const content = strip_mention(typeof data.content === \"string\" ? data.content : \"\");\n return { channel_id, content, author_id: typeof info.id === \"string\" ? info.id : \"\" };\n}\n\nfunction strip_mention(content: string): string {\n const bot_id = process.env.LICH_DISCORD_BOT_ID;\n if (bot_id === undefined) {\n return content.trim();\n }\n const mention = `<@${bot_id}>`;\n return content.startsWith(mention) === true ? content.slice(mention.length).trim() : content.trim();\n}\n\nfunction is_hello(d: Record<string, unknown> | undefined): d is { heartbeat_interval: number } {\n return d !== undefined && typeof d.heartbeat_interval === \"number\";\n}\n\nfunction parse_payload(data: unknown): DiscordPayload | undefined {\n if (typeof data !== \"string\") {\n return undefined;\n }\n try {\n return JSON.parse(data) as DiscordPayload;\n } catch {\n return undefined;\n }\n}\n\nfunction split_chunks(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n chunks.push(rest.slice(0, limit));\n rest = rest.slice(limit);\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}","/**\n * Telegram adapter: long-poll getUpdates over fetch and sendMessage\n * replies with 4096-char splitting. Missing token degrades to an idle\n * adapter instead of failing the gateway.\n */\nimport { sleep } from \"../util/sleep.js\";\nimport { logger } from \"../util/log.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\nimport { create_idle_adapter, run_inbound_message } from \"./types.js\";\n\nexport const TELEGRAM_MAX_MESSAGE_CHARS = 4096;\n\ninterface TelegramUpdate {\n update_id?: number;\n message?: {\n text?: string;\n chat?: { id?: number | string };\n from?: { id?: number | string; is_bot?: boolean };\n };\n}\n\nexport const TELEGRAM_BACKOFF_MS = [2000, 4000, 8000, 16000, 30000] as const;\n\nexport function create_telegram_adapter(params: AdapterParams): PlatformAdapter {\n const token = read_platform_token(params.config, \"telegram\");\n if (token === undefined) {\n return create_idle_adapter(\"telegram\", `${platform_token_env(params.config, \"telegram\")} not set`);\n }\n let running = false;\n return {\n name: \"telegram\",\n start: async () => {\n running = true;\n void poll_loop(params, token, () => running);\n },\n stop: async () => {\n running = false;\n },\n };\n}\n\nasync function poll_loop(params: AdapterParams, token: string, keep_running: () => boolean): Promise<void> {\n let offset = 0;\n let backoff_index = 0;\n while (keep_running()) {\n try {\n const updates = await fetch_updates(token, offset);\n backoff_index = 0;\n for (const update of updates) {\n offset = update.update_id !== undefined ? update.update_id + 1 : offset;\n void deliver_update(params, token, update);\n }\n } catch (error) {\n logger.warn(\"gateway telegram poll failed; backing off\", error);\n await sleep(TELEGRAM_BACKOFF_MS[backoff_index] ?? 30000);\n backoff_index = Math.min(backoff_index + 1, TELEGRAM_BACKOFF_MS.length - 1);\n }\n }\n}\n\nasync function fetch_updates(token: string, offset: number): Promise<TelegramUpdate[]> {\n const url = api_url(token, \"getUpdates\") + `?timeout=50&offset=${offset}`;\n const response = await fetch(url, { signal: AbortSignal.timeout(60_000) });\n if (response.ok === false) {\n throw new Error(`getUpdates http ${response.status}`);\n }\n const body = (await response.json()) as { result?: TelegramUpdate[] };\n return Array.isArray(body.result) === true ? body.result : [];\n}\n\nasync function deliver_update(params: AdapterParams, token: string, update: TelegramUpdate): Promise<void> {\n const message = update.message;\n if (message === undefined || message.from?.is_bot === true) {\n return;\n }\n const chat_id = message.chat?.id;\n if (chat_id === undefined) {\n return;\n }\n const text = message.text ?? \"media not supported yet\";\n const reply = await run_inbound_message(\n params.handle_message,\n \"telegram\",\n String(chat_id),\n String(message.from?.id ?? \"unknown\"),\n text,\n );\n await send_reply(token, String(chat_id), reply);\n}\n\nasync function send_reply(token: string, chat_id: string, text: string): Promise<void> {\n for (const chunk of split_text(text, TELEGRAM_MAX_MESSAGE_CHARS)) {\n await post_json(api_url(token, \"sendMessage\"), { chat_id, text: chunk });\n }\n}\n\nasync function post_json(url: string, payload: Record<string, unknown>): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(30_000),\n });\n if (response.ok === false) {\n logger.warn(`gateway telegram sendMessage failed with http ${response.status}`);\n }\n}\n\nfunction api_url(token: string, method: string): string {\n return `https://api.telegram.org/bot${token}/${method}`;\n}\n\n/** Splits at the last newline/space inside each window; hard cut fallback. */\nexport function split_text(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n const window = rest.slice(0, limit + 1);\n const newline = window.lastIndexOf(\"\\n\");\n const space = window.lastIndexOf(\" \");\n const cut = newline > 0 ? newline : space > 0 ? space : limit;\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).trimStart();\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}","/**\n * Twitch adapter: IRC-over-WebSocket on irc-ws.chat.twitch.tv. Handles\n * CAP/JOIN setup, tag-prefixed PRIVMSG parsing, PING/PONG, and 512-char\n * message splitting. Absent config degrades to an idle adapter.\n */\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter, RawSocket } from \"./types.js\";\nimport { create_idle_adapter, open_socket, run_inbound_message } from \"./types.js\";\n\nexport const TWITCH_IRC_URL = \"wss://irc-ws.chat.twitch.tv:443\";\nexport const TWITCH_MESSAGE_CAP = 512;\n\ninterface TwitchConfig {\n token: string;\n nick: string;\n channels: string[];\n}\n\ninterface ParsedLine {\n kind: \"ping\" | \"privmsg\" | \"other\";\n channel: string;\n user: string;\n text: string;\n}\n\nexport function create_twitch_adapter(params: AdapterParams): PlatformAdapter {\n const twitch = read_twitch_env(params.config);\n if (twitch === undefined) {\n const token_env = platform_token_env(params.config, \"twitch\");\n const reason = token_env === \"LICH_TWITCH_OAUTH_TOKEN\" ? \"LICH_TWITCH_OAUTH_TOKEN / NICK not set\" : `${token_env} / LICH_TWITCH_NICK not set`;\n return create_idle_adapter(\"twitch\", reason);\n }\n let running = false;\n let socket: RawSocket | undefined;\n return {\n name: \"twitch\",\n start: async () => {\n running = true;\n void irc_loop(params, twitch, () => running, (opened) => {\n socket = opened;\n });\n },\n stop: async () => {\n running = false;\n socket?.close();\n socket = undefined;\n },\n };\n}\n\nfunction read_twitch_env(config: AgentConfig): TwitchConfig | undefined {\n const raw_token = read_platform_token(config, \"twitch\");\n const nick = process.env.LICH_TWITCH_NICK;\n const channels_env = process.env.LICH_TWITCH_CHANNELS;\n if (raw_token === undefined || raw_token.length === 0 || nick === undefined || nick.length === 0) {\n return undefined;\n }\n const token = raw_token.startsWith(\"oauth:\") === true ? raw_token : `oauth:${raw_token}`;\n const channels = (channels_env ?? \"\")\n .split(\",\")\n .map((channel) => channel.trim().toLowerCase())\n .filter((channel) => channel.length > 0);\n if (channels.length === 0) {\n return undefined;\n }\n return { token, nick, channels };\n}\n\nasync function irc_loop(\n params: AdapterParams,\n twitch: TwitchConfig,\n keep_running: () => boolean,\n set_socket: (socket: RawSocket) => void,\n): Promise<void> {\n while (keep_running()) {\n try {\n const socket = await open_socket(TWITCH_IRC_URL);\n set_socket(socket);\n await irc_session(params, twitch, socket, keep_running);\n } catch (error) {\n logger.warn(\"gateway twitch connection failed; reconnecting in 5s\", error);\n await new Promise((resolve) => setTimeout(resolve, 5000));\n }\n }\n}\n\nasync function irc_session(\n params: AdapterParams,\n twitch: TwitchConfig,\n socket: RawSocket,\n keep_running: () => boolean,\n): Promise<void> {\n const closed = new Promise<void>((resolve) => {\n socket.onclose = () => resolve();\n });\n socket.onmessage = (event) => {\n for (const line of String(event.data).split(\"\\r\\n\")) {\n void handle_irc_line(params, twitch, socket, line);\n }\n };\n socket.send(\"CAP REQ :twitch.tv/tags twitch.tv/commands\");\n socket.send(`PASS ${twitch.token}`);\n socket.send(`NICK ${twitch.nick}`);\n for (const channel of twitch.channels) {\n socket.send(`JOIN #${channel}`);\n }\n await closed;\n}\n\nasync function handle_irc_line(\n params: AdapterParams,\n twitch: TwitchConfig,\n socket: RawSocket,\n line: string,\n): Promise<void> {\n if (line.length === 0) {\n return;\n }\n const parsed = parse_irc_line(line);\n if (parsed.kind === \"ping\") {\n socket.send(\"PONG :tmi.twitch.tv\");\n return;\n }\n if (parsed.kind !== \"privmsg\" || parsed.user === twitch.nick) {\n return;\n }\n const reply = await run_inbound_message(\n params.handle_message,\n \"twitch\",\n parsed.channel,\n parsed.user,\n parsed.text,\n );\n send_twitch_message(socket, parsed.channel, reply);\n}\n\nfunction send_twitch_message(socket: RawSocket, channel: string, text: string): void {\n for (const chunk of split_chunks(text, TWITCH_MESSAGE_CAP)) {\n socket.send(`PRIVMSG #${channel} :${chunk}`);\n }\n}\n\nexport function parse_irc_line(line: string): ParsedLine {\n if (line.startsWith(\"PING\") === true) {\n return { kind: \"ping\", channel: \"\", user: \"\", text: \"\" };\n }\n const privmsg = match_privmsg(line);\n if (privmsg === undefined) {\n return { kind: \"other\", channel: \"\", user: \"\", text: \"\" };\n }\n return privmsg;\n}\n\nfunction match_privmsg(line: string): ParsedLine | undefined {\n const without_tags = line.startsWith(\"@\") === true ? line.slice(line.indexOf(\" \") + 1) : line;\n const body = / PRIVMSG #([\\w]+) :/.exec(without_tags);\n if (body === null || body.index < 0) {\n return undefined;\n }\n const prefix = without_tags.slice(0, body.index);\n const ident = prefix.startsWith(\":\") === true ? prefix.slice(1) : prefix;\n const user = ident.slice(ident.lastIndexOf(\"!\") + 1).split(\"@\")[0] ?? \"\";\n const channel = body[1] ?? \"\";\n const text = without_tags.slice(body.index + body[0].length);\n return { kind: \"privmsg\", channel, user, text };\n}\n\nfunction split_chunks(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n chunks.push(rest.slice(0, limit));\n rest = rest.slice(limit);\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}","/**\n * Webhook adapter: the default zero-config platform. A plain node:http\n * server exposing POST /message (+ GET /health), optionally guarded by a\n * shared token via the x-lich-token header.\n */\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { logger } from \"../util/log.js\";\nimport { format_agent_reply } from \"./format.js\";\nimport { read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\n\nexport const DEFAULT_GATEWAY_PORT = 8089;\n\ninterface WebhookAdapterParams extends AdapterParams {\n port?: number;\n /** Test hook: reports the resolved bound port once listening. */\n on_listening?: (port: number) => void;\n}\n\ninterface IncomingPayload {\n platform?: unknown;\n chat_id?: unknown;\n user_id?: unknown;\n text?: unknown;\n}\n\nexport function create_webhook_adapter(params: WebhookAdapterParams): PlatformAdapter {\n const port = params.port ?? read_port_env() ?? DEFAULT_GATEWAY_PORT;\n const token = read_platform_token(params.config, \"webhook\");\n let server: Server | undefined;\n\n return {\n name: \"webhook\",\n start: async () => {\n server = createServer((request, response) => {\n void dispatch_webhook(params, request, response, token);\n });\n await listen_on(server, port, (bound) => {\n params.on_listening?.(bound);\n });\n },\n stop: async () => {\n await close_server(server);\n server = undefined;\n },\n };\n}\n\nasync function dispatch_webhook(\n params: AdapterParams,\n request: IncomingMessage,\n response: ServerResponse,\n token: string | undefined,\n): Promise<void> {\n try {\n if (request.method === \"GET\" && request.url === \"/health\") {\n send_json(response, 200, { status: \"ok\" });\n return;\n }\n if (request.method !== \"POST\" || request.url !== \"/message\") {\n send_json(response, 404, { error: \"not found\" });\n return;\n }\n if (token !== undefined && request.headers[\"x-lich-token\"] !== token) {\n send_json(response, 401, { error: \"unauthorized\" });\n return;\n }\n await handle_message_post(params, request, response);\n } catch (error) {\n logger.error(\"gateway webhook request failed\", error);\n if (response.headersSent === false) {\n send_json(response, 500, { error: \"internal error\" });\n }\n }\n}\n\nasync function handle_message_post(\n params: AdapterParams,\n request: IncomingMessage,\n response: ServerResponse,\n): Promise<void> {\n const body = await read_body(request);\n const payload = parse_payload(body);\n const text = payload.text;\n if (text === undefined) {\n send_json(response, 400, { error: \"text is required\" });\n return;\n }\n const platform = payload.platform ?? \"webhook\";\n const chat_id = payload.chat_id ?? \"default\";\n const user_id = payload.user_id ?? \"anonymous\";\n const reply = await params.handle_message(String(platform), String(chat_id), String(user_id), String(text));\n respond_json_text(response, 200, format_agent_reply(reply ?? \"\", undefined, \"webhook\"));\n}\n\nfunction read_body(request: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n request.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString(\"utf8\");\n });\n request.on(\"end\", () => resolve(body));\n request.on(\"error\", reject);\n });\n}\n\nfunction parse_payload(body: string): IncomingPayload {\n if (body.trim().length === 0) {\n return {};\n }\n try {\n const parsed = JSON.parse(body) as unknown;\n if (typeof parsed !== \"object\" || parsed === null) {\n return {};\n }\n return parsed as IncomingPayload;\n } catch {\n return {};\n }\n}\n\nfunction send_json(response: ServerResponse, status: number, payload: Record<string, unknown>): void {\n respond_json_text(response, status, JSON.stringify(payload));\n}\n\nfunction respond_json_text(response: ServerResponse, status: number, json_text: string): void {\n response.statusCode = status;\n response.setHeader(\"content-type\", \"application/json\");\n response.end(json_text);\n}\n\nfunction listen_on(server: Server, port: number, on_listening: (port: number) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, \"0.0.0.0\", () => {\n const bound = (server.address() as { port?: number } | null)?.port ?? port;\n logger.info(`gateway webhook listening on :${bound}`);\n on_listening(bound);\n resolve();\n });\n });\n}\n\nasync function close_server(server: Server | undefined): Promise<void> {\n if (server === undefined) {\n return;\n }\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n}\n\nfunction read_port_env(): number | undefined {\n const raw = process.env.LICH_GATEWAY_PORT;\n if (raw === undefined || raw.length === 0) {\n return undefined;\n }\n const port = Number(raw);\n return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;\n}","/**\n * Gateway reply formatting: per-platform shaping and whitespace-aware\n * splitting for size-capped transports (telegram 4096, discord 2000, …).\n */\nimport type { Usage } from \"../providers/types.js\";\n\nconst USAGE_FOOTER_PREFIX = \"\\n\\n_tokens: \";\n\nexport function format_agent_reply(text: string, usage: Usage | undefined, platform: string): string {\n if (platform === \"webhook\") {\n return JSON.stringify({ reply: text, usage: usage ?? null });\n }\n const total_tokens = usage?.total_tokens ?? 0;\n if (total_tokens > 0) {\n return `${text}${USAGE_FOOTER_PREFIX}${total_tokens}_`;\n }\n return text;\n}\n\n/** Splits text into chunks of at most `limit` chars, preferring whitespace. */\nexport function split_text(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n const cut = find_split_point(rest, limit);\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).trimStart();\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}\n\n/** Last newline or space inside the window; falls back to a hard cut. */\nfunction find_split_point(text: string, limit: number): number {\n const window = text.slice(0, limit + 1);\n const newline = window.lastIndexOf(\"\\n\");\n if (newline > 0) {\n return newline;\n }\n const space = window.lastIndexOf(\" \");\n if (space > 0) {\n return space;\n }\n return limit;\n}","/**\n * Gateway runner: wires the shared Agent, the conversation bus, and the\n * requested platform adapters, then stays alive until a signal arrives.\n */\nimport { create_agent_with_plugins, type Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\nimport { GatewayBus } from \"./bus.js\";\nimport { create_discord_adapter } from \"./discord.js\";\nimport { create_telegram_adapter } from \"./telegram.js\";\nimport { create_twitch_adapter } from \"./twitch.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\nimport { create_webhook_adapter } from \"./webhook.js\";\n\n/** Preload plugins, then hand that same agent to the bus factory. */\nexport async function create_gateway_bus(config: AgentConfig): Promise<{ agent: Agent; bus: GatewayBus }> {\n const agent = await create_agent_with_plugins(config);\n const bus = new GatewayBus({ config, agent_factory: () => agent, wire_tool_logging: true });\n return { agent, bus };\n}\n\nexport async function run_gateway(config: AgentConfig, platforms: readonly string[]): Promise<number> {\n const valid: string[] = [];\n for (const platform of platforms) {\n if (is_known_platform(platform) === true) {\n valid.push(platform);\n } else {\n process.stderr.write(`lich: unknown gateway platform \"${platform}\" (skipping)\\n`);\n }\n }\n if (valid.length === 0) {\n process.stderr.write(\"lich: gateway needs at least one valid platform (webhook|telegram|discord|twitch)\\n\");\n return 1;\n }\n const { bus } = await create_gateway_bus(config);\n const adapters = build_adapters(config, bus, valid);\n install_signal_handlers(bus, adapters);\n logger.info(`gateway starting: platforms=${valid.join(\",\")}, port=${process.env.LICH_GATEWAY_PORT ?? \"8089\"}, pid=${process.pid}`);\n await start_all_adapters(adapters);\n return await new Promise<number>(() => undefined);\n}\n\nfunction is_known_platform(platform: string): boolean {\n return platform === \"webhook\" || platform === \"telegram\" || platform === \"discord\" || platform === \"twitch\";\n}\n\nfunction build_adapters(config: AgentConfig, bus: GatewayBus, platforms: readonly string[]): PlatformAdapter[] {\n const params: AdapterParams = {\n config,\n handle_message: (platform, chat_id, user_id, text) => bus.handle(platform, chat_id, user_id, text),\n get_agent: () => {\n throw new Error(\"get_agent is reserved for future use\");\n },\n reply_router: () => undefined,\n };\n const adapters: PlatformAdapter[] = [];\n for (const platform of platforms) {\n const adapter = create_platform_adapter(params, platform);\n if (adapter !== undefined) {\n adapters.push(adapter);\n }\n }\n return adapters;\n}\n\nfunction create_platform_adapter(params: AdapterParams, platform: string): PlatformAdapter | undefined {\n switch (platform) {\n case \"webhook\":\n return create_webhook_adapter({ ...params, port: read_webhook_port() });\n case \"telegram\":\n return create_telegram_adapter(params);\n case \"discord\":\n return create_discord_adapter(params);\n case \"twitch\":\n return create_twitch_adapter(params);\n default:\n return undefined;\n }\n}\n\nfunction read_webhook_port(): number | undefined {\n const raw = process.env.LICH_GATEWAY_PORT;\n if (raw === undefined || raw.length === 0) {\n return undefined;\n }\n const port = Number(raw);\n return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;\n}\n\n/** Adapter start failures log and are skipped; the rest keep running. */\nasync function start_all_adapters(adapters: readonly PlatformAdapter[]): Promise<void> {\n for (const adapter of adapters) {\n try {\n await adapter.start();\n logger.info(`gateway adapter started: ${adapter.name}`);\n } catch (error) {\n logger.error(`gateway adapter failed to start: ${adapter.name}`, error);\n }\n }\n}\n\nfunction install_signal_handlers(bus: GatewayBus, adapters: readonly PlatformAdapter[]): void {\n const shutdown = (): void => {\n for (const adapter of adapters) {\n void adapter\n .stop()\n .catch((error: unknown) => logger.warn(`gateway adapter stop failed: ${adapter.name}`, error));\n }\n bus.stop();\n process.exit(0);\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n}"],"mappings":"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","names":["split_chunks","parse_payload"]}
package/dist/index.d.ts CHANGED
@@ -212,6 +212,8 @@ declare class ProviderError extends Error {
212
212
  */
213
213
 
214
214
  declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
215
+ /** Display name used by the TUI banner. */
216
+ agent_name: z.ZodDefault<z.ZodString>;
215
217
  system_prompt: z.ZodOptional<z.ZodString>;
216
218
  max_turns: z.ZodDefault<z.ZodNumber>;
217
219
  providers: z.ZodArray<z.ZodObject<{
@@ -255,9 +257,21 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
255
257
  terminal_timeout_ms: z.ZodDefault<z.ZodNumber>;
256
258
  /** Plugin entry module specifiers, relative to work_dir or absolute. */
257
259
  plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
260
+ gateway: z.ZodOptional<z.ZodObject<{
261
+ platforms: z.ZodDefault<z.ZodArray<z.ZodEnum<["webhook", "telegram", "discord", "twitch"]>, "many">>;
262
+ /** Env-var names that hold tokens. Never store the secrets themselves. */
263
+ token_envs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
264
+ }, "strip", z.ZodTypeAny, {
265
+ platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
266
+ token_envs: Record<string, string>;
267
+ }, {
268
+ platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
269
+ token_envs?: Record<string, string> | undefined;
270
+ }>>;
258
271
  log_level: z.ZodDefault<z.ZodEnum<["debug", "info", "warn", "error"]>>;
259
272
  }, "strip", z.ZodTypeAny, {
260
273
  plugins: string[];
274
+ agent_name: string;
261
275
  max_turns: number;
262
276
  providers: z.objectOutputType<{
263
277
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
@@ -280,6 +294,10 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
280
294
  system_prompt?: string | undefined;
281
295
  work_dir?: string | undefined;
282
296
  session_dir?: string | undefined;
297
+ gateway?: {
298
+ platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
299
+ token_envs: Record<string, string>;
300
+ } | undefined;
283
301
  }, {
284
302
  providers: z.objectInputType<{
285
303
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
@@ -295,6 +313,7 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
295
313
  plugins?: string[] | undefined;
296
314
  temperature?: number | undefined;
297
315
  max_tokens?: number | undefined;
316
+ agent_name?: string | undefined;
298
317
  system_prompt?: string | undefined;
299
318
  max_turns?: number | undefined;
300
319
  work_dir?: string | undefined;
@@ -303,12 +322,17 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
303
322
  compress_threshold?: number | undefined;
304
323
  session_dir?: string | undefined;
305
324
  terminal_timeout_ms?: number | undefined;
325
+ gateway?: {
326
+ platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
327
+ token_envs?: Record<string, string> | undefined;
328
+ } | undefined;
306
329
  log_level?: "debug" | "info" | "warn" | "error" | undefined;
307
330
  }>, {
308
331
  work_dir: string;
309
332
  providers: ProviderConfig[];
310
333
  session_dir: string;
311
334
  plugins: string[];
335
+ agent_name: string;
312
336
  max_turns: number;
313
337
  tools_enabled: string[] | "all";
314
338
  context_budget_tokens: number;
@@ -318,6 +342,10 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
318
342
  temperature?: number | undefined;
319
343
  max_tokens?: number | undefined;
320
344
  system_prompt?: string | undefined;
345
+ gateway?: {
346
+ platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
347
+ token_envs: Record<string, string>;
348
+ } | undefined;
321
349
  }, {
322
350
  providers: z.objectInputType<{
323
351
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
@@ -333,6 +361,7 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
333
361
  plugins?: string[] | undefined;
334
362
  temperature?: number | undefined;
335
363
  max_tokens?: number | undefined;
364
+ agent_name?: string | undefined;
336
365
  system_prompt?: string | undefined;
337
366
  max_turns?: number | undefined;
338
367
  work_dir?: string | undefined;
@@ -341,6 +370,10 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
341
370
  compress_threshold?: number | undefined;
342
371
  session_dir?: string | undefined;
343
372
  terminal_timeout_ms?: number | undefined;
373
+ gateway?: {
374
+ platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
375
+ token_envs?: Record<string, string> | undefined;
376
+ } | undefined;
344
377
  log_level?: "debug" | "info" | "warn" | "error" | undefined;
345
378
  }>;
346
379
  type AgentConfig = z.infer<typeof agent_config_schema>;
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  plugin_errors_summary,
16
16
  register_builtin_tools,
17
17
  run_agent
18
- } from "./chunk-MLFJW4JU.js";
18
+ } from "./chunk-CV2YH3FH.js";
19
19
  export {
20
20
  Agent,
21
21
  AgentEmitter,
@@ -2,9 +2,9 @@ import {
2
2
  LICH_VERSION
3
3
  } from "./chunk-ZVK3MUPC.js";
4
4
  import {
5
- create_agent,
5
+ create_agent_with_plugins,
6
6
  truncate_text
7
- } from "./chunk-MLFJW4JU.js";
7
+ } from "./chunk-CV2YH3FH.js";
8
8
 
9
9
  // src/tui.tsx
10
10
  import { render } from "ink";
@@ -90,6 +90,9 @@ function parse_command(raw_input) {
90
90
  }
91
91
  return { kind: "slash", name: body.slice(0, space_index), args: body.slice(space_index + 1).trim() };
92
92
  }
93
+ function tui_banner_text(agent_name, version, model, kind) {
94
+ return `${agent_name} v${version} \u2014 ${model} (${kind})`;
95
+ }
93
96
  function format_usage(total_tokens) {
94
97
  return total_tokens.toLocaleString("en-US");
95
98
  }
@@ -412,7 +415,7 @@ function TuiApp({ agent }) {
412
415
  );
413
416
  const provider = agent.config.providers[0];
414
417
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", minHeight: 8, children: [
415
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: `lich v${LICH_VERSION} \u2014 ${provider?.model ?? "unknown"} (${provider?.kind ?? "unknown"})` }),
418
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: tui_banner_text(agent.config.agent_name, LICH_VERSION, provider?.model ?? "unknown", provider?.kind ?? "unknown") }),
416
419
  /* @__PURE__ */ jsx4(MessageView, { blocks, state }),
417
420
  /* @__PURE__ */ jsx4(StatusBar, { state, model: provider?.model ?? "unknown" }),
418
421
  /* @__PURE__ */ jsx4(CommandBar, { busy: state.phase !== "idle", on_submit: submit })
@@ -422,7 +425,7 @@ function TuiApp({ agent }) {
422
425
  // src/tui.tsx
423
426
  import { jsx as jsx5 } from "react/jsx-runtime";
424
427
  async function run_tui(config) {
425
- const agent = create_agent(config);
428
+ const agent = await create_agent_with_plugins(config);
426
429
  const instance = render(/* @__PURE__ */ jsx5(TuiApp, { agent }));
427
430
  await instance.waitUntilExit();
428
431
  return 0;
@@ -430,4 +433,4 @@ async function run_tui(config) {
430
433
  export {
431
434
  run_tui
432
435
  };
433
- //# sourceMappingURL=tui-VYBJSGRV.js.map
436
+ //# sourceMappingURL=tui-DT7XWDTX.js.map