noumen 0.1.0 → 0.2.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/README.md +767 -51
- package/dist/a2a/index.d.ts +148 -0
- package/dist/a2a/index.js +579 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/acp/index.d.ts +129 -0
- package/dist/acp/index.js +498 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/agent-BrkbZyOT.d.ts +1028 -0
- package/dist/cache-DVqaCX8v.d.ts +38 -0
- package/dist/chunk-2ZTGQLYK.js +356 -0
- package/dist/chunk-2ZTGQLYK.js.map +1 -0
- package/dist/chunk-42PHHZUA.js +132 -0
- package/dist/chunk-42PHHZUA.js.map +1 -0
- package/dist/chunk-4SQA2UCV.js +26 -0
- package/dist/chunk-4SQA2UCV.js.map +1 -0
- package/dist/chunk-5GEX6ZSB.js +179 -0
- package/dist/chunk-5GEX6ZSB.js.map +1 -0
- package/dist/chunk-7ZMN7XJE.js +94 -0
- package/dist/chunk-7ZMN7XJE.js.map +1 -0
- package/dist/chunk-AMYIJSAZ.js +57 -0
- package/dist/chunk-AMYIJSAZ.js.map +1 -0
- package/dist/chunk-BGG2E6JD.js +10 -0
- package/dist/chunk-BGG2E6JD.js.map +1 -0
- package/dist/chunk-BZSFUEWM.js +43 -0
- package/dist/chunk-BZSFUEWM.js.map +1 -0
- package/dist/chunk-CPFHEPW4.js +139 -0
- package/dist/chunk-CPFHEPW4.js.map +1 -0
- package/dist/chunk-D43BWEZA.js +346 -0
- package/dist/chunk-D43BWEZA.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-JACGEMTF.js +43 -0
- package/dist/chunk-JACGEMTF.js.map +1 -0
- package/dist/chunk-JX7CLUCV.js +21 -0
- package/dist/chunk-JX7CLUCV.js.map +1 -0
- package/dist/chunk-KXDB56YW.js +39 -0
- package/dist/chunk-KXDB56YW.js.map +1 -0
- package/dist/chunk-KY6ZPWHO.js +112 -0
- package/dist/chunk-KY6ZPWHO.js.map +1 -0
- package/dist/chunk-NBDFQYUZ.js +7992 -0
- package/dist/chunk-NBDFQYUZ.js.map +1 -0
- package/dist/chunk-OGXNFXFA.js +196 -0
- package/dist/chunk-OGXNFXFA.js.map +1 -0
- package/dist/chunk-QTJ7VTJY.js +1994 -0
- package/dist/chunk-QTJ7VTJY.js.map +1 -0
- package/dist/chunk-UVSSQBDY.js +192 -0
- package/dist/chunk-UVSSQBDY.js.map +1 -0
- package/dist/chunk-Y45R3PQL.js +684 -0
- package/dist/chunk-Y45R3PQL.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +868 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +64 -0
- package/dist/client/index.js +409 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CRRO2376.js +10 -0
- package/dist/client-CRRO2376.js.map +1 -0
- package/dist/headless-Q7XHHZIW.js +143 -0
- package/dist/headless-Q7XHHZIW.js.map +1 -0
- package/dist/history-snip-64GYP4ZL.js +12 -0
- package/dist/history-snip-64GYP4ZL.js.map +1 -0
- package/dist/index.d.ts +1305 -418
- package/dist/index.js +384 -1757
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc/index.d.ts +54 -0
- package/dist/jsonrpc/index.js +34 -0
- package/dist/jsonrpc/index.js.map +1 -0
- package/dist/lsp/index.d.ts +36 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp-PS3BWIHC.js +8 -0
- package/dist/lsp-PS3BWIHC.js.map +1 -0
- package/dist/manager-DLXK63XC.js +8 -0
- package/dist/manager-DLXK63XC.js.map +1 -0
- package/dist/mcp/index.d.ts +111 -0
- package/dist/mcp/index.js +104 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp-auth-AEI2R4ZC.js +9 -0
- package/dist/mcp-auth-AEI2R4ZC.js.map +1 -0
- package/dist/ollama-YNXAYP3R.js +18 -0
- package/dist/ollama-YNXAYP3R.js.map +1 -0
- package/dist/provider-factory-34MSWJZ3.js +20 -0
- package/dist/provider-factory-34MSWJZ3.js.map +1 -0
- package/dist/providers/anthropic.d.ts +19 -0
- package/dist/providers/anthropic.js +33 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +39 -0
- package/dist/providers/bedrock.js +54 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/gemini.d.ts +16 -0
- package/dist/providers/gemini.js +224 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +18 -0
- package/dist/providers/openai.js +8 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +16 -0
- package/dist/providers/openrouter.js +23 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/vertex.d.ts +40 -0
- package/dist/providers/vertex.js +64 -0
- package/dist/providers/vertex.js.map +1 -0
- package/dist/render-GRN4ZSSW.js +14 -0
- package/dist/render-GRN4ZSSW.js.map +1 -0
- package/dist/resolve-XM52G7YE.js +14 -0
- package/dist/resolve-XM52G7YE.js.map +1 -0
- package/dist/server/index.d.ts +128 -0
- package/dist/server/index.js +626 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server-Cg1yWGaV.d.ts +96 -0
- package/dist/spinner-OJNR6NFO.js +8 -0
- package/dist/spinner-OJNR6NFO.js.map +1 -0
- package/dist/types-2kTLUCnD.d.ts +107 -0
- package/dist/types-3c88cRKH.d.ts +547 -0
- package/dist/types-CwKKucOF.d.ts +620 -0
- package/dist/types-DwdzmXfs.d.ts +107 -0
- package/dist/types-NIyVwQ4h.d.ts +109 -0
- package/dist/types-QwfylltH.d.ts +71 -0
- package/package.json +134 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/config.ts","../../src/cli/repl.ts","../../src/cli/init.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { loadCliConfig, mergeConfig, type MergedConfig } from \"./config.js\";\nimport { createProvider, detectProvider, type ProviderName } from \"./provider-factory.js\";\nimport { startRepl } from \"./repl.js\";\nimport { renderEvent, createRenderState, promptPermission } from \"./render.js\";\nimport { runInit } from \"./init.js\";\nimport * as os from \"node:os\";\nimport { Agent, LocalSandbox, UnsandboxedLocal, type Sandbox, type DiagnoseResult, type DiagnoseCheckResult } from \"../index.js\";\nimport type { ThinkingConfig } from \"../thinking/types.js\";\nimport type { PermissionMode } from \"../permissions/types.js\";\n\nconst VERSION = \"0.1.0\";\n\nasync function listLocalOllamaModels(baseURL: string): Promise<string[]> {\n try {\n const res = await fetch(`${baseURL}/api/tags`, {\n signal: AbortSignal.timeout(2000),\n });\n if (!res.ok) return [];\n const data = (await res.json()) as { models?: Array<{ name: string }> };\n return (data.models ?? []).map((m) => m.name);\n } catch {\n return [];\n }\n}\n\nfunction parseThinking(level: string | undefined): ThinkingConfig | undefined {\n if (!level || level === \"off\") return { type: \"disabled\" };\n const budgets: Record<string, number> = {\n low: 1024,\n medium: 10240,\n high: 32768,\n };\n const budget = budgets[level];\n if (budget) return { type: \"enabled\", budgetTokens: budget };\n return undefined;\n}\n\n/**\n * Create the CLI sandbox. Defaults to OS-level sandboxed `LocalSandbox`.\n * Use `--no-sandbox` to explicitly opt out.\n */\nfunction createCliSandbox(config: MergedConfig): Sandbox {\n if (config.noSandbox) {\n return UnsandboxedLocal({ cwd: config.cwd });\n }\n\n const sandboxOpts: import(\"../index.js\").LocalSandboxOptions = {\n cwd: config.cwd,\n };\n\n const allowWrite = config.sandboxAllowWrite\n ? (config.sandboxAllowWrite as string).split(\",\").map((s: string) => s.trim())\n : undefined;\n const allowDomains = config.sandboxAllowDomain\n ? (config.sandboxAllowDomain as string).split(\",\").map((s: string) => s.trim())\n : undefined;\n\n if (allowWrite || allowDomains) {\n sandboxOpts.sandbox = {\n filesystem: allowWrite ? { allowWrite: [config.cwd, ...allowWrite] } : undefined,\n network: allowDomains ? { allowedDomains: allowDomains } : undefined,\n };\n }\n\n return LocalSandbox(sandboxOpts);\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf-8\").trim();\n}\n\nasync function main(): Promise<void> {\n const program = new Command(\"noumen\")\n .version(VERSION)\n .description(\"AI coding agent — bring your own provider\")\n .option(\"-p, --provider <name>\", \"openai | anthropic | gemini | openrouter | bedrock | vertex | ollama\")\n .option(\"-m, --model <model>\", \"model name\")\n .option(\"--api-key <key>\", \"API key (overrides env vars)\")\n .option(\"--base-url <url>\", \"override provider base URL\")\n .option(\"--cwd <dir>\", \"working directory\")\n .option(\"--permission <mode>\", \"permission mode (default, plan, acceptEdits, auto, bypassPermissions, dontAsk)\")\n .option(\"--thinking <level>\", \"thinking level: off, low, medium, high\")\n .option(\"--max-turns <n>\", \"max agent turns\", parseInt)\n .option(\"--json\", \"emit JSONL stream events to stdout\")\n .option(\"--quiet\", \"only output final text\")\n .option(\"--verbose\", \"show tool calls and thinking\")\n .option(\"--headless\", \"NDJSON stdin/stdout protocol for programmatic control\")\n .option(\"--no-sandbox\", \"disable OS-level sandboxing (use UnsandboxedLocal)\")\n .option(\"--sandbox-allow-write <paths>\", \"comma-separated paths to allow writing in sandbox\")\n .option(\"--sandbox-allow-domain <domains>\", \"comma-separated domains to allow in sandbox\")\n .option(\"-c, --prompt <text>\", \"one-shot prompt (non-interactive)\")\n .argument(\"[prompt...]\", \"inline prompt\")\n .allowExcessArguments(true)\n .action(async (args: string[]) => {\n const opts = program.opts();\n\n if (args.length > 0 && !opts.prompt) {\n opts.prompt = args.join(\" \");\n }\n\n if (!process.stdin.isTTY && !opts.prompt) {\n opts.prompt = await readStdin();\n if (!opts.prompt) {\n process.stderr.write(chalk.red(\"No input provided.\\n\"));\n process.exit(1);\n }\n }\n\n const cwd = opts.cwd ?? process.cwd();\n const config = loadCliConfig(cwd);\n const merged = mergeConfig(config, opts);\n\n await runAgent(merged);\n });\n\n program\n .command(\"init\")\n .description(\"create .noumen/config.json in the current directory\")\n .action(async () => {\n await runInit();\n process.exit(0);\n });\n\n program\n .command(\"sessions\")\n .description(\"list past sessions\")\n .action(async () => {\n await listSessions();\n process.exit(0);\n });\n\n program\n .command(\"resume <session-id>\")\n .description(\"resume a previous session\")\n .action(async (sessionId: string) => {\n await resumeSession(sessionId);\n });\n\n program\n .command(\"doctor\")\n .description(\"run health checks on provider, sandbox, MCP, and LSP\")\n .action(async () => {\n await runDoctor();\n });\n\n await program.parseAsync(process.argv);\n}\n\nasync function runAgent(config: MergedConfig): Promise<void> {\n const providerName: ProviderName | undefined =\n (config.provider as ProviderName | undefined) ?? await detectProvider();\n\n if (!providerName) {\n if (!process.stdin.isTTY) {\n process.stderr.write(chalk.red(\"No provider specified.\\n\"));\n process.exit(1);\n }\n process.stderr.write(\n chalk.bold(\"Welcome to noumen!\\n\\n\") +\n chalk.dim(\"No provider detected. Let's set one up.\\n\\n\"),\n );\n\n const { createInterface } = await import(\"node:readline/promises\");\n const rl = createInterface({ input: process.stdin, output: process.stderr, terminal: true });\n\n try {\n const { SUPPORTED_PROVIDERS, isOllamaRunning, ollamaBaseURL } = await import(\"./provider-factory.js\");\n const providerAnswer = await rl.question(\n ` Provider (${SUPPORTED_PROVIDERS.join(\", \")}) [${chalk.bold(\"ollama\")}]: `,\n );\n const picked = providerAnswer.trim() || \"ollama\";\n if (!(SUPPORTED_PROVIDERS as readonly string[]).includes(picked)) {\n process.stderr.write(chalk.red(`Unknown provider: ${picked}\\n`));\n process.exit(1);\n }\n\n if (picked === \"ollama\") {\n if (!(await isOllamaRunning())) {\n process.stderr.write(\n chalk.yellow(`\\n Ollama doesn't appear to be running at ${ollamaBaseURL()}.\\n`) +\n chalk.yellow(` Install it from https://ollama.com, then run:\\n\\n`) +\n ` ${chalk.cyan(\"ollama pull qwen2.5-coder:32b\")}\\n` +\n ` ${chalk.cyan(\"ollama serve\")}\\n\\n` +\n chalk.yellow(` Then re-run noumen.\\n`),\n );\n rl.close();\n process.exit(1);\n }\n\n const models = await listLocalOllamaModels(ollamaBaseURL());\n if (models.length > 0) {\n process.stderr.write(chalk.dim(` Available models: ${models.join(\", \")}\\n`));\n const defaultModel = models.includes(\"qwen2.5-coder:32b\") ? \"qwen2.5-coder:32b\" : models[0];\n const modelAnswer = await rl.question(\n ` Model [${chalk.bold(defaultModel)}]: `,\n );\n config.model = modelAnswer.trim() || defaultModel;\n }\n }\n\n const needsKey = ![\"bedrock\", \"vertex\", \"ollama\"].includes(picked);\n let apiKey: string | undefined;\n if (needsKey) {\n const keyAnswer = await rl.question(` API key: `);\n apiKey = keyAnswer.trim();\n if (!apiKey) {\n process.stderr.write(chalk.red(\"API key is required.\\n\"));\n process.exit(1);\n }\n }\n\n rl.close();\n config.provider = picked;\n if (apiKey) config.apiKey = apiKey;\n } catch {\n rl.close();\n process.exit(1);\n }\n\n return runAgent(config);\n }\n\n if (!config.model) {\n const { DEFAULT_MODELS } = await import(\"./provider-factory.js\");\n if (providerName === \"ollama\") {\n const { ollamaBaseURL } = await import(\"./provider-factory.js\");\n const models = await listLocalOllamaModels(ollamaBaseURL());\n const preferred = DEFAULT_MODELS[providerName];\n config.model = models.includes(preferred) ? preferred : models[0] ?? preferred;\n } else {\n config.model = DEFAULT_MODELS[providerName];\n }\n }\n\n const provider = await createProvider(providerName, {\n apiKey: config.apiKey,\n model: config.model,\n baseURL: config.baseURL,\n });\n\n const thinking = parseThinking(config.thinking);\n const permissionMode = (config.permissions ?? \"default\") as PermissionMode;\n\n const agent = new Agent({\n provider: provider,\n sandbox: createCliSandbox(config),\n options: {\n cwd: config.cwd,\n model: config.model,\n systemPrompt: config.systemPrompt,\n permissions: {\n mode: permissionMode,\n },\n thinking,\n autoCompact: config.autoCompact ?? true,\n enableSubagents: config.enableSubagents ?? true,\n enableTasks: config.enableTasks ?? false,\n enablePlanMode: config.enablePlanMode ?? true,\n enableWorktrees: config.enableWorktrees ?? false,\n mcpServers: config.mcpServers,\n lsp: config.lsp,\n hooks: config.hooks,\n webSearch: config.webSearch,\n sessionDir: config.sessionDir ?? \".noumen/sessions\",\n projectContext: { cwd: config.cwd, homeDir: os.homedir() },\n costTracking: { enabled: true },\n retry: true,\n },\n });\n\n if (config.headless) {\n const { runHeadless } = await import(\"./headless.js\");\n await runHeadless(agent, config);\n return;\n }\n\n await agent.init();\n\n if (config.prompt) {\n await runOneShot(agent, config);\n } else {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n chalk.red(\"Interactive mode requires a TTY. Use -c or pipe input.\\n\"),\n );\n process.exit(1);\n }\n await startRepl(agent, config);\n }\n\n await agent.close();\n}\n\nasync function runOneShot(agent: Agent, config: MergedConfig): Promise<void> {\n const { startSpinner } = await import(\"./spinner.js\");\n const { isVisibleEvent } = await import(\"./render.js\");\n const thread = agent.createThread();\n const state = createRenderState();\n const runOpts = config.maxTurns ? { maxTurns: config.maxTurns } : undefined;\n\n const spinner =\n !config.json && !config.quiet ? startSpinner(\"Thinking\") : null;\n\n try {\n for await (const event of thread.run(config.prompt!, runOpts)) {\n if (!state.showedActivity && spinner && isVisibleEvent(event, config)) {\n spinner.stop();\n state.showedActivity = true;\n }\n renderEvent(event, config, state);\n }\n } finally {\n spinner?.stop();\n }\n\n if (config.quiet && state.accumulatedText) {\n process.stdout.write(state.accumulatedText);\n if (!state.accumulatedText.endsWith(\"\\n\")) {\n process.stdout.write(\"\\n\");\n }\n } else if (state.accumulatedText && !state.accumulatedText.endsWith(\"\\n\")) {\n process.stdout.write(\"\\n\");\n }\n}\n\nasync function listSessions(): Promise<void> {\n const cwd = process.cwd();\n const config = loadCliConfig(cwd);\n const merged = mergeConfig(config, { cwd });\n\n const providerName: ProviderName | undefined = (merged.provider as ProviderName | undefined) ?? await detectProvider();\n if (!providerName) {\n process.stderr.write(chalk.red(\"No provider configured. Run `noumen init` first.\\n\"));\n process.exit(1);\n }\n\n const provider = await createProvider(providerName, {\n apiKey: merged.apiKey,\n model: merged.model,\n baseURL: merged.baseURL,\n });\n\n const agent = new Agent({\n provider: provider,\n sandbox: UnsandboxedLocal({ cwd }),\n options: { cwd, sessionDir: merged.sessionDir ?? \".noumen/sessions\" },\n });\n\n const sessions = await agent.listSessions();\n if (sessions.length === 0) {\n process.stdout.write(chalk.dim(\"No saved sessions.\\n\"));\n return;\n }\n\n process.stdout.write(chalk.bold(\"Sessions:\\n\"));\n for (const s of sessions) {\n const title = s.title ? chalk.white(` ${s.title}`) : \"\";\n process.stdout.write(\n ` ${chalk.cyan(s.sessionId.slice(0, 8))} ${chalk.dim(s.createdAt)} ${chalk.dim(`${s.messageCount} msgs`)}${title}\\n`,\n );\n }\n}\n\nasync function resumeSession(sessionId: string): Promise<void> {\n const cwd = process.cwd();\n const config = loadCliConfig(cwd);\n const merged = mergeConfig(config, { cwd });\n\n const providerName: ProviderName | undefined = (merged.provider as ProviderName | undefined) ?? await detectProvider();\n if (!providerName) {\n process.stderr.write(chalk.red(\"No provider configured. Run `noumen init` first.\\n\"));\n process.exit(1);\n }\n\n const provider = await createProvider(providerName, {\n apiKey: merged.apiKey,\n model: merged.model,\n baseURL: merged.baseURL,\n });\n\n const thinking = parseThinking(merged.thinking);\n const permissionMode = (merged.permissions ?? \"default\") as PermissionMode;\n\n const agent = new Agent({\n provider: provider,\n sandbox: createCliSandbox(merged),\n options: {\n cwd,\n model: merged.model,\n permissions: { mode: permissionMode },\n thinking,\n autoCompact: merged.autoCompact ?? true,\n enableSubagents: merged.enableSubagents ?? true,\n enablePlanMode: merged.enablePlanMode ?? true,\n mcpServers: merged.mcpServers,\n lsp: merged.lsp,\n hooks: merged.hooks,\n sessionDir: merged.sessionDir ?? \".noumen/sessions\",\n projectContext: { cwd, homeDir: os.homedir() },\n costTracking: { enabled: true },\n retry: true,\n },\n });\n\n await agent.init();\n\n // Match full session ID from partial prefix\n const sessions = await agent.listSessions();\n const match = sessions.find(\n (s) => s.sessionId === sessionId || s.sessionId.startsWith(sessionId),\n );\n\n if (!match) {\n process.stderr.write(chalk.red(`Session not found: ${sessionId}\\n`));\n process.exit(1);\n }\n\n process.stderr.write(\n chalk.dim(`Resuming session ${match.sessionId.slice(0, 8)}...\\n\\n`),\n );\n\n const { createInterface } = await import(\"node:readline/promises\");\n const rl = createInterface({ input: process.stdin, output: process.stderr, terminal: true });\n\n const thread = agent.resumeThread(match.sessionId);\n\n // Enter REPL with the resumed thread\n const { renderEvent: render, createRenderState: makeState } = await import(\"./render.js\");\n\n process.stderr.write(chalk.dim(\"Session resumed. Type a message to continue.\\n\\n\"));\n\n try {\n while (true) {\n let input: string;\n try {\n input = await rl.question(chalk.blue(\"> \"));\n } catch {\n break;\n }\n if (!input.trim()) continue;\n if (input.trim() === \"/quit\" || input.trim() === \"/exit\") break;\n\n const state = makeState();\n for await (const event of thread.run(input)) {\n render(event, merged, state);\n }\n if (state.accumulatedText && !state.accumulatedText.endsWith(\"\\n\")) {\n process.stdout.write(\"\\n\");\n }\n }\n } finally {\n rl.close();\n await agent.close();\n }\n}\n\nasync function runDoctor(): Promise<void> {\n const cwd = process.cwd();\n const config = loadCliConfig(cwd);\n const merged = mergeConfig(config, { cwd });\n\n const providerName: ProviderName | undefined = (merged.provider as ProviderName | undefined) ?? await detectProvider();\n if (!providerName) {\n process.stderr.write(chalk.red(\"No provider configured. Run `noumen init` first.\\n\"));\n process.exit(1);\n }\n\n const provider = await createProvider(providerName, {\n apiKey: merged.apiKey,\n model: merged.model,\n baseURL: merged.baseURL,\n });\n\n const agent = new Agent({\n provider: provider,\n sandbox: createCliSandbox(merged),\n options: {\n cwd,\n model: merged.model,\n mcpServers: merged.mcpServers,\n lsp: merged.lsp,\n sessionDir: merged.sessionDir ?? \".noumen/sessions\",\n },\n });\n\n await agent.init();\n\n process.stderr.write(chalk.bold(\"\\nnoumen doctor\\n\\n\"));\n const result = await agent.diagnose();\n printDiagnoseResult(result);\n await agent.close();\n process.exit(result.overall ? 0 : 1);\n}\n\nfunction formatCheckLine(label: string, check: DiagnoseCheckResult, extra?: string): string {\n const icon = check.ok ? chalk.green(\"✓\") : chalk.red(\"✗\");\n const timing = check.latencyMs > 0 ? chalk.dim(` (${check.latencyMs}ms)`) : \"\";\n const suffix = extra ? chalk.dim(` ${extra}`) : \"\";\n const errMsg = check.error ? chalk.red(` ${check.error}`) : \"\";\n const warnMsg = !check.error && check.warning ? chalk.yellow(` ${check.warning}`) : \"\";\n return ` ${icon} ${label}${timing}${suffix}${errMsg}${warnMsg}\\n`;\n}\n\nfunction printDiagnoseResult(r: DiagnoseResult): void {\n const modelLabel = r.provider.model ? ` (${r.provider.model})` : \"\";\n process.stderr.write(formatCheckLine(`Provider${modelLabel}`, r.provider));\n process.stderr.write(formatCheckLine(\"Sandbox: filesystem\", r.sandbox.fs));\n process.stderr.write(formatCheckLine(\"Sandbox: shell\", r.sandbox.computer));\n process.stderr.write(formatCheckLine(\n \"Sandbox: OS-level (sandbox-runtime)\",\n r.sandboxRuntime,\n r.sandboxRuntime.platform,\n ));\n\n for (const [name, check] of Object.entries(r.mcp)) {\n const parts: string[] = [];\n if (check.status) parts.push(check.status);\n if (check.toolCount != null) parts.push(`${check.toolCount} tools`);\n const extra = parts.length ? parts.join(\", \") : undefined;\n process.stderr.write(formatCheckLine(`MCP: ${name}`, check, extra));\n }\n\n for (const [name, check] of Object.entries(r.lsp)) {\n const extra = check.state ?? undefined;\n process.stderr.write(formatCheckLine(`LSP: ${name}`, check, extra));\n }\n\n process.stderr.write(\"\\n\");\n if (r.overall) {\n process.stderr.write(` ${chalk.green(\"Overall: healthy\")}\\n\\n`);\n } else {\n process.stderr.write(` ${chalk.red(\"Overall: unhealthy\")}\\n\\n`);\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(chalk.red(`Fatal: ${err.message}\\n`));\n process.exit(1);\n});\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport type { McpServerConfig } from \"../mcp/types.js\";\nimport type { LspServerConfig } from \"../lsp/types.js\";\nimport type { HookDefinition } from \"../hooks/types.js\";\nimport type { WebSearchConfig } from \"../tools/web-search.js\";\n\nexport interface CliConfig {\n provider?: string;\n model?: string;\n apiKey?: string;\n baseURL?: string;\n permissions?: string;\n thinking?: string;\n sandbox?: string;\n mcpServers?: Record<string, McpServerConfig>;\n lsp?: Record<string, LspServerConfig>;\n hooks?: HookDefinition[];\n autoCompact?: boolean;\n enableSubagents?: boolean;\n enableTasks?: boolean;\n enablePlanMode?: boolean;\n enableWorktrees?: boolean;\n webSearch?: WebSearchConfig;\n costLimit?: number;\n maxTurns?: number;\n systemPrompt?: string;\n sessionDir?: string;\n}\n\n/**\n * Load global config from ~/.noumen/config.json.\n * Returns empty object if not found or invalid.\n */\nexport function loadGlobalConfig(): CliConfig {\n const globalPath = path.join(os.homedir(), \".noumen\", \"config.json\");\n try {\n const raw = fs.readFileSync(globalPath, \"utf-8\");\n return JSON.parse(raw) as CliConfig;\n } catch {\n return {};\n }\n}\n\n/**\n * Walk up from `cwd` looking for `.noumen/config.json`.\n * Returns parsed config or empty object if none found.\n */\nfunction loadProjectConfig(cwd: string): CliConfig {\n let dir = path.resolve(cwd);\n const root = path.parse(dir).root;\n\n while (true) {\n const candidate = path.join(dir, \".noumen\", \"config.json\");\n try {\n const raw = fs.readFileSync(candidate, \"utf-8\");\n return JSON.parse(raw) as CliConfig;\n } catch {\n // not found or invalid — keep walking\n }\n const parent = path.dirname(dir);\n if (parent === dir || dir === root) break;\n dir = parent;\n }\n\n return {};\n}\n\n/**\n * Load config with layering: global (~/.noumen/config.json) < project < flags.\n * Project-level values override global values.\n */\nexport function loadCliConfig(cwd: string): CliConfig {\n const global = loadGlobalConfig();\n const project = loadProjectConfig(cwd);\n return { ...global, ...project };\n}\n\nexport interface MergedConfig extends CliConfig {\n cwd: string;\n json?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n headless?: boolean;\n prompt?: string;\n noSandbox?: boolean;\n sandboxAllowWrite?: string;\n sandboxAllowDomain?: string;\n}\n\n/**\n * Merge CLI flags over the config file values. Flags take precedence.\n */\nexport function mergeConfig(\n config: CliConfig,\n flags: Record<string, unknown>,\n): MergedConfig {\n const cwd = (flags.cwd as string) ?? process.cwd();\n return {\n ...config,\n ...(flags.provider !== undefined && { provider: flags.provider as string }),\n ...(flags.model !== undefined && { model: flags.model as string }),\n ...(flags.apiKey !== undefined && { apiKey: flags.apiKey as string }),\n ...(flags.baseUrl !== undefined && { baseURL: flags.baseUrl as string }),\n ...(flags.permission !== undefined && { permissions: flags.permission as string }),\n ...(flags.thinking !== undefined && { thinking: flags.thinking as string }),\n ...(flags.maxTurns !== undefined && { maxTurns: flags.maxTurns as number }),\n ...(flags.systemPrompt !== undefined && { systemPrompt: flags.systemPrompt as string }),\n cwd,\n json: flags.json as boolean | undefined,\n quiet: flags.quiet as boolean | undefined,\n verbose: flags.verbose as boolean | undefined,\n headless: flags.headless as boolean | undefined,\n prompt: flags.prompt as string | undefined,\n noSandbox: flags.sandbox === false ? true : undefined,\n sandboxAllowWrite: flags.sandboxAllowWrite as string | undefined,\n sandboxAllowDomain: flags.sandboxAllowDomain as string | undefined,\n };\n}\n","import * as readline from \"node:readline/promises\";\nimport chalk from \"chalk\";\nimport type { Agent } from \"../agent.js\";\nimport type { Thread } from \"../thread.js\";\nimport type { MergedConfig } from \"./config.js\";\nimport { renderEvent, createRenderState, promptPermission, isVisibleEvent } from \"./render.js\";\nimport { DEFAULT_MODELS, createProvider, SUPPORTED_PROVIDERS, type ProviderName } from \"./provider-factory.js\";\nimport { startSpinner } from \"./spinner.js\";\n\nexport async function startRepl(\n code: Agent,\n config: MergedConfig,\n): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n terminal: true,\n });\n\n let thread = code.createThread({\n userInputHandler: (q) => promptPermission(rl, \"agent\", q).then((ok) => ok ? \"yes\" : \"no\"),\n });\n\n let runningTurn = false;\n let currentThread: Thread = thread;\n\n const sigintHandler = () => {\n if (runningTurn) {\n currentThread.abort();\n runningTurn = false;\n process.stderr.write(chalk.yellow(\"\\n Cancelled.\\n\\n\"));\n } else {\n process.stderr.write(chalk.dim(\"\\nGoodbye.\\n\"));\n rl.close();\n process.exit(0);\n }\n };\n process.on(\"SIGINT\", sigintHandler);\n\n printWelcome(config);\n\n try {\n while (true) {\n let input: string;\n try {\n input = await rl.question(chalk.blue(\"> \"));\n } catch {\n break;\n }\n\n if (!input.trim()) continue;\n\n if (input.startsWith(\"/\")) {\n const result = await handleSlashCommand(\n input,\n thread,\n code,\n config,\n rl,\n );\n if (result === \"quit\") break;\n if (result === \"new\") {\n thread = code.createThread({\n userInputHandler: (q) => promptPermission(rl, \"agent\", q).then((ok) => ok ? \"yes\" : \"no\"),\n });\n currentThread = thread;\n }\n continue;\n }\n\n const state = createRenderState();\n const runOpts = config.maxTurns ? { maxTurns: config.maxTurns } : undefined;\n\n const spinner =\n !config.json && !config.quiet ? startSpinner(\"Thinking\") : null;\n\n runningTurn = true;\n try {\n for await (const event of thread.run(input, runOpts)) {\n if (!state.showedActivity && spinner && isVisibleEvent(event, config)) {\n spinner.stop();\n state.showedActivity = true;\n }\n renderEvent(event, config, state);\n }\n } catch (err) {\n if ((err as Error)?.name === \"AbortError\") {\n // already handled by SIGINT handler\n } else {\n throw err;\n }\n } finally {\n spinner?.stop();\n runningTurn = false;\n }\n\n if (state.accumulatedText && !state.accumulatedText.endsWith(\"\\n\")) {\n process.stdout.write(\"\\n\");\n }\n }\n } finally {\n process.removeListener(\"SIGINT\", sigintHandler);\n rl.close();\n }\n}\n\nfunction printWelcome(config: MergedConfig): void {\n const provider = config.provider ?? \"auto\";\n const model = config.model ?? DEFAULT_MODELS[provider] ?? \"default\";\n process.stderr.write(\n chalk.bold(\"noumen\") +\n chalk.dim(` — ${provider}/${model}`) +\n \"\\n\" +\n chalk.dim(\"Type a message to begin. /help for commands, Ctrl+C to cancel.\") +\n \"\\n\\n\",\n );\n}\n\ntype SlashResult = \"continue\" | \"quit\" | \"new\";\n\nasync function handleSlashCommand(\n input: string,\n thread: Thread,\n code: Agent,\n config: MergedConfig,\n _rl: readline.Interface,\n): Promise<SlashResult> {\n const [cmd] = input.trim().split(/\\s+/);\n\n switch (cmd) {\n case \"/quit\":\n case \"/exit\":\n case \"/q\":\n process.stderr.write(chalk.dim(\"Goodbye.\\n\"));\n return \"quit\";\n\n case \"/new\":\n process.stderr.write(chalk.dim(\"Starting new conversation.\\n\\n\"));\n return \"new\";\n\n case \"/session\":\n process.stderr.write(chalk.dim(`Session: ${thread.sessionId}\\n`));\n return \"continue\";\n\n case \"/cost\": {\n const summary = code.getCostSummary();\n if (summary) {\n process.stderr.write(\n chalk.dim(\n `Cost: $${summary.totalCostUSD.toFixed(4)} | ` +\n `Input: ${summary.totalInputTokens} tokens | ` +\n `Output: ${summary.totalOutputTokens} tokens\\n`,\n ),\n );\n } else {\n process.stderr.write(chalk.dim(\"Cost tracking not enabled.\\n\"));\n }\n return \"continue\";\n }\n\n case \"/sessions\": {\n const sessions = await code.listSessions();\n if (sessions.length === 0) {\n process.stderr.write(chalk.dim(\"No saved sessions.\\n\"));\n } else {\n for (const s of sessions.slice(0, 20)) {\n process.stderr.write(\n chalk.dim(\n ` ${s.sessionId.slice(0, 8)} ${s.createdAt ?? \"\"} ${s.messageCount ?? 0} messages\\n`,\n ),\n );\n }\n }\n return \"continue\";\n }\n\n case \"/model\": {\n const arg = input.trim().split(/\\s+/).slice(1).join(\" \");\n if (!arg) {\n process.stderr.write(chalk.dim(`Current model: ${thread.getModel()}\\n`));\n } else {\n thread.setModel(arg);\n process.stderr.write(chalk.dim(`Model set to ${arg}\\n`));\n }\n return \"continue\";\n }\n\n case \"/provider\": {\n const parts = input.trim().split(/\\s+/).slice(1);\n const providerName = parts[0];\n const modelArg = parts[1];\n if (!providerName) {\n process.stderr.write(\n chalk.dim(`Current: ${config.provider ?? \"auto\"}/${thread.getModel()}\\n`) +\n chalk.dim(`Available: ${SUPPORTED_PROVIDERS.join(\", \")}\\n`),\n );\n return \"continue\";\n }\n if (!(SUPPORTED_PROVIDERS as readonly string[]).includes(providerName)) {\n process.stderr.write(chalk.red(`Unknown provider: ${providerName}. Available: ${SUPPORTED_PROVIDERS.join(\", \")}\\n`));\n return \"continue\";\n }\n try {\n const model = modelArg ?? DEFAULT_MODELS[providerName];\n const provider = await createProvider(providerName as ProviderName, {\n apiKey: config.apiKey,\n model,\n baseURL: config.baseURL,\n });\n thread.setProvider(provider, model);\n config.provider = providerName;\n config.model = model;\n process.stderr.write(chalk.dim(`Switched to ${providerName}/${model}\\n`));\n } catch (err) {\n process.stderr.write(chalk.red(`Failed to switch: ${(err as Error).message}\\n`));\n }\n return \"continue\";\n }\n\n case \"/verbose\":\n config.verbose = !config.verbose;\n process.stderr.write(\n chalk.dim(`Verbose mode: ${config.verbose ? \"on\" : \"off\"}\\n`),\n );\n return \"continue\";\n\n case \"/help\":\n process.stderr.write(\n chalk.dim(\n [\n \"Commands:\",\n \" /quit, /exit Exit the REPL\",\n \" /new Start a new conversation\",\n \" /model [name] Show or change the model\",\n \" /provider [name] Show or switch provider (and model)\",\n \" /session Show current session ID\",\n \" /sessions List saved sessions\",\n \" /cost Show token usage and cost\",\n \" /verbose Toggle verbose output\",\n \" /help Show this help\",\n \"\",\n \"Shortcuts:\",\n \" Ctrl+C Cancel current turn / exit when idle\",\n \"\",\n ].join(\"\\n\"),\n ),\n );\n return \"continue\";\n\n default:\n process.stderr.write(\n chalk.yellow(`Unknown command: ${cmd}. Try /help\\n`),\n );\n return \"continue\";\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline/promises\";\nimport chalk from \"chalk\";\nimport { SUPPORTED_PROVIDERS, DEFAULT_MODELS, isOllamaRunning, ollamaBaseURL } from \"./provider-factory.js\";\n\nconst PERMISSION_MODES = [\"default\", \"plan\", \"acceptEdits\", \"auto\", \"bypassPermissions\"];\n\nexport async function runInit(): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n process.stdout.write(chalk.bold(\"noumen init\") + \"\\n\\n\");\n\n const provider = await askChoice(\n rl,\n \"Provider\",\n SUPPORTED_PROVIDERS,\n \"anthropic\",\n );\n\n let defaultModel = DEFAULT_MODELS[provider] ?? \"\";\n\n if (provider === \"ollama\") {\n const ollamaModels = await listOllamaModels();\n if (ollamaModels.length > 0) {\n process.stdout.write(chalk.dim(` Available models: ${ollamaModels.join(\", \")}\\n`));\n if (ollamaModels.includes(defaultModel)) {\n // keep the default\n } else {\n defaultModel = ollamaModels[0];\n }\n } else {\n process.stdout.write(\n chalk.yellow(\n `\\n Ollama doesn't appear to be running at ${ollamaBaseURL()}.\\n` +\n ` Install it from https://ollama.com, then run:\\n` +\n ` ollama pull ${defaultModel}\\n` +\n ` ollama serve\\n\\n`,\n ),\n );\n }\n }\n\n const model = await askDefault(rl, \"Model\", defaultModel);\n\n const permissions = await askChoice(\n rl,\n \"Permission mode\",\n PERMISSION_MODES,\n \"default\",\n );\n\n const config: Record<string, unknown> = { provider };\n if (model !== defaultModel) config.model = model;\n if (permissions !== \"default\") config.permissions = permissions;\n\n const dir = path.join(process.cwd(), \".noumen\");\n fs.mkdirSync(dir, { recursive: true });\n\n const configPath = path.join(dir, \"config.json\");\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n process.stdout.write(chalk.green(` Created ${configPath}\\n`));\n\n const noumenMdPath = path.join(process.cwd(), \"NOUMEN.md\");\n if (!fs.existsSync(noumenMdPath)) {\n const create = await askDefault(rl, \"Create NOUMEN.md?\", \"Y\");\n if (create.toLowerCase() === \"y\" || create.toLowerCase() === \"yes\") {\n fs.writeFileSync(noumenMdPath, NOUMEN_MD_TEMPLATE);\n process.stdout.write(chalk.green(` Created ${noumenMdPath}\\n`));\n }\n }\n\n process.stdout.write(\n \"\\n\" + chalk.dim(\"Run `noumen` to start a session.\") + \"\\n\",\n );\n } finally {\n rl.close();\n }\n}\n\nasync function listOllamaModels(): Promise<string[]> {\n if (!(await isOllamaRunning())) return [];\n try {\n const res = await fetch(`${ollamaBaseURL()}/api/tags`, {\n signal: AbortSignal.timeout(2000),\n });\n if (!res.ok) return [];\n const data = (await res.json()) as { models?: Array<{ name: string }> };\n return (data.models ?? []).map((m) => m.name);\n } catch {\n return [];\n }\n}\n\nasync function askChoice(\n rl: readline.Interface,\n label: string,\n choices: string[],\n defaultValue: string,\n): Promise<string> {\n const hint = choices.join(\", \");\n const answer = await rl.question(\n ` ${label} (${hint}) [${chalk.bold(defaultValue)}]: `,\n );\n const trimmed = answer.trim();\n if (!trimmed) return defaultValue;\n if (choices.includes(trimmed)) return trimmed;\n process.stdout.write(chalk.yellow(` Invalid choice, using ${defaultValue}\\n`));\n return defaultValue;\n}\n\nasync function askDefault(\n rl: readline.Interface,\n label: string,\n defaultValue: string,\n): Promise<string> {\n const answer = await rl.question(\n ` ${label} [${chalk.bold(defaultValue)}]: `,\n );\n return answer.trim() || defaultValue;\n}\n\nconst NOUMEN_MD_TEMPLATE = `# Project Instructions\n\nAdd project-specific instructions for the AI agent here.\nThese instructions are loaded automatically when running noumen in this directory.\n\n## Guidelines\n\n- Describe your project's coding conventions\n- Note important architectural decisions\n- List files or patterns the agent should be aware of\n`;\n"],"mappings":"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","names":["chalk","fs","path","readline","chalk","chalk","os","chalk","createInterface","SUPPORTED_PROVIDERS","isOllamaRunning","ollamaBaseURL","DEFAULT_MODELS","startSpinner","isVisibleEvent"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { S as StreamEvent } from '../types-3c88cRKH.js';
|
|
2
|
+
import { P as PermissionResponse } from '../types-DwdzmXfs.js';
|
|
3
|
+
|
|
4
|
+
interface ClientOptions {
|
|
5
|
+
/** Base URL of the noumen server, e.g. "http://localhost:3001". */
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
/** Bearer token for authentication. */
|
|
8
|
+
token?: string;
|
|
9
|
+
/** Transport preference. "auto" tries WebSocket first, falls back to SSE. */
|
|
10
|
+
transport?: "ws" | "sse" | "auto";
|
|
11
|
+
/** Extra headers to send with HTTP requests (SSE transport). */
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
interface PermissionRequestEvent {
|
|
15
|
+
toolName: string;
|
|
16
|
+
input: Record<string, unknown>;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
interface ClientRunOptions {
|
|
20
|
+
/** Provide a session ID to resume an existing session. */
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
/** Called when the agent requests permission to run a tool. Return approval/denial. */
|
|
24
|
+
onPermissionRequest?: (req: PermissionRequestEvent) => Promise<PermissionResponse>;
|
|
25
|
+
/** Called when the agent asks the user a question. Return the answer. */
|
|
26
|
+
onUserInput?: (question: string) => Promise<string>;
|
|
27
|
+
}
|
|
28
|
+
declare class NoumenClient {
|
|
29
|
+
private baseUrl;
|
|
30
|
+
private token?;
|
|
31
|
+
private transport;
|
|
32
|
+
private headers;
|
|
33
|
+
constructor(options: ClientOptions);
|
|
34
|
+
run(prompt: string, opts?: ClientRunOptions): AsyncGenerator<StreamEvent>;
|
|
35
|
+
sendMessage(sessionId: string, prompt: string, opts?: Omit<ClientRunOptions, "sessionId">): AsyncGenerator<StreamEvent>;
|
|
36
|
+
abort(sessionId: string): Promise<void>;
|
|
37
|
+
listSessions(): Promise<Array<{
|
|
38
|
+
id: string;
|
|
39
|
+
lastActivity: number;
|
|
40
|
+
done: boolean;
|
|
41
|
+
}>>;
|
|
42
|
+
private resolveTransport;
|
|
43
|
+
private runWs;
|
|
44
|
+
private sendMessageWs;
|
|
45
|
+
private driveWs;
|
|
46
|
+
private buildWsUrl;
|
|
47
|
+
private runSse;
|
|
48
|
+
private sendMessageSse;
|
|
49
|
+
/**
|
|
50
|
+
* SSE stream consumer with automatic reconnection, liveness detection,
|
|
51
|
+
* and event deduplication via sequence numbers.
|
|
52
|
+
*/
|
|
53
|
+
private consumeSseStreamWithReconnect;
|
|
54
|
+
/**
|
|
55
|
+
* Single SSE connection attempt. Yields events and returns when the stream
|
|
56
|
+
* ends (either gracefully via turn_complete or from a dropped connection).
|
|
57
|
+
* The liveness timer triggers an AbortError if no frames arrive within
|
|
58
|
+
* LIVENESS_TIMEOUT_MS.
|
|
59
|
+
*/
|
|
60
|
+
private consumeSseStream;
|
|
61
|
+
private httpFetch;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { type ClientOptions, type ClientRunOptions, NoumenClient, type PermissionRequestEvent };
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import "../chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/client/index.ts
|
|
4
|
+
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
5
|
+
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
6
|
+
var RECONNECT_GIVE_UP_MS = 6e5;
|
|
7
|
+
var LIVENESS_TIMEOUT_MS = 45e3;
|
|
8
|
+
var PERMANENT_HTTP_CODES = /* @__PURE__ */ new Set([401, 403, 404]);
|
|
9
|
+
var WS_OPEN = 1;
|
|
10
|
+
var WS_CONNECTING = 0;
|
|
11
|
+
var NoumenClient = class {
|
|
12
|
+
baseUrl;
|
|
13
|
+
token;
|
|
14
|
+
transport;
|
|
15
|
+
headers;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
18
|
+
this.token = options.token;
|
|
19
|
+
this.transport = options.transport ?? "auto";
|
|
20
|
+
this.headers = options.headers ?? {};
|
|
21
|
+
}
|
|
22
|
+
async *run(prompt, opts) {
|
|
23
|
+
const transport = this.resolveTransport();
|
|
24
|
+
if (transport === "ws") {
|
|
25
|
+
yield* this.runWs(prompt, opts);
|
|
26
|
+
} else {
|
|
27
|
+
yield* this.runSse(prompt, opts);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async *sendMessage(sessionId, prompt, opts) {
|
|
31
|
+
const transport = this.resolveTransport();
|
|
32
|
+
if (transport === "ws") {
|
|
33
|
+
yield* this.sendMessageWs(sessionId, prompt, opts);
|
|
34
|
+
} else {
|
|
35
|
+
yield* this.sendMessageSse(sessionId, prompt, opts);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async abort(sessionId) {
|
|
39
|
+
await this.httpFetch(`/sessions/${sessionId}`, { method: "DELETE" });
|
|
40
|
+
}
|
|
41
|
+
async listSessions() {
|
|
42
|
+
const res = await this.httpFetch("/sessions", { method: "GET" });
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
// -------------------------------------------------------------------------
|
|
46
|
+
// Transport resolution
|
|
47
|
+
// -------------------------------------------------------------------------
|
|
48
|
+
resolveTransport() {
|
|
49
|
+
if (this.transport === "ws") return "ws";
|
|
50
|
+
if (this.transport === "sse") return "sse";
|
|
51
|
+
if (typeof globalThis.WebSocket !== "undefined") return "ws";
|
|
52
|
+
return "sse";
|
|
53
|
+
}
|
|
54
|
+
// -------------------------------------------------------------------------
|
|
55
|
+
// WebSocket transport
|
|
56
|
+
// -------------------------------------------------------------------------
|
|
57
|
+
async *runWs(prompt, opts) {
|
|
58
|
+
const ws = new globalThis.WebSocket(this.buildWsUrl());
|
|
59
|
+
yield* this.driveWs(ws, opts, () => {
|
|
60
|
+
ws.send(JSON.stringify({
|
|
61
|
+
type: "run",
|
|
62
|
+
prompt,
|
|
63
|
+
sessionId: opts?.sessionId
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async *sendMessageWs(sessionId, prompt, opts) {
|
|
68
|
+
const ws = new globalThis.WebSocket(this.buildWsUrl());
|
|
69
|
+
yield* this.driveWs(ws, opts, () => {
|
|
70
|
+
ws.send(JSON.stringify({
|
|
71
|
+
type: "message",
|
|
72
|
+
sessionId,
|
|
73
|
+
prompt
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async *driveWs(ws, opts, onOpen) {
|
|
78
|
+
const queue = [];
|
|
79
|
+
let waiter = null;
|
|
80
|
+
function enqueue(item) {
|
|
81
|
+
if (waiter) {
|
|
82
|
+
const w = waiter;
|
|
83
|
+
waiter = null;
|
|
84
|
+
w(item);
|
|
85
|
+
} else {
|
|
86
|
+
queue.push(item);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function dequeue() {
|
|
90
|
+
if (queue.length > 0) return Promise.resolve(queue.shift());
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
waiter = resolve;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
ws.addEventListener("open", () => onOpen());
|
|
96
|
+
ws.addEventListener("message", async (msg) => {
|
|
97
|
+
try {
|
|
98
|
+
const data = JSON.parse(typeof msg.data === "string" ? msg.data : String(msg.data));
|
|
99
|
+
if (data.type === "session_created") return;
|
|
100
|
+
if (data.type === "permission_request" && opts && "onPermissionRequest" in opts && opts.onPermissionRequest) {
|
|
101
|
+
try {
|
|
102
|
+
const response = await opts.onPermissionRequest({
|
|
103
|
+
toolName: data.toolName,
|
|
104
|
+
input: data.input,
|
|
105
|
+
message: data.message
|
|
106
|
+
});
|
|
107
|
+
ws.send(JSON.stringify({
|
|
108
|
+
type: "permission_response",
|
|
109
|
+
sessionId: data.sessionId,
|
|
110
|
+
...response
|
|
111
|
+
}));
|
|
112
|
+
} catch {
|
|
113
|
+
ws.send(JSON.stringify({
|
|
114
|
+
type: "permission_response",
|
|
115
|
+
sessionId: data.sessionId,
|
|
116
|
+
allow: false,
|
|
117
|
+
feedback: "Client error handling permission request"
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
enqueue({ event: data });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (data.type === "user_input_request" && opts?.onUserInput) {
|
|
124
|
+
try {
|
|
125
|
+
const answer = await opts.onUserInput(data.question);
|
|
126
|
+
ws.send(JSON.stringify({
|
|
127
|
+
type: "input_response",
|
|
128
|
+
sessionId: data.sessionId,
|
|
129
|
+
answer
|
|
130
|
+
}));
|
|
131
|
+
} catch {
|
|
132
|
+
ws.send(JSON.stringify({
|
|
133
|
+
type: "input_response",
|
|
134
|
+
sessionId: data.sessionId,
|
|
135
|
+
answer: ""
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
enqueue({ event: data });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (data.type === "turn_complete") {
|
|
142
|
+
enqueue({ event: data });
|
|
143
|
+
enqueue({ done: true });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
enqueue({ event: data });
|
|
147
|
+
} catch (err) {
|
|
148
|
+
enqueue({ error: err instanceof Error ? err : new Error(String(err)) });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
ws.addEventListener("close", () => enqueue({ done: true }));
|
|
152
|
+
ws.addEventListener("error", (e) => {
|
|
153
|
+
enqueue({ error: new Error("WebSocket error: " + String(e)) });
|
|
154
|
+
});
|
|
155
|
+
const handleAbort = () => {
|
|
156
|
+
ws.close();
|
|
157
|
+
enqueue({ done: true });
|
|
158
|
+
};
|
|
159
|
+
opts?.signal?.addEventListener("abort", handleAbort);
|
|
160
|
+
try {
|
|
161
|
+
while (true) {
|
|
162
|
+
const item = await dequeue();
|
|
163
|
+
if ("done" in item) return;
|
|
164
|
+
if ("error" in item) throw item.error;
|
|
165
|
+
yield item.event;
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
opts?.signal?.removeEventListener("abort", handleAbort);
|
|
169
|
+
if (ws.readyState === WS_OPEN || ws.readyState === WS_CONNECTING) {
|
|
170
|
+
ws.close();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
buildWsUrl() {
|
|
175
|
+
const wsBase = this.baseUrl.replace(/^http/, "ws");
|
|
176
|
+
const url = new URL("/ws", wsBase);
|
|
177
|
+
if (this.token) url.searchParams.set("token", this.token);
|
|
178
|
+
return url.toString();
|
|
179
|
+
}
|
|
180
|
+
// -------------------------------------------------------------------------
|
|
181
|
+
// SSE transport
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
async *runSse(prompt, opts) {
|
|
184
|
+
const createRes = await this.httpFetch("/sessions", {
|
|
185
|
+
method: "POST",
|
|
186
|
+
body: JSON.stringify({ prompt, sessionId: opts?.sessionId })
|
|
187
|
+
});
|
|
188
|
+
if (!createRes.ok) {
|
|
189
|
+
throw new Error(`Failed to create session: ${createRes.status} ${await createRes.text()}`);
|
|
190
|
+
}
|
|
191
|
+
const { sessionId, eventsUrl } = await createRes.json();
|
|
192
|
+
yield* this.consumeSseStreamWithReconnect(sessionId, eventsUrl, opts);
|
|
193
|
+
}
|
|
194
|
+
async *sendMessageSse(sessionId, prompt, opts) {
|
|
195
|
+
const msgRes = await this.httpFetch(`/sessions/${sessionId}/messages`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
body: JSON.stringify({ prompt })
|
|
198
|
+
});
|
|
199
|
+
if (!msgRes.ok) {
|
|
200
|
+
throw new Error(`Failed to send message: ${msgRes.status} ${await msgRes.text()}`);
|
|
201
|
+
}
|
|
202
|
+
yield* this.consumeSseStreamWithReconnect(sessionId, `/sessions/${sessionId}/events`, opts);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* SSE stream consumer with automatic reconnection, liveness detection,
|
|
206
|
+
* and event deduplication via sequence numbers.
|
|
207
|
+
*/
|
|
208
|
+
async *consumeSseStreamWithReconnect(sessionId, eventsPath, opts) {
|
|
209
|
+
const state = { lastSeqNum: 0 };
|
|
210
|
+
let reconnectAttempts = 0;
|
|
211
|
+
let reconnectStartTime = null;
|
|
212
|
+
let turnComplete = false;
|
|
213
|
+
while (!turnComplete) {
|
|
214
|
+
if (opts?.signal?.aborted) return;
|
|
215
|
+
try {
|
|
216
|
+
for await (const event of this.consumeSseStream(sessionId, eventsPath, opts, state)) {
|
|
217
|
+
yield event;
|
|
218
|
+
if (event.type === "turn_complete") {
|
|
219
|
+
turnComplete = true;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (turnComplete) return;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
if (opts?.signal?.aborted) return;
|
|
226
|
+
if (err.name === "AbortError") return;
|
|
227
|
+
const msg = err.message ?? "";
|
|
228
|
+
const statusMatch = msg.match(/SSE connection failed: (\d+)/);
|
|
229
|
+
if (statusMatch && PERMANENT_HTTP_CODES.has(parseInt(statusMatch[1], 10))) {
|
|
230
|
+
throw err;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (opts?.signal?.aborted) return;
|
|
234
|
+
const now = Date.now();
|
|
235
|
+
if (!reconnectStartTime) reconnectStartTime = now;
|
|
236
|
+
const elapsed = now - reconnectStartTime;
|
|
237
|
+
if (elapsed >= RECONNECT_GIVE_UP_MS) {
|
|
238
|
+
throw new Error(`SSE reconnection failed after ${Math.round(elapsed / 1e3)}s`);
|
|
239
|
+
}
|
|
240
|
+
reconnectAttempts++;
|
|
241
|
+
const baseDelay = Math.min(
|
|
242
|
+
RECONNECT_BASE_DELAY_MS * Math.pow(2, reconnectAttempts - 1),
|
|
243
|
+
RECONNECT_MAX_DELAY_MS
|
|
244
|
+
);
|
|
245
|
+
const delay = Math.max(0, baseDelay + baseDelay * 0.25 * (2 * Math.random() - 1));
|
|
246
|
+
await sleep(delay, opts?.signal);
|
|
247
|
+
if (opts?.signal?.aborted) return;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Single SSE connection attempt. Yields events and returns when the stream
|
|
252
|
+
* ends (either gracefully via turn_complete or from a dropped connection).
|
|
253
|
+
* The liveness timer triggers an AbortError if no frames arrive within
|
|
254
|
+
* LIVENESS_TIMEOUT_MS.
|
|
255
|
+
*/
|
|
256
|
+
async *consumeSseStream(sessionId, eventsPath, opts, state) {
|
|
257
|
+
const url = `${this.baseUrl}${eventsPath}`;
|
|
258
|
+
const headers = { ...this.headers };
|
|
259
|
+
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
260
|
+
if (state.lastSeqNum > 0) headers["Last-Event-ID"] = String(state.lastSeqNum);
|
|
261
|
+
const ac = new AbortController();
|
|
262
|
+
if (opts?.signal) {
|
|
263
|
+
if (opts.signal.aborted) {
|
|
264
|
+
ac.abort();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
opts.signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
268
|
+
}
|
|
269
|
+
let livenessTimer = null;
|
|
270
|
+
const resetLiveness = () => {
|
|
271
|
+
if (livenessTimer) clearTimeout(livenessTimer);
|
|
272
|
+
livenessTimer = setTimeout(() => ac.abort(), LIVENESS_TIMEOUT_MS);
|
|
273
|
+
};
|
|
274
|
+
const clearLiveness = () => {
|
|
275
|
+
if (livenessTimer) {
|
|
276
|
+
clearTimeout(livenessTimer);
|
|
277
|
+
livenessTimer = null;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const response = await globalThis.fetch(url, { headers, signal: ac.signal });
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
clearLiveness();
|
|
283
|
+
throw new Error(`SSE connection failed: ${response.status}`);
|
|
284
|
+
}
|
|
285
|
+
const reader = response.body?.getReader();
|
|
286
|
+
if (!reader) {
|
|
287
|
+
clearLiveness();
|
|
288
|
+
throw new Error("No response body");
|
|
289
|
+
}
|
|
290
|
+
const decoder = new TextDecoder();
|
|
291
|
+
let buffer = "";
|
|
292
|
+
resetLiveness();
|
|
293
|
+
let currentEventId = 0;
|
|
294
|
+
try {
|
|
295
|
+
while (true) {
|
|
296
|
+
const { done, value } = await reader.read();
|
|
297
|
+
if (done) break;
|
|
298
|
+
resetLiveness();
|
|
299
|
+
buffer += decoder.decode(value, { stream: true });
|
|
300
|
+
const lines = buffer.split("\n");
|
|
301
|
+
buffer = lines.pop() ?? "";
|
|
302
|
+
for (const line of lines) {
|
|
303
|
+
if (line.startsWith(":")) {
|
|
304
|
+
resetLiveness();
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (line.startsWith("id: ")) {
|
|
308
|
+
const seqNum = parseInt(line.slice(4), 10);
|
|
309
|
+
if (!isNaN(seqNum)) currentEventId = seqNum;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!line.startsWith("data: ")) continue;
|
|
313
|
+
const json = line.slice(6);
|
|
314
|
+
if (!json) continue;
|
|
315
|
+
if (currentEventId > 0 && currentEventId <= state.lastSeqNum) {
|
|
316
|
+
currentEventId = 0;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (currentEventId > state.lastSeqNum) {
|
|
320
|
+
state.lastSeqNum = currentEventId;
|
|
321
|
+
}
|
|
322
|
+
let parsed;
|
|
323
|
+
try {
|
|
324
|
+
parsed = JSON.parse(json);
|
|
325
|
+
} catch {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
currentEventId = 0;
|
|
329
|
+
const eventType = parsed.type;
|
|
330
|
+
if (eventType === "permission_request" && opts && "onPermissionRequest" in opts && opts.onPermissionRequest) {
|
|
331
|
+
try {
|
|
332
|
+
const permResponse = await opts.onPermissionRequest({
|
|
333
|
+
toolName: parsed.toolName,
|
|
334
|
+
input: parsed.input,
|
|
335
|
+
message: parsed.message
|
|
336
|
+
});
|
|
337
|
+
await this.httpFetch(`/sessions/${sessionId}/permissions`, {
|
|
338
|
+
method: "POST",
|
|
339
|
+
body: JSON.stringify(permResponse)
|
|
340
|
+
});
|
|
341
|
+
} catch {
|
|
342
|
+
await this.httpFetch(`/sessions/${sessionId}/permissions`, {
|
|
343
|
+
method: "POST",
|
|
344
|
+
body: JSON.stringify({ allow: false })
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (eventType === "user_input_request" && opts?.onUserInput) {
|
|
349
|
+
try {
|
|
350
|
+
const answer = await opts.onUserInput(parsed.question);
|
|
351
|
+
await this.httpFetch(`/sessions/${sessionId}/input`, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: JSON.stringify({ answer })
|
|
354
|
+
});
|
|
355
|
+
} catch {
|
|
356
|
+
await this.httpFetch(`/sessions/${sessionId}/input`, {
|
|
357
|
+
method: "POST",
|
|
358
|
+
body: JSON.stringify({ answer: "" })
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
yield parsed;
|
|
363
|
+
if (eventType === "turn_complete") {
|
|
364
|
+
ac.abort();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} catch (err) {
|
|
370
|
+
if (err.name !== "AbortError") throw err;
|
|
371
|
+
} finally {
|
|
372
|
+
clearLiveness();
|
|
373
|
+
try {
|
|
374
|
+
reader.releaseLock();
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
if (!ac.signal.aborted) ac.abort();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// -------------------------------------------------------------------------
|
|
381
|
+
// HTTP helpers
|
|
382
|
+
// -------------------------------------------------------------------------
|
|
383
|
+
httpFetch(path, init) {
|
|
384
|
+
const url = `${this.baseUrl}${path}`;
|
|
385
|
+
const headers = {
|
|
386
|
+
"Content-Type": "application/json",
|
|
387
|
+
...this.headers
|
|
388
|
+
};
|
|
389
|
+
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
390
|
+
return globalThis.fetch(url, { ...init, headers: { ...headers, ...init?.headers } });
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
function sleep(ms, signal) {
|
|
394
|
+
return new Promise((resolve) => {
|
|
395
|
+
if (signal?.aborted) {
|
|
396
|
+
resolve();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const timer = setTimeout(resolve, ms);
|
|
400
|
+
signal?.addEventListener("abort", () => {
|
|
401
|
+
clearTimeout(timer);
|
|
402
|
+
resolve();
|
|
403
|
+
}, { once: true });
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
export {
|
|
407
|
+
NoumenClient
|
|
408
|
+
};
|
|
409
|
+
//# sourceMappingURL=index.js.map
|