arcie 0.1.4 → 0.1.6

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.
Files changed (64) hide show
  1. package/dist/agent/index.d.ts +1 -1
  2. package/dist/build-TWNCPXBU.js +82 -0
  3. package/dist/build-TWNCPXBU.js.map +1 -0
  4. package/dist/channels/index.d.ts +1 -1
  5. package/dist/{chunk-2ZSKZYWK.js → chunk-4WSILP75.js} +37 -164
  6. package/dist/chunk-4WSILP75.js.map +1 -0
  7. package/dist/chunk-HVBDOHTL.js +148 -0
  8. package/dist/chunk-HVBDOHTL.js.map +1 -0
  9. package/dist/chunk-HWD2Z2CH.js +742 -0
  10. package/dist/chunk-HWD2Z2CH.js.map +1 -0
  11. package/dist/chunk-KVSX4MLK.js +139 -0
  12. package/dist/chunk-KVSX4MLK.js.map +1 -0
  13. package/dist/chunk-N5R3TEEZ.js +15 -0
  14. package/dist/chunk-N5R3TEEZ.js.map +1 -0
  15. package/dist/chunk-SGWQZZ2A.js +54 -0
  16. package/dist/chunk-SGWQZZ2A.js.map +1 -0
  17. package/dist/chunk-XOFWQUOO.js +30 -0
  18. package/dist/chunk-XOFWQUOO.js.map +1 -0
  19. package/dist/cli/index.d.ts +7 -0
  20. package/dist/cli/index.js +156 -231
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/context/index.d.ts +1 -1
  23. package/dist/dev-ZZDVOWUZ.js +1967 -0
  24. package/dist/dev-ZZDVOWUZ.js.map +1 -0
  25. package/dist/hooks/index.d.ts +1 -1
  26. package/dist/{index-b4NcDl3m.d.ts → index-CElLRVTP.d.ts} +1 -1
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.js +4 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/init-5F7CIVLN.js +822 -0
  31. package/dist/init-5F7CIVLN.js.map +1 -0
  32. package/dist/instructions/index.d.ts +1 -1
  33. package/dist/runner/index.d.ts +2 -2
  34. package/dist/runner/index.js +2 -1
  35. package/dist/scaffold-web-chat-OQL5C2GK.js +7 -0
  36. package/dist/scaffold-web-chat-OQL5C2GK.js.map +1 -0
  37. package/dist/schedules/index.d.ts +1 -1
  38. package/dist/skills/index.d.ts +1 -1
  39. package/dist/tools/index.d.ts +1 -1
  40. package/dist/{types-BxOQTzx1.d.ts → types-DwxwdSa2.d.ts} +1 -1
  41. package/package.json +1 -1
  42. package/templates/default/.env.local +12 -0
  43. package/templates/web-chat/.env.local.example +4 -0
  44. package/templates/web-chat/README.md +51 -0
  45. package/templates/web-chat/app/api/chat/route.ts +43 -0
  46. package/templates/web-chat/app/globals.css +65 -0
  47. package/templates/web-chat/app/layout.tsx +15 -0
  48. package/templates/web-chat/app/page.tsx +9 -0
  49. package/templates/web-chat/components/chat.tsx +219 -0
  50. package/templates/web-chat/components/input-bar.tsx +181 -0
  51. package/templates/web-chat/components/message.tsx +123 -0
  52. package/templates/web-chat/components/tool-call.tsx +79 -0
  53. package/templates/web-chat/components/ui/button.tsx +46 -0
  54. package/templates/web-chat/components/ui/input.tsx +19 -0
  55. package/templates/web-chat/components.json +20 -0
  56. package/templates/web-chat/lib/stream.ts +41 -0
  57. package/templates/web-chat/lib/types.ts +62 -0
  58. package/templates/web-chat/lib/utils.ts +6 -0
  59. package/templates/web-chat/next.config.mjs +6 -0
  60. package/templates/web-chat/package.json +33 -0
  61. package/templates/web-chat/postcss.config.mjs +6 -0
  62. package/templates/web-chat/tailwind.config.ts +61 -0
  63. package/templates/web-chat/tsconfig.json +23 -0
  64. package/dist/chunk-2ZSKZYWK.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/init.ts","../src/cli/model-catalog.ts","../src/cli/tui/diagnostics.ts","../src/cli/setup/select-input.ts","../src/cli/setup/text-input.ts","../src/cli/setup/setup-panel.ts","../src/cli/setup/tui-prompter.ts"],"sourcesContent":["import {\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve, join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { exec } from \"node:child_process\";\nimport { fetchModelCatalog, type CatalogModel } from \"./model-catalog\";\nimport { checkProviderKeys, isProviderKeySet, providerEnvVar } from \"./tui/diagnostics\";\nimport { createTuiPrompter } from \"./setup/tui-prompter\";\nimport type { Prompter } from \"./setup/prompter\";\nimport { scaffoldWebChat } from \"./scaffold-web-chat\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Walk up from the current file to find the installed package's `templates/`\n * directory. Bundlers (tsup) may collapse `src/cli/init.ts` into a flat\n * `dist/*.js`, so `../../templates` from the source file is not a reliable\n * post-build path. The templates directory always sits at the package root\n * regardless of where the bundle lands.\n */\nfunction resolveTemplatesDir(): string {\n let current = __dirname;\n for (let i = 0; i < 6; i += 1) {\n const candidate = resolve(current, \"templates\");\n if (existsSync(candidate)) return candidate;\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return resolve(__dirname, \"../../templates\");\n}\n\nconst TEMPLATES_DIR = resolveTemplatesDir();\n\nfunction copyTemplate(src: string, dest: string): void {\n for (const entry of collectFiles(src)) {\n const relative = entry.replace(src, \"\").replace(/^\\//, \"\");\n const destPath = join(dest, relative);\n if (entry.endsWith(\".gitkeep\")) {\n const parentDir = destPath.replace(\"/.gitkeep\", \"\");\n mkdirSync(parentDir, { recursive: true });\n writeFileSync(join(parentDir, \".gitkeep\"), \"\");\n continue;\n }\n mkdirSync(dirname(destPath), { recursive: true });\n copyFileSync(entry, destPath);\n }\n}\n\nfunction collectFiles(dir: string): string[] {\n const results: string[] = [];\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n if (statSync(full).isDirectory()) results.push(...collectFiles(full));\n else results.push(full);\n }\n return results;\n}\n\nfunction updatePackageJson(dir: string, name: string): void {\n const pkgPath = join(dir, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n pkg.name = name;\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n}\n\nfunction detectEnvKey(): string | null {\n return process.env.CENCORI_API_KEY ?? null;\n}\n\nfunction uncommentEnvLine(envPath: string, key: string, value: string): void {\n const content = readFileSync(envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const updated = lines.map((line) => {\n const trimmed = line.trim();\n if (trimmed.startsWith(`# ${key}=`) || trimmed.startsWith(`${key}=`)) {\n return `${key}=${value}`;\n }\n return line;\n });\n if (updated.join(\"\\n\") === content) updated.push(`${key}=${value}`);\n writeFileSync(envPath, updated.join(\"\\n\") + \"\\n\");\n}\n\nfunction writeAgentModel(agentDir: string, model: string): void {\n const agentFile = join(agentDir, \"agent/agent.ts\");\n const content = readFileSync(agentFile, \"utf-8\");\n const updated = content.replace(/model:\\s*\"[^\"]+\"/, `model: \"${model}\"`);\n writeFileSync(agentFile, updated);\n}\n\nfunction runNpmInstall(cwd: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const proc = exec(\"npm install\", { cwd });\n proc.once(\"exit\", (code) => (code === 0 ? resolve() : reject(new Error(`npm exit ${code}`))));\n proc.once(\"error\", reject);\n });\n}\n\nconst IGNORED_DIRECTORY_ENTRIES = new Set([\".DS_Store\", \".git\", \".gitkeep\"]);\n\nfunction directoryHasMeaningfulContent(dir: string): boolean {\n try {\n return readdirSync(dir).some((entry) => !IGNORED_DIRECTORY_ENTRIES.has(entry));\n } catch {\n return false;\n }\n}\n\nfunction looksLikeArcieProject(dir: string): boolean {\n return existsSync(join(dir, \"agent\", \"agent.ts\"));\n}\n\n/**\n * Refuses to wipe a directory that is not clearly the user's project scratch\n * space. Blocks the root, the user's home, first-level system paths, and\n * anything with fewer than two non-root segments so an operator can't\n * accidentally rm-rf `/tmp` by giving init a slightly wrong argument.\n */\nfunction assertSafeToClear(dir: string): void {\n const absolute = resolve(dir);\n const home = homedir();\n if (absolute === \"/\" || absolute === home) {\n throw new Error(`Refusing to clear ${absolute}: too dangerous`);\n }\n const parts = absolute.split(\"/\").filter((part) => part.length > 0);\n if (parts.length < 2) {\n throw new Error(`Refusing to clear ${absolute}: not enough path depth`);\n }\n const dangerousFirst = new Set([\"etc\", \"var\", \"usr\", \"bin\", \"sbin\", \"System\", \"Library\"]);\n if (parts.length === 2 && dangerousFirst.has(parts[0]!)) {\n throw new Error(`Refusing to clear ${absolute}: system path`);\n }\n}\n\nfunction clearDirectoryContents(dir: string): void {\n assertSafeToClear(dir);\n for (const entry of readdirSync(dir)) {\n rmSync(join(dir, entry), { recursive: true, force: true });\n }\n}\n\nexport async function initCommand(\n name: string | undefined,\n options: { template: string },\n): Promise<void> {\n const targetDir = name ? resolve(process.cwd(), name) : resolve(process.cwd(), \".\");\n\n const templateDir = resolve(TEMPLATES_DIR, options.template);\n if (!existsSync(templateDir)) {\n console.error(` Template not found: ${options.template}`);\n process.exit(1);\n }\n\n const prompter = createTuiPrompter();\n try {\n let mode: InitMode = \"fresh\";\n if (existsSync(targetDir) && directoryHasMeaningfulContent(targetDir)) {\n const decision = await resolveExistingDirectory(prompter, targetDir);\n if (decision === \"cancel\") return;\n if (decision === \"overwrite\") {\n try {\n clearDirectoryContents(targetDir);\n prompter.log.warning(`Cleared ${targetDir}`);\n } catch (err) {\n prompter.log.error(err instanceof Error ? err.message : String(err));\n return;\n }\n } else {\n mode = \"resume\";\n }\n }\n await runInit(prompter, { targetDir, templateDir, name, mode });\n } finally {\n prompter.stop();\n }\n}\n\nasync function maybeScaffoldWebChat(prompter: Prompter, targetDir: string): Promise<void> {\n const choice = await prompter.select({\n message: \"Add a web chat UI now? (Next.js + shadcn under channels/web)\",\n options: [\n { value: \"yes\", label: \"Yes — scaffold channels/web now\" },\n { value: \"no\", label: \"No — I'll add it later with `arcie channels add web`\" },\n ],\n });\n if (choice !== \"yes\") return;\n\n try {\n const result = scaffoldWebChat(targetDir);\n if (result.alreadyExisted) {\n prompter.log.info(`channels/web already exists — left it untouched`);\n return;\n }\n prompter.log.success(`Scaffolded ${result.targetPath}`);\n prompter.section(\"Next steps\", [\n `cd ${result.targetPath.replace(process.cwd() + \"/\", \"\")}`,\n \"npm install\",\n \"cp .env.local.example .env.local\",\n \"npm run dev\",\n ]);\n } catch (err) {\n prompter.log.error(err instanceof Error ? err.message : String(err));\n }\n}\n\nasync function maybePromptProviderKey(\n prompter: Prompter,\n targetDir: string,\n modelId: string,\n): Promise<void> {\n if (process.env.CENCORI_API_KEY) return;\n const info = providerEnvVar(modelId);\n if (info === undefined) return;\n if (isProviderKeySet(targetDir, info.envVar)) return;\n\n const key = await prompter.text({\n message: `Paste your ${info.envVar} for ${info.provider} (optional — skip with Enter)`,\n mask: true,\n });\n if (key === undefined || key.length === 0) return;\n\n const envPath = join(targetDir, \".env.local\");\n uncommentEnvLine(envPath, info.envVar, key);\n process.env[info.envVar] = key;\n prompter.log.success(`Wrote ${info.envVar} to .env.local`);\n}\n\ntype InitMode = \"fresh\" | \"resume\";\ntype ExistingDirectoryDecision = \"overwrite\" | \"resume\" | \"cancel\";\n\nasync function resolveExistingDirectory(\n prompter: Prompter,\n targetDir: string,\n): Promise<ExistingDirectoryDecision> {\n const looksArcie = looksLikeArcieProject(targetDir);\n const message = looksArcie\n ? `${targetDir} already contains an arcie project.`\n : `${targetDir} is not empty.`;\n const options = looksArcie\n ? ([\n {\n value: \"resume\" as const,\n label: \"Resume — keep files, reconfigure model + API key\",\n },\n {\n value: \"overwrite\" as const,\n label: \"Overwrite — delete existing files and scaffold fresh\",\n },\n { value: \"cancel\" as const, label: \"Cancel\" },\n ])\n : ([\n {\n value: \"overwrite\" as const,\n label: \"Overwrite — delete existing files and scaffold fresh\",\n },\n {\n value: \"resume\" as const,\n label: \"Keep existing files and reconfigure\",\n },\n { value: \"cancel\" as const, label: \"Cancel\" },\n ]);\n const choice = await prompter.select({ message, options });\n return choice ?? \"cancel\";\n}\n\nasync function runInit(\n prompter: Prompter,\n input: { targetDir: string; templateDir: string; name: string | undefined; mode: InitMode },\n): Promise<void> {\n const { targetDir, templateDir, name, mode } = input;\n\n const needsScaffold = mode === \"fresh\" || !looksLikeArcieProject(targetDir);\n if (needsScaffold) {\n const scaffold = prompter.spinner(`Creating agent in ${targetDir}`);\n copyTemplate(templateDir, targetDir);\n if (name) updatePackageJson(targetDir, name);\n scaffold.stop({ kind: \"success\", message: `Created agent in ${targetDir}` });\n } else {\n prompter.log.info(`Resuming setup for ${targetDir}`);\n }\n\n const nodeModules = join(targetDir, \"node_modules\");\n if (existsSync(nodeModules)) {\n prompter.log.info(\"Dependencies already installed\");\n } else {\n const install = prompter.spinner(\"Installing dependencies\");\n try {\n await runNpmInstall(targetDir);\n install.stop({ kind: \"success\", message: \"Installed dependencies\" });\n } catch {\n install.stop({ kind: \"warning\", message: \"Dependency install skipped\" });\n }\n }\n\n if (detectEnvKey() === null) {\n const key = await prompter.text({\n message: \"Paste your CENCORI_API_KEY (optional — skip with Enter)\",\n mask: true,\n });\n if (key !== undefined && key.length > 0) {\n uncommentEnvLine(join(targetDir, \".env.local\"), \"CENCORI_API_KEY\", key);\n process.env.CENCORI_API_KEY = key;\n prompter.log.success(\"Wrote CENCORI_API_KEY to .env.local\");\n }\n }\n\n const catalogSpinner = prompter.spinner(\"Loading model catalog\");\n const catalog = await fetchModelCatalog();\n catalogSpinner.stop();\n\n const modelId = await prompter.searchableSelect({\n message: \"What model would you like to use?\",\n placeholder: \"type to filter…\",\n options: catalog.map((model: CatalogModel) => ({\n value: model.id,\n label: model.name,\n description: model.provider,\n })),\n });\n if (modelId === undefined) return;\n writeAgentModel(targetDir, modelId);\n prompter.log.success(`Set model to ${modelId}`);\n\n await maybePromptProviderKey(prompter, targetDir, modelId);\n\n const diagnostics = checkProviderKeys(targetDir, modelId);\n if (diagnostics.length > 0) {\n prompter.section(\n \"Checks\",\n diagnostics.map(\n (d) => `${d.severity === \"info\" ? \"✓\" : \"⚠\"} ${d.message}${d.fix ? ` — ${d.fix}` : \"\"}`,\n ),\n );\n }\n\n await maybeScaffoldWebChat(prompter, targetDir);\n\n const action = await prompter.select({\n message: \"How would you like to continue?\",\n options: [\n { value: \"chat\", label: `Start arcie dev — talk to '${name ?? \".\"}' in your terminal` },\n { value: \"server\", label: \"Start arcie dev server\" },\n { value: \"code\", label: \"Open in editor\" },\n ],\n });\n if (action === undefined) return;\n\n switch (action) {\n case \"chat\": {\n const { devCommand } = await import(\"./dev\");\n prompter.stop();\n await devCommand({ port: \"3000\", agentDir: targetDir, input: true });\n break;\n }\n case \"server\": {\n const { devCommand } = await import(\"./dev\");\n prompter.stop();\n await devCommand({ port: \"3000\", agentDir: targetDir, input: false });\n break;\n }\n case \"code\": {\n prompter.log.info(`cd ${name ?? \".\"} && code .`);\n break;\n }\n }\n}\n","export interface CatalogModel {\n id: string;\n name: string;\n provider: string;\n}\n\nconst MODELS: CatalogModel[] = [\n { id: \"anthropic/claude-sonnet-5\", name: \"Claude Sonnet 5\", provider: \"Anthropic\" },\n { id: \"anthropic/claude-opus-4.8\", name: \"Claude Opus 4.8\", provider: \"Anthropic\" },\n { id: \"anthropic/claude-haiku-4\", name: \"Claude Haiku 4\", provider: \"Anthropic\" },\n { id: \"openai/gpt-5.5\", name: \"GPT 5.5\", provider: \"OpenAI\" },\n { id: \"openai/gpt-5\", name: \"GPT 5\", provider: \"OpenAI\" },\n { id: \"openai/gpt-4.5\", name: \"GPT 4.5\", provider: \"OpenAI\" },\n { id: \"openai/gpt-4o\", name: \"GPT 4o\", provider: \"OpenAI\" },\n { id: \"openai/o3\", name: \"o3\", provider: \"OpenAI\" },\n { id: \"openai/o4-mini\", name: \"o4-mini\", provider: \"OpenAI\" },\n { id: \"google/gemini-2.5-pro\", name: \"Gemini 2.5 Pro\", provider: \"Google\" },\n { id: \"google/gemini-2.5-flash\", name: \"Gemini 2.5 Flash\", provider: \"Google\" },\n { id: \"google/gemini-2.0-flash\", name: \"Gemini 2.0 Flash\", provider: \"Google\" },\n { id: \"meta/llama-4\", name: \"Llama 4\", provider: \"Meta\" },\n { id: \"meta/llama-3.3-70b\", name: \"Llama 3.3 70B\", provider: \"Meta\" },\n { id: \"meta/llama-3.1-405b\", name: \"Llama 3.1 405B\", provider: \"Meta\" },\n { id: \"mistral/mistral-large-4\", name: \"Mistral Large 4\", provider: \"Mistral\" },\n { id: \"mistral/mistral-small-4\", name: \"Mistral Small 4\", provider: \"Mistral\" },\n { id: \"deepseek/deepseek-v4\", name: \"DeepSeek V4\", provider: \"DeepSeek\" },\n { id: \"deepseek/deepseek-r1\", name: \"DeepSeek R1\", provider: \"DeepSeek\" },\n { id: \"groq/llama-3.3-70b-versatile\", name: \"Llama 3.3 70B Versatile\", provider: \"Groq\" },\n { id: \"groq/llama-3.1-8b-instant\", name: \"Llama 3.1 8B Instant\", provider: \"Groq\" },\n];\n\nexport async function fetchModelCatalog(): Promise<CatalogModel[]> {\n await new Promise((r) => setTimeout(r, 600 + Math.random() * 400));\n return MODELS;\n}","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { grey, dim, yellow, green } from \"../style\";\n\nexport interface Diagnostic {\n severity: \"info\" | \"warn\" | \"error\";\n message: string;\n fix?: string;\n}\n\nconst PROVIDER_KEY_MAP: Record<string, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n groq: \"GROQ_API_KEY\",\n deepseek: \"DEEPSEEK_API_KEY\",\n mistral: \"MISTRAL_API_KEY\",\n google: \"GOOGLE_API_KEY\",\n meta: \"TOGETHER_API_KEY\",\n};\n\nexport function providerEnvVar(modelId: string): { provider: string; envVar: string } | undefined {\n const provider = modelId.split(\"/\")[0];\n if (!provider) return undefined;\n const envVar = PROVIDER_KEY_MAP[provider];\n return envVar ? { provider, envVar } : undefined;\n}\n\nexport function isProviderKeySet(agentDir: string, envVar: string): boolean {\n if (process.env[envVar] && process.env[envVar] !== \"\") return true;\n const envPath = join(agentDir, \"..\", \".env.local\");\n if (!existsSync(envPath)) return false;\n const content = readFileSync(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(`${envVar}=`) && !trimmed.startsWith(`#`)) return true;\n }\n return false;\n}\n\nexport function checkProviderKeys(agentDir: string, modelId: string): Diagnostic[] {\n const results: Diagnostic[] = [];\n\n const info = providerEnvVar(modelId);\n if (info === undefined) return results;\n const { provider, envVar } = info;\n\n const envPath = join(agentDir, \"..\", \".env.local\");\n let hasKey = false;\n\n if (existsSync(envPath)) {\n const content = readFileSync(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(`${envVar}=`) && !trimmed.startsWith(`#`)) {\n hasKey = true;\n break;\n }\n }\n }\n\n const inProcess = process.env[envVar] && process.env[envVar] !== \"\";\n\n if (!hasKey && !inProcess) {\n results.push({\n severity: \"warn\",\n message: `Missing ${envVar} for provider: ${provider}`,\n fix: `Add ${envVar}=your_key_here to .env.local`,\n });\n } else {\n results.push({\n severity: \"info\",\n message: `${envVar} is configured`,\n });\n }\n\n return results;\n}\n\nexport function printDiagnostics(diagnostics: Diagnostic[]): void {\n for (const d of diagnostics) {\n const icon = d.severity === \"error\" ? yellow(\"\\u26A0\")\n : d.severity === \"warn\" ? yellow(\"\\u26A0\")\n : grey(\"\\u2713\");\n const msg = d.severity === \"error\" ? yellow(d.message)\n : d.severity === \"warn\" ? yellow(d.message)\n : grey(d.message);\n console.log(` ${icon} ${msg}`);\n if (d.fix) {\n console.log(` ${dim(d.fix)}`);\n }\n }\n}\n","import type { SelectOption } from \"./prompter\";\n\nexport interface SelectState<T = string> {\n readonly options: readonly SelectOption<T>[];\n readonly matches: readonly SelectOption<T>[];\n readonly selectedIndex: number;\n readonly query: string;\n}\n\nexport interface CreateSelectStateOptions<T> {\n readonly initialValue?: T;\n readonly query?: string;\n}\n\nexport function createSelectState<T>(\n options: readonly SelectOption<T>[],\n init: CreateSelectStateOptions<T> = {},\n): SelectState<T> {\n const query = init.query ?? \"\";\n const matches = filterOptions(options, query);\n const initial = init.initialValue;\n const selectedIndex =\n initial === undefined\n ? 0\n : Math.max(\n 0,\n matches.findIndex((option) => option.value === initial),\n );\n return { options, matches, selectedIndex, query };\n}\n\nexport function setQuery<T>(state: SelectState<T>, query: string): SelectState<T> {\n if (query === state.query) return state;\n const matches = filterOptions(state.options, query);\n const carried = state.matches[state.selectedIndex];\n const carriedIndex =\n carried === undefined\n ? -1\n : matches.findIndex((option) => option.value === carried.value);\n return {\n options: state.options,\n matches,\n selectedIndex: carriedIndex >= 0 ? carriedIndex : 0,\n query,\n };\n}\n\nexport function moveUp<T>(state: SelectState<T>): SelectState<T> {\n const count = state.matches.length;\n if (count === 0) return state;\n const selectedIndex = (state.selectedIndex - 1 + count) % count;\n return { ...state, selectedIndex };\n}\n\nexport function moveDown<T>(state: SelectState<T>): SelectState<T> {\n const count = state.matches.length;\n if (count === 0) return state;\n const selectedIndex = (state.selectedIndex + 1) % count;\n return { ...state, selectedIndex };\n}\n\nexport function selected<T>(state: SelectState<T>): SelectOption<T> | undefined {\n return state.matches[state.selectedIndex];\n}\n\nfunction filterOptions<T>(\n options: readonly SelectOption<T>[],\n query: string,\n): readonly SelectOption<T>[] {\n const normalized = query.trim().toLowerCase();\n if (normalized.length === 0) return options;\n return options.filter((option) => optionMatches(option, normalized));\n}\n\nfunction optionMatches<T>(option: SelectOption<T>, query: string): boolean {\n if (option.label.toLowerCase().includes(query)) return true;\n if (option.description !== undefined && option.description.toLowerCase().includes(query)) {\n return true;\n }\n return false;\n}\n","import { EMPTY_LINE, applyLineEditorKey, lineOf, type LineState } from \"../tui/line-editor\";\nimport type { TerminalKey } from \"../tui/key-parser\";\n\nexport interface TextInputState {\n readonly line: LineState;\n readonly message: string;\n readonly placeholder?: string;\n readonly mask: boolean;\n readonly validate?: (value: string) => string | undefined;\n readonly validationError?: string;\n}\n\nexport interface CreateTextInputOptions {\n readonly message: string;\n readonly placeholder?: string;\n readonly defaultValue?: string;\n readonly mask?: boolean;\n readonly validate?: (value: string) => string | undefined;\n}\n\nexport function createTextInputState(options: CreateTextInputOptions): TextInputState {\n const line = options.defaultValue ? lineOf(options.defaultValue) : EMPTY_LINE;\n const state: TextInputState = {\n line,\n message: options.message,\n mask: options.mask ?? false,\n };\n const result: TextInputState = { ...state };\n if (options.placeholder !== undefined) (result as { placeholder?: string }).placeholder = options.placeholder;\n if (options.validate !== undefined) (result as { validate?: (v: string) => string | undefined }).validate = options.validate;\n return result;\n}\n\nexport function applyTextInputKey(\n state: TextInputState,\n key: TerminalKey,\n): TextInputState | undefined {\n const nextLine = applyLineEditorKey(state.line, key, { multiline: false });\n if (nextLine === undefined) return undefined;\n const next: TextInputState = { ...state, line: nextLine };\n if (state.validationError !== undefined) {\n (next as { validationError?: string }).validationError = undefined;\n }\n return next;\n}\n\n/**\n * Runs the caller-supplied `validate` against the current draft and returns\n * a state carrying the error message, or `undefined` when the draft is\n * accepted. Submit paths call this and only complete the promise when it\n * returns `undefined`.\n */\nexport function validateTextInput(state: TextInputState): TextInputState | undefined {\n if (state.validate === undefined) return undefined;\n const error = state.validate(state.line.text);\n if (error === undefined) return undefined;\n return { ...state, validationError: error };\n}\n","import type { Theme } from \"../tui/theme\";\nimport { maskLine, visibleLine } from \"../tui/line-editor\";\nimport { inputTextWidth, sliceVisible, visibleLength } from \"../ui/terminal-text\";\nimport type { SelectState } from \"./select-input\";\nimport type { TextInputState } from \"./text-input\";\n\nexport interface PanelCaret {\n readonly row: number;\n readonly col: number;\n}\n\nexport interface PanelFrame {\n readonly rows: string[];\n readonly caret: PanelCaret;\n}\n\n/** Maximum select options rendered at once; longer lists window around the highlight. */\nconst MAX_VISIBLE_OPTIONS = 10;\n\nexport function renderTextPanel(\n state: TextInputState,\n theme: Theme,\n width: number,\n): PanelFrame {\n const c = theme.colors;\n const rows: string[] = [renderHeader(state.message, theme, width)];\n\n const promptGlyph = c.cyan(theme.glyph.prompt);\n const promptWidth = visibleLength(`${theme.glyph.prompt} `);\n const budget = Math.max(1, width - promptWidth - 2);\n\n const displayLine = state.mask ? maskLine(state.line) : state.line;\n const view = visibleLine(displayLine, budget);\n const under = view.under.length === 0 ? \" \" : view.under;\n const inputRow = ` ${promptGlyph} ${view.before}${c.inverse(under)}${view.after}`;\n\n if (state.line.text.length === 0 && state.placeholder !== undefined) {\n const placeholder = c.dim(sliceVisible(state.placeholder, budget));\n rows.push(` ${promptGlyph} ${c.inverse(\" \")}${placeholder}`);\n } else {\n rows.push(inputRow);\n }\n\n if (state.validationError !== undefined) {\n rows.push(` ${c.red(theme.glyph.error)} ${c.red(state.validationError)}`);\n }\n\n const caretCol =\n 3 + promptWidth + inputTextWidth(state.mask ? maskLine(state.line).text.slice(0, maskLine(state.line).cursor) : view.before);\n\n return { rows, caret: { row: 2, col: caretCol } };\n}\n\nexport function renderSelectPanel<T>(\n state: SelectState<T>,\n message: string,\n theme: Theme,\n width: number,\n options: { searchable?: boolean; placeholder?: string } = {},\n): PanelFrame {\n const rows: string[] = [renderHeader(message, theme, width)];\n let caretRow = 1;\n let caretCol = 3;\n\n if (options.searchable === true) {\n const budget = Math.max(1, width - 4);\n const [filterRow, cursorCol] = renderFilterRow(state.query, budget, theme, options.placeholder);\n rows.push(filterRow);\n caretRow = 2;\n caretCol = cursorCol;\n }\n\n rows.push(...renderOptionRows(state, theme, width));\n return { rows, caret: { row: caretRow, col: caretCol } };\n}\n\nfunction renderHeader(message: string, theme: Theme, width: number): string {\n const c = theme.colors;\n const glyph = c.yellow(c.bold(theme.glyph.question));\n const trimmed = message.trim();\n const budget = Math.max(1, width - 2);\n return `${glyph} ${c.bold(sliceVisible(trimmed, budget))}`;\n}\n\nfunction renderFilterRow(\n query: string,\n budget: number,\n theme: Theme,\n placeholder: string | undefined,\n): [string, number] {\n const c = theme.colors;\n const prefix = ` ${c.dim(\"/\")} `;\n const prefixWidth = visibleLength(prefix);\n const empty = query.length === 0;\n const visible = empty\n ? placeholder !== undefined\n ? c.dim(sliceVisible(placeholder, budget))\n : c.dim(sliceVisible(\"type to filter\", budget))\n : sliceVisible(query, budget);\n const row = `${prefix}${empty ? c.inverse(\" \") : `${visible}${c.inverse(\" \")}`}`;\n const caretCol = 1 + prefixWidth + (empty ? 0 : visibleLength(visible));\n return [row, caretCol];\n}\n\nfunction renderOptionRows<T>(state: SelectState<T>, theme: Theme, width: number): string[] {\n const c = theme.colors;\n if (state.matches.length === 0) {\n return [` ${c.dim(\"no matches\")}`];\n }\n\n const count = state.matches.length;\n const viewSize = Math.min(count, MAX_VISIBLE_OPTIONS);\n const start = Math.max(\n 0,\n Math.min(state.selectedIndex - Math.floor(viewSize / 2), count - viewSize),\n );\n const end = Math.min(start + viewSize, count);\n const visible = state.matches.slice(start, end);\n\n const labelWidth = Math.min(\n Math.max(...visible.map((option) => visibleLength(option.label))),\n Math.max(8, Math.floor(width * 0.5)),\n );\n\n return visible.map((option, offset) => {\n const isCursor = start + offset === state.selectedIndex;\n const pointer = isCursor ? c.cyan(theme.glyph.selectedPointer) : \" \";\n const label = padLabel(option.label, labelWidth);\n const styledLabel = isCursor ? c.bold(label) : label;\n const description =\n option.description !== undefined ? ` ${c.dim(option.description)}` : \"\";\n return `${pointer} ${styledLabel}${description}`;\n });\n}\n\nfunction padLabel(label: string, width: number): string {\n const visible = visibleLength(label);\n if (visible >= width) return sliceVisible(label, width);\n return `${label}${\" \".repeat(width - visible)}`;\n}\n\n","import { LiveRegion, type TerminalOutput } from \"../tui/renderer/live-region\";\nimport { attachKeyStream } from \"../tui/attach-keys\";\nimport { createTheme, detectUnicode, type Theme } from \"../tui/theme\";\nimport type { TerminalKey } from \"../tui/key-parser\";\nimport { applyLineEditorKey } from \"../tui/line-editor\";\nimport { renderCliTaggedLine } from \"../ui/output\";\nimport type {\n AcknowledgePromptOptions,\n Prompter,\n SelectPromptOptions,\n SearchableSelectPromptOptions,\n SpinnerHandle,\n SpinnerOutcome,\n TextPromptOptions,\n} from \"./prompter\";\nimport {\n createSelectState,\n moveDown,\n moveUp,\n selected,\n setQuery,\n type SelectState,\n} from \"./select-input\";\nimport {\n applyTextInputKey,\n createTextInputState,\n validateTextInput,\n type TextInputState,\n} from \"./text-input\";\nimport { renderSelectPanel, renderTextPanel } from \"./setup-panel\";\n\nexport interface TuiPrompterOptions {\n readonly output?: TerminalOutput;\n readonly theme?: Theme;\n readonly spinnerIntervalMs?: number;\n}\n\nexport function createTuiPrompter(options: TuiPrompterOptions = {}): Prompter {\n return new TuiPrompter(options);\n}\n\nclass TuiPrompter implements Prompter {\n readonly #region: LiveRegion;\n readonly #theme: Theme;\n readonly #spinnerIntervalMs: number;\n #detachKeys: (() => void) | undefined;\n #spinnerTimer: ReturnType<typeof setInterval> | undefined;\n #spinnerFrame = 0;\n #spinnerMessage = \"\";\n #stopped = false;\n\n constructor(options: TuiPrompterOptions) {\n this.#theme = options.theme ?? createTheme({ unicode: detectUnicode() });\n this.#region = new LiveRegion({ output: options.output });\n this.#spinnerIntervalMs = options.spinnerIntervalMs ?? 90;\n }\n\n get log() {\n return {\n info: (message: string) => this.#writeTagged(\"info\", message, \"info\"),\n success: (message: string) => this.#writeTagged(\"ok\", message, \"success\"),\n warning: (message: string) => this.#writeTagged(\"warn\", message, \"warning\"),\n error: (message: string) => this.#writeTagged(\"error\", message, \"danger\"),\n };\n }\n\n section(title: string, lines: readonly string[]): void {\n const c = this.#theme.colors;\n const rows = [c.bold(c.cyan(title))];\n for (const line of lines) rows.push(` ${c.dim(line)}`);\n this.#region.commit(rows);\n }\n\n spinner(message: string): SpinnerHandle {\n this.#stopSpinner();\n this.#spinnerMessage = message;\n this.#spinnerFrame = 0;\n this.#paintSpinner();\n this.#spinnerTimer = setInterval(() => {\n this.#spinnerFrame += 1;\n this.#paintSpinner();\n }, this.#spinnerIntervalMs);\n this.#spinnerTimer.unref?.();\n\n return {\n update: (next: string) => {\n this.#spinnerMessage = next;\n this.#paintSpinner();\n },\n stop: (outcome: SpinnerOutcome = { kind: \"silent\" }) => {\n this.#stopSpinner();\n this.#region.paint([], { row: 1, col: 1 });\n if (outcome.kind === \"silent\") return;\n this.#writeTagged(\n outcome.kind === \"success\" ? \"ok\" : outcome.kind === \"warning\" ? \"warn\" : \"error\",\n outcome.message ?? this.#spinnerMessage,\n outcome.kind === \"success\"\n ? \"success\"\n : outcome.kind === \"warning\"\n ? \"warning\"\n : \"danger\",\n );\n },\n };\n }\n\n async text(options: TextPromptOptions): Promise<string | undefined> {\n this.#stopSpinner();\n return this.#runPrompt<string>((resolve) => {\n let state: TextInputState = createTextInputState(options);\n const paint = () => this.#paintFrame(this.#textFrame(state));\n paint();\n return (key) => {\n if (key.type === \"ctrl-c\" || key.type === \"escape\") {\n resolve(undefined);\n return \"end\";\n }\n if (key.type === \"enter\") {\n const failed = validateTextInput(state);\n if (failed !== undefined) {\n state = failed;\n paint();\n return \"continue\";\n }\n resolve(state.line.text);\n return \"end\";\n }\n const next = applyTextInputKey(state, key);\n if (next !== undefined) {\n state = next;\n paint();\n }\n return \"continue\";\n };\n });\n }\n\n async select<T>(options: SelectPromptOptions<T>): Promise<T | undefined> {\n return this.#selectImpl(options, false);\n }\n\n async searchableSelect<T>(options: SearchableSelectPromptOptions<T>): Promise<T | undefined> {\n return this.#selectImpl(options, true);\n }\n\n async acknowledge(options: AcknowledgePromptOptions): Promise<void> {\n this.#stopSpinner();\n await this.#runPrompt<void>((resolve) => {\n const c = this.#theme.colors;\n const rows = [`${c.yellow(c.bold(this.#theme.glyph.question))} ${c.bold(options.message)}`];\n for (const line of options.lines ?? []) rows.push(` ${c.dim(line)}`);\n rows.push(` ${c.dim(\"press enter to continue\")}`);\n this.#region.paint(rows, { row: rows.length, col: 1 });\n return (key) => {\n if (key.type === \"ctrl-c\" || key.type === \"escape\" || key.type === \"enter\") {\n resolve();\n return \"end\";\n }\n return \"continue\";\n };\n });\n }\n\n stop(): void {\n if (this.#stopped) return;\n this.#stopped = true;\n this.#stopSpinner();\n this.#detachKeys?.();\n this.#detachKeys = undefined;\n this.#region.stop();\n }\n\n async #selectImpl<T>(\n options: SelectPromptOptions<T> | SearchableSelectPromptOptions<T>,\n searchable: boolean,\n ): Promise<T | undefined> {\n this.#stopSpinner();\n return this.#runPrompt<T>((resolve) => {\n let state: SelectState<T> = createSelectState(options.options, {\n initialValue: options.initialValue,\n });\n const placeholder =\n \"placeholder\" in options ? (options as SearchableSelectPromptOptions<T>).placeholder : undefined;\n const paint = () =>\n this.#paintFrame(\n renderSelectPanel(state, options.message, this.#theme, this.#region.columns, {\n searchable,\n placeholder,\n }),\n );\n paint();\n return (key) => {\n if (key.type === \"ctrl-c\" || key.type === \"escape\") {\n resolve(undefined);\n return \"end\";\n }\n if (key.type === \"enter\") {\n const chosen = selected(state);\n if (chosen === undefined) return \"continue\";\n resolve(chosen.value);\n return \"end\";\n }\n if (key.type === \"up\" || key.type === \"ctrl-p\") {\n state = moveUp(state);\n paint();\n return \"continue\";\n }\n if (key.type === \"down\" || key.type === \"ctrl-n\") {\n state = moveDown(state);\n paint();\n return \"continue\";\n }\n if (searchable) {\n if (key.type === \"backspace\") {\n state = setQuery(state, state.query.slice(0, -1));\n paint();\n return \"continue\";\n }\n if (key.type === \"text\") {\n state = setQuery(state, `${state.query}${key.value}`);\n paint();\n return \"continue\";\n }\n const draft = applyLineEditorKey({ text: state.query, cursor: state.query.length }, key, {\n multiline: false,\n });\n if (draft !== undefined && draft.text !== state.query) {\n state = setQuery(state, draft.text);\n paint();\n }\n }\n return \"continue\";\n };\n });\n }\n\n #runPrompt<T>(\n setup: (resolve: (value: T | undefined) => void) => (key: TerminalKey) => \"continue\" | \"end\",\n ): Promise<T | undefined> {\n return new Promise<T | undefined>((resolve) => {\n let done = false;\n const detach = attachKeyStream((key) => {\n if (done) return;\n const outcome = onKey(key);\n if (outcome === \"end\") {\n done = true;\n this.#detachKeys?.();\n this.#detachKeys = undefined;\n this.#region.paint([], { row: 1, col: 1 });\n }\n });\n this.#detachKeys = detach;\n const onKey = setup((value) => resolve(value));\n });\n }\n\n #textFrame(state: TextInputState) {\n return renderTextPanel(state, this.#theme, this.#region.columns);\n }\n\n #paintFrame(frame: { rows: string[]; caret: { row: number; col: number } }): void {\n this.#region.paint(frame.rows, frame.caret);\n }\n\n #paintSpinner(): void {\n const glyph = this.#theme.spinner[this.#spinnerFrame % this.#theme.spinner.length] ?? \"-\";\n const row = `${this.#theme.colors.cyan(glyph)} ${this.#spinnerMessage}`;\n this.#region.paint([row], { row: 1, col: 1 });\n }\n\n #stopSpinner(): void {\n if (this.#spinnerTimer !== undefined) {\n clearInterval(this.#spinnerTimer);\n this.#spinnerTimer = undefined;\n }\n }\n\n #writeTagged(tag: string, message: string, tone: \"info\" | \"success\" | \"warning\" | \"danger\"): void {\n const themeAdapter = {\n accent: this.#theme.colors.cyan,\n color: this.#theme.color,\n danger: this.#theme.colors.red,\n heading: (t: string) => this.#theme.colors.bold(this.#theme.colors.cyan(t)),\n info: this.#theme.colors.blue,\n label: this.#theme.colors.bold,\n muted: this.#theme.colors.dim,\n plain: (t: string) => t,\n subagent: this.#theme.colors.orange,\n success: this.#theme.colors.green,\n warning: this.#theme.colors.yellow,\n };\n this.#region.commit([\n renderCliTaggedLine(themeAdapter, { message, tag, tone }),\n ]);\n }\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","resolve","existsSync","join","readFileSync","resolve"]}
@@ -1,4 +1,4 @@
1
- import { I as InstructionsConfig } from '../types-BxOQTzx1.js';
1
+ import { I as InstructionsConfig } from '../types-DwxwdSa2.js';
2
2
  import 'zod';
3
3
 
4
4
  declare function defineInstructions(source: string | InstructionsConfig): InstructionsConfig;
@@ -1,4 +1,4 @@
1
- export { f as RunOptions, g as RunResult, r as runAgent, s as streamAgent } from '../index-b4NcDl3m.js';
2
- import '../types-BxOQTzx1.js';
1
+ export { f as RunOptions, g as RunResult, r as runAgent, s as streamAgent } from '../index-CElLRVTP.js';
2
+ import '../types-DwxwdSa2.js';
3
3
  import '../protocol/events.js';
4
4
  import 'zod';
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  runAgent,
3
3
  streamAgent
4
- } from "../chunk-2ZSKZYWK.js";
4
+ } from "../chunk-4WSILP75.js";
5
+ import "../chunk-KVSX4MLK.js";
5
6
  import "../chunk-QE46QFID.js";
6
7
  import "../chunk-PHTJLJOV.js";
7
8
  export {
@@ -0,0 +1,7 @@
1
+ import {
2
+ scaffoldWebChat
3
+ } from "./chunk-SGWQZZ2A.js";
4
+ export {
5
+ scaffoldWebChat
6
+ };
7
+ //# sourceMappingURL=scaffold-web-chat-OQL5C2GK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,4 +1,4 @@
1
- import { a as ScheduleConfig } from '../types-BxOQTzx1.js';
1
+ import { i as ScheduleConfig } from '../types-DwxwdSa2.js';
2
2
  import 'zod';
3
3
 
4
4
  declare function defineSchedule(config: ScheduleConfig): ScheduleConfig;
@@ -1,4 +1,4 @@
1
- import { b as SkillConfig } from '../types-BxOQTzx1.js';
1
+ import { k as SkillConfig } from '../types-DwxwdSa2.js';
2
2
  import 'zod';
3
3
 
4
4
  declare function defineSkill(config: SkillConfig): SkillConfig;
@@ -1,4 +1,4 @@
1
- import { c as ToolConfig } from '../types-BxOQTzx1.js';
1
+ import { T as ToolConfig } from '../types-DwxwdSa2.js';
2
2
  import 'zod';
3
3
 
4
4
  declare function defineTool<TInput = unknown, TOutput = unknown>(config: ToolConfig<TInput, TOutput>): ToolConfig<TInput, TOutput>;
@@ -148,4 +148,4 @@ interface Session {
148
148
  metadata: Record<string, unknown>;
149
149
  }
150
150
 
151
- export type { AgentConfig as A, ChannelConfig as C, HookConfig as H, InstructionsConfig as I, PolicyConfig as P, Session as S, TurnContext as T, ScheduleConfig as a, SkillConfig as b, ToolConfig as c, ConnectionConfig as d, SessionConfig as e, AgentManifest as f, ApprovalStrategy as g, CencoriConfig as h, ChannelRequest as i, ChannelResponse as j, HookEvent as k, HookPayload as l, SubagentManifest as m, ToolCallContext as n };
151
+ export type { AgentManifest as A, ConnectionConfig as C, HookConfig as H, InstructionsConfig as I, PolicyConfig as P, SessionConfig as S, ToolConfig as T, AgentConfig as a, ApprovalStrategy as b, CencoriConfig as c, ChannelConfig as d, ChannelRequest as e, ChannelResponse as f, HookEvent as g, HookPayload as h, ScheduleConfig as i, Session as j, SkillConfig as k, SubagentManifest as l, ToolCallContext as m, TurnContext as n };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "arcie",
3
3
  "type": "module",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "description": "The electronic line — build agents at the speed of light.",
6
6
  "bin": {
7
7
  "arcie": "dist/cli/index.js"
@@ -0,0 +1,12 @@
1
+ # Cencori AI Gateway
2
+ # CENCORI_API_KEY=
3
+ # CENCORI_PROJECT_ID=
4
+
5
+ # Or bring your own provider (local dev server needs at least one)
6
+ # OPENAI_API_KEY=
7
+ # ANTHROPIC_API_KEY=
8
+ # GROQ_API_KEY=
9
+ # DEEPSEEK_API_KEY=
10
+ # MISTRAL_API_KEY=
11
+ # GOOGLE_API_KEY=
12
+ # TOGETHER_API_KEY=
@@ -0,0 +1,4 @@
1
+ # URL of the arcie dev / production HTTP server. The Next.js server route
2
+ # proxies chat messages to this URL. Keep this server-side only (no NEXT_PUBLIC_
3
+ # prefix) so the browser never talks to arcie directly.
4
+ ARCIE_URL=http://localhost:3000
@@ -0,0 +1,51 @@
1
+ # web-chat
2
+
3
+ A Next.js chat UI for your arcie agent. Talks to arcie's HTTP server through a
4
+ server route so the browser never sees the agent's endpoint directly.
5
+
6
+ ## Setup
7
+
8
+ ```bash
9
+ cp .env.local.example .env.local
10
+ # edit ARCIE_URL if your agent isn't on http://localhost:3000
11
+
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
16
+ Open http://localhost:3001 in a browser. In another terminal, start your agent:
17
+
18
+ ```bash
19
+ # from the parent agent directory
20
+ arcie dev
21
+ ```
22
+
23
+ ## Deploy
24
+
25
+ ```bash
26
+ # Vercel
27
+ npx vercel
28
+
29
+ # Any Node host
30
+ npm run build && npm start
31
+ ```
32
+
33
+ Set `ARCIE_URL` in the host's environment to point at your deployed agent's
34
+ HTTP endpoint.
35
+
36
+ ## Customize
37
+
38
+ Everything is yours to edit:
39
+
40
+ - `app/page.tsx` — the chat page
41
+ - `components/chat.tsx` — the main chat container
42
+ - `components/message.tsx` — user + assistant message bubbles
43
+ - `components/tool-call.tsx` — how tool calls render
44
+ - `app/globals.css` — theme tokens (colors, radius)
45
+ - `tailwind.config.ts` — Tailwind config
46
+
47
+ Add more shadcn components:
48
+
49
+ ```bash
50
+ npx shadcn@latest add card avatar
51
+ ```
@@ -0,0 +1,43 @@
1
+ import { NextRequest } from "next/server";
2
+
3
+ export const runtime = "nodejs";
4
+ export const dynamic = "force-dynamic";
5
+
6
+ const ARCIE_URL = process.env.ARCIE_URL ?? "http://localhost:3000";
7
+
8
+ export async function POST(req: NextRequest) {
9
+ const { message } = (await req.json()) as { message: string };
10
+ if (typeof message !== "string" || message.length === 0) {
11
+ return new Response("message required", { status: 400 });
12
+ }
13
+
14
+ let upstream: Response;
15
+ try {
16
+ upstream = await fetch(ARCIE_URL, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify({ message, stream: true }),
20
+ });
21
+ } catch (error) {
22
+ return Response.json(
23
+ {
24
+ error: "Could not reach arcie server",
25
+ detail: error instanceof Error ? error.message : String(error),
26
+ hint: `Is arcie running at ${ARCIE_URL}? Start it with \`arcie dev\` in the agent directory.`,
27
+ },
28
+ { status: 502 },
29
+ );
30
+ }
31
+
32
+ if (!upstream.ok || upstream.body === null) {
33
+ return new Response(await upstream.text(), { status: upstream.status });
34
+ }
35
+
36
+ return new Response(upstream.body, {
37
+ headers: {
38
+ "Content-Type": "application/x-ndjson",
39
+ "Cache-Control": "no-cache, no-transform",
40
+ Connection: "keep-alive",
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,65 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 240 10% 3.9%;
9
+ --card: 0 0% 100%;
10
+ --card-foreground: 240 10% 3.9%;
11
+ --primary: 240 5.9% 10%;
12
+ --primary-foreground: 0 0% 98%;
13
+ --secondary: 240 4.8% 95.9%;
14
+ --secondary-foreground: 240 5.9% 10%;
15
+ --muted: 240 4.8% 95.9%;
16
+ --muted-foreground: 240 3.8% 46.1%;
17
+ --accent: 240 4.8% 95.9%;
18
+ --accent-foreground: 240 5.9% 10%;
19
+ --destructive: 0 84.2% 60.2%;
20
+ --destructive-foreground: 0 0% 98%;
21
+ --border: 240 5.9% 90%;
22
+ --input: 240 5.9% 90%;
23
+ --ring: 240 5.9% 10%;
24
+ --radius: 0.5rem;
25
+ }
26
+
27
+ .dark {
28
+ --background: 240 10% 3.9%;
29
+ --foreground: 0 0% 98%;
30
+ --card: 240 10% 3.9%;
31
+ --card-foreground: 0 0% 98%;
32
+ --primary: 0 0% 98%;
33
+ --primary-foreground: 240 5.9% 10%;
34
+ --secondary: 240 3.7% 15.9%;
35
+ --secondary-foreground: 0 0% 98%;
36
+ --muted: 240 3.7% 15.9%;
37
+ --muted-foreground: 240 5% 64.9%;
38
+ --accent: 240 3.7% 15.9%;
39
+ --accent-foreground: 0 0% 98%;
40
+ --destructive: 0 62.8% 30.6%;
41
+ --destructive-foreground: 0 0% 98%;
42
+ --border: 240 3.7% 15.9%;
43
+ --input: 240 3.7% 15.9%;
44
+ --ring: 240 4.9% 83.9%;
45
+ }
46
+ }
47
+
48
+ @layer base {
49
+ * {
50
+ @apply border-border;
51
+ }
52
+ html,
53
+ body {
54
+ @apply bg-background text-foreground antialiased;
55
+ min-height: 100%;
56
+ }
57
+ /* Hide Next.js dev-mode injected UI (SegmentExplorer, portals, toasts). */
58
+ nextjs-portal,
59
+ [data-nextjs-toast-wrapper],
60
+ [data-nextjs-dialog-overlay],
61
+ [data-nextjs-scroll-focus-boundary],
62
+ [data-nextjs-devtools-panel] {
63
+ display: none !important;
64
+ }
65
+ }
@@ -0,0 +1,15 @@
1
+ import type { Metadata } from "next";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "arcie",
6
+ description: "Chat with your arcie agent.",
7
+ };
8
+
9
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
10
+ return (
11
+ <html lang="en" className="h-full dark" suppressHydrationWarning>
12
+ <body className="h-full">{children}</body>
13
+ </html>
14
+ );
15
+ }
@@ -0,0 +1,9 @@
1
+ import { Chat } from "@/components/chat";
2
+
3
+ export default function Page() {
4
+ return (
5
+ <main className="h-full">
6
+ <Chat />
7
+ </main>
8
+ );
9
+ }
@@ -0,0 +1,219 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { InputBar } from "@/components/input-bar";
5
+ import { Message } from "@/components/message";
6
+ import { readArcieStream } from "@/lib/stream";
7
+ import type { UiMessage, UiToolCall } from "@/lib/types";
8
+
9
+ function newId(prefix: string): string {
10
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
11
+ }
12
+
13
+ export function Chat() {
14
+ const [messages, setMessages] = React.useState<UiMessage[]>([]);
15
+ const [streaming, setStreaming] = React.useState(false);
16
+ const abortRef = React.useRef<AbortController | undefined>(undefined);
17
+ const containerRef = React.useRef<HTMLDivElement>(null);
18
+
19
+ React.useEffect(() => {
20
+ const el = containerRef.current;
21
+ if (el === null) return;
22
+ el.scrollTop = el.scrollHeight;
23
+ }, [messages]);
24
+
25
+ const stop = () => {
26
+ abortRef.current?.abort();
27
+ setStreaming(false);
28
+ };
29
+
30
+ const clearAll = () => {
31
+ if (streaming) stop();
32
+ setMessages([]);
33
+ };
34
+
35
+ const copy = (text: string) => {
36
+ void navigator.clipboard.writeText(text);
37
+ };
38
+
39
+ const send = React.useCallback(
40
+ async (text: string, historyOverride?: UiMessage[]) => {
41
+ const base = historyOverride ?? messages;
42
+ const userMessage: UiMessage = { id: newId("u"), role: "user", content: text };
43
+ const assistantId = newId("a");
44
+ const assistantMessage: UiMessage = {
45
+ id: assistantId,
46
+ role: "assistant",
47
+ content: "",
48
+ streaming: true,
49
+ toolCalls: [],
50
+ };
51
+ const isRegeneration = historyOverride !== undefined;
52
+ setMessages((prev) => (isRegeneration ? [...base, assistantMessage] : [...prev, userMessage, assistantMessage]));
53
+ setStreaming(true);
54
+
55
+ const controller = new AbortController();
56
+ abortRef.current = controller;
57
+ const startedAt = Date.now();
58
+
59
+ const patchAssistant = (patch: (prev: UiMessage) => UiMessage) => {
60
+ setMessages((prev) =>
61
+ prev.map((message) => (message.id === assistantId ? patch(message) : message)),
62
+ );
63
+ };
64
+
65
+ try {
66
+ const response = await fetch("/api/chat", {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/json" },
69
+ body: JSON.stringify({ message: text }),
70
+ signal: controller.signal,
71
+ });
72
+ if (!response.ok) {
73
+ const body = await response.text();
74
+ patchAssistant((m) => ({
75
+ ...m,
76
+ content: body || `Server error (${response.status})`,
77
+ streaming: false,
78
+ errored: true,
79
+ }));
80
+ return;
81
+ }
82
+
83
+ const toolIndex = new Map<string, number>();
84
+
85
+ for await (const event of readArcieStream(response)) {
86
+ switch (event.type) {
87
+ case "message.appended": {
88
+ const delta = (event.data as { delta?: string }).delta ?? "";
89
+ patchAssistant((m) => ({ ...m, content: `${m.content}${delta}` }));
90
+ break;
91
+ }
92
+ case "message.completed": {
93
+ const finished = (event.data as { text?: string | null }).text;
94
+ patchAssistant((m) => ({
95
+ ...m,
96
+ content: typeof finished === "string" && finished.length > 0 ? finished : m.content,
97
+ }));
98
+ break;
99
+ }
100
+ case "reasoning.appended": {
101
+ const delta = (event.data as { delta?: string }).delta ?? "";
102
+ patchAssistant((m) => ({ ...m, reasoning: `${m.reasoning ?? ""}${delta}` }));
103
+ break;
104
+ }
105
+ case "tool.started": {
106
+ const data = event.data as { name: string; callId: string; input: unknown };
107
+ const call: UiToolCall = {
108
+ callId: data.callId,
109
+ name: data.name,
110
+ input: data.input,
111
+ status: "running",
112
+ };
113
+ patchAssistant((m) => {
114
+ const toolCalls = m.toolCalls ?? [];
115
+ toolIndex.set(data.callId, toolCalls.length);
116
+ return { ...m, toolCalls: [...toolCalls, call] };
117
+ });
118
+ break;
119
+ }
120
+ case "tool.completed": {
121
+ const data = event.data as {
122
+ callId: string;
123
+ output: unknown;
124
+ status: string;
125
+ error?: { code: string; message: string };
126
+ };
127
+ patchAssistant((m) => {
128
+ const toolCalls = [...(m.toolCalls ?? [])];
129
+ const idx = toolIndex.get(data.callId);
130
+ if (idx === undefined || toolCalls[idx] === undefined) return m;
131
+ const previous = toolCalls[idx];
132
+ const isApproval =
133
+ data.status === "pending" && data.error?.code === "needs_approval";
134
+ toolCalls[idx] = {
135
+ ...previous,
136
+ status: isApproval
137
+ ? "approval"
138
+ : data.status === "completed"
139
+ ? "done"
140
+ : "error",
141
+ output: data.output,
142
+ errorMessage: data.error?.message,
143
+ };
144
+ return { ...m, toolCalls };
145
+ });
146
+ break;
147
+ }
148
+ case "step.failed":
149
+ case "turn.failed":
150
+ case "session.failed": {
151
+ const data = event.data as { code?: string; message?: string };
152
+ patchAssistant((m) => ({
153
+ ...m,
154
+ content: data.message ?? "Something went wrong.",
155
+ streaming: false,
156
+ errored: true,
157
+ }));
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ } catch (error) {
163
+ if (!controller.signal.aborted) {
164
+ patchAssistant((m) => ({
165
+ ...m,
166
+ content: error instanceof Error ? error.message : String(error),
167
+ streaming: false,
168
+ errored: true,
169
+ }));
170
+ }
171
+ } finally {
172
+ const latencyMs = Date.now() - startedAt;
173
+ patchAssistant((m) => ({ ...m, streaming: false, latencyMs }));
174
+ setStreaming(false);
175
+ abortRef.current = undefined;
176
+ }
177
+ },
178
+ [messages],
179
+ );
180
+
181
+ const regenerate = () => {
182
+ const lastUserIndex = messages.map((m) => m.role).lastIndexOf("user");
183
+ if (lastUserIndex === -1) return;
184
+ const historyBefore = messages.slice(0, lastUserIndex);
185
+ const lastUser = messages[lastUserIndex]!;
186
+ setMessages([...historyBefore, lastUser]);
187
+ void send(lastUser.content, [...historyBefore, lastUser]);
188
+ };
189
+
190
+ return (
191
+ <div className="fixed inset-0 flex flex-col overflow-hidden bg-background text-foreground">
192
+ <div ref={containerRef} className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
193
+ <div className="mx-auto flex min-h-full w-full max-w-3xl flex-col justify-start gap-6 pt-8 pb-4">
194
+ {messages.map((message, index) => {
195
+ const isLast =
196
+ message.role === "assistant" && index === messages.length - 1 && !streaming;
197
+ return (
198
+ <Message
199
+ key={message.id}
200
+ message={message}
201
+ isLast={isLast}
202
+ onCopy={copy}
203
+ onRegenerate={regenerate}
204
+ />
205
+ );
206
+ })}
207
+ </div>
208
+ </div>
209
+
210
+ <InputBar
211
+ onSend={(text) => void send(text)}
212
+ onStop={stop}
213
+ onClear={clearAll}
214
+ streaming={streaming}
215
+ hasMessages={messages.length > 0}
216
+ />
217
+ </div>
218
+ );
219
+ }