@spec2tools/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/agent.d.ts +38 -0
- package/dist/agent.js +126 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +312 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# @spec2tools/cli
|
|
2
|
+
|
|
3
|
+
CLI for interacting with OpenAPI-based AI tools.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @spec2tools/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @spec2tools/cli start --spec ./openapi.yaml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Set Environment Variables
|
|
20
|
+
|
|
21
|
+
Define a `.env` file and set the `OPENAI_API_KEY` environment variable:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
OPENAI_API_KEY=your-api-key
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Start the Agent
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# With a remote OpenAPI spec URL
|
|
31
|
+
npx @spec2tools/cli start --spec https://api.example.com/openapi.json
|
|
32
|
+
|
|
33
|
+
# With a local file
|
|
34
|
+
npx @spec2tools/cli start --spec ./openapi.yaml
|
|
35
|
+
|
|
36
|
+
# Skip authentication
|
|
37
|
+
npx @spec2tools/cli start --spec ./openapi.yaml --no-auth
|
|
38
|
+
|
|
39
|
+
# Provide token directly
|
|
40
|
+
npx @spec2tools/cli start --spec ./openapi.yaml --token "your-access-token"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Chat Mode
|
|
44
|
+
|
|
45
|
+
Once started, you can interact with the AI agent naturally:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
> What can you do?
|
|
49
|
+
I have access to the following tools:
|
|
50
|
+
- createUser: Create a new user
|
|
51
|
+
- getUser: Retrieve user by ID
|
|
52
|
+
- listUsers: List all users
|
|
53
|
+
|
|
54
|
+
> Create a user named John with email john@example.com
|
|
55
|
+
[Calling createUser with {"name":"John","email":"john@example.com"}]
|
|
56
|
+
Created user successfully: { id: "123", name: "John", email: "john@example.com" }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Special Commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# List available tools
|
|
63
|
+
> /tools
|
|
64
|
+
|
|
65
|
+
# Call a tool directly
|
|
66
|
+
> /call createUser --name "John" --email "john@example.com"
|
|
67
|
+
|
|
68
|
+
# Show tool schema
|
|
69
|
+
> /schema createUser
|
|
70
|
+
|
|
71
|
+
# Clear conversation history
|
|
72
|
+
> /clear
|
|
73
|
+
|
|
74
|
+
# Show help
|
|
75
|
+
> /help
|
|
76
|
+
|
|
77
|
+
# Exit
|
|
78
|
+
> /exit
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
The `examples/` directory contains sample OpenAPI specifications:
|
|
84
|
+
|
|
85
|
+
- `sample-api.yaml` - Simple API without authentication
|
|
86
|
+
- `authenticated-api.yaml` - API with OAuth2 authentication
|
|
87
|
+
- `context7.yaml` - Context7 API with OAuth2 (PKCE + dynamic client registration)
|
|
88
|
+
|
|
89
|
+
## Supported OpenAPI Features
|
|
90
|
+
|
|
91
|
+
### Supported
|
|
92
|
+
- `GET`, `POST`, `PUT`, `PATCH`, `DELETE` operations
|
|
93
|
+
- Path parameters (string, number, boolean)
|
|
94
|
+
- Query parameters (string, number, boolean)
|
|
95
|
+
- Request body with simple JSON schemas (primitives, flat objects)
|
|
96
|
+
- Security schemes: OAuth2 (authorization code with PKCE), API Key, Bearer token
|
|
97
|
+
- OAuth2 Dynamic Client Registration (auto-registers client with the auth server)
|
|
98
|
+
|
|
99
|
+
### Not Supported (throws error)
|
|
100
|
+
- Nested objects beyond 1 level
|
|
101
|
+
- Arrays of objects
|
|
102
|
+
- `anyOf`, `oneOf`, `allOf` schemas
|
|
103
|
+
- File uploads
|
|
104
|
+
- `$ref` references
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Tool } from '@spec2tools/core';
|
|
2
|
+
interface AgentConfig {
|
|
3
|
+
tools: Tool[];
|
|
4
|
+
model?: string;
|
|
5
|
+
maxSteps?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* AI Agent that uses OpenAPI tools
|
|
9
|
+
*/
|
|
10
|
+
export declare class Agent {
|
|
11
|
+
private tools;
|
|
12
|
+
private model;
|
|
13
|
+
private maxSteps;
|
|
14
|
+
private conversationHistory;
|
|
15
|
+
constructor(config: AgentConfig);
|
|
16
|
+
/**
|
|
17
|
+
* Get available tools description for the agent
|
|
18
|
+
*/
|
|
19
|
+
getToolsDescription(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Process a user message and return the response
|
|
22
|
+
*/
|
|
23
|
+
chat(userMessage: string): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Clear conversation history
|
|
26
|
+
*/
|
|
27
|
+
clearHistory(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get the list of tool names
|
|
30
|
+
*/
|
|
31
|
+
getToolNames(): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Get a specific tool by name
|
|
34
|
+
*/
|
|
35
|
+
getTool(name: string): Tool | undefined;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=agent.d.ts.map
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { generateText, tool, stepCountIs } from 'ai';
|
|
2
|
+
import { openai } from '@ai-sdk/openai';
|
|
3
|
+
import { ToolExecutionError } from '@spec2tools/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
const MAX_OUTPUT_LENGTH = 500;
|
|
6
|
+
/**
|
|
7
|
+
* Trim a string if it exceeds the maximum length
|
|
8
|
+
*/
|
|
9
|
+
function trimOutput(value) {
|
|
10
|
+
const str = typeof value === 'string' ? value : JSON.stringify(value);
|
|
11
|
+
if (str.length > MAX_OUTPUT_LENGTH) {
|
|
12
|
+
return str.substring(0, MAX_OUTPUT_LENGTH) + '...';
|
|
13
|
+
}
|
|
14
|
+
return str;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* AI Agent that uses OpenAPI tools
|
|
18
|
+
*/
|
|
19
|
+
export class Agent {
|
|
20
|
+
tools;
|
|
21
|
+
model;
|
|
22
|
+
maxSteps;
|
|
23
|
+
conversationHistory;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.tools = config.tools;
|
|
26
|
+
this.model = config.model || 'gpt-5.1-chat-latest';
|
|
27
|
+
this.maxSteps = config.maxSteps || 10;
|
|
28
|
+
this.conversationHistory = [];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get available tools description for the agent
|
|
32
|
+
*/
|
|
33
|
+
getToolsDescription() {
|
|
34
|
+
if (this.tools.length === 0) {
|
|
35
|
+
return 'No tools available.';
|
|
36
|
+
}
|
|
37
|
+
const toolDescriptions = this.tools.map((tool) => {
|
|
38
|
+
return `- ${tool.name}: ${tool.description}`;
|
|
39
|
+
});
|
|
40
|
+
return `I have access to the following tools:\n${toolDescriptions.join('\n')}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Process a user message and return the response
|
|
44
|
+
*/
|
|
45
|
+
async chat(userMessage) {
|
|
46
|
+
// Add user message to history
|
|
47
|
+
this.conversationHistory.push({
|
|
48
|
+
role: 'user',
|
|
49
|
+
content: userMessage,
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
// Build AI SDK tools from our tool definitions
|
|
53
|
+
const aiTools = {};
|
|
54
|
+
for (const t of this.tools) {
|
|
55
|
+
const toolExecute = t.execute;
|
|
56
|
+
const toolName = t.name;
|
|
57
|
+
aiTools[t.name] = tool({
|
|
58
|
+
description: t.description,
|
|
59
|
+
inputSchema: t.parameters,
|
|
60
|
+
execute: async (params) => {
|
|
61
|
+
console.log(chalk.dim(`\n[Calling ${toolName} with ${JSON.stringify(params)}]`));
|
|
62
|
+
try {
|
|
63
|
+
const result = await toolExecute(params);
|
|
64
|
+
console.log(chalk.dim(`[${toolName} returned: ${trimOutput(result)}]\n`));
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof ToolExecutionError) {
|
|
69
|
+
console.log(chalk.red(`[${toolName} failed: ${error.message}]\n`));
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Build system prompt
|
|
78
|
+
const systemPrompt = `You are a helpful AI assistant with access to various API tools.
|
|
79
|
+
When the user asks you to perform actions, use the available tools to help them.
|
|
80
|
+
Always explain what you're doing and present results in a clear, readable format.
|
|
81
|
+
If a tool call fails, explain the error to the user.`;
|
|
82
|
+
// Generate response with tool use
|
|
83
|
+
const result = await generateText({
|
|
84
|
+
model: openai(this.model),
|
|
85
|
+
system: systemPrompt,
|
|
86
|
+
messages: this.conversationHistory,
|
|
87
|
+
tools: aiTools,
|
|
88
|
+
stopWhen: stepCountIs(this.maxSteps),
|
|
89
|
+
});
|
|
90
|
+
// Add assistant response to history
|
|
91
|
+
this.conversationHistory.push({
|
|
92
|
+
role: 'assistant',
|
|
93
|
+
content: result.text,
|
|
94
|
+
});
|
|
95
|
+
return result.text;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
+
// Add error response to history
|
|
100
|
+
this.conversationHistory.push({
|
|
101
|
+
role: 'assistant',
|
|
102
|
+
content: `I encountered an error: ${errorMessage}`,
|
|
103
|
+
});
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clear conversation history
|
|
109
|
+
*/
|
|
110
|
+
clearHistory() {
|
|
111
|
+
this.conversationHistory = [];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the list of tool names
|
|
115
|
+
*/
|
|
116
|
+
getToolNames() {
|
|
117
|
+
return this.tools.map((t) => t.name);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get a specific tool by name
|
|
121
|
+
*/
|
|
122
|
+
getTool(name) {
|
|
123
|
+
return this.tools.find((t) => t.name === name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=agent.js.map
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
import { loadOpenAPISpec, extractBaseUrl, extractAuthConfig, parseOperations, formatToolSchema, formatToolSignature, AuthManager, createExecutableTools, executeToolByName, UnsupportedSchemaError, AuthenticationError, ToolExecutionError, SpecLoadError, } from '@spec2tools/core';
|
|
6
|
+
import { Agent } from './agent.js';
|
|
7
|
+
const VERSION = '0.1.6';
|
|
8
|
+
export function createCLI() {
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name('spec2tools')
|
|
12
|
+
.description('Dynamically convert OpenAPI specs into AI agent tools at runtime')
|
|
13
|
+
.version(VERSION);
|
|
14
|
+
program
|
|
15
|
+
.command('start')
|
|
16
|
+
.description('Start the agent with an OpenAPI specification')
|
|
17
|
+
.requiredOption('-s, --spec <path>', 'Path or URL to OpenAPI specification')
|
|
18
|
+
.option('--no-auth', 'Skip authentication even if required by spec')
|
|
19
|
+
.option('--token <token>', 'Provide access token directly')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
await startAgent(options);
|
|
22
|
+
});
|
|
23
|
+
return program;
|
|
24
|
+
}
|
|
25
|
+
async function startAgent(options) {
|
|
26
|
+
const spinner = ora();
|
|
27
|
+
try {
|
|
28
|
+
// Load OpenAPI spec
|
|
29
|
+
spinner.start('Loading OpenAPI specification...');
|
|
30
|
+
const spec = await loadOpenAPISpec(options.spec);
|
|
31
|
+
spinner.succeed(`Loaded: ${spec.info.title} v${spec.info.version}`);
|
|
32
|
+
// Extract base URL
|
|
33
|
+
const baseUrl = extractBaseUrl(spec);
|
|
34
|
+
console.log(chalk.dim(`Base URL: ${baseUrl}`));
|
|
35
|
+
// Extract auth config
|
|
36
|
+
const authConfig = extractAuthConfig(spec);
|
|
37
|
+
// Parse operations
|
|
38
|
+
spinner.start('Parsing operations...');
|
|
39
|
+
const toolDefs = parseOperations(spec);
|
|
40
|
+
spinner.succeed(`Found ${toolDefs.length} operations`);
|
|
41
|
+
if (toolDefs.length === 0) {
|
|
42
|
+
console.log(chalk.yellow('No operations found in the specification.'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Initialize auth manager
|
|
46
|
+
const authManager = new AuthManager(authConfig);
|
|
47
|
+
// Handle authentication
|
|
48
|
+
if (options.token) {
|
|
49
|
+
authManager.setAccessToken(options.token);
|
|
50
|
+
console.log(chalk.green('Using provided access token'));
|
|
51
|
+
}
|
|
52
|
+
else if (options.auth && authManager.requiresAuth()) {
|
|
53
|
+
console.log(chalk.yellow(`\nAuthentication required (${authConfig.type})`));
|
|
54
|
+
await authManager.authenticate();
|
|
55
|
+
console.log(chalk.green('Authentication successful!'));
|
|
56
|
+
}
|
|
57
|
+
else if (authManager.requiresAuth()) {
|
|
58
|
+
console.log(chalk.yellow('\nWarning: API requires authentication but --no-auth was specified'));
|
|
59
|
+
}
|
|
60
|
+
// Create executable tools
|
|
61
|
+
const tools = createExecutableTools(toolDefs, baseUrl, authManager);
|
|
62
|
+
// Initialize session
|
|
63
|
+
const session = {
|
|
64
|
+
baseUrl,
|
|
65
|
+
tools,
|
|
66
|
+
authConfig,
|
|
67
|
+
accessToken: authManager.getAccessToken(),
|
|
68
|
+
};
|
|
69
|
+
// Start chat loop
|
|
70
|
+
await startChatLoop(session);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
spinner.fail();
|
|
74
|
+
if (error instanceof UnsupportedSchemaError) {
|
|
75
|
+
console.error(chalk.red(`\nSchema Error: ${error.message}`));
|
|
76
|
+
console.error(chalk.dim('This OpenAPI specification contains features that are not supported.'));
|
|
77
|
+
}
|
|
78
|
+
else if (error instanceof AuthenticationError) {
|
|
79
|
+
console.error(chalk.red(`\nAuth Error: ${error.message}`));
|
|
80
|
+
}
|
|
81
|
+
else if (error instanceof SpecLoadError) {
|
|
82
|
+
console.error(chalk.red(`\nSpec Error: ${error.message}`));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : String(error)}`));
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function startChatLoop(session) {
|
|
91
|
+
// Initialize agent
|
|
92
|
+
const agent = new Agent({ tools: session.tools });
|
|
93
|
+
console.log(chalk.bold('\n--- Spec2Tools ---'));
|
|
94
|
+
console.log(chalk.dim('Type your message or use special commands:'));
|
|
95
|
+
console.log(chalk.dim(' /tools - List available tools'));
|
|
96
|
+
console.log(chalk.dim(' /call - Call a tool directly'));
|
|
97
|
+
console.log(chalk.dim(' /schema - Show tool schema'));
|
|
98
|
+
console.log(chalk.dim(' /help - Show help'));
|
|
99
|
+
console.log(chalk.dim(' /exit - Exit the CLI'));
|
|
100
|
+
console.log('');
|
|
101
|
+
const rl = readline.createInterface({
|
|
102
|
+
input: process.stdin,
|
|
103
|
+
output: process.stdout,
|
|
104
|
+
});
|
|
105
|
+
const prompt = () => {
|
|
106
|
+
rl.question(chalk.cyan('> '), async (input) => {
|
|
107
|
+
const trimmedInput = input.trim();
|
|
108
|
+
if (!trimmedInput) {
|
|
109
|
+
prompt();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Pause readline during async operations to prevent corruption
|
|
113
|
+
rl.pause();
|
|
114
|
+
try {
|
|
115
|
+
await handleInput(trimmedInput, session, agent);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
rl.resume();
|
|
122
|
+
prompt();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
rl.on('close', () => {
|
|
127
|
+
console.log(chalk.dim('\nGoodbye!'));
|
|
128
|
+
process.exit(0);
|
|
129
|
+
});
|
|
130
|
+
prompt();
|
|
131
|
+
}
|
|
132
|
+
async function handleInput(input, session, agent) {
|
|
133
|
+
// Handle special commands
|
|
134
|
+
if (input.startsWith('/')) {
|
|
135
|
+
await handleCommand(input, session, agent);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Regular chat message
|
|
139
|
+
const spinner = ora('Thinking...').start();
|
|
140
|
+
try {
|
|
141
|
+
const response = await agent.chat(input);
|
|
142
|
+
spinner.stop();
|
|
143
|
+
console.log(chalk.white('\n' + response + '\n'));
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
spinner.fail('Failed to get response');
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function handleCommand(input, session, agent) {
|
|
151
|
+
const parts = input.slice(1).split(/\s+/);
|
|
152
|
+
const command = parts[0].toLowerCase();
|
|
153
|
+
const args = parts.slice(1);
|
|
154
|
+
switch (command) {
|
|
155
|
+
case 'tools':
|
|
156
|
+
listTools(session.tools);
|
|
157
|
+
break;
|
|
158
|
+
case 'call':
|
|
159
|
+
await callTool(session.tools, args);
|
|
160
|
+
break;
|
|
161
|
+
case 'schema':
|
|
162
|
+
showSchema(session.tools, args[0]);
|
|
163
|
+
break;
|
|
164
|
+
case 'help':
|
|
165
|
+
showHelp();
|
|
166
|
+
break;
|
|
167
|
+
case 'exit':
|
|
168
|
+
case 'quit':
|
|
169
|
+
console.log(chalk.dim('Goodbye!'));
|
|
170
|
+
process.exit(0);
|
|
171
|
+
case 'clear':
|
|
172
|
+
agent.clearHistory();
|
|
173
|
+
console.log(chalk.dim('Conversation history cleared.'));
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
console.log(chalk.yellow(`Unknown command: ${command}`));
|
|
177
|
+
console.log(chalk.dim('Type /help for available commands.'));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function listTools(tools) {
|
|
181
|
+
console.log(chalk.bold('\nAvailable tools:'));
|
|
182
|
+
tools.forEach((tool, index) => {
|
|
183
|
+
const signature = formatToolSignature(tool);
|
|
184
|
+
console.log(chalk.cyan(`${index + 1}. ${signature}`));
|
|
185
|
+
console.log(chalk.dim(` ${tool.description}`));
|
|
186
|
+
});
|
|
187
|
+
console.log('');
|
|
188
|
+
}
|
|
189
|
+
async function callTool(tools, args) {
|
|
190
|
+
if (args.length === 0) {
|
|
191
|
+
console.log(chalk.yellow('Usage: /call <toolName> [--param value ...]'));
|
|
192
|
+
console.log(chalk.dim('Example: /call createUser --name "John" --email "john@example.com"'));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const toolName = args[0];
|
|
196
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
197
|
+
if (!tool) {
|
|
198
|
+
console.log(chalk.red(`Tool not found: ${toolName}`));
|
|
199
|
+
console.log(chalk.dim('Use /tools to see available tools.'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// Parse remaining args as --key value pairs
|
|
203
|
+
const params = parseCallArgs(args.slice(1));
|
|
204
|
+
const spinner = ora(`Calling ${toolName}...`).start();
|
|
205
|
+
try {
|
|
206
|
+
const result = await executeToolByName(tools, toolName, params);
|
|
207
|
+
spinner.succeed(`${toolName} completed`);
|
|
208
|
+
console.log(chalk.white('\nResult:'));
|
|
209
|
+
console.log(JSON.stringify(result, null, 2));
|
|
210
|
+
console.log('');
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
spinner.fail(`${toolName} failed`);
|
|
214
|
+
if (error instanceof ToolExecutionError) {
|
|
215
|
+
console.error(chalk.red(error.message));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function parseCallArgs(args) {
|
|
223
|
+
const params = {};
|
|
224
|
+
let i = 0;
|
|
225
|
+
while (i < args.length) {
|
|
226
|
+
const arg = args[i];
|
|
227
|
+
if (arg.startsWith('--')) {
|
|
228
|
+
const key = arg.slice(2);
|
|
229
|
+
const value = args[i + 1];
|
|
230
|
+
if (value === undefined) {
|
|
231
|
+
params[key] = true;
|
|
232
|
+
i++;
|
|
233
|
+
}
|
|
234
|
+
else if (value.startsWith('--')) {
|
|
235
|
+
params[key] = true;
|
|
236
|
+
i++;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// Try to parse as JSON, number, or boolean
|
|
240
|
+
params[key] = parseValue(value);
|
|
241
|
+
i += 2;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
i++;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return params;
|
|
249
|
+
}
|
|
250
|
+
function parseValue(value) {
|
|
251
|
+
// Remove quotes if present
|
|
252
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
253
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
254
|
+
return value.slice(1, -1);
|
|
255
|
+
}
|
|
256
|
+
// Try parsing as number
|
|
257
|
+
const num = Number(value);
|
|
258
|
+
if (!isNaN(num)) {
|
|
259
|
+
return num;
|
|
260
|
+
}
|
|
261
|
+
// Check for boolean
|
|
262
|
+
if (value.toLowerCase() === 'true')
|
|
263
|
+
return true;
|
|
264
|
+
if (value.toLowerCase() === 'false')
|
|
265
|
+
return false;
|
|
266
|
+
// Try parsing as JSON
|
|
267
|
+
try {
|
|
268
|
+
return JSON.parse(value);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function showSchema(tools, toolName) {
|
|
275
|
+
if (!toolName) {
|
|
276
|
+
console.log(chalk.yellow('Usage: /schema <toolName>'));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
280
|
+
if (!tool) {
|
|
281
|
+
console.log(chalk.red(`Tool not found: ${toolName}`));
|
|
282
|
+
console.log(chalk.dim('Use /tools to see available tools.'));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
console.log(chalk.bold(`\nSchema for ${toolName}:`));
|
|
286
|
+
console.log(chalk.white(formatToolSchema(tool)));
|
|
287
|
+
console.log('');
|
|
288
|
+
}
|
|
289
|
+
function showHelp() {
|
|
290
|
+
console.log(chalk.bold('\nAgent CLI Help'));
|
|
291
|
+
console.log('');
|
|
292
|
+
console.log(chalk.cyan('Chat Mode:'));
|
|
293
|
+
console.log(' Just type your message to chat with the AI agent.');
|
|
294
|
+
console.log(' The agent can use available tools to help you.');
|
|
295
|
+
console.log('');
|
|
296
|
+
console.log(chalk.cyan('Special Commands:'));
|
|
297
|
+
console.log(chalk.white(' /tools') + chalk.dim(' - List all available tools'));
|
|
298
|
+
console.log(chalk.white(' /call <tool> [args]') +
|
|
299
|
+
chalk.dim(' - Call a tool directly'));
|
|
300
|
+
console.log(chalk.white(' /schema <tool>') + chalk.dim(' - Show tool parameter schema'));
|
|
301
|
+
console.log(chalk.white(' /clear') + chalk.dim(' - Clear conversation history'));
|
|
302
|
+
console.log(chalk.white(' /help') + chalk.dim(' - Show this help message'));
|
|
303
|
+
console.log(chalk.white(' /exit') + chalk.dim(' - Exit the CLI'));
|
|
304
|
+
console.log('');
|
|
305
|
+
console.log(chalk.cyan('Examples:'));
|
|
306
|
+
console.log(chalk.dim(' > What can you do?'));
|
|
307
|
+
console.log(chalk.dim(' > Create a user named John with email john@example.com'));
|
|
308
|
+
console.log(chalk.dim(' > /call createUser --name "John" --email "john@example.com"'));
|
|
309
|
+
console.log(chalk.dim(' > /schema createUser'));
|
|
310
|
+
console.log('');
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spec2tools/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for interacting with OpenAPI-based AI tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"spec2tools": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/**/*.js",
|
|
19
|
+
"dist/**/*.d.ts"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"openapi",
|
|
23
|
+
"cli",
|
|
24
|
+
"ai",
|
|
25
|
+
"agent",
|
|
26
|
+
"tools",
|
|
27
|
+
"ai-sdk"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/CahidArda/spec2tools.git",
|
|
33
|
+
"directory": "packages/cli"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ai-sdk/openai": "^3.0.25",
|
|
37
|
+
"ai": "^6.0.68",
|
|
38
|
+
"chalk": "^5.3.0",
|
|
39
|
+
"commander": "^14.0.0",
|
|
40
|
+
"dotenv": "^17.2.3",
|
|
41
|
+
"ora": "^8.1.0",
|
|
42
|
+
"@spec2tools/core": "0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.0.0",
|
|
46
|
+
"typescript": "^5.6.0"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc",
|
|
53
|
+
"postbuild": "chmod +x dist/index.js",
|
|
54
|
+
"dev": "tsc --watch",
|
|
55
|
+
"start": "node dist/index.js",
|
|
56
|
+
"typecheck": "tsc --noEmit"
|
|
57
|
+
}
|
|
58
|
+
}
|