@stan-chen/simple-cli 0.2.5 → 0.2.7
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/README.md +77 -67
- package/dist/agents/jules.d.ts +21 -0
- package/dist/agents/jules.js +206 -0
- package/dist/agents/jules_client.d.ts +21 -0
- package/dist/agents/jules_client.js +158 -0
- package/dist/anyllm.py +6 -1
- package/dist/async_tasks.d.ts +20 -0
- package/dist/async_tasks.js +110 -0
- package/dist/builtins.d.ts +380 -20
- package/dist/builtins.js +387 -10
- package/dist/claw/jit.d.ts +5 -0
- package/dist/claw/jit.js +14 -0
- package/dist/cli.js +55 -4
- package/dist/config.d.ts +27 -0
- package/dist/config.js +21 -0
- package/dist/engine.js +79 -34
- package/dist/llm.d.ts +12 -6
- package/dist/llm.js +162 -56
- package/dist/mcp.js +3 -2
- package/dist/scheduler.d.ts +23 -0
- package/dist/scheduler.js +145 -0
- package/dist/swarm/remote_worker.d.ts +14 -0
- package/dist/swarm/remote_worker.js +9 -0
- package/dist/swarm/server.d.ts +17 -0
- package/dist/swarm/server.js +39 -0
- package/dist/tui.js +2 -7
- package/package.json +4 -4
- /package/{assets → docs/assets}/logo.jpeg +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { spawn, exec } from 'child_process';
|
|
2
|
+
import { mkdir, writeFile, readFile, readdir } from 'fs/promises';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
export class AsyncTaskManager {
|
|
8
|
+
static instance;
|
|
9
|
+
tasksDir;
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.tasksDir = join(cwd, '.agent', 'tasks');
|
|
12
|
+
}
|
|
13
|
+
static getInstance(cwd = process.cwd()) {
|
|
14
|
+
if (!AsyncTaskManager.instance) {
|
|
15
|
+
AsyncTaskManager.instance = new AsyncTaskManager(cwd);
|
|
16
|
+
}
|
|
17
|
+
return AsyncTaskManager.instance;
|
|
18
|
+
}
|
|
19
|
+
async init() {
|
|
20
|
+
if (!existsSync(this.tasksDir)) {
|
|
21
|
+
await mkdir(this.tasksDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async startTask(command, args, env = {}) {
|
|
25
|
+
await this.init();
|
|
26
|
+
const id = `task-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
|
|
27
|
+
const logFile = join(this.tasksDir, `${id}.log`);
|
|
28
|
+
const metaFile = join(this.tasksDir, `${id}.json`);
|
|
29
|
+
// Prepare Task Metadata
|
|
30
|
+
const taskCmd = `${command} ${args.join(' ')}`;
|
|
31
|
+
// Spawn detached process
|
|
32
|
+
const out = await import('fs').then(fs => fs.openSync(logFile, 'a'));
|
|
33
|
+
const err = await import('fs').then(fs => fs.openSync(logFile, 'a'));
|
|
34
|
+
const child = spawn(command, args, {
|
|
35
|
+
detached: true,
|
|
36
|
+
stdio: ['ignore', out, err],
|
|
37
|
+
env: { ...process.env, ...env }
|
|
38
|
+
});
|
|
39
|
+
child.unref();
|
|
40
|
+
const task = {
|
|
41
|
+
id,
|
|
42
|
+
command: taskCmd,
|
|
43
|
+
startTime: Date.now(),
|
|
44
|
+
pid: child.pid,
|
|
45
|
+
logFile,
|
|
46
|
+
status: 'running'
|
|
47
|
+
};
|
|
48
|
+
await writeFile(metaFile, JSON.stringify(task, null, 2));
|
|
49
|
+
return id;
|
|
50
|
+
}
|
|
51
|
+
async getTaskStatus(id) {
|
|
52
|
+
await this.init();
|
|
53
|
+
const metaFile = join(this.tasksDir, `${id}.json`);
|
|
54
|
+
if (!existsSync(metaFile)) {
|
|
55
|
+
throw new Error(`Task ${id} not found`);
|
|
56
|
+
}
|
|
57
|
+
const task = JSON.parse(await readFile(metaFile, 'utf-8'));
|
|
58
|
+
// Check if process is still running
|
|
59
|
+
if (task.status === 'running') {
|
|
60
|
+
try {
|
|
61
|
+
// process.kill(pid, 0) checks if process exists
|
|
62
|
+
process.kill(task.pid, 0);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
// Process not found, it must have finished
|
|
66
|
+
task.status = 'completed'; // We act optimistic, need to check exit code?
|
|
67
|
+
// Actually, we can't easily check exit code of detached process without waiting for it
|
|
68
|
+
// Ideally, we'd check the log file for success indicators or have the process write its exit code.
|
|
69
|
+
// For now, let's check log file for "SUCCESS" or "FAILURE" if possible,
|
|
70
|
+
// or just mark as 'completed' (meaning finished execution).
|
|
71
|
+
const logs = await this.getTaskLogs(id, 1000); // Check last lines
|
|
72
|
+
if (logs.includes('FAILURE') || logs.includes('Error:')) {
|
|
73
|
+
task.status = 'failed';
|
|
74
|
+
}
|
|
75
|
+
else if (logs.includes('SUCCESS') || logs.includes('PR Created:')) {
|
|
76
|
+
task.status = 'completed';
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
task.status = 'completed'; // Assumed success
|
|
80
|
+
}
|
|
81
|
+
await writeFile(metaFile, JSON.stringify(task, null, 2));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return task;
|
|
85
|
+
}
|
|
86
|
+
async getTaskLogs(id, lines = 20) {
|
|
87
|
+
await this.init();
|
|
88
|
+
const logFile = join(this.tasksDir, `${id}.log`);
|
|
89
|
+
if (!existsSync(logFile))
|
|
90
|
+
return '';
|
|
91
|
+
const content = await readFile(logFile, 'utf-8');
|
|
92
|
+
const allLines = content.split('\n');
|
|
93
|
+
return allLines.slice(-lines).join('\n');
|
|
94
|
+
}
|
|
95
|
+
async listTasks() {
|
|
96
|
+
await this.init();
|
|
97
|
+
const files = await readdir(this.tasksDir);
|
|
98
|
+
const tasks = [];
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
if (file.endsWith('.json')) {
|
|
101
|
+
try {
|
|
102
|
+
const content = await readFile(join(this.tasksDir, file), 'utf-8');
|
|
103
|
+
tasks.push(JSON.parse(content));
|
|
104
|
+
}
|
|
105
|
+
catch { }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return tasks.sort((a, b) => b.startTime - a.startTime);
|
|
109
|
+
}
|
|
110
|
+
}
|