paean 0.10.10 → 0.10.11
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/dist/worker/index.d.ts +4 -2
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +5 -4
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/worker.d.ts +0 -7
- package/dist/commands/worker.d.ts.map +0 -1
- package/dist/commands/worker.js +0 -594
- package/dist/commands/worker.js.map +0 -1
- package/dist/mcp/worker-tools.d.ts +0 -16
- package/dist/mcp/worker-tools.d.ts.map +0 -1
- package/dist/mcp/worker-tools.js +0 -392
- package/dist/mcp/worker-tools.js.map +0 -1
- package/dist/ui/hooks/useWorker.d.ts +0 -49
- package/dist/ui/hooks/useWorker.d.ts.map +0 -1
- package/dist/ui/hooks/useWorker.js +0 -194
- package/dist/ui/hooks/useWorker.js.map +0 -1
- package/dist/worker/service.d.ts +0 -158
- package/dist/worker/service.d.ts.map +0 -1
- package/dist/worker/service.js +0 -818
- package/dist/worker/service.js.map +0 -1
- package/dist/worker/supervisor.d.ts +0 -121
- package/dist/worker/supervisor.d.ts.map +0 -1
- package/dist/worker/supervisor.js +0 -984
- package/dist/worker/supervisor.js.map +0 -1
package/dist/worker/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Worker Module Exports
|
|
3
|
+
*
|
|
4
|
+
* The task polling/execution WorkerService and SupervisorAgent have been removed
|
|
5
|
+
* (replaced by loop + gateway heartbeat). This module now only exports the
|
|
6
|
+
* executor framework and configuration used by MCP agent tools.
|
|
3
7
|
*/
|
|
4
|
-
export { WorkerService, getWorker, resetWorker } from './service.js';
|
|
5
8
|
export { type WorkerConfig, type WorkerState, type TaskContext, type TaskResult, type WorkerEvent, type WorkerEventHandler, type WorkerStatus, DEFAULT_WORKER_CONFIG, buildTaskPrompt, type ExecutorType, type ExecutorConfig, type ExecutorOptions, type ExecutorResult, type SupervisorDecision, type VerificationResult, type RecoveryStrategy, type PromptCommandOptions, type PromptResult, type WorkerExecutorConfig, DEFAULT_EXECUTOR_CONFIG, type AvailabilityStatus, type AvailabilityAuthStatus, } from './types.js';
|
|
6
|
-
export { SupervisorAgent, getSupervisor, resetSupervisor } from './supervisor.js';
|
|
7
9
|
export { type AgentExecutor, ExecutorRegistry, getExecutorRegistry, resetExecutorRegistry, } from './executors/index.js';
|
|
8
10
|
export { InternalExecutor } from './executors/internal.js';
|
|
9
11
|
export { ClaudeExecutor } from './executors/claude.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,qBAAqB,EACrB,eAAe,EAEf,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,uBAAuB,EAEvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC9B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,KAAK,aAAa,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EAEnB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
|
package/dist/worker/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Worker Module Exports
|
|
3
|
+
*
|
|
4
|
+
* The task polling/execution WorkerService and SupervisorAgent have been removed
|
|
5
|
+
* (replaced by loop + gateway heartbeat). This module now only exports the
|
|
6
|
+
* executor framework and configuration used by MCP agent tools.
|
|
3
7
|
*/
|
|
4
|
-
export { WorkerService, getWorker, resetWorker } from './service.js';
|
|
5
8
|
export { DEFAULT_WORKER_CONFIG, buildTaskPrompt, DEFAULT_EXECUTOR_CONFIG, } from './types.js';
|
|
6
|
-
// Supervisor Agent
|
|
7
|
-
export { SupervisorAgent, getSupervisor, resetSupervisor } from './supervisor.js';
|
|
8
9
|
// Executor Framework
|
|
9
10
|
export { ExecutorRegistry, getExecutorRegistry, resetExecutorRegistry, } from './executors/index.js';
|
|
10
11
|
// Individual Executors
|
|
@@ -17,7 +18,7 @@ export { OpenCodeExecutor } from './executors/opencode.js';
|
|
|
17
18
|
export { DroidExecutor } from './executors/droid.js';
|
|
18
19
|
export { ArticulateExecutor } from './executors/articulate.js';
|
|
19
20
|
export { PaeanclawExecutor } from './executors/paeanclaw.js';
|
|
20
|
-
// Worker Config
|
|
21
|
+
// Worker Config
|
|
21
22
|
export { getWorkerConfigPath, loadWorkerConfig, saveWorkerConfig, initWorkerConfigIfNeeded, mergeWorkerConfig, isPathInSandbox, getSandboxPaths, validateSandboxPath,
|
|
22
23
|
// Executor preference management
|
|
23
24
|
getExecutorPreference, setExecutorPreference, isExecutorUserDisabled, setExecutorEnabled, getAllExecutorPreferences, } from './config.js';
|
package/dist/worker/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAQH,qBAAqB,EACrB,eAAe,EAYf,uBAAuB,GAI1B,MAAM,YAAY,CAAC;AAEpB,qBAAqB;AACrB,OAAO,EAEH,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,uBAAuB;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,gBAAgB;AAChB,OAAO,EAGH,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB;AACnB,iCAAiC;AACjC,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/commands/worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2EpC,eAAO,MAAM,aAAa,SAC8C,CAAC"}
|
package/dist/commands/worker.js
DELETED
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Worker Command
|
|
3
|
-
* CLI commands for the Local Autonomous Worker
|
|
4
|
-
*/
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import { existsSync, statSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import { WorkerService, DEFAULT_WORKER_CONFIG, getSupervisor, getExecutorRegistry, InternalExecutor, ClaudeExecutor, GeminiExecutor, CursorExecutor, CodexExecutor, OpenCodeExecutor, DroidExecutor, ArticulateExecutor, PaeanclawExecutor, } from '../worker/index.js';
|
|
10
|
-
import { isAuthenticated, getConfigDir } from '../utils/config.js';
|
|
11
|
-
import * as output from '../utils/output.js';
|
|
12
|
-
import { createTodoItem } from '../api/todo.js';
|
|
13
|
-
function getWorkerPidFile() {
|
|
14
|
-
return join(getConfigDir(), 'worker.pid');
|
|
15
|
-
}
|
|
16
|
-
function writeWorkerPid(workspace) {
|
|
17
|
-
const info = {
|
|
18
|
-
pid: process.pid,
|
|
19
|
-
startedAt: new Date().toISOString(),
|
|
20
|
-
workspace,
|
|
21
|
-
};
|
|
22
|
-
try {
|
|
23
|
-
writeFileSync(getWorkerPidFile(), JSON.stringify(info, null, 2));
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
// Best effort
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function readWorkerPid() {
|
|
30
|
-
try {
|
|
31
|
-
if (!existsSync(getWorkerPidFile()))
|
|
32
|
-
return null;
|
|
33
|
-
const data = JSON.parse(readFileSync(getWorkerPidFile(), 'utf-8'));
|
|
34
|
-
return data;
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function removeWorkerPid() {
|
|
41
|
-
try {
|
|
42
|
-
unlinkSync(getWorkerPidFile());
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Ignore
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function isWorkerRunning(pid) {
|
|
49
|
-
try {
|
|
50
|
-
process.kill(pid, 0);
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export const workerCommand = new Command('worker')
|
|
58
|
-
.description('Autonomous worker mode for continuous task execution');
|
|
59
|
-
// Subcommand: worker start
|
|
60
|
-
workerCommand
|
|
61
|
-
.command('start')
|
|
62
|
-
.description('Start the autonomous worker loop')
|
|
63
|
-
.option('-i, --interval <ms>', 'Poll interval in milliseconds', String(DEFAULT_WORKER_CONFIG.pollInterval))
|
|
64
|
-
.option('-r, --max-retries <n>', 'Maximum retries per task', String(DEFAULT_WORKER_CONFIG.maxRetries))
|
|
65
|
-
.option('-t, --timeout <ms>', 'Task timeout in milliseconds', String(DEFAULT_WORKER_CONFIG.taskTimeout))
|
|
66
|
-
.option('--no-verification', 'Disable verification step')
|
|
67
|
-
.option('-d, --debug', 'Enable debug logging')
|
|
68
|
-
.action(async (options) => {
|
|
69
|
-
console.log(chalk.yellow('\n [Deprecated] `paean worker start` will be removed in a future release.'));
|
|
70
|
-
console.log(chalk.yellow(' Use `paean --worker` to enable the worker within the interactive CLI.\n'));
|
|
71
|
-
requireAuth();
|
|
72
|
-
// Validate and clamp config values to prevent NaN and unreasonable ranges
|
|
73
|
-
const parsedInterval = parseInt(options.interval, 10);
|
|
74
|
-
const parsedRetries = parseInt(options.maxRetries, 10);
|
|
75
|
-
const parsedTimeout = parseInt(options.timeout, 10);
|
|
76
|
-
const config = {
|
|
77
|
-
pollInterval: Math.max(1000, isNaN(parsedInterval) ? DEFAULT_WORKER_CONFIG.pollInterval : parsedInterval),
|
|
78
|
-
maxRetries: Math.max(1, Math.min(10, isNaN(parsedRetries) ? DEFAULT_WORKER_CONFIG.maxRetries : parsedRetries)),
|
|
79
|
-
taskTimeout: Math.max(10000, isNaN(parsedTimeout) ? DEFAULT_WORKER_CONFIG.taskTimeout : parsedTimeout),
|
|
80
|
-
verificationEnabled: options.verification !== false,
|
|
81
|
-
debug: options.debug ?? false,
|
|
82
|
-
autonomousMode: true,
|
|
83
|
-
};
|
|
84
|
-
console.log('');
|
|
85
|
-
console.log(chalk.bold.cyan(' ╔════════════════════════════════════════════╗'));
|
|
86
|
-
console.log(chalk.bold.cyan(' ║') + chalk.bold.white(' PAEAN WORKER ') + chalk.green('●') + chalk.white(' Starting...') + chalk.bold.cyan(' ║'));
|
|
87
|
-
console.log(chalk.bold.cyan(' ╚════════════════════════════════════════════╝'));
|
|
88
|
-
console.log('');
|
|
89
|
-
output.tableRow('Poll Interval', `${config.pollInterval / 1000}s`, 20);
|
|
90
|
-
output.tableRow('Max Retries', String(config.maxRetries), 20);
|
|
91
|
-
output.tableRow('Task Timeout', `${config.taskTimeout / 60000}min`, 20);
|
|
92
|
-
output.tableRow('Verification', config.verificationEnabled ? 'Enabled' : 'Disabled', 20);
|
|
93
|
-
console.log('');
|
|
94
|
-
const worker = new WorkerService(config);
|
|
95
|
-
// Write PID file for status command
|
|
96
|
-
writeWorkerPid(config.workspace);
|
|
97
|
-
// Track statistics
|
|
98
|
-
const startTime = Date.now();
|
|
99
|
-
// Event handler for UI updates
|
|
100
|
-
worker.onEvent((event) => {
|
|
101
|
-
const elapsed = formatDuration(Date.now() - startTime);
|
|
102
|
-
switch (event.type) {
|
|
103
|
-
case 'started':
|
|
104
|
-
console.log(chalk.green('✓') + chalk.dim(' Worker started'));
|
|
105
|
-
console.log(chalk.dim(` Press Ctrl+C to stop\n`));
|
|
106
|
-
break;
|
|
107
|
-
case 'stopped':
|
|
108
|
-
console.log(chalk.yellow('●') + chalk.dim(' Worker stopped'));
|
|
109
|
-
break;
|
|
110
|
-
case 'task_claimed':
|
|
111
|
-
console.log(chalk.cyan('▶') + ` Task claimed: ${chalk.white(truncate(event.task.content, 50))}`);
|
|
112
|
-
console.log(chalk.dim(` ID: ${event.task.id} | Priority: ${event.task.priority}`));
|
|
113
|
-
break;
|
|
114
|
-
case 'task_started':
|
|
115
|
-
console.log(chalk.blue('◉') + ` Executing (attempt ${event.attempt}/${config.maxRetries})...`);
|
|
116
|
-
break;
|
|
117
|
-
case 'task_completed':
|
|
118
|
-
console.log(chalk.green('✓') + ` Task completed in ${formatDuration(event.duration)}`);
|
|
119
|
-
console.log('');
|
|
120
|
-
break;
|
|
121
|
-
case 'task_failed':
|
|
122
|
-
console.log(chalk.red('✗') + ` Task failed: ${chalk.dim(truncate(event.error, 60))}`);
|
|
123
|
-
if (event.willRetry) {
|
|
124
|
-
console.log(chalk.yellow('↻') + chalk.dim(' Will retry...'));
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
case 'task_verification_failed':
|
|
128
|
-
console.log(chalk.yellow('⚠') + ' Verification failed, will retry...');
|
|
129
|
-
break;
|
|
130
|
-
case 'poll_empty':
|
|
131
|
-
// Silent - no tasks available
|
|
132
|
-
if (config.debug) {
|
|
133
|
-
console.log(chalk.dim(` [${elapsed}] No pending tasks`));
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
case 'error':
|
|
137
|
-
console.log(chalk.red('✗') + ` Error: ${event.error}`);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
// Handle graceful shutdown with Codex-style double-press Ctrl+C
|
|
142
|
-
let cleaningUp = false;
|
|
143
|
-
let lastSigintTime = 0;
|
|
144
|
-
const cleanup = async () => {
|
|
145
|
-
if (cleaningUp)
|
|
146
|
-
return;
|
|
147
|
-
cleaningUp = true;
|
|
148
|
-
// Remove only our own listeners to prevent re-entry
|
|
149
|
-
// (don't use removeAllListeners which would remove Ink/other library handlers)
|
|
150
|
-
process.off('SIGINT', handleSigint);
|
|
151
|
-
process.off('SIGTERM', cleanup);
|
|
152
|
-
console.log('');
|
|
153
|
-
console.log(chalk.yellow('Stopping worker...'));
|
|
154
|
-
try {
|
|
155
|
-
await worker.stop();
|
|
156
|
-
}
|
|
157
|
-
catch (err) {
|
|
158
|
-
if (config.debug) {
|
|
159
|
-
console.log(chalk.dim(` Warning: cleanup error: ${err.message}`));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
removeWorkerPid();
|
|
163
|
-
const state = worker.getState();
|
|
164
|
-
console.log('');
|
|
165
|
-
console.log(chalk.bold('Session Summary:'));
|
|
166
|
-
output.tableRow('Completed', String(state.completedCount), 15);
|
|
167
|
-
output.tableRow('Failed', String(state.failedCount), 15);
|
|
168
|
-
output.tableRow('Uptime', formatDuration(Date.now() - startTime), 15);
|
|
169
|
-
console.log('');
|
|
170
|
-
process.exit(0);
|
|
171
|
-
};
|
|
172
|
-
const handleSigint = () => {
|
|
173
|
-
const now = Date.now();
|
|
174
|
-
if (now - lastSigintTime < 1000) {
|
|
175
|
-
// Double-press within 1s: force quit immediately
|
|
176
|
-
cleanup();
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
lastSigintTime = now;
|
|
180
|
-
console.log('');
|
|
181
|
-
console.log(chalk.dim(' Press Ctrl+C again to stop the worker'));
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
process.on('SIGINT', handleSigint);
|
|
185
|
-
process.on('SIGTERM', cleanup);
|
|
186
|
-
// Start worker
|
|
187
|
-
try {
|
|
188
|
-
await worker.start();
|
|
189
|
-
// Keep process alive
|
|
190
|
-
await new Promise(() => { });
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
console.log(chalk.red('✗') + ` Failed to start worker: ${error.message}`);
|
|
194
|
-
process.exit(1);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
// Subcommand: worker status
|
|
198
|
-
workerCommand
|
|
199
|
-
.command('status')
|
|
200
|
-
.description('Check worker status')
|
|
201
|
-
.option('--json', 'Output as JSON')
|
|
202
|
-
.action((options) => {
|
|
203
|
-
const pidInfo = readWorkerPid();
|
|
204
|
-
if (!pidInfo) {
|
|
205
|
-
console.log(chalk.dim('No worker is currently running.'));
|
|
206
|
-
console.log(chalk.dim('Use `paean worker start` to start a worker.'));
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const running = isWorkerRunning(pidInfo.pid);
|
|
210
|
-
if (!running) {
|
|
211
|
-
removeWorkerPid();
|
|
212
|
-
console.log(chalk.dim('No worker is currently running (stale PID file cleaned up).'));
|
|
213
|
-
console.log(chalk.dim('Use `paean worker start` to start a worker.'));
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (options.json) {
|
|
217
|
-
console.log(JSON.stringify({ ...pidInfo, running }, null, 2));
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const uptime = formatDuration(Date.now() - new Date(pidInfo.startedAt).getTime());
|
|
221
|
-
console.log(chalk.bold.blue('\nWorker Status\n'));
|
|
222
|
-
output.tableRow('Status', chalk.green('Running'), 15);
|
|
223
|
-
output.tableRow('PID', String(pidInfo.pid), 15);
|
|
224
|
-
output.tableRow('Uptime', uptime, 15);
|
|
225
|
-
output.tableRow('Started', new Date(pidInfo.startedAt).toLocaleString(), 15);
|
|
226
|
-
if (pidInfo.workspace) {
|
|
227
|
-
output.tableRow('Workspace', pidInfo.workspace, 15);
|
|
228
|
-
}
|
|
229
|
-
console.log('');
|
|
230
|
-
});
|
|
231
|
-
// Subcommand: worker prompt
|
|
232
|
-
workerCommand
|
|
233
|
-
.command('prompt')
|
|
234
|
-
.description('Execute a natural language task with intelligent routing')
|
|
235
|
-
.argument('[prompt...]', 'Natural language task prompt')
|
|
236
|
-
.option('-p, --prompt <text>', 'Prompt text (alternative to positional)')
|
|
237
|
-
.option('-e, --executor <type>', 'Force specific executor (internal|claude|gemini|cursor|codex|opencode|droid|articulate)')
|
|
238
|
-
.option('--auto-approve', 'Skip confirmation prompts')
|
|
239
|
-
.option('--dry-run', 'Show plan without execution')
|
|
240
|
-
.option('--batch', 'Auto-decompose complex tasks into parallel subtasks')
|
|
241
|
-
.option('--chain', 'Auto-decompose into dependent chain steps (outputs feed into next)')
|
|
242
|
-
.option('-w, --workspace <path>', 'Working directory')
|
|
243
|
-
.option('-v, --verbose', 'Enable verbose logging')
|
|
244
|
-
.option('--create-task', 'Create as persistent task instead of immediate execution')
|
|
245
|
-
.option('--priority <level>', 'Task priority if creating (high|medium|low)', 'medium')
|
|
246
|
-
.action(async (promptArgs, options) => {
|
|
247
|
-
requireAuth();
|
|
248
|
-
// Combine prompt from various sources
|
|
249
|
-
const promptText = options.prompt || promptArgs.join(' ');
|
|
250
|
-
if (!promptText.trim()) {
|
|
251
|
-
output.error('No prompt provided. Usage: paean worker prompt "Your task"');
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
console.log('');
|
|
255
|
-
console.log(chalk.bold.cyan(' ╔════════════════════════════════════════════╗'));
|
|
256
|
-
console.log(chalk.bold.cyan(' ║') + chalk.bold.white(' PAEAN WORKER ') + chalk.magenta('◆') + chalk.white(' Prompt Mode') + chalk.bold.cyan(' ║'));
|
|
257
|
-
console.log(chalk.bold.cyan(' ╚════════════════════════════════════════════╝'));
|
|
258
|
-
console.log('');
|
|
259
|
-
const debug = options.verbose ?? false;
|
|
260
|
-
const cwd = options.workspace || process.cwd();
|
|
261
|
-
// Validate workspace path
|
|
262
|
-
if (options.workspace) {
|
|
263
|
-
if (!existsSync(cwd)) {
|
|
264
|
-
output.error(`Workspace path does not exist: ${cwd}`);
|
|
265
|
-
process.exit(1);
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
if (!statSync(cwd).isDirectory()) {
|
|
269
|
-
output.error(`Workspace path is not a directory: ${cwd}`);
|
|
270
|
-
process.exit(1);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
output.error(`Cannot access workspace path: ${cwd}`);
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Initialize supervisor
|
|
279
|
-
const supervisor = getSupervisor({ debug, cloudEnabled: true });
|
|
280
|
-
// Show input prompt
|
|
281
|
-
console.log(chalk.cyan('▶') + chalk.bold(' Prompt:'));
|
|
282
|
-
console.log(chalk.dim(` ${truncate(promptText, 70)}`));
|
|
283
|
-
console.log('');
|
|
284
|
-
// Grooming phase
|
|
285
|
-
console.log(chalk.blue('◉') + ' Analyzing task...');
|
|
286
|
-
const startTime = Date.now();
|
|
287
|
-
const decision = await supervisor.groomTask(promptText, {
|
|
288
|
-
projectPath: cwd,
|
|
289
|
-
});
|
|
290
|
-
console.log(chalk.green('✓') + ' Task analyzed');
|
|
291
|
-
console.log('');
|
|
292
|
-
// Display decision
|
|
293
|
-
console.log(chalk.bold('📋 Supervisor Decision:'));
|
|
294
|
-
output.tableRow('Executor', decision.selectedExecutor, 18);
|
|
295
|
-
output.tableRow('Confidence', `${(decision.confidence * 100).toFixed(0)}%`, 18);
|
|
296
|
-
output.tableRow('Strategy', decision.verificationStrategy || 'none', 18);
|
|
297
|
-
if (decision.tags?.length) {
|
|
298
|
-
output.tableRow('Tags', decision.tags.join(', '), 18);
|
|
299
|
-
}
|
|
300
|
-
console.log('');
|
|
301
|
-
if (debug) {
|
|
302
|
-
console.log(chalk.dim('Reasoning: ' + decision.reasoning));
|
|
303
|
-
console.log('');
|
|
304
|
-
}
|
|
305
|
-
// Show groomed prompt
|
|
306
|
-
console.log(chalk.bold('📝 Groomed Prompt:'));
|
|
307
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
308
|
-
const lines = decision.groomedPrompt.split('\n').slice(0, 5);
|
|
309
|
-
for (const line of lines) {
|
|
310
|
-
console.log(chalk.dim(' ' + truncate(line, 60)));
|
|
311
|
-
}
|
|
312
|
-
if (decision.groomedPrompt.split('\n').length > 5) {
|
|
313
|
-
console.log(chalk.dim(' ... (truncated)'));
|
|
314
|
-
}
|
|
315
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
316
|
-
console.log('');
|
|
317
|
-
// Subtasks if any
|
|
318
|
-
if (decision.subtasks?.length) {
|
|
319
|
-
console.log(chalk.bold('📂 Subtasks:'));
|
|
320
|
-
for (const subtask of decision.subtasks) {
|
|
321
|
-
console.log(chalk.dim(` • ${truncate(subtask, 60)}`));
|
|
322
|
-
}
|
|
323
|
-
console.log('');
|
|
324
|
-
}
|
|
325
|
-
// Chain mode - dependent sequential tasks
|
|
326
|
-
if (options.chain) {
|
|
327
|
-
console.log(chalk.blue('◉') + ' Analyzing for chain decomposition...');
|
|
328
|
-
const chainResult = await supervisor.decomposeChain(promptText, { projectPath: cwd });
|
|
329
|
-
if (chainResult.isChain && chainResult.chain && chainResult.chain.length > 0) {
|
|
330
|
-
const chainId = `chain-${Date.now()}`;
|
|
331
|
-
console.log(chalk.green('✓') + ` Decomposed into ${chainResult.chain.length} chain steps`);
|
|
332
|
-
console.log(chalk.dim(` Reasoning: ${chainResult.reasoning}`));
|
|
333
|
-
console.log('');
|
|
334
|
-
console.log(chalk.bold('🔗 Creating chain tasks:'));
|
|
335
|
-
const createdSteps = [];
|
|
336
|
-
// Determine executor: user-specified --executor takes priority over LLM suggestions
|
|
337
|
-
const forcedExecutor = options.executor;
|
|
338
|
-
if (forcedExecutor) {
|
|
339
|
-
console.log(chalk.cyan('ℹ') + ` Using forced executor: ${chalk.bold(forcedExecutor)}`);
|
|
340
|
-
}
|
|
341
|
-
for (let i = 0; i < chainResult.chain.length; i++) {
|
|
342
|
-
const step = chainResult.chain[i];
|
|
343
|
-
try {
|
|
344
|
-
await createTodoItem({
|
|
345
|
-
content: step.content,
|
|
346
|
-
priority: step.priority || options.priority,
|
|
347
|
-
metadata: {
|
|
348
|
-
chainId,
|
|
349
|
-
stepId: step.stepId,
|
|
350
|
-
stepIndex: i + 1,
|
|
351
|
-
totalSteps: chainResult.chain.length,
|
|
352
|
-
dependsOn: step.dependsOn,
|
|
353
|
-
expectedOutputs: step.expectedOutputs,
|
|
354
|
-
inputFrom: step.inputFrom,
|
|
355
|
-
suggestedExecutor: forcedExecutor || step.suggestedExecutor,
|
|
356
|
-
workingDirectory: cwd,
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
createdSteps.push(step.stepId);
|
|
360
|
-
const deps = step.dependsOn?.length ? ` (depends: ${step.dependsOn.join(', ')})` : '';
|
|
361
|
-
console.log(chalk.green(' ✓') + ` [${step.stepId}] ${truncate(step.content, 45)}${deps}`);
|
|
362
|
-
}
|
|
363
|
-
catch (err) {
|
|
364
|
-
console.log(chalk.red(' ✗') + ` Failed: ${err.message}`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
console.log('');
|
|
368
|
-
console.log(chalk.green('✓') + ` Created chain: ${chainId}`);
|
|
369
|
-
if (chainResult.estimatedDurationMinutes) {
|
|
370
|
-
console.log(chalk.dim(` Estimated duration: ~${chainResult.estimatedDurationMinutes} minutes`));
|
|
371
|
-
}
|
|
372
|
-
console.log(chalk.dim(` Run 'paean worker start' to process this chain.`));
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
else {
|
|
376
|
-
console.log(chalk.yellow('ℹ') + ' Task is not a chain, proceeding with single execution');
|
|
377
|
-
console.log('');
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// Batch mode - decompose and queue
|
|
381
|
-
if (options.batch) {
|
|
382
|
-
console.log(chalk.blue('◉') + ' Analyzing for batch decomposition...');
|
|
383
|
-
const decomposition = await supervisor.decomposeTask(promptText, { projectPath: cwd });
|
|
384
|
-
if (decomposition.shouldDecompose && decomposition.subtasks && decomposition.subtasks.length > 1) {
|
|
385
|
-
console.log(chalk.green('✓') + ` Decomposed into ${decomposition.subtasks.length} subtasks`);
|
|
386
|
-
console.log(chalk.dim(` Reasoning: ${decomposition.reasoning}`));
|
|
387
|
-
console.log('');
|
|
388
|
-
console.log(chalk.bold('📋 Creating batch tasks:'));
|
|
389
|
-
const createdIds = [];
|
|
390
|
-
// Determine executor: user-specified --executor takes priority over LLM suggestions
|
|
391
|
-
const forcedBatchExecutor = options.executor;
|
|
392
|
-
if (forcedBatchExecutor) {
|
|
393
|
-
console.log(chalk.cyan('ℹ') + ` Using forced executor: ${chalk.bold(forcedBatchExecutor)}`);
|
|
394
|
-
}
|
|
395
|
-
for (let i = 0; i < decomposition.subtasks.length; i++) {
|
|
396
|
-
const subtask = decomposition.subtasks[i];
|
|
397
|
-
try {
|
|
398
|
-
const result = await createTodoItem({
|
|
399
|
-
content: subtask.content,
|
|
400
|
-
priority: subtask.priority || options.priority,
|
|
401
|
-
metadata: {
|
|
402
|
-
parentPrompt: promptText,
|
|
403
|
-
subtaskIndex: i + 1,
|
|
404
|
-
totalSubtasks: decomposition.subtasks.length,
|
|
405
|
-
verificationHint: subtask.verificationHint,
|
|
406
|
-
suggestedExecutor: forcedBatchExecutor || subtask.suggestedExecutor,
|
|
407
|
-
workingDirectory: cwd,
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
createdIds.push(result.data.id);
|
|
411
|
-
console.log(chalk.green(' ✓') + ` [${i + 1}/${decomposition.subtasks.length}] ${truncate(subtask.content, 50)}`);
|
|
412
|
-
}
|
|
413
|
-
catch (err) {
|
|
414
|
-
console.log(chalk.red(' ✗') + ` Failed: ${err.message}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
console.log('');
|
|
418
|
-
console.log(chalk.green('✓') + ` Created ${createdIds.length} tasks`);
|
|
419
|
-
if (decomposition.estimatedDurationMinutes) {
|
|
420
|
-
console.log(chalk.dim(` Estimated duration: ~${decomposition.estimatedDurationMinutes} minutes`));
|
|
421
|
-
}
|
|
422
|
-
console.log(chalk.dim(` Run 'paean worker start' to process these tasks.`));
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
console.log(chalk.yellow('ℹ') + ' Task is atomic, proceeding with single execution');
|
|
427
|
-
console.log('');
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
// Dry run mode - stop here
|
|
431
|
-
if (options.dryRun) {
|
|
432
|
-
console.log(chalk.yellow('●') + ' Dry run mode - execution skipped');
|
|
433
|
-
console.log(chalk.dim(` Duration: ${formatDuration(Date.now() - startTime)}`));
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
// Create as persistent task
|
|
437
|
-
if (options.createTask) {
|
|
438
|
-
console.log(chalk.blue('◉') + ' Creating persistent task...');
|
|
439
|
-
try {
|
|
440
|
-
const result = await createTodoItem({
|
|
441
|
-
content: promptText,
|
|
442
|
-
description: decision.groomedPrompt,
|
|
443
|
-
priority: options.priority,
|
|
444
|
-
tags: decision.tags,
|
|
445
|
-
});
|
|
446
|
-
console.log(chalk.green('✓') + ` Task created: ${result.data.id}`);
|
|
447
|
-
console.log(chalk.dim(` Use 'paean worker start' to process this task.`));
|
|
448
|
-
}
|
|
449
|
-
catch (err) {
|
|
450
|
-
console.log(chalk.red('✗') + ` Failed to create task: ${err.message}`);
|
|
451
|
-
}
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
// Initialize executor registry
|
|
455
|
-
const registry = getExecutorRegistry();
|
|
456
|
-
// Register all executors
|
|
457
|
-
registry.register(new InternalExecutor());
|
|
458
|
-
registry.register(new ClaudeExecutor());
|
|
459
|
-
registry.register(new GeminiExecutor());
|
|
460
|
-
registry.register(new CursorExecutor());
|
|
461
|
-
registry.register(new CodexExecutor());
|
|
462
|
-
registry.register(new OpenCodeExecutor());
|
|
463
|
-
registry.register(new DroidExecutor());
|
|
464
|
-
registry.register(new ArticulateExecutor());
|
|
465
|
-
registry.register(new PaeanclawExecutor());
|
|
466
|
-
// Determine which executor to use
|
|
467
|
-
const executorType = options.executor || decision.selectedExecutor;
|
|
468
|
-
// Check availability
|
|
469
|
-
const available = await registry.getAvailable();
|
|
470
|
-
if (debug) {
|
|
471
|
-
console.log(chalk.dim(`Available executors: ${available.join(', ')}`));
|
|
472
|
-
}
|
|
473
|
-
if (!available.includes(executorType) && executorType !== 'internal') {
|
|
474
|
-
console.log(chalk.yellow('⚠') + ` Executor '${executorType}' not available, falling back to internal`);
|
|
475
|
-
}
|
|
476
|
-
const finalExecutor = available.includes(executorType) ? executorType : 'internal';
|
|
477
|
-
// Execute
|
|
478
|
-
console.log(chalk.blue('◉') + ` Executing with ${chalk.bold(finalExecutor)} executor...`);
|
|
479
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
480
|
-
const result = await registry.execute(finalExecutor, decision.groomedPrompt, {
|
|
481
|
-
cwd,
|
|
482
|
-
skipPermissions: options.autoApprove,
|
|
483
|
-
timeout: 600000,
|
|
484
|
-
onOutput: (text, stream) => {
|
|
485
|
-
// Display streaming output with nice formatting
|
|
486
|
-
const lines = text.split('\n');
|
|
487
|
-
for (const line of lines) {
|
|
488
|
-
if (line.trim()) {
|
|
489
|
-
const prefix = stream === 'stderr' ? chalk.yellow('│') : chalk.dim('│');
|
|
490
|
-
const displayLine = line.length > 70 ? line.slice(0, 67) + '...' : line;
|
|
491
|
-
console.log(prefix + ' ' + displayLine);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
497
|
-
const totalDuration = Date.now() - startTime;
|
|
498
|
-
// Display result
|
|
499
|
-
if (result.success) {
|
|
500
|
-
console.log(chalk.green('✓') + ` Execution completed`);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
console.log(chalk.red('✗') + ` Execution failed`);
|
|
504
|
-
if (result.error) {
|
|
505
|
-
console.log(chalk.dim(` Error: ${truncate(result.error, 60)}`));
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
console.log('');
|
|
509
|
-
console.log(chalk.bold('📊 Summary:'));
|
|
510
|
-
output.tableRow('Status', result.success ? 'Success' : 'Failed', 15);
|
|
511
|
-
output.tableRow('Executor', finalExecutor, 15);
|
|
512
|
-
output.tableRow('Duration', formatDuration(totalDuration), 15);
|
|
513
|
-
console.log('');
|
|
514
|
-
// Show output preview if verbose
|
|
515
|
-
if (debug && result.output) {
|
|
516
|
-
console.log(chalk.bold('Output Preview:'));
|
|
517
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
518
|
-
const outputLines = result.output.split('\n').slice(0, 10);
|
|
519
|
-
for (const line of outputLines) {
|
|
520
|
-
console.log(chalk.dim(truncate(line, 70)));
|
|
521
|
-
}
|
|
522
|
-
if (result.output.split('\n').length > 10) {
|
|
523
|
-
console.log(chalk.dim('... (truncated)'));
|
|
524
|
-
}
|
|
525
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
526
|
-
}
|
|
527
|
-
process.exit(result.success ? 0 : 1);
|
|
528
|
-
});
|
|
529
|
-
// Subcommand: worker schedules
|
|
530
|
-
workerCommand
|
|
531
|
-
.command('schedules')
|
|
532
|
-
.description('List and manage scheduled/recurring tasks')
|
|
533
|
-
.option('--active', 'Show only active schedules')
|
|
534
|
-
.option('--paused', 'Show only paused schedules')
|
|
535
|
-
.option('-s, --search <query>', 'Search by task content')
|
|
536
|
-
.action(async (options) => {
|
|
537
|
-
requireAuth();
|
|
538
|
-
try {
|
|
539
|
-
const { getScheduledTasks } = await import('../api/todo.js');
|
|
540
|
-
const enabled = options.active ? true : options.paused ? false : undefined;
|
|
541
|
-
const result = await getScheduledTasks({
|
|
542
|
-
enabled,
|
|
543
|
-
search: options.search,
|
|
544
|
-
});
|
|
545
|
-
if (!result.success || !result.data?.items?.length) {
|
|
546
|
-
console.log(chalk.yellow('\n No scheduled tasks found.\n'));
|
|
547
|
-
console.log(chalk.dim(' Scheduled tasks are created via the Paean agent. Try:'));
|
|
548
|
-
console.log(chalk.dim(' "@Paean send a sales overview every weekday at 9am"\n'));
|
|
549
|
-
process.exit(0);
|
|
550
|
-
}
|
|
551
|
-
const items = result.data.items;
|
|
552
|
-
console.log('');
|
|
553
|
-
console.log(chalk.bold.cyan(` 📅 Scheduled Tasks (${items.length})`));
|
|
554
|
-
console.log(chalk.dim(' ' + '─'.repeat(55)));
|
|
555
|
-
console.log('');
|
|
556
|
-
for (const item of items) {
|
|
557
|
-
const schedule = item.schedule;
|
|
558
|
-
const enabled = schedule?.enabled;
|
|
559
|
-
const statusBadge = enabled
|
|
560
|
-
? chalk.green('● active')
|
|
561
|
-
: chalk.yellow('○ paused');
|
|
562
|
-
console.log(` ${chalk.bold(item.content)}`);
|
|
563
|
-
console.log(` ${chalk.dim('ID:')} ${item.id} ${statusBadge}`);
|
|
564
|
-
if (schedule?.cron) {
|
|
565
|
-
console.log(` ${chalk.dim('Cron:')} ${schedule.cron} ${chalk.dim('TZ:')} ${schedule.timezone || 'UTC'}`);
|
|
566
|
-
}
|
|
567
|
-
if (schedule?.nextTriggerAt && enabled) {
|
|
568
|
-
console.log(` ${chalk.dim('Next:')} ${schedule.nextTriggerAt}`);
|
|
569
|
-
}
|
|
570
|
-
if (schedule?.lastTriggeredAt) {
|
|
571
|
-
console.log(` ${chalk.dim('Last:')} ${schedule.lastTriggeredAt}`);
|
|
572
|
-
}
|
|
573
|
-
if ((schedule?.consecutiveFailures || 0) > 0) {
|
|
574
|
-
console.log(` ${chalk.red('Failures:')} ${schedule.consecutiveFailures}/${schedule.maxConsecutiveFailures || 3}`);
|
|
575
|
-
}
|
|
576
|
-
console.log('');
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
catch (error) {
|
|
580
|
-
output.error(`Failed to fetch scheduled tasks: ${error.message}`);
|
|
581
|
-
process.exit(1);
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
// Helper functions
|
|
585
|
-
function requireAuth() {
|
|
586
|
-
if (!isAuthenticated()) {
|
|
587
|
-
output.error('Not authenticated. Please run "paean login" first.');
|
|
588
|
-
process.exit(1);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
// Use shared utilities from output module
|
|
592
|
-
const truncate = output.truncate;
|
|
593
|
-
const formatDuration = output.formatDuration;
|
|
594
|
-
//# sourceMappingURL=worker.js.map
|