clarity-ai 1.3.1 → 2.0.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/.env.example +6 -0
- package/bin/clarity.js +1 -1
- package/config.json.example +8 -0
- package/package.json +10 -11
- package/src/agents/BaseAgent.js +0 -54
- package/src/agents/CodeAgent.js +0 -57
- package/src/agents/FileAgent.js +0 -39
- package/src/agents/MonitorAgent.js +0 -54
- package/src/agents/ShellAgent.js +0 -31
- package/src/agents/WebAgent.js +0 -39
- package/src/agents/manager.js +0 -116
- package/src/commands/index.js +0 -777
- package/src/config/keys.js +0 -104
- package/src/config/paths.js +0 -22
- package/src/config/settings.js +0 -28
- package/src/index.js +0 -87
- package/src/memory/context.js +0 -38
- package/src/memory/store.js +0 -54
- package/src/providers/claude.js +0 -61
- package/src/providers/deepseek.js +0 -53
- package/src/providers/gemini.js +0 -48
- package/src/providers/groq.js +0 -56
- package/src/providers/index.js +0 -39
- package/src/providers/openai.js +0 -52
- package/src/providers/openrouter.js +0 -52
- package/src/tools/bash.js +0 -25
- package/src/tools/code.js +0 -52
- package/src/tools/files.js +0 -62
- package/src/tools/git.js +0 -67
- package/src/tools/index.js +0 -40
- package/src/tools/pkg.js +0 -46
- package/src/tools/search.js +0 -29
- package/src/tools/system.js +0 -29
- package/src/tools/web.js +0 -24
- package/src/ui/banner.js +0 -25
- package/src/ui/blocks.js +0 -130
- package/src/ui/chatbox.js +0 -260
- package/src/ui/colors.js +0 -27
- package/src/ui/prompt.js +0 -126
- package/src/ui/spinner.js +0 -49
- package/src/utils/logger.js +0 -40
- package/src/utils/markdown.js +0 -25
- package/src/utils/termux.js +0 -38
- package/src/utils/version-check.js +0 -76
package/.env.example
ADDED
package/bin/clarity.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '../src/
|
|
2
|
+
import '../src/main.js';
|
package/package.json
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clarity-ai",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Autonomous AI Agent CLI for Termux",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "src/index.js",
|
|
7
6
|
"bin": {
|
|
8
7
|
"clarity": "bin/clarity.js"
|
|
9
8
|
},
|
|
10
9
|
"scripts": {
|
|
11
10
|
"start": "node bin/clarity.js",
|
|
12
|
-
"
|
|
11
|
+
"dev": "node --watch bin/clarity.js"
|
|
13
12
|
},
|
|
14
13
|
"engines": {
|
|
15
14
|
"node": ">=18.0.0"
|
|
16
15
|
},
|
|
17
16
|
"dependencies": {
|
|
18
17
|
"chalk": "^5.3.0",
|
|
18
|
+
"figlet": "^1.7.0",
|
|
19
|
+
"gradient-string": "^2.0.2",
|
|
20
|
+
"ora": "^8.1.0",
|
|
19
21
|
"inquirer": "^9.2.12",
|
|
20
22
|
"conf": "^12.0.0",
|
|
21
23
|
"marked": "^12.0.0",
|
|
22
24
|
"marked-terminal": "^7.1.0",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"cli-table3": "^0.6.3",
|
|
25
|
+
"cli-table3": "^0.6.5",
|
|
26
|
+
"wrap-ansi": "^9.0.1",
|
|
26
27
|
"semver": "^7.6.0",
|
|
27
28
|
"glob": "^10.3.12"
|
|
28
29
|
},
|
|
@@ -31,10 +32,8 @@
|
|
|
31
32
|
"cli",
|
|
32
33
|
"termux",
|
|
33
34
|
"agent",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"gemini",
|
|
37
|
-
"groq"
|
|
35
|
+
"autonomous",
|
|
36
|
+
"clarity"
|
|
38
37
|
],
|
|
39
38
|
"license": "MIT"
|
|
40
39
|
}
|
package/src/agents/BaseAgent.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import logger from '../utils/logger.js';
|
|
2
|
-
|
|
3
|
-
class BaseAgent {
|
|
4
|
-
constructor(config = {}) {
|
|
5
|
-
this.id = config.id || `agent_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
6
|
-
this.name = config.name || 'Agent';
|
|
7
|
-
this.type = config.type || 'base';
|
|
8
|
-
this.status = 'stopped';
|
|
9
|
-
this.logs = [];
|
|
10
|
-
this.config = config;
|
|
11
|
-
this._interval = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async start() {
|
|
15
|
-
this.status = 'running';
|
|
16
|
-
this.log('info', 'Agent started');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async stop() {
|
|
20
|
-
this.status = 'stopped';
|
|
21
|
-
if (this._interval) {
|
|
22
|
-
clearInterval(this._interval);
|
|
23
|
-
this._interval = null;
|
|
24
|
-
}
|
|
25
|
-
this.log('info', 'Agent stopped');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async pause() {
|
|
29
|
-
this.status = 'paused';
|
|
30
|
-
this.log('info', 'Agent paused');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async resume() {
|
|
34
|
-
this.status = 'running';
|
|
35
|
-
this.log('info', 'Agent resumed');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
log(level, msg) {
|
|
39
|
-
const entry = { level, msg, time: new Date().toISOString() };
|
|
40
|
-
this.logs.push(entry);
|
|
41
|
-
if (this.logs.length > 1000) this.logs.shift();
|
|
42
|
-
logger[level](`[${this.id}] ${msg}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
emit(event, data) {
|
|
46
|
-
this.log('info', `Event: ${event} ${data ? JSON.stringify(data) : ''}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
getStatus() {
|
|
50
|
-
return { id: this.id, name: this.name, type: this.type, status: this.status, logCount: this.logs.length };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default BaseAgent;
|
package/src/agents/CodeAgent.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
import { watch, readFileSync, existsSync } from 'fs';
|
|
3
|
-
import { resolve } from 'path';
|
|
4
|
-
|
|
5
|
-
class CodeAgent extends BaseAgent {
|
|
6
|
-
constructor(config) {
|
|
7
|
-
super({ ...config, type: 'code', name: config.name || 'Code Watcher' });
|
|
8
|
-
this.file = config.file;
|
|
9
|
-
this.language = config.language || 'auto';
|
|
10
|
-
this.autoFix = config.autoFix || false;
|
|
11
|
-
this._watcher = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async start() {
|
|
15
|
-
if (!this.file) {
|
|
16
|
-
this.log('error', 'No file specified');
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
await super.start();
|
|
20
|
-
const filePath = resolve(this.file);
|
|
21
|
-
this.log('info', `Watching ${filePath} for errors`);
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const content = readFileSync(filePath, 'utf8');
|
|
25
|
-
this.analyze(content);
|
|
26
|
-
} catch {}
|
|
27
|
-
|
|
28
|
-
this._watcher = watch(filePath, (eventType) => {
|
|
29
|
-
if (eventType === 'change') {
|
|
30
|
-
try {
|
|
31
|
-
const content = readFileSync(filePath, 'utf8');
|
|
32
|
-
this.analyze(content);
|
|
33
|
-
} catch {}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
analyze(content) {
|
|
39
|
-
const lines = content.split('\n');
|
|
40
|
-
const errors = [];
|
|
41
|
-
lines.forEach((line, i) => {
|
|
42
|
-
if (line.length > 200) errors.push({ line: i + 1, msg: 'Line too long' });
|
|
43
|
-
if (line.includes('\t')) errors.push({ line: i + 1, msg: 'Tab character detected' });
|
|
44
|
-
});
|
|
45
|
-
if (errors.length > 0) {
|
|
46
|
-
this.log('warn', `Found ${errors.length} issues in ${this.file}`);
|
|
47
|
-
errors.forEach(e => this.log('warn', ` L${e.line}: ${e.msg}`));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async stop() {
|
|
52
|
-
if (this._watcher) { this._watcher.close(); this._watcher = null; }
|
|
53
|
-
await super.stop();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export default CodeAgent;
|
package/src/agents/FileAgent.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
import { watch } from 'fs';
|
|
3
|
-
import { resolve } from 'path';
|
|
4
|
-
|
|
5
|
-
class FileAgent extends BaseAgent {
|
|
6
|
-
constructor(config) {
|
|
7
|
-
super({ ...config, type: 'file', name: config.name || 'File Watcher' });
|
|
8
|
-
this.watchDir = config.watchDir || '.';
|
|
9
|
-
this.pattern = config.pattern || '*';
|
|
10
|
-
this.onChangePrompt = config.onChangePrompt || 'File changed: {path}';
|
|
11
|
-
this._watcher = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async start() {
|
|
15
|
-
await super.start();
|
|
16
|
-
try {
|
|
17
|
-
const dir = resolve(this.watchDir);
|
|
18
|
-
this._watcher = watch(dir, { recursive: true }, (eventType, filename) => {
|
|
19
|
-
if (filename && filename.match(this.pattern.replace('*', '.*'))) {
|
|
20
|
-
this.log('info', `File ${eventType}: ${filename}`);
|
|
21
|
-
this.emit('fileChange', { eventType, filename });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
this.log('info', `Watching ${dir} for changes`);
|
|
25
|
-
} catch (err) {
|
|
26
|
-
this.log('error', `Watch error: ${err.message}`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async stop() {
|
|
31
|
-
if (this._watcher) {
|
|
32
|
-
this._watcher.close();
|
|
33
|
-
this._watcher = null;
|
|
34
|
-
}
|
|
35
|
-
await super.stop();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default FileAgent;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
|
|
4
|
-
class MonitorAgent extends BaseAgent {
|
|
5
|
-
constructor(config) {
|
|
6
|
-
super({ ...config, type: 'monitor', name: config.name || 'System Monitor' });
|
|
7
|
-
this.intervalMs = config.intervalMs || 5000;
|
|
8
|
-
this.alertThresholds = config.alertThresholds || { cpu: 90, memory: 90 };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async start() {
|
|
12
|
-
await super.start();
|
|
13
|
-
this.log('info', `Monitoring system every ${this.intervalMs}ms`);
|
|
14
|
-
this._interval = setInterval(() => this.check(), this.intervalMs);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
check() {
|
|
18
|
-
const mem = process.memoryUsage();
|
|
19
|
-
const memPct = Math.round((mem.heapUsed / mem.heapTotal) * 100);
|
|
20
|
-
const load = os.loadavg()[0];
|
|
21
|
-
const totalMem = os.totalmem();
|
|
22
|
-
const freeMem = os.freemem();
|
|
23
|
-
const memUsage = Math.round(((totalMem - freeMem) / totalMem) * 100);
|
|
24
|
-
|
|
25
|
-
const stats = {
|
|
26
|
-
cpu: load.toFixed(2),
|
|
27
|
-
memory: memPct,
|
|
28
|
-
systemMem: memUsage,
|
|
29
|
-
heap: Math.round(mem.heapUsed / 1024 / 1024) + 'MB',
|
|
30
|
-
uptime: Math.round(process.uptime()) + 's',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
if (memPct > this.alertThresholds.memory) {
|
|
34
|
-
this.log('warn', `High memory usage: ${memPct}%`);
|
|
35
|
-
}
|
|
36
|
-
if (load > this.alertThresholds.cpu / 100) {
|
|
37
|
-
this.log('warn', `High CPU load: ${load}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
this.emit('stats', stats);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getStats() {
|
|
44
|
-
const mem = process.memoryUsage();
|
|
45
|
-
return {
|
|
46
|
-
cpu: os.loadavg()[0].toFixed(2),
|
|
47
|
-
memory: Math.round((mem.heapUsed / mem.heapTotal) * 100),
|
|
48
|
-
uptime: Math.round(process.uptime()),
|
|
49
|
-
os: `${os.platform()} ${os.arch()}`,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default MonitorAgent;
|
package/src/agents/ShellAgent.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
import { exec } from 'child_process';
|
|
3
|
-
import { promisify } from 'util';
|
|
4
|
-
|
|
5
|
-
const execAsync = promisify(exec);
|
|
6
|
-
|
|
7
|
-
class ShellAgent extends BaseAgent {
|
|
8
|
-
constructor(config) {
|
|
9
|
-
super({ ...config, type: 'shell', name: config.name || 'Shell Runner' });
|
|
10
|
-
this.command = config.command || 'echo hello';
|
|
11
|
-
this.intervalMs = config.intervalMs || 60000;
|
|
12
|
-
this.storeOutput = config.storeOutput !== false;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async start() {
|
|
16
|
-
await super.start();
|
|
17
|
-
this.log('info', `Running command every ${this.intervalMs}ms: ${this.command}`);
|
|
18
|
-
this._interval = setInterval(async () => {
|
|
19
|
-
try {
|
|
20
|
-
const { stdout, stderr } = await execAsync(this.command);
|
|
21
|
-
if (this.storeOutput) this.log('info', `Output: ${stdout.trim().slice(0, 200)}`);
|
|
22
|
-
if (stderr) this.log('warn', `Stderr: ${stderr.trim().slice(0, 200)}`);
|
|
23
|
-
this.emit('commandComplete', { command: this.command, stdout, stderr });
|
|
24
|
-
} catch (err) {
|
|
25
|
-
this.log('error', `Command failed: ${err.message}`);
|
|
26
|
-
}
|
|
27
|
-
}, this.intervalMs);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default ShellAgent;
|
package/src/agents/WebAgent.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
|
|
3
|
-
class WebAgent extends BaseAgent {
|
|
4
|
-
constructor(config) {
|
|
5
|
-
super({ ...config, type: 'web', name: config.name || 'Web Fetcher' });
|
|
6
|
-
this.url = config.url;
|
|
7
|
-
this.intervalMs = config.intervalMs || 300000;
|
|
8
|
-
this.lastContent = '';
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async start() {
|
|
12
|
-
if (!this.url) {
|
|
13
|
-
this.log('error', 'No URL configured');
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
await super.start();
|
|
17
|
-
this.log('info', `Fetching ${this.url} every ${this.intervalMs}ms`);
|
|
18
|
-
await this.fetch();
|
|
19
|
-
this._interval = setInterval(() => this.fetch(), this.intervalMs);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async fetch() {
|
|
23
|
-
try {
|
|
24
|
-
const res = await fetch(this.url);
|
|
25
|
-
const text = await res.text();
|
|
26
|
-
const clean = text.replace(/<[^>]+>/g, '').trim().slice(0, 500);
|
|
27
|
-
if (this.lastContent && clean !== this.lastContent) {
|
|
28
|
-
this.log('info', 'Content changed!');
|
|
29
|
-
this.emit('contentChange', { url: this.url, preview: clean.slice(0, 100) });
|
|
30
|
-
}
|
|
31
|
-
this.lastContent = clean;
|
|
32
|
-
this.log('info', `Fetched ${this.url} (${text.length} bytes)`);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
this.log('error', `Fetch failed: ${err.message}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default WebAgent;
|
package/src/agents/manager.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import BaseAgent from './BaseAgent.js';
|
|
2
|
-
import FileAgent from './FileAgent.js';
|
|
3
|
-
import ShellAgent from './ShellAgent.js';
|
|
4
|
-
import WebAgent from './WebAgent.js';
|
|
5
|
-
import CodeAgent from './CodeAgent.js';
|
|
6
|
-
import MonitorAgent from './MonitorAgent.js';
|
|
7
|
-
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
|
8
|
-
import { resolve, dirname } from 'path';
|
|
9
|
-
import paths from '../config/paths.js';
|
|
10
|
-
import blocks from '../ui/blocks.js';
|
|
11
|
-
|
|
12
|
-
const AGENT_MAP = {
|
|
13
|
-
file: FileAgent,
|
|
14
|
-
shell: ShellAgent,
|
|
15
|
-
web: WebAgent,
|
|
16
|
-
code: CodeAgent,
|
|
17
|
-
monitor: MonitorAgent,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
class AgentManager {
|
|
21
|
-
constructor() {
|
|
22
|
-
this.agents = new Map();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async start(agentType, config = {}) {
|
|
26
|
-
const AgentClass = AGENT_MAP[agentType];
|
|
27
|
-
if (!AgentClass) throw new Error(`Unknown agent type: ${agentType}`);
|
|
28
|
-
|
|
29
|
-
const agent = new AgentClass(config);
|
|
30
|
-
this.agents.set(agent.id, agent);
|
|
31
|
-
await agent.start();
|
|
32
|
-
this.save();
|
|
33
|
-
return agent.id;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async stop(agentId) {
|
|
37
|
-
const agent = this.agents.get(agentId);
|
|
38
|
-
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
39
|
-
await agent.stop();
|
|
40
|
-
this.agents.delete(agentId);
|
|
41
|
-
this.save();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
list() {
|
|
45
|
-
return Array.from(this.agents.values()).map(a => a.getStatus());
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getLogs(agentId, lines = 50) {
|
|
49
|
-
const agent = this.agents.get(agentId);
|
|
50
|
-
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
51
|
-
return agent.logs.slice(-lines);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async restart(agentId) {
|
|
55
|
-
const agent = this.agents.get(agentId);
|
|
56
|
-
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
57
|
-
await agent.stop();
|
|
58
|
-
await agent.start();
|
|
59
|
-
this.save();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
save() {
|
|
63
|
-
try {
|
|
64
|
-
const dir = dirname(paths.agents);
|
|
65
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
66
|
-
const data = Array.from(this.agents.values()).map(a => ({
|
|
67
|
-
type: a.type, config: a.config, id: a.id, name: a.name, status: a.status
|
|
68
|
-
}));
|
|
69
|
-
writeFileSync(paths.agents, JSON.stringify(data, null, 2));
|
|
70
|
-
} catch {}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async restore() {
|
|
74
|
-
try {
|
|
75
|
-
if (!existsSync(paths.agents)) return;
|
|
76
|
-
const data = JSON.parse(readFileSync(paths.agents, 'utf8'));
|
|
77
|
-
for (const item of data) {
|
|
78
|
-
const AgentClass = AGENT_MAP[item.type];
|
|
79
|
-
if (AgentClass && item.status === 'running') {
|
|
80
|
-
const agent = new AgentClass(item.config);
|
|
81
|
-
this.agents.set(agent.id, agent);
|
|
82
|
-
await agent.start();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
} catch {}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
showStatus() {
|
|
89
|
-
const list = this.list();
|
|
90
|
-
if (list.length === 0) {
|
|
91
|
-
blocks.info('Agents', 'No agents running.');
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const headers = ['ID', 'Name', 'Type', 'Status', 'Logs'];
|
|
95
|
-
const rows = list.map(a => [
|
|
96
|
-
a.id.slice(0, 12),
|
|
97
|
-
a.name,
|
|
98
|
-
a.type,
|
|
99
|
-
statusBadge(a.status),
|
|
100
|
-
String(a.logCount),
|
|
101
|
-
]);
|
|
102
|
-
blocks.table(headers, rows);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function statusBadge(status) {
|
|
107
|
-
switch (status) {
|
|
108
|
-
case 'running': return '● running';
|
|
109
|
-
case 'error': return '✗ error';
|
|
110
|
-
case 'idle': return '○ idle';
|
|
111
|
-
default: return '– stopped';
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const agentManager = new AgentManager();
|
|
116
|
-
export default agentManager;
|