Adding cf/agents tests (#11)
* Cloning demo repo * added test for agents/mcp --------- Co-authored-by: Glen Maddern <glen@glenmaddern.com>
This commit is contained in:
parent
ace3220fca
commit
7f019fed7f
12 changed files with 10420 additions and 10 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
|
@ -33,9 +33,9 @@ jobs:
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
working-directory: examples/inspector
|
working-directory: examples/inspector
|
||||||
|
|
||||||
# - name: Install dependencies - cf-agents example
|
- name: Install dependencies - cf-agents example
|
||||||
# run: pnpm install
|
run: pnpm install
|
||||||
# working-directory: examples/servers/cf-agents
|
working-directory: examples/servers/cf-agents
|
||||||
|
|
||||||
- name: Install dependencies - hono-mcp example
|
- name: Install dependencies - hono-mcp example
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
|
||||||
172
examples/servers/cf-agents/.gitignore
vendored
Normal file
172
examples/servers/cf-agents/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
\*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
\*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
\*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
\*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.\*
|
||||||
|
|
||||||
|
# wrangler project
|
||||||
|
|
||||||
|
.dev.vars
|
||||||
|
.wrangler/
|
||||||
50
examples/servers/cf-agents/README.md
Normal file
50
examples/servers/cf-agents/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Building a Remote MCP Server on Cloudflare (Without Auth)
|
||||||
|
|
||||||
|
This example allows you to deploy a remote MCP server that doesn't require authentication on Cloudflare Workers.
|
||||||
|
|
||||||
|
## Get started:
|
||||||
|
|
||||||
|
[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless)
|
||||||
|
|
||||||
|
This will deploy your MCP server to a URL like: `remote-mcp-server-authless.<your-account>.workers.dev/sse`
|
||||||
|
|
||||||
|
Alternatively, you can use the command line below to get the remote MCP Server created on your local machine:
|
||||||
|
```bash
|
||||||
|
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customizing your MCP Server
|
||||||
|
|
||||||
|
To add your own [tools](https://developers.cloudflare.com/agents/model-context-protocol/tools/) to the MCP server, define each tool inside the `init()` method of `src/index.ts` using `this.server.tool(...)`.
|
||||||
|
|
||||||
|
## Connect to Cloudflare AI Playground
|
||||||
|
|
||||||
|
You can connect to your MCP server from the Cloudflare AI Playground, which is a remote MCP client:
|
||||||
|
|
||||||
|
1. Go to https://playground.ai.cloudflare.com/
|
||||||
|
2. Enter your deployed MCP server URL (`remote-mcp-server-authless.<your-account>.workers.dev/sse`)
|
||||||
|
3. You can now use your MCP tools directly from the playground!
|
||||||
|
|
||||||
|
## Connect Claude Desktop to your MCP server
|
||||||
|
|
||||||
|
You can also connect to your remote MCP server from local MCP clients, by using the [mcp-remote proxy](https://www.npmjs.com/package/mcp-remote).
|
||||||
|
|
||||||
|
To connect to your MCP server from Claude Desktop, follow [Anthropic's Quickstart](https://modelcontextprotocol.io/quickstart/user) and within Claude Desktop go to Settings > Developer > Edit Config.
|
||||||
|
|
||||||
|
Update with this configuration:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"calculator": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"mcp-remote",
|
||||||
|
"http://localhost:8787/sse" // or remote-mcp-server-authless.your-account.workers.dev/sse
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Claude and you should see the tools become available.
|
||||||
49
examples/servers/cf-agents/biome.json
Normal file
49
examples/servers/cf-agents/biome.json
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.0.4/schema.json",
|
||||||
|
"assist": {
|
||||||
|
"actions": {
|
||||||
|
"source": {
|
||||||
|
"useSortedKeys": "on"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"includes": ["!worker-configuration.d.ts"]
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentWidth": 4,
|
||||||
|
"lineWidth": 100
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"style": {
|
||||||
|
"noInferrableTypes": "error",
|
||||||
|
"noNonNullAssertion": "off",
|
||||||
|
"noParameterAssign": "error",
|
||||||
|
"noUnusedTemplateLiteral": "error",
|
||||||
|
"noUselessElse": "error",
|
||||||
|
"useAsConstAssertion": "error",
|
||||||
|
"useDefaultParameterLast": "error",
|
||||||
|
"useEnumInitializers": "error",
|
||||||
|
"useNumberNamespace": "error",
|
||||||
|
"useSelfClosingElements": "error",
|
||||||
|
"useSingleVarDeclarator": "error"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noConfusingVoidType": "off",
|
||||||
|
"noDebugger": "off",
|
||||||
|
"noExplicitAny": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": false,
|
||||||
|
"vcs": {
|
||||||
|
"clientKind": "git",
|
||||||
|
"enabled": true,
|
||||||
|
"useIgnoreFile": true
|
||||||
|
}
|
||||||
|
}
|
||||||
25
examples/servers/cf-agents/package.json
Normal file
25
examples/servers/cf-agents/package.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "remote-mcp-server-authless",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"deploy": "wrangler deploy",
|
||||||
|
"dev": "wrangler dev",
|
||||||
|
"format": "biome format --write",
|
||||||
|
"lint:fix": "biome lint --fix",
|
||||||
|
"start": "wrangler dev",
|
||||||
|
"cf-typegen": "wrangler types",
|
||||||
|
"type-check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
||||||
|
"zod": "^3.25.67"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^2.0.4",
|
||||||
|
"@types/node": "^24.0.7",
|
||||||
|
"agents": "^0.0.100",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"wrangler": "^4.22.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
1959
examples/servers/cf-agents/pnpm-lock.yaml
generated
Normal file
1959
examples/servers/cf-agents/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
71
examples/servers/cf-agents/src/index.ts
Normal file
71
examples/servers/cf-agents/src/index.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { McpAgent } from 'agents/mcp'
|
||||||
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
// Define our MCP agent with tools
|
||||||
|
export class MyMCP extends McpAgent {
|
||||||
|
server = new McpServer({
|
||||||
|
name: 'Authless Calculator',
|
||||||
|
version: '1.0.0',
|
||||||
|
})
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
// Simple addition tool
|
||||||
|
this.server.tool('add', { a: z.number(), b: z.number() }, async ({ a, b }) => ({
|
||||||
|
content: [{ type: 'text', text: String(a + b) }],
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Calculator tool with multiple operations
|
||||||
|
this.server.tool(
|
||||||
|
'calculate',
|
||||||
|
{
|
||||||
|
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
|
||||||
|
a: z.number(),
|
||||||
|
b: z.number(),
|
||||||
|
},
|
||||||
|
async ({ operation, a, b }) => {
|
||||||
|
let result: number
|
||||||
|
switch (operation) {
|
||||||
|
case 'add':
|
||||||
|
result = a + b
|
||||||
|
break
|
||||||
|
case 'subtract':
|
||||||
|
result = a - b
|
||||||
|
break
|
||||||
|
case 'multiply':
|
||||||
|
result = a * b
|
||||||
|
break
|
||||||
|
case 'divide':
|
||||||
|
if (b === 0)
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Error: Cannot divide by zero',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
result = a / b
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return { content: [{ type: 'text', text: String(result) }] }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
||||||
|
const url = new URL(request.url)
|
||||||
|
|
||||||
|
if (url.pathname === '/sse' || url.pathname === '/sse/message') {
|
||||||
|
return MyMCP.serveSSE('/sse').fetch(request, env, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname === '/mcp') {
|
||||||
|
return MyMCP.serve('/mcp').fetch(request, env, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response('Not found', { status: 404 })
|
||||||
|
},
|
||||||
|
}
|
||||||
20
examples/servers/cf-agents/tsconfig.json
Normal file
20
examples/servers/cf-agents/tsconfig.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2021",
|
||||||
|
"lib": ["es2021"],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"module": "es2022",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"noEmit": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["./worker-configuration.d.ts", "node"]
|
||||||
|
},
|
||||||
|
"include": ["worker-configuration.d.ts", "src/**/*.ts"]
|
||||||
|
}
|
||||||
7943
examples/servers/cf-agents/worker-configuration.d.ts
vendored
Normal file
7943
examples/servers/cf-agents/worker-configuration.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
62
examples/servers/cf-agents/wrangler.jsonc
Normal file
62
examples/servers/cf-agents/wrangler.jsonc
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* For more details on how to configure Wrangler, refer to:
|
||||||
|
* https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "cf-agents",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"compatibility_date": "2025-03-10",
|
||||||
|
"compatibility_flags": ["nodejs_compat"],
|
||||||
|
"migrations": [
|
||||||
|
{
|
||||||
|
"new_sqlite_classes": ["MyMCP"],
|
||||||
|
"tag": "v1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"durable_objects": {
|
||||||
|
"bindings": [
|
||||||
|
{
|
||||||
|
"class_name": "MyMCP",
|
||||||
|
"name": "MCP_OBJECT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observability": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Smart Placement
|
||||||
|
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
|
||||||
|
*/
|
||||||
|
// "placement": { "mode": "smart" },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bindings
|
||||||
|
* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
|
||||||
|
* databases, object storage, AI inference, real-time communication and more.
|
||||||
|
* https://developers.cloudflare.com/workers/runtime-apis/bindings/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment Variables
|
||||||
|
* https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
|
||||||
|
*/
|
||||||
|
// "vars": { "MY_VARIABLE": "production_value" },
|
||||||
|
/**
|
||||||
|
* Note: Use secrets to store sensitive data.
|
||||||
|
* https://developers.cloudflare.com/workers/configuration/secrets/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static Assets
|
||||||
|
* https://developers.cloudflare.com/workers/static-assets/binding/
|
||||||
|
*/
|
||||||
|
// "assets": { "directory": "./public/", "binding": "ASSETS" },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service Bindings (communicate between multiple Workers)
|
||||||
|
* https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
|
||||||
|
*/
|
||||||
|
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
|
||||||
|
}
|
||||||
|
|
@ -18,18 +18,21 @@ function getMCPServers() {
|
||||||
throw new Error('hono-mcp port not found in test state')
|
throw new Error('hono-mcp port not found in test state')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.cfAgentsPort) {
|
||||||
|
throw new Error('cf-agents port not found in test state')
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'hono-mcp',
|
name: 'hono-mcp',
|
||||||
url: `http://localhost:${state.honoPort}/mcp`,
|
url: `http://localhost:${state.honoPort}/mcp`,
|
||||||
expectedTools: 1, // Minimum expected tools count
|
expectedTools: 1, // Minimum expected tools count
|
||||||
},
|
},
|
||||||
// Add more servers here as they become available
|
{
|
||||||
// {
|
name: 'cf-agents',
|
||||||
// name: 'another-server',
|
url: `http://localhost:${state.cfAgentsPort}/mcp`,
|
||||||
// url: 'http://localhost:9902/mcp',
|
expectedTools: 1, // Minimum expected tools count
|
||||||
// expectedTools: 1,
|
},
|
||||||
// },
|
|
||||||
]
|
]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Test environment not properly initialized: ${error}`)
|
throw new Error(`Test environment not properly initialized: ${error}`)
|
||||||
|
|
@ -161,6 +164,15 @@ describe('MCP Connection Integration Tests', () => {
|
||||||
// Ignore errors - process might already be dead
|
// Ignore errors - process might already be dead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (state?.cfAgentsServer && !state.cfAgentsServer.killed) {
|
||||||
|
console.log('🔥 Force cleanup cf-agents server...')
|
||||||
|
state.cfAgentsServer.kill('SIGKILL')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore errors - process might already be dead
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (state?.staticServer) {
|
if (state?.staticServer) {
|
||||||
state.staticServer.close()
|
state.staticServer.close()
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,11 @@ const testStateFile = join(cacheDir, 'test-state.json')
|
||||||
|
|
||||||
interface GlobalState {
|
interface GlobalState {
|
||||||
honoServer?: ChildProcess
|
honoServer?: ChildProcess
|
||||||
|
cfAgentsServer?: ChildProcess
|
||||||
staticServer?: Server
|
staticServer?: Server
|
||||||
staticPort?: number
|
staticPort?: number
|
||||||
honoPort?: number
|
honoPort?: number
|
||||||
|
cfAgentsPort?: number
|
||||||
processGroupId?: number
|
processGroupId?: number
|
||||||
allChildProcesses?: Set<number>
|
allChildProcesses?: Set<number>
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +254,48 @@ export default async function globalSetup() {
|
||||||
await waitForOutput(honoServer, 'Ready on')
|
await waitForOutput(honoServer, 'Ready on')
|
||||||
state.honoServer = honoServer
|
state.honoServer = honoServer
|
||||||
|
|
||||||
// Step 4: Start simple static file server for inspector
|
// Step 4: Find available port and start cf-agents server
|
||||||
|
console.log('🔍 Finding available port starting from 9902...')
|
||||||
|
const cfAgentsPort = await findAvailablePortFromBase(9902)
|
||||||
|
console.log(`📍 Using port ${cfAgentsPort} for cf-agents server`)
|
||||||
|
|
||||||
|
console.log('🚀 Starting cf-agents server...')
|
||||||
|
const cfAgentsDir = join(rootDir, 'examples/servers/cf-agents')
|
||||||
|
const cfAgentsServer = spawn('pnpm', ['dev', `--port=${cfAgentsPort}`], {
|
||||||
|
cwd: cfAgentsDir,
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
shell: true,
|
||||||
|
detached: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track all child processes
|
||||||
|
if (cfAgentsServer.pid) {
|
||||||
|
state.allChildProcesses!.add(cfAgentsServer.pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.cfAgentsPort = cfAgentsPort
|
||||||
|
state.cfAgentsServer = cfAgentsServer
|
||||||
|
|
||||||
|
cfAgentsServer.stdout?.on('data', (data) => {
|
||||||
|
console.log(`[cf-agents] ${data.toString()}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
cfAgentsServer.stderr?.on('data', (data) => {
|
||||||
|
console.log(`[cf-agents] ${data.toString()}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track when the process exits to remove it from our tracking
|
||||||
|
cfAgentsServer.on('exit', () => {
|
||||||
|
if (cfAgentsServer.pid) {
|
||||||
|
state.allChildProcesses!.delete(cfAgentsServer.pid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for cf-agents server to be ready
|
||||||
|
await waitForOutput(cfAgentsServer, 'Ready on')
|
||||||
|
state.cfAgentsServer = cfAgentsServer
|
||||||
|
|
||||||
|
// Step 5: Start simple static file server for inspector
|
||||||
console.log('🌐 Starting static file server for inspector...')
|
console.log('🌐 Starting static file server for inspector...')
|
||||||
const inspectorDistDir = join(inspectorDir, 'dist')
|
const inspectorDistDir = join(inspectorDir, 'dist')
|
||||||
const staticPort = await findAvailablePort(8000)
|
const staticPort = await findAvailablePort(8000)
|
||||||
|
|
@ -315,6 +358,7 @@ export default async function globalSetup() {
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
honoPort: state.honoPort,
|
honoPort: state.honoPort,
|
||||||
|
cfAgentsPort: state.cfAgentsPort,
|
||||||
staticPort: state.staticPort,
|
staticPort: state.staticPort,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
|
|
@ -330,6 +374,9 @@ export default async function globalSetup() {
|
||||||
if (state.honoServer) {
|
if (state.honoServer) {
|
||||||
state.honoServer.kill()
|
state.honoServer.kill()
|
||||||
}
|
}
|
||||||
|
if (state.cfAgentsServer) {
|
||||||
|
state.cfAgentsServer.kill()
|
||||||
|
}
|
||||||
if (state.staticServer) {
|
if (state.staticServer) {
|
||||||
state.staticServer.close()
|
state.staticServer.close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue