chore: change run command, remove workspace cli argument
This commit is contained in:
32
README.md
32
README.md
@@ -42,7 +42,7 @@ mkdir -p ~/.nanobot
|
||||
**2. Chat**
|
||||
|
||||
```bash
|
||||
bun run start agent
|
||||
bun run nanobot agent
|
||||
```
|
||||
|
||||
That's it.
|
||||
@@ -54,22 +54,21 @@ That's it.
|
||||
Chat with the agent from your terminal. Does not require a running gateway.
|
||||
|
||||
```
|
||||
bun run start agent [options]
|
||||
bun run nanobot agent [options]
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
||||
| `-m, --message <text>` | Send a single message and exit (non-interactive) |
|
||||
| `-w, --workspace <path>` | Override the workspace directory |
|
||||
| `-M, --model <model>` | Override the model for this session |
|
||||
|
||||
**Interactive mode** (default when no `-m` is given):
|
||||
|
||||
```bash
|
||||
bun run start agent
|
||||
bun run start agent -c ~/.nanobot-work/config.json
|
||||
bun run start agent -w /tmp/scratch
|
||||
bun run nanobot agent
|
||||
bun run nanobot agent -c ~/.nanobot-work/config.json
|
||||
bun run nanobot agent -w /tmp/scratch
|
||||
```
|
||||
|
||||
Press `Ctrl+C` to exit.
|
||||
@@ -77,8 +76,8 @@ Press `Ctrl+C` to exit.
|
||||
**Single-shot mode:**
|
||||
|
||||
```bash
|
||||
bun run start agent -m "What time is it in Tokyo?"
|
||||
bun run start agent -m "Summarize the file ./notes.md"
|
||||
bun run nanobot agent -m "What time is it in Tokyo?"
|
||||
bun run nanobot agent -m "Summarize the file ./notes.md"
|
||||
```
|
||||
|
||||
### `gateway` — Mattermost bot
|
||||
@@ -86,7 +85,7 @@ bun run start agent -m "Summarize the file ./notes.md"
|
||||
Runs the full stack: Mattermost WebSocket channel, agent loop, cron scheduler, and heartbeat.
|
||||
|
||||
```
|
||||
bun run start gateway [options]
|
||||
bun run nanobot gateway [options]
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
@@ -94,8 +93,8 @@ bun run start gateway [options]
|
||||
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
||||
|
||||
```bash
|
||||
bun run start gateway
|
||||
bun run start gateway -c ~/.nanobot-work/config.json
|
||||
bun run nanobot gateway
|
||||
bun run nanobot gateway -c ~/.nanobot-work/config.json
|
||||
```
|
||||
|
||||
Handles `SIGINT` / `SIGTERM` for graceful shutdown.
|
||||
@@ -110,7 +109,6 @@ Environment variable overrides:
|
||||
|----------|-------------------|
|
||||
| `NANOBOT_CONFIG` | path to config file |
|
||||
| `NANOBOT_MODEL` | `agent.model` |
|
||||
| `NANOBOT_WORKSPACE` | `agent.workspacePath` |
|
||||
|
||||
### Full config reference
|
||||
|
||||
@@ -198,7 +196,7 @@ For Ollama, set `providers.ollama.apiBase` (default: `http://localhost:11434/api
|
||||
}
|
||||
```
|
||||
|
||||
4. Run `bun run start gateway`
|
||||
4. Run `bun run nanobot gateway`
|
||||
|
||||
`allowFrom` controls which users the bot responds to. Use `["*"]` to allow all users.
|
||||
|
||||
@@ -233,10 +231,10 @@ Run separate instances with different configs — useful for isolated workspaces
|
||||
|
||||
```bash
|
||||
# Instance A
|
||||
bun run start gateway -c ~/.nanobot-a/config.json
|
||||
bun run nanobot gateway -c ~/.nanobot-a/config.json
|
||||
|
||||
# Instance B
|
||||
bun run start gateway -c ~/.nanobot-b/config.json
|
||||
bun run nanobot gateway -c ~/.nanobot-b/config.json
|
||||
```
|
||||
|
||||
Each instance needs its own config file. Set a different `agent.workspacePath` per instance to keep memory, sessions, and cron jobs isolated:
|
||||
@@ -252,10 +250,10 @@ Each instance needs its own config file. Set a different `agent.workspacePath` p
|
||||
To run a local CLI session against a specific instance:
|
||||
|
||||
```bash
|
||||
bun run start agent -c ~/.nanobot-a/config.json -m "Hello"
|
||||
bun run nanobot agent -c ~/.nanobot-a/config.json -m "Hello"
|
||||
|
||||
# Temporarily override the workspace for a one-off run
|
||||
bun run start agent -c ~/.nanobot-a/config.json -w /tmp/scratch
|
||||
bun run nanobot agent -c ~/.nanobot-a/config.json -w /tmp/scratch
|
||||
```
|
||||
|
||||
## Linux service (systemd)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "module",
|
||||
"module": "index.ts",
|
||||
"scripts": {
|
||||
"start": "bun run index.ts",
|
||||
"nanobot": "bun run index.ts",
|
||||
"dev": "bun --watch run index.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"fmt": "oxfmt --check",
|
||||
|
||||
129
src/cli/agent.ts
129
src/cli/agent.ts
@@ -1,4 +1,3 @@
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { createInterface } from 'node:readline';
|
||||
import { Command } from 'commander';
|
||||
import pc from 'picocolors';
|
||||
@@ -8,85 +7,79 @@ import type { Config } from '../config/types.ts';
|
||||
import { makeProvider } from '../provider/index.ts';
|
||||
|
||||
export function agentCommand(program: Command, config: Config, workspace: string): void {
|
||||
mkdirSync(workspace, { recursive: true });
|
||||
|
||||
program
|
||||
.command('agent')
|
||||
.description('Run the agent interactively or send a single message.')
|
||||
.option('-c, --config <path>', 'Path to config.json')
|
||||
.option('-m, --message <text>', 'Single message to process (non-interactive)')
|
||||
.option('-w, --workspace <path>', 'Workspace path override')
|
||||
.option('-M, --model <model>', 'Model override')
|
||||
.action(
|
||||
async (opts: { config?: string; message?: string; workspace?: string; model?: string }) => {
|
||||
const model = opts.model ?? config.agent.model;
|
||||
const provider = makeProvider(
|
||||
config.providers,
|
||||
model,
|
||||
config.agent.maxTokens,
|
||||
config.agent.temperature,
|
||||
);
|
||||
const bus = new MessageBus();
|
||||
.action(async (opts: { config?: string; message?: string; model?: string }) => {
|
||||
const model = opts.model ?? config.agent.model;
|
||||
const provider = makeProvider(
|
||||
config.providers,
|
||||
model,
|
||||
config.agent.maxTokens,
|
||||
config.agent.temperature,
|
||||
);
|
||||
const bus = new MessageBus();
|
||||
|
||||
const agentLoop = new AgentLoop({
|
||||
bus,
|
||||
provider,
|
||||
workspace,
|
||||
model,
|
||||
maxIterations: config.agent.maxToolIterations,
|
||||
contextWindowTokens: config.agent.contextWindowTokens,
|
||||
braveApiKey: config.tools.web.braveApiKey,
|
||||
webProxy: config.tools.web.proxy,
|
||||
execConfig: config.tools.exec,
|
||||
restrictToWorkspace: config.tools.restrictToWorkspace,
|
||||
});
|
||||
const agentLoop = new AgentLoop({
|
||||
bus,
|
||||
provider,
|
||||
workspace,
|
||||
model,
|
||||
maxIterations: config.agent.maxToolIterations,
|
||||
contextWindowTokens: config.agent.contextWindowTokens,
|
||||
braveApiKey: config.tools.web.braveApiKey,
|
||||
webProxy: config.tools.web.proxy,
|
||||
execConfig: config.tools.exec,
|
||||
restrictToWorkspace: config.tools.restrictToWorkspace,
|
||||
});
|
||||
|
||||
// Single-shot mode
|
||||
if (opts.message) {
|
||||
const result = await agentLoop.processDirect(opts.message);
|
||||
console.log(result);
|
||||
return;
|
||||
}
|
||||
// Single-shot mode
|
||||
if (opts.message) {
|
||||
const result = await agentLoop.processDirect(opts.message);
|
||||
console.log(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Interactive mode
|
||||
console.info(pc.green('nanobot interactive mode. Type your message, Ctrl+C to exit.'));
|
||||
// Interactive mode
|
||||
console.info(pc.green('nanobot interactive mode. Type your message, Ctrl+C to exit.'));
|
||||
|
||||
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||
|
||||
const promptUser = () => {
|
||||
rl.question(pc.cyan('You: '), async (input) => {
|
||||
const text = input.trim();
|
||||
if (!text) {
|
||||
promptUser();
|
||||
return;
|
||||
}
|
||||
|
||||
const onProgress = async (content: string, opts?: { toolHint?: boolean }) => {
|
||||
if (opts?.toolHint) {
|
||||
process.stdout.write(pc.dim(` [${content}]\n`));
|
||||
} else {
|
||||
process.stdout.write(pc.dim(` ${content}\n`));
|
||||
}
|
||||
};
|
||||
|
||||
const result = await agentLoop.processDirect(
|
||||
text,
|
||||
'cli:interactive',
|
||||
'cli',
|
||||
'interactive',
|
||||
onProgress,
|
||||
);
|
||||
console.log(pc.bold('Bot:'), result);
|
||||
const promptUser = () => {
|
||||
rl.question(pc.cyan('You: '), async (input) => {
|
||||
const text = input.trim();
|
||||
if (!text) {
|
||||
promptUser();
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
rl.on('close', () => {
|
||||
agentLoop.stop();
|
||||
process.exit(0);
|
||||
const onProgress = async (content: string, opts?: { toolHint?: boolean }) => {
|
||||
if (opts?.toolHint) {
|
||||
process.stdout.write(pc.dim(` [${content}]\n`));
|
||||
} else {
|
||||
process.stdout.write(pc.dim(` ${content}\n`));
|
||||
}
|
||||
};
|
||||
|
||||
const result = await agentLoop.processDirect(
|
||||
text,
|
||||
'cli:interactive',
|
||||
'cli',
|
||||
'interactive',
|
||||
onProgress,
|
||||
);
|
||||
console.log(pc.bold('Bot:'), result);
|
||||
promptUser();
|
||||
});
|
||||
};
|
||||
|
||||
promptUser();
|
||||
},
|
||||
);
|
||||
rl.on('close', () => {
|
||||
agentLoop.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
promptUser();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,15 +3,19 @@ import { Command } from 'commander';
|
||||
import { loadConfig, resolveWorkspacePath } from '../config/loader.ts';
|
||||
import { agentCommand } from './agent.ts';
|
||||
import { gatewayCommand } from './gateway.ts';
|
||||
import pc from 'picocolors';
|
||||
|
||||
export function createCli(): Command {
|
||||
const program = new Command('nanobot')
|
||||
.description('nanobot — personal AI assistant')
|
||||
.option('-c, --config <path>', 'Path to config.json')
|
||||
.version('1.0.0');
|
||||
|
||||
const globalOpts = program.opts();
|
||||
const config = loadConfig(globalOpts.config);
|
||||
const workspace = resolveWorkspacePath(config.agent.workspacePath);
|
||||
|
||||
console.info(pc.magenta(`workspace path: ${workspace}`));
|
||||
mkdirSync(workspace, { recursive: true });
|
||||
|
||||
gatewayCommand(program, config, workspace);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { Command } from 'commander';
|
||||
import pc from 'picocolors';
|
||||
import { AgentLoop } from '../agent/loop.ts';
|
||||
@@ -11,12 +10,9 @@ import { HeartbeatService } from '../heartbeat/service.ts';
|
||||
import { makeProvider } from '../provider/index.ts';
|
||||
|
||||
export function gatewayCommand(program: Command, config: Config, workspace: string): void {
|
||||
mkdirSync(workspace, { recursive: true });
|
||||
|
||||
program
|
||||
.command('gateway')
|
||||
.description('Start the full gateway: Mattermost channel, agent loop, cron, and heartbeat.')
|
||||
.option('-c, --config <path>', 'Path to config.json')
|
||||
.action(async (_opts: { config?: string }) => {
|
||||
const provider = makeProvider(
|
||||
config.providers,
|
||||
|
||||
Reference in New Issue
Block a user