+
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/four-apples-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@inkeep/create-agents": patch
"@inkeep/agents-manage-api": patch
"@inkeep/agents-run-api": patch
---

Port error logging
16 changes: 16 additions & 0 deletions agents-docs/content/docs/quick-start/start-development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ pnpm dev

The Visual Builder will open automatically at http://localhost:3000.

#### Service Ports

The development environment starts multiple services on different ports:

| Service | Port | URL | Description |
| --- | --- | --- | --- |
| **Visual Builder** | 3000 | http://localhost:3000 | Web interface for building and managing agents |
| **Manage API** | 3002 | http://localhost:3002 | API for managing agent configurations and projects |
| **Run API** | 3003 | http://localhost:3003 | API for executing agent conversations with your agents |

<Note>
Make sure these ports are available before starting the development environment. If any port is in use, you'll need to free it up first.
</Note>

### Step 3: Install the Inkeep CLI

```bash
Expand All @@ -44,3 +58,5 @@ inkeep push
Open the Visual Builder at http://localhost:3000, click into the weather graph, and then click the "Try It" button to start chatting with the agent.

Next, we recommend setting up observability to see live traces of your agent. See [Traces](/quick-start/traces) for more information.


1 change: 1 addition & 0 deletions agents-manage-api/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
],
server: {
port: 3002,
strictPort: true,
},
optimizeDeps: {
exclude: ['keytar'],
Expand Down
1 change: 1 addition & 0 deletions agents-run-api/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
],
server: {
port: 3003,
strictPort: true,
},
optimizeDeps: {
exclude: ['keytar'],
Expand Down
113 changes: 103 additions & 10 deletions packages/create-agents/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { type ContentReplacement, cloneTemplate, getAvailableTemplates } from '.

const execAsync = promisify(exec);

const manageApiPort = '3002';
const runApiPort = '3003';

export const defaultGoogleModelConfigurations = {
base: {
model: 'google/gemini-2.5-flash',
Expand Down Expand Up @@ -51,8 +54,6 @@ type FileConfig = {
openAiKey?: string;
anthropicKey?: string;
googleKey?: string;
manageApiPort?: string;
runApiPort?: string;
modelSettings: Record<string, any>;
customProject?: boolean;
};
Expand All @@ -70,8 +71,6 @@ export const createAgents = async (
) => {
let { dirName, openAiKey, anthropicKey, googleKey, template, customProjectId } = args;
const tenantId = 'default';
const manageApiPort = '3002';
const runApiPort = '3003';

let projectId: string;
let templateName: string;
Expand Down Expand Up @@ -207,7 +206,9 @@ export const createAgents = async (

// Ensure models are always configured - fail if none were set
if (Object.keys(defaultModelSettings).length === 0) {
p.cancel('Cannot continue without a model configuration for project. Please provide an API key for at least one AI provider.');
p.cancel(
'Cannot continue without a model configuration for project. Please provide an API key for at least one AI provider.'
);
process.exit(1);
}

Expand Down Expand Up @@ -252,8 +253,6 @@ export const createAgents = async (
openAiKey,
anthropicKey,
googleKey,
manageApiPort: manageApiPort || '3002',
runApiPort: runApiPort || '3003',
modelSettings: defaultModelSettings,
customProject: !!customProjectId,
};
Expand Down Expand Up @@ -317,8 +316,8 @@ export const createAgents = async (
` cd ${dirName}\n` +
` pnpm dev # Start development servers\n\n` +
`${color.yellow('Available services:')}\n` +
` • Manage API: http://localhost:${manageApiPort || '3002'}\n` +
` • Run API: http://localhost:${runApiPort || '3003'}\n` +
` • Manage API: http://localhost:3002\n` +
` • Run API: http://localhost:3003\n` +
` • Manage UI: Available with management API\n` +
`\n${color.yellow('Configuration:')}\n` +
` • Edit .env for environment variables\n` +
Expand Down Expand Up @@ -403,7 +402,68 @@ async function installDependencies() {
await execAsync('pnpm install');
}

/**
* Check if a port is available
*/
async function isPortAvailable(port: number): Promise<boolean> {
const net = await import('node:net');
return new Promise((resolve) => {
const server = net.createServer();
server.once('error', () => {
resolve(false);
});
server.once('listening', () => {
server.close();
resolve(true);
});
server.listen(port);
});
}

/**
* Display port conflict error and exit
*/
function displayPortConflictError(unavailablePorts: {
runApi: boolean;
manageApi: boolean;
}): never {
let errorMessage = '';
if (unavailablePorts.runApi) {
errorMessage += `${color.red(`Run API port ${runApiPort} is already in use`)}\n`;
}
if (unavailablePorts.manageApi) {
errorMessage += `${color.red(`Manage API port ${manageApiPort} is already in use`)}\n`;
}

p.cancel(
`\n${color.red('✗ Port conflicts detected')}\n\n` +
`${errorMessage}\n` +
`${color.yellow('Please free up the ports and try again.')}\n`
);
process.exit(1);
}

/**
* Check port availability and display errors if needed
*/
async function checkPortsAvailability(): Promise<void> {
const [runApiAvailable, manageApiAvailable] = await Promise.all([
isPortAvailable(Number(runApiPort)),
isPortAvailable(Number(manageApiPort)),
]);

if (!runApiAvailable || !manageApiAvailable) {
displayPortConflictError({
runApi: !runApiAvailable,
manageApi: !manageApiAvailable,
});
}
}

async function setupProjectInDatabase(config: FileConfig) {
// Proactively check if ports are available BEFORE starting servers
await checkPortsAvailability();

// Start development servers in background
const { spawn } = await import('node:child_process');
const devProcess = spawn('pnpm', ['dev:apis'], {
Expand All @@ -412,8 +472,41 @@ async function setupProjectInDatabase(config: FileConfig) {
cwd: process.cwd(),
});

// Track if port errors occur during startup (as a safety fallback)
const portErrors = { runApi: false, manageApi: false };

// Regex patterns for detecting port errors in output
const portErrorPatterns = {
runApi: new RegExp(
`(EADDRINUSE.*:${runApiPort}|port ${runApiPort}.*already|Port ${runApiPort}.*already|run-api.*Error.*Port)`,
'i'
),
manageApi: new RegExp(
`(EADDRINUSE.*:${manageApiPort}|port ${manageApiPort}.*already|Port ${manageApiPort}.*already|manage-api.*Error.*Port)`,
'i'
),
};

// Monitor output for port errors (fallback in case ports become unavailable between check and start)
const checkForPortErrors = (data: Buffer) => {
const output = data.toString();
if (portErrorPatterns.runApi.test(output)) {
portErrors.runApi = true;
}
if (portErrorPatterns.manageApi.test(output)) {
portErrors.manageApi = true;
}
};

devProcess.stdout.on('data', checkForPortErrors);

// Give servers time to start
await new Promise((resolve) => setTimeout(resolve, 5000));
await new Promise((resolve) => setTimeout(resolve, 3000));

// Check if any port errors occurred during startup
if (portErrors.runApi || portErrors.manageApi) {
displayPortConflictError(portErrors);
}

// Run inkeep push
try {
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载