Repository navigation
feat(examples): degit-able starter templates — chat, mcp-client, proxy-failover - #1185
Conversation
…proxy-failover Three self-contained starters under examples/starters/, each with its own package.json (@juspay/neurolink ^9.88.12), strict tsconfig, README with a degit quickstart, and .env.example (placeholder names only): - chat: streaming terminal chat via neurolink.stream() with auto provider selection (chunk shape verified against src/lib/types/stream.ts) - mcp-client: .mcp-config.json auto-load (externalServerManager.ts:355) registering the filesystem MCP server; one generate() call where the model reads a shipped sample.txt; documents the neurolink mcp add equivalent - proxy-failover: ops walkthrough for neurolink proxy setup + account pooling, plus a status health CLI against the local /status endpoint (default port verified at src/cli/commands/proxy.ts:2135) with a clean proxy-not-running message All three verified: real npm install against the registry, tsc --noEmit clean under strict, prettier-clean, README steps match shipped files.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
📝 WalkthroughWalkthroughAdds three TypeScript starter applications: a streaming terminal chat, an MCP filesystem client, and a Claude proxy status checker. Each includes project configuration, environment templates, executable code, and README documentation. ChangesStarter examples
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant readline
participant main
participant NeuroLink
participant Provider
User->>readline: enter prompt
readline->>main: return input
main->>NeuroLink: stream prompt
NeuroLink->>Provider: request streamed reply
Provider-->>NeuroLink: text chunks
NeuroLink-->>main: stream events
main-->>User: write reply chunks
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
Pull request overview
Adds a small “starter templates” gallery under examples/starters/ intended to be degit-able, standalone projects (each with its own package.json, tsconfig, and README) to help users quickly try key NeuroLink features.
Changes:
- Added
examples/starters/README.mdindex documenting available starters and thedegitflow. - Added
chatstarter: a minimal terminal chat loop usingneurolink.stream()for streaming output. - Added
mcp-clientandproxy-failoverstarters: MCP filesystem-server auto-load demo and a local Claude proxy/statushealth/status CLI.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| examples/starters/README.md | Index page for the starter gallery and copy-out instructions. |
| examples/starters/chat/.env.example | Provider key placeholders for the chat starter. |
| examples/starters/chat/README.md | Usage docs for the streaming terminal chat starter. |
| examples/starters/chat/package.json | Standalone dependencies/scripts for the chat starter. |
| examples/starters/chat/src/index.ts | Streaming chat loop using NeuroLink.stream() and readline/promises. |
| examples/starters/chat/tsconfig.json | TypeScript configuration for the chat starter. |
| examples/starters/mcp-client/.env.example | Provider key placeholders for the MCP starter. |
| examples/starters/mcp-client/.mcp-config.json | Local MCP filesystem server config intended to be auto-loaded from cwd. |
| examples/starters/mcp-client/README.md | Documentation for MCP auto-load + filesystem tool usage example. |
| examples/starters/mcp-client/package.json | Standalone dependencies/scripts for the MCP starter. |
| examples/starters/mcp-client/sample.txt | Fixture file intended to be read via MCP filesystem tools. |
| examples/starters/mcp-client/src/index.ts | MCP status/servers validation + a generate() prompting file tool usage. |
| examples/starters/mcp-client/tsconfig.json | TypeScript configuration for the MCP starter. |
| examples/starters/proxy-failover/.env.example | Proxy host/port configuration for the status checker. |
| examples/starters/proxy-failover/README.md | Walkthrough for setting up the proxy + what the status checker prints. |
| examples/starters/proxy-failover/package.json | Standalone dependencies/scripts for the proxy status starter. |
| examples/starters/proxy-failover/src/index.ts | CLI that fetches /status and prints proxy/account summary. |
| examples/starters/proxy-failover/tsconfig.json | TypeScript configuration for the proxy status starter. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function isProxyStatusResponse(value: unknown): value is ProxyStatusResponse { | ||
| if (typeof value !== "object" || value === null) { | ||
| return false; | ||
| } | ||
| const candidate = value as Record<string, unknown>; | ||
| return ( | ||
| typeof candidate.status === "string" && | ||
| typeof candidate.pid === "number" && | ||
| typeof candidate.stats === "object" && | ||
| candidate.stats !== null | ||
| ); | ||
| } |
| const body: unknown = await response.json(); | ||
| if (!isProxyStatusResponse(body)) { | ||
| console.error( | ||
| `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`, | ||
| ); | ||
| process.exitCode = 1; | ||
| return; | ||
| } | ||
|
|
||
| printStatus(body); | ||
| } |
| const host = process.env.PROXY_HOST ?? "127.0.0.1"; | ||
| const port = Number.parseInt(process.env.PROXY_PORT ?? "55669", 10); | ||
|
|
||
| await checkProxyStatus(host, port); |
| "forceConsistentCasingInFileNames": true, | ||
| "noEmit": true, | ||
| "outDir": "dist" |
| @@ -0,0 +1,22 @@ | |||
| { | |||
| "name": "proxy-failover", | |||
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
examples/starters/proxy-failover/src/index.ts (1)
127-128: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueUse
||instead of??for environment variable fallbacks.If
PROXY_PORTis set to an empty string in the shell environment,??will preserve the empty string rather than falling back to the default, causingNumber.parseIntto returnNaNand the subsequent fetch request to fail cryptically. Using||ensures the default is applied for empty strings as well.♻️ Proposed refactor
-const host = process.env.PROXY_HOST ?? "127.0.0.1"; -const port = Number.parseInt(process.env.PROXY_PORT ?? "55669", 10); +const host = process.env.PROXY_HOST || "127.0.0.1"; +const port = Number.parseInt(process.env.PROXY_PORT || "55669", 10);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/starters/proxy-failover/src/index.ts` around lines 127 - 128, Update the environment-variable fallbacks in the host and port initialization to use || instead of ??, so empty PROXY_HOST or PROXY_PORT values select their defaults. Preserve the existing default values and parsing behavior in the proxy startup configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/starters/chat/src/index.ts`:
- Around line 40-64: Wrap the interactive loop in main with a try...finally
block so rl.close() always executes when rl.question or another loop operation
fails. In the finally cleanup, also invoke neurolink.shutdown() to release SDK
resources such as HTTP keep-alive agents, preserving the existing normal exit
and error handling behavior.
In `@examples/starters/proxy-failover/src/index.ts`:
- Around line 115-122: Wrap the response.json() call in the request flow with
try/catch so malformed response bodies do not produce an unhandled exception. In
the catch path, report the invalid response using the existing console error and
set process.exitCode to 1 before returning; keep the existing
isProxyStatusResponse validation for successfully parsed JSON.
- Around line 45-51: Update the type guard surrounding the candidate status
validation to also verify that candidate.stats.accounts is an array before
asserting ProxyStatusResponse. Preserve the existing status, pid, and stats
checks so printStatus can safely read accounts.length and iterate over the
validated collection.
---
Nitpick comments:
In `@examples/starters/proxy-failover/src/index.ts`:
- Around line 127-128: Update the environment-variable fallbacks in the host and
port initialization to use || instead of ??, so empty PROXY_HOST or PROXY_PORT
values select their defaults. Preserve the existing default values and parsing
behavior in the proxy startup configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bf64fcf2-3a50-4f5e-be2f-23673df9de51
📒 Files selected for processing (18)
examples/starters/README.mdexamples/starters/chat/.env.exampleexamples/starters/chat/README.mdexamples/starters/chat/package.jsonexamples/starters/chat/src/index.tsexamples/starters/chat/tsconfig.jsonexamples/starters/mcp-client/.env.exampleexamples/starters/mcp-client/.mcp-config.jsonexamples/starters/mcp-client/README.mdexamples/starters/mcp-client/package.jsonexamples/starters/mcp-client/sample.txtexamples/starters/mcp-client/src/index.tsexamples/starters/mcp-client/tsconfig.jsonexamples/starters/proxy-failover/.env.exampleexamples/starters/proxy-failover/README.mdexamples/starters/proxy-failover/package.jsonexamples/starters/proxy-failover/src/index.tsexamples/starters/proxy-failover/tsconfig.json
| const main = async (): Promise<void> => { | ||
| printWelcome(); | ||
|
|
||
| for (;;) { | ||
| const line = await rl.question("you> "); | ||
| const text = line.trim(); | ||
|
|
||
| if (text.length === 0) { | ||
| continue; | ||
| } | ||
| if (text === "/exit") { | ||
| break; | ||
| } | ||
|
|
||
| stdout.write("ai> "); | ||
| try { | ||
| await streamReply(text); | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
| console.error(`\n[error] ${message}\n`); | ||
| } | ||
| } | ||
|
|
||
| rl.close(); | ||
| }; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Ensure resources are cleaned up safely on early exit.
If rl.question throws (e.g., EOF received), or if another operation within the loop fails unexpectedly, main() will reject and completely bypass the rl.close() call. This leaves the readline interface active and prevents the Node.js process from exiting gracefully, even after the global .catch() intercepts the error.
Additionally, like the mcp-client starter, it is a good practice to invoke neurolink.shutdown() to ensure underlying HTTP keep-alive agents in the SDK are swiftly torn down. Wrap the interactive loop in a try...finally block to guarantee these cleanups run regardless of how the function exits.
🐛 Proposed fix to guarantee resource cleanup
const main = async (): Promise<void> => {
printWelcome();
- for (;;) {
- const line = await rl.question("you> ");
- const text = line.trim();
-
- if (text.length === 0) {
- continue;
- }
- if (text === "/exit") {
- break;
- }
-
- stdout.write("ai> ");
- try {
- await streamReply(text);
- } catch (error) {
- const message = error instanceof Error ? error.message : String(error);
- console.error(`\n[error] ${message}\n`);
- }
- }
-
- rl.close();
+ try {
+ for (;;) {
+ const line = await rl.question("you> ");
+ const text = line.trim();
+
+ if (text.length === 0) {
+ continue;
+ }
+ if (text === "/exit") {
+ break;
+ }
+
+ stdout.write("ai> ");
+ try {
+ await streamReply(text);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : String(error);
+ console.error(`\n[error] ${message}\n`);
+ }
+ }
+ } finally {
+ rl.close();
+ await neurolink.shutdown();
+ }
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const main = async (): Promise<void> => { | |
| printWelcome(); | |
| for (;;) { | |
| const line = await rl.question("you> "); | |
| const text = line.trim(); | |
| if (text.length === 0) { | |
| continue; | |
| } | |
| if (text === "/exit") { | |
| break; | |
| } | |
| stdout.write("ai> "); | |
| try { | |
| await streamReply(text); | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : String(error); | |
| console.error(`\n[error] ${message}\n`); | |
| } | |
| } | |
| rl.close(); | |
| }; | |
| const main = async (): Promise<void> => { | |
| printWelcome(); | |
| try { | |
| for (;;) { | |
| const line = await rl.question("you> "); | |
| const text = line.trim(); | |
| if (text.length === 0) { | |
| continue; | |
| } | |
| if (text === "/exit") { | |
| break; | |
| } | |
| stdout.write("ai> "); | |
| try { | |
| await streamReply(text); | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : String(error); | |
| console.error(`\n[error] ${message}\n`); | |
| } | |
| } | |
| } finally { | |
| rl.close(); | |
| await neurolink.shutdown(); | |
| } | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/starters/chat/src/index.ts` around lines 40 - 64, Wrap the
interactive loop in main with a try...finally block so rl.close() always
executes when rl.question or another loop operation fails. In the finally
cleanup, also invoke neurolink.shutdown() to release SDK resources such as HTTP
keep-alive agents, preserving the existing normal exit and error handling
behavior.
| return ( | ||
| typeof candidate.status === "string" && | ||
| typeof candidate.pid === "number" && | ||
| typeof candidate.stats === "object" && | ||
| candidate.stats !== null | ||
| ); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Validate the accounts array to prevent runtime crashes.
The type guard asserts that value is a ProxyStatusResponse, but it does not verify that stats.accounts is an array. If accounts is missing or is not an array, printStatus will crash with a TypeError when trying to read its .length or iterate over it.
🐛 Proposed fix to add array validation
- return (
- typeof candidate.status === "string" &&
- typeof candidate.pid === "number" &&
- typeof candidate.stats === "object" &&
- candidate.stats !== null
- );
+ return (
+ typeof candidate.status === "string" &&
+ typeof candidate.pid === "number" &&
+ typeof candidate.stats === "object" &&
+ candidate.stats !== null &&
+ Array.isArray((candidate.stats as Record<string, unknown>).accounts)
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return ( | |
| typeof candidate.status === "string" && | |
| typeof candidate.pid === "number" && | |
| typeof candidate.stats === "object" && | |
| candidate.stats !== null | |
| ); | |
| } | |
| return ( | |
| typeof candidate.status === "string" && | |
| typeof candidate.pid === "number" && | |
| typeof candidate.stats === "object" && | |
| candidate.stats !== null && | |
| Array.isArray((candidate.stats as Record<string, unknown>).accounts) | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/starters/proxy-failover/src/index.ts` around lines 45 - 51, Update
the type guard surrounding the candidate status validation to also verify that
candidate.stats.accounts is an array before asserting ProxyStatusResponse.
Preserve the existing status, pid, and stats checks so printStatus can safely
read accounts.length and iterate over the validated collection.
| const body: unknown = await response.json(); | ||
| if (!isProxyStatusResponse(body)) { | ||
| console.error( | ||
| `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`, | ||
| ); | ||
| process.exitCode = 1; | ||
| return; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Handle potential JSON parsing exceptions.
If the server responds with a 200 OK but the body is not valid JSON (e.g., returning an HTML page from a captive portal or a misconfigured reverse proxy), response.json() will throw an unhandled SyntaxError, causing the script to crash. Defensively wrap the JSON parsing in a try...catch block.
🛡️ Proposed fix to handle parsing errors gracefully
- const body: unknown = await response.json();
- if (!isProxyStatusResponse(body)) {
- console.error(
- `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`,
- );
- process.exitCode = 1;
- return;
- }
+ let body: unknown;
+ try {
+ body = await response.json();
+ } catch {
+ console.error(`Invalid JSON response from ${url}. Is this a NeuroLink proxy?`);
+ process.exitCode = 1;
+ return;
+ }
+
+ if (!isProxyStatusResponse(body)) {
+ console.error(
+ `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`,
+ );
+ process.exitCode = 1;
+ return;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const body: unknown = await response.json(); | |
| if (!isProxyStatusResponse(body)) { | |
| console.error( | |
| `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`, | |
| ); | |
| process.exitCode = 1; | |
| return; | |
| } | |
| let body: unknown; | |
| try { | |
| body = await response.json(); | |
| } catch { | |
| console.error(`Invalid JSON response from ${url}. Is this a NeuroLink proxy?`); | |
| process.exitCode = 1; | |
| return; | |
| } | |
| if (!isProxyStatusResponse(body)) { | |
| console.error( | |
| `Unexpected response shape from ${url}. Is this a NeuroLink proxy?`, | |
| ); | |
| process.exitCode = 1; | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/starters/proxy-failover/src/index.ts` around lines 115 - 122, Wrap
the response.json() call in the request flow with try/catch so malformed
response bodies do not produce an unhandled exception. In the catch path, report
the invalid response using the existing console error and set process.exitCode
to 1 before returning; keep the existing isProxyStatusResponse validation for
successfully parsed JSON.
Tara-ag
left a comment
There was a problem hiding this comment.
Review Summary
Reviewed all 18 added files in examples/starters/. Existing review comments (Copilot + CodeRabbit) already cover the major issues in proxy-failover/src/index.ts (permissive type guard, unhandled response.json(), invalid PROXY_PORT parsing), chat/tsconfig.json (outDir with noEmit), proxy-failover/package.json (inconsistent package name), and chat/src/index.ts (missing guaranteed cleanup). I did not duplicate those.
New comments raised (2 MINOR)
examples/starters/mcp-client/src/index.ts:83— Top-levelawaitdiverges from thechatstarter's defensivemain().catch(...)pattern and can produce a cryptic unhandled-rejection dump if copied into a non-ESM context.examples/starters/proxy-failover/src/index.ts:130— Top-levelawaitleaves any unexpected rejection to Node's default formatter, undermining the starter's goal of clean, user-friendly error messages.
Blocking assessment
- No hardcoded secrets or credentials.
- No new critical security vulnerabilities.
- No public SDK API breakage.
- No non-negotiable CLAUDE.md rule violations in the added code.
- Fewer than 3 MAJOR issues newly raised.
Decision: Approve with minor inline suggestions. The existing major feedback should be addressed before merge, but those points are already tracked by prior reviewers.
| } | ||
| } | ||
|
|
||
| await main(); |
There was a problem hiding this comment.
💡 MINOR: Top-level await may fail cryptically on older Node / non-ESM consumers.
The starter uses "type": "module" and Node 20, so top-level await is technically fine. However, if a user copies this into a CJS project or runs it through a transpiler that doesn't preserve TLA, the file will fail to load. Wrapping the call in an async IIFE and handling rejection explicitly mirrors the chat starter's pattern and gives a cleaner failure surface.
Suggested fix:
main().catch((error: unknown) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});| const host = process.env.PROXY_HOST ?? "127.0.0.1"; | ||
| const port = Number.parseInt(process.env.PROXY_PORT ?? "55669", 10); | ||
|
|
||
| await checkProxyStatus(host, port); |
There was a problem hiding this comment.
💡 MINOR: Top-level await leaves unhandled rejections to Node's default formatter.
If checkProxyStatus rejects for any reason not already caught inside the function, the process will print Node's default unhandled-rejection output rather than the clean error message this starter aims for. The chat starter wraps its entry point in main().catch(...) for consistent, user-friendly failures.
Suggested fix:
checkProxyStatus(host, port).catch((error: unknown) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});|
🎉 This PR is included in version 9.89.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
What
Three self-contained starter templates under
examples/starters/+ an index README. Each is copy-out-and-run:chatneurolink.stream(), automatic provider selection, one-line provider pinningmcp-client.mcp-config.jsonauto-load registering the filesystem MCP server; agenerate()call where the model reads a shippedsample.txt; theneurolink mcp addCLI equivalentproxy-failoverneurolink proxy setup+ account-pooling walkthrough, plus a status health CLI against the local/statusendpoint with a clean not-running messageVerification (all three)
npm installagainst the registry succeededtsc --noEmitclean understrict— noanyanywhere (only guarded narrowing)src/lib/types/stream.ts:664-672,.mcp-config.jsonauto-loadsrc/lib/mcp/externalServerManager.ts:355, proxy status portsrc/cli/commands/proxy.ts:2135).env.examplefiles contain placeholder names only; no lockfiles ornode_modulesshippedWhy
Growth playbook play #7 (marketing repo
docs/growth-exec/growth-playbook.md): the template/starter gallery is the single biggest owned discovery+retention asset for Vercel AI SDK and Mastra in this exact category — this is NeuroLink's first step there, timed for the July 22 launch traffic.Summary by CodeRabbit
New Features
Documentation