@wundr.io/cli 1.0.9 → 1.0.10
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/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/computer-setup.js +21 -21
- package/dist/commands/computer-setup.js.map +1 -1
- package/dist/commands/orchestrator.d.ts +7 -0
- package/dist/commands/orchestrator.d.ts.map +1 -0
- package/dist/commands/orchestrator.js +571 -0
- package/dist/commands/orchestrator.js.map +1 -0
- package/package.json +5 -4
- package/src/cli.ts +3 -3
- package/src/commands/computer-setup.ts +23 -23
- package/src/commands/{vp.ts → orchestrator.ts} +91 -91
|
@@ -486,11 +486,11 @@ interface DeveloperProfile {
|
|
|
486
486
|
|
|
487
487
|
/**
|
|
488
488
|
* Sets up Fleet-Scale Autonomous Engineering mode
|
|
489
|
-
* Installs
|
|
489
|
+
* Installs Orchestrator Daemon scripts, Memory Bank templates, and IPRE governance defaults
|
|
490
490
|
*/
|
|
491
491
|
async function setupFleetMode(profile: DeveloperProfile): Promise<void> {
|
|
492
492
|
const wundrDir = path.join(os.homedir(), '.wundr');
|
|
493
|
-
const
|
|
493
|
+
const orchestratorDaemonDir = path.join(wundrDir, 'orchestrator-daemon');
|
|
494
494
|
const governanceDir = path.join(wundrDir, 'governance');
|
|
495
495
|
const templatesDir = path.join(wundrDir, 'templates');
|
|
496
496
|
|
|
@@ -499,23 +499,23 @@ async function setupFleetMode(profile: DeveloperProfile): Promise<void> {
|
|
|
499
499
|
);
|
|
500
500
|
|
|
501
501
|
// Create directory structure
|
|
502
|
-
await fs.mkdir(
|
|
502
|
+
await fs.mkdir(orchestratorDaemonDir, { recursive: true });
|
|
503
503
|
await fs.mkdir(governanceDir, { recursive: true });
|
|
504
504
|
await fs.mkdir(path.join(templatesDir, 'memory-bank'), { recursive: true });
|
|
505
505
|
await fs.mkdir(path.join(templatesDir, 'sub-agents'), { recursive: true });
|
|
506
506
|
|
|
507
|
-
// 1. Install
|
|
508
|
-
const
|
|
507
|
+
// 1. Install Orchestrator Daemon configuration
|
|
508
|
+
const orchestratorConfig = {
|
|
509
509
|
version: '1.0.0',
|
|
510
510
|
identity: {
|
|
511
511
|
name: profile.name,
|
|
512
512
|
email: profile.email,
|
|
513
|
-
role: '
|
|
513
|
+
role: 'Orchestrator-Supervisor',
|
|
514
514
|
},
|
|
515
515
|
resourceLimits: {
|
|
516
516
|
maxSessions: 10,
|
|
517
517
|
tokenBudget: {
|
|
518
|
-
subscription: 0.8, // 80% for
|
|
518
|
+
subscription: 0.8, // 80% for Orchestrator & Session Managers
|
|
519
519
|
api: 0.2, // 20% for sub-agent swarms
|
|
520
520
|
},
|
|
521
521
|
},
|
|
@@ -537,20 +537,20 @@ async function setupFleetMode(profile: DeveloperProfile): Promise<void> {
|
|
|
537
537
|
};
|
|
538
538
|
|
|
539
539
|
await fs.writeFile(
|
|
540
|
-
path.join(
|
|
541
|
-
generateYamlContent(
|
|
540
|
+
path.join(orchestratorDaemonDir, 'config.yaml'),
|
|
541
|
+
generateYamlContent(orchestratorConfig),
|
|
542
542
|
'utf-8',
|
|
543
543
|
);
|
|
544
|
-
console.log(chalk.green(' ✓
|
|
544
|
+
console.log(chalk.green(' ✓ Orchestrator Daemon configuration installed'));
|
|
545
545
|
|
|
546
|
-
// 2. Copy
|
|
547
|
-
const
|
|
548
|
-
name:
|
|
549
|
-
role: Tier1-
|
|
546
|
+
// 2. Copy Orchestrator Charter template
|
|
547
|
+
const orchestratorCharter = `---
|
|
548
|
+
name: orchestrator-supervisor
|
|
549
|
+
role: Tier1-Orchestrator
|
|
550
550
|
identity:
|
|
551
551
|
name: '${profile.name}'
|
|
552
552
|
email: '${profile.email}'
|
|
553
|
-
slackHandle: '@
|
|
553
|
+
slackHandle: '@orchestrator-supervisor'
|
|
554
554
|
|
|
555
555
|
responsibilities:
|
|
556
556
|
- triage_requests
|
|
@@ -578,11 +578,11 @@ hardConstraints:
|
|
|
578
578
|
`;
|
|
579
579
|
|
|
580
580
|
await fs.writeFile(
|
|
581
|
-
path.join(
|
|
582
|
-
|
|
581
|
+
path.join(orchestratorDaemonDir, 'orchestrator-charter.md'),
|
|
582
|
+
orchestratorCharter,
|
|
583
583
|
'utf-8',
|
|
584
584
|
);
|
|
585
|
-
console.log(chalk.green(' ✓
|
|
585
|
+
console.log(chalk.green(' ✓ Orchestrator Charter template deployed'));
|
|
586
586
|
|
|
587
587
|
// 3. Set up token budgeting configuration
|
|
588
588
|
const tokenBudgetConfig = {
|
|
@@ -617,12 +617,12 @@ hardConstraints:
|
|
|
617
617
|
},
|
|
618
618
|
throttlingPolicy: {
|
|
619
619
|
onWarning: ['pause_non_critical_sessions', 'queue_new_requests'],
|
|
620
|
-
onCritical: ['pause_all_except_critical', '
|
|
620
|
+
onCritical: ['pause_all_except_critical', 'notify_orchestrator_human'],
|
|
621
621
|
},
|
|
622
622
|
};
|
|
623
623
|
|
|
624
624
|
await fs.writeFile(
|
|
625
|
-
path.join(
|
|
625
|
+
path.join(orchestratorDaemonDir, 'token-budget.yaml'),
|
|
626
626
|
generateYamlContent(tokenBudgetConfig),
|
|
627
627
|
'utf-8',
|
|
628
628
|
);
|
|
@@ -845,9 +845,9 @@ git worktree list
|
|
|
845
845
|
chalk.cyan('\n✅ Fleet-Scale Autonomous Engineering mode setup complete!\n'),
|
|
846
846
|
);
|
|
847
847
|
console.log(chalk.white('Files created:'));
|
|
848
|
-
console.log(chalk.gray(' ~/.wundr/
|
|
849
|
-
console.log(chalk.gray(' ~/.wundr/
|
|
850
|
-
console.log(chalk.gray(' ~/.wundr/
|
|
848
|
+
console.log(chalk.gray(' ~/.wundr/orchestrator-daemon/config.yaml'));
|
|
849
|
+
console.log(chalk.gray(' ~/.wundr/orchestrator-daemon/orchestrator-charter.md'));
|
|
850
|
+
console.log(chalk.gray(' ~/.wundr/orchestrator-daemon/token-budget.yaml'));
|
|
851
851
|
console.log(chalk.gray(' ~/.wundr/templates/memory-bank/'));
|
|
852
852
|
console.log(chalk.gray(' ~/.wundr/governance/ipre-defaults.yaml'));
|
|
853
853
|
console.log(chalk.gray(' ~/.wundr/RESOURCE_LIMITS.md'));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Manages the
|
|
2
|
+
* Orchestrator Daemon CLI Commands
|
|
3
|
+
* Manages the Orchestrator Daemon for agent orchestration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as fs from 'fs/promises';
|
|
@@ -14,13 +14,13 @@ import ora from 'ora';
|
|
|
14
14
|
import YAML from 'yaml';
|
|
15
15
|
|
|
16
16
|
// Constants
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
17
|
+
const ORCHESTRATOR_CONFIG_DIR = path.join(os.homedir(), '.wundr', 'orchestrator-daemon');
|
|
18
|
+
const ORCHESTRATOR_CONFIG_FILE = path.join(ORCHESTRATOR_CONFIG_DIR, 'config.yaml');
|
|
19
|
+
const ORCHESTRATOR_PID_FILE = path.join(ORCHESTRATOR_CONFIG_DIR, 'daemon.pid');
|
|
20
|
+
const ORCHESTRATOR_LOG_FILE = path.join(ORCHESTRATOR_CONFIG_DIR, 'daemon.log');
|
|
21
21
|
|
|
22
22
|
// Types
|
|
23
|
-
interface
|
|
23
|
+
interface OrchestratorConfig {
|
|
24
24
|
daemon: {
|
|
25
25
|
port: number;
|
|
26
26
|
host: string;
|
|
@@ -74,24 +74,24 @@ interface DaemonStatus {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Utility functions
|
|
77
|
-
function getDefaultConfig():
|
|
77
|
+
function getDefaultConfig(): OrchestratorConfig {
|
|
78
78
|
return {
|
|
79
79
|
daemon: {
|
|
80
80
|
port: 8787,
|
|
81
81
|
host: '127.0.0.1',
|
|
82
|
-
name: '
|
|
82
|
+
name: 'orchestrator-daemon',
|
|
83
83
|
maxSessions: 100,
|
|
84
84
|
heartbeatInterval: 30000,
|
|
85
85
|
shutdownTimeout: 10000,
|
|
86
86
|
},
|
|
87
87
|
identity: {
|
|
88
|
-
name: '
|
|
89
|
-
email: '
|
|
90
|
-
slackHandle: '@
|
|
88
|
+
name: 'Orchestrator',
|
|
89
|
+
email: 'orchestrator@wundr.local',
|
|
90
|
+
slackHandle: '@orchestrator',
|
|
91
91
|
},
|
|
92
92
|
subsystems: {
|
|
93
93
|
triage: {
|
|
94
|
-
memoryBankPath: path.join(
|
|
94
|
+
memoryBankPath: path.join(ORCHESTRATOR_CONFIG_DIR, 'memory-bank'),
|
|
95
95
|
enableRAG: false,
|
|
96
96
|
},
|
|
97
97
|
intervention: {
|
|
@@ -117,11 +117,11 @@ function getDefaultConfig(): VPConfig {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
async function loadConfig(): Promise<
|
|
120
|
+
async function loadConfig(): Promise<OrchestratorConfig> {
|
|
121
121
|
try {
|
|
122
|
-
if (existsSync(
|
|
123
|
-
const content = await fs.readFile(
|
|
124
|
-
const parsed = YAML.parse(content) as Partial<
|
|
122
|
+
if (existsSync(ORCHESTRATOR_CONFIG_FILE)) {
|
|
123
|
+
const content = await fs.readFile(ORCHESTRATOR_CONFIG_FILE, 'utf-8');
|
|
124
|
+
const parsed = YAML.parse(content) as Partial<OrchestratorConfig>;
|
|
125
125
|
return { ...getDefaultConfig(), ...parsed };
|
|
126
126
|
}
|
|
127
127
|
} catch (error) {
|
|
@@ -130,14 +130,14 @@ async function loadConfig(): Promise<VPConfig> {
|
|
|
130
130
|
return getDefaultConfig();
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
async function saveConfig(config:
|
|
134
|
-
await fs.mkdir(
|
|
135
|
-
await fs.writeFile(
|
|
133
|
+
async function saveConfig(config: OrchestratorConfig): Promise<void> {
|
|
134
|
+
await fs.mkdir(ORCHESTRATOR_CONFIG_DIR, { recursive: true });
|
|
135
|
+
await fs.writeFile(ORCHESTRATOR_CONFIG_FILE, YAML.stringify(config), 'utf-8');
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
async function ensureConfigDir(): Promise<void> {
|
|
139
|
-
if (!existsSync(
|
|
140
|
-
await fs.mkdir(
|
|
139
|
+
if (!existsSync(ORCHESTRATOR_CONFIG_DIR)) {
|
|
140
|
+
await fs.mkdir(ORCHESTRATOR_CONFIG_DIR, { recursive: true });
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -145,8 +145,8 @@ async function getDaemonStatus(): Promise<DaemonStatus> {
|
|
|
145
145
|
const status: DaemonStatus = { running: false };
|
|
146
146
|
|
|
147
147
|
try {
|
|
148
|
-
if (existsSync(
|
|
149
|
-
const pidContent = await fs.readFile(
|
|
148
|
+
if (existsSync(ORCHESTRATOR_PID_FILE)) {
|
|
149
|
+
const pidContent = await fs.readFile(ORCHESTRATOR_PID_FILE, 'utf-8');
|
|
150
150
|
const pid = parseInt(pidContent.trim(), 10);
|
|
151
151
|
|
|
152
152
|
// Check if process is running
|
|
@@ -161,7 +161,7 @@ async function getDaemonStatus(): Promise<DaemonStatus> {
|
|
|
161
161
|
status.host = config.daemon.host;
|
|
162
162
|
|
|
163
163
|
// Read additional status info if available
|
|
164
|
-
const statusFile = path.join(
|
|
164
|
+
const statusFile = path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json');
|
|
165
165
|
if (existsSync(statusFile)) {
|
|
166
166
|
const statusContent = await fs.readFile(statusFile, 'utf-8');
|
|
167
167
|
const statusData = JSON.parse(statusContent);
|
|
@@ -173,7 +173,7 @@ async function getDaemonStatus(): Promise<DaemonStatus> {
|
|
|
173
173
|
}
|
|
174
174
|
} catch {
|
|
175
175
|
// Process not running, clean up stale PID file
|
|
176
|
-
await fs.unlink(
|
|
176
|
+
await fs.unlink(ORCHESTRATOR_PID_FILE).catch(() => {});
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
} catch (error) {
|
|
@@ -201,29 +201,29 @@ function formatUptime(ms: number): string {
|
|
|
201
201
|
return `${seconds}s`;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
// Create
|
|
205
|
-
export function
|
|
206
|
-
const command = new Command('
|
|
204
|
+
// Create Orchestrator command
|
|
205
|
+
export function createOrchestratorCommand(): Command {
|
|
206
|
+
const command = new Command('orchestrator')
|
|
207
207
|
.description(
|
|
208
|
-
'Manage the
|
|
208
|
+
'Manage the Orchestrator Daemon for agent orchestration',
|
|
209
209
|
)
|
|
210
210
|
.addHelpText(
|
|
211
211
|
'after',
|
|
212
212
|
chalk.gray(`
|
|
213
213
|
Examples:
|
|
214
|
-
${chalk.green('wundr
|
|
215
|
-
${chalk.green('wundr
|
|
216
|
-
${chalk.green('wundr
|
|
217
|
-
${chalk.green('wundr
|
|
218
|
-
${chalk.green('wundr
|
|
219
|
-
${chalk.green('wundr
|
|
214
|
+
${chalk.green('wundr orchestrator start')} Start the Orchestrator Daemon
|
|
215
|
+
${chalk.green('wundr orchestrator start --port 9000')} Start on custom port
|
|
216
|
+
${chalk.green('wundr orchestrator status')} Check daemon status
|
|
217
|
+
${chalk.green('wundr orchestrator stop')} Stop the daemon gracefully
|
|
218
|
+
${chalk.green('wundr orchestrator config show')} View current configuration
|
|
219
|
+
${chalk.green('wundr orchestrator config set daemon.port=9000')} Update configuration
|
|
220
220
|
`),
|
|
221
221
|
);
|
|
222
222
|
|
|
223
223
|
// Start command
|
|
224
224
|
command
|
|
225
225
|
.command('start')
|
|
226
|
-
.description('Start the
|
|
226
|
+
.description('Start the Orchestrator Daemon')
|
|
227
227
|
.option('-p, --port <number>', 'Port to listen on')
|
|
228
228
|
.option('-c, --config <path>', 'Path to configuration file')
|
|
229
229
|
.option('-v, --verbose', 'Enable verbose logging')
|
|
@@ -235,7 +235,7 @@ Examples:
|
|
|
235
235
|
// Status command (default)
|
|
236
236
|
command
|
|
237
237
|
.command('status', { isDefault: true })
|
|
238
|
-
.description('Check
|
|
238
|
+
.description('Check Orchestrator Daemon status')
|
|
239
239
|
.option('--json', 'Output as JSON')
|
|
240
240
|
.action(async options => {
|
|
241
241
|
await showStatus(options);
|
|
@@ -244,7 +244,7 @@ Examples:
|
|
|
244
244
|
// Stop command
|
|
245
245
|
command
|
|
246
246
|
.command('stop')
|
|
247
|
-
.description('Stop the
|
|
247
|
+
.description('Stop the Orchestrator Daemon gracefully')
|
|
248
248
|
.option('-f, --force', 'Force immediate termination')
|
|
249
249
|
.option('-t, --timeout <ms>', 'Shutdown timeout in milliseconds', '10000')
|
|
250
250
|
.action(async options => {
|
|
@@ -254,7 +254,7 @@ Examples:
|
|
|
254
254
|
// Config command group
|
|
255
255
|
const configCmd = command
|
|
256
256
|
.command('config')
|
|
257
|
-
.description('View or edit
|
|
257
|
+
.description('View or edit Orchestrator configuration');
|
|
258
258
|
|
|
259
259
|
configCmd
|
|
260
260
|
.command('show')
|
|
@@ -282,7 +282,7 @@ Examples:
|
|
|
282
282
|
// Logs command
|
|
283
283
|
command
|
|
284
284
|
.command('logs')
|
|
285
|
-
.description('View
|
|
285
|
+
.description('View Orchestrator Daemon logs')
|
|
286
286
|
.option('-f, --follow', 'Follow log output')
|
|
287
287
|
.option('-n, --lines <number>', 'Number of lines to show', '50')
|
|
288
288
|
.action(async options => {
|
|
@@ -299,7 +299,7 @@ async function startDaemon(options: {
|
|
|
299
299
|
verbose?: boolean;
|
|
300
300
|
detach?: boolean;
|
|
301
301
|
}): Promise<void> {
|
|
302
|
-
const spinner = ora('Starting
|
|
302
|
+
const spinner = ora('Starting Orchestrator Daemon...').start();
|
|
303
303
|
|
|
304
304
|
try {
|
|
305
305
|
await ensureConfigDir();
|
|
@@ -308,13 +308,13 @@ async function startDaemon(options: {
|
|
|
308
308
|
const currentStatus = await getDaemonStatus();
|
|
309
309
|
if (currentStatus.running) {
|
|
310
310
|
spinner.fail(
|
|
311
|
-
`
|
|
311
|
+
`Orchestrator Daemon is already running (PID: ${currentStatus.pid}, port: ${currentStatus.port})`,
|
|
312
312
|
);
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
// Load configuration
|
|
317
|
-
let config:
|
|
317
|
+
let config: OrchestratorConfig;
|
|
318
318
|
if (options.config && existsSync(options.config)) {
|
|
319
319
|
const content = await fs.readFile(options.config, 'utf-8');
|
|
320
320
|
config = { ...getDefaultConfig(), ...YAML.parse(content) };
|
|
@@ -331,25 +331,25 @@ async function startDaemon(options: {
|
|
|
331
331
|
// Save current config for daemon use
|
|
332
332
|
await saveConfig(config);
|
|
333
333
|
|
|
334
|
-
spinner.text = 'Initializing
|
|
334
|
+
spinner.text = 'Initializing Orchestrator Daemon subsystems...';
|
|
335
335
|
|
|
336
|
-
// Dynamically import
|
|
336
|
+
// Dynamically import OrchestratorDaemon at runtime to avoid TypeScript rootDir issues
|
|
337
337
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
338
338
|
let daemon: any;
|
|
339
339
|
try {
|
|
340
340
|
// Use require for CommonJS compatibility or dynamic import for ESM
|
|
341
341
|
const daemonModulePath = require
|
|
342
|
-
.resolve('@wundr/
|
|
342
|
+
.resolve('@wundr/orchestrator-daemon')
|
|
343
343
|
.replace(/\.js$/, '');
|
|
344
344
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
345
345
|
const daemonModule = require(daemonModulePath);
|
|
346
|
-
const
|
|
346
|
+
const OrchestratorDaemon = daemonModule.OrchestratorDaemon || daemonModule.default?.OrchestratorDaemon;
|
|
347
347
|
|
|
348
|
-
if (!
|
|
349
|
-
throw new Error('
|
|
348
|
+
if (!OrchestratorDaemon) {
|
|
349
|
+
throw new Error('OrchestratorDaemon class not found in module');
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
daemon = new
|
|
352
|
+
daemon = new OrchestratorDaemon({
|
|
353
353
|
name: config.daemon.name,
|
|
354
354
|
port: config.daemon.port,
|
|
355
355
|
host: config.daemon.host,
|
|
@@ -360,13 +360,13 @@ async function startDaemon(options: {
|
|
|
360
360
|
});
|
|
361
361
|
} catch (importError) {
|
|
362
362
|
// Fallback: try to spawn the daemon as a subprocess
|
|
363
|
-
spinner.fail('Failed to load
|
|
364
|
-
console.error(chalk.red('\nThe
|
|
363
|
+
spinner.fail('Failed to load Orchestrator Daemon module');
|
|
364
|
+
console.error(chalk.red('\nThe Orchestrator Daemon module could not be loaded.'));
|
|
365
365
|
console.error(chalk.gray('Options to resolve:'));
|
|
366
|
-
console.error(chalk.white(' 1. Install: npm install @wundr/
|
|
366
|
+
console.error(chalk.white(' 1. Install: npm install @wundr/orchestrator-daemon'));
|
|
367
367
|
console.error(
|
|
368
368
|
chalk.white(
|
|
369
|
-
' 2. Or build from source: cd scripts/
|
|
369
|
+
' 2. Or build from source: cd scripts/orchestrator-daemon && npm run build',
|
|
370
370
|
),
|
|
371
371
|
);
|
|
372
372
|
console.error(
|
|
@@ -378,7 +378,7 @@ async function startDaemon(options: {
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
// Write PID file
|
|
381
|
-
await fs.writeFile(
|
|
381
|
+
await fs.writeFile(ORCHESTRATOR_PID_FILE, String(process.pid));
|
|
382
382
|
|
|
383
383
|
// Set up status updates
|
|
384
384
|
const updateStatus = () => {
|
|
@@ -398,7 +398,7 @@ async function startDaemon(options: {
|
|
|
398
398
|
),
|
|
399
399
|
};
|
|
400
400
|
fs.writeFile(
|
|
401
|
-
path.join(
|
|
401
|
+
path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json'),
|
|
402
402
|
JSON.stringify(statusData, null, 2),
|
|
403
403
|
).catch(() => {});
|
|
404
404
|
};
|
|
@@ -407,8 +407,8 @@ async function startDaemon(options: {
|
|
|
407
407
|
|
|
408
408
|
// Set up cleanup handlers
|
|
409
409
|
const cleanup = async () => {
|
|
410
|
-
await fs.unlink(
|
|
411
|
-
await fs.unlink(path.join(
|
|
410
|
+
await fs.unlink(ORCHESTRATOR_PID_FILE).catch(() => {});
|
|
411
|
+
await fs.unlink(path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json')).catch(() => {});
|
|
412
412
|
};
|
|
413
413
|
|
|
414
414
|
daemon.on('stopped', cleanup);
|
|
@@ -417,15 +417,15 @@ async function startDaemon(options: {
|
|
|
417
417
|
await daemon.start();
|
|
418
418
|
|
|
419
419
|
spinner.succeed(
|
|
420
|
-
`
|
|
420
|
+
`Orchestrator Daemon started successfully on ${config.daemon.host}:${config.daemon.port}`,
|
|
421
421
|
);
|
|
422
422
|
|
|
423
423
|
console.log(chalk.gray('\nDaemon Information:'));
|
|
424
424
|
console.log(chalk.white(` PID: ${process.pid}`));
|
|
425
425
|
console.log(chalk.white(` Port: ${config.daemon.port}`));
|
|
426
426
|
console.log(chalk.white(` Host: ${config.daemon.host}`));
|
|
427
|
-
console.log(chalk.white(` Config: ${
|
|
428
|
-
console.log(chalk.white(` Logs: ${
|
|
427
|
+
console.log(chalk.white(` Config: ${ORCHESTRATOR_CONFIG_FILE}`));
|
|
428
|
+
console.log(chalk.white(` Logs: ${ORCHESTRATOR_LOG_FILE}`));
|
|
429
429
|
|
|
430
430
|
if (options.verbose) {
|
|
431
431
|
console.log(chalk.gray('\nVerbose mode enabled - showing detailed logs'));
|
|
@@ -440,7 +440,7 @@ async function startDaemon(options: {
|
|
|
440
440
|
});
|
|
441
441
|
}
|
|
442
442
|
} catch (error) {
|
|
443
|
-
spinner.fail('Failed to start
|
|
443
|
+
spinner.fail('Failed to start Orchestrator Daemon');
|
|
444
444
|
console.error(
|
|
445
445
|
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
446
446
|
);
|
|
@@ -448,7 +448,7 @@ async function startDaemon(options: {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
async function showStatus(options: { json?: boolean }): Promise<void> {
|
|
451
|
-
const spinner = ora('Checking
|
|
451
|
+
const spinner = ora('Checking Orchestrator Daemon status...').start();
|
|
452
452
|
|
|
453
453
|
try {
|
|
454
454
|
const status = await getDaemonStatus();
|
|
@@ -474,7 +474,7 @@ async function showStatus(options: { json?: boolean }): Promise<void> {
|
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
-
console.log(chalk.cyan('\
|
|
477
|
+
console.log(chalk.cyan('\nOrchestrator Daemon Status\n'));
|
|
478
478
|
console.log(chalk.gray('='.repeat(50)));
|
|
479
479
|
|
|
480
480
|
if (status.running) {
|
|
@@ -521,11 +521,11 @@ async function showStatus(options: { json?: boolean }): Promise<void> {
|
|
|
521
521
|
} else {
|
|
522
522
|
console.log(chalk.yellow('Status: STOPPED'));
|
|
523
523
|
console.log(chalk.gray('\nDaemon is not running.'));
|
|
524
|
-
console.log(chalk.gray('Start it with: wundr
|
|
524
|
+
console.log(chalk.gray('Start it with: wundr orchestrator start'));
|
|
525
525
|
}
|
|
526
526
|
|
|
527
527
|
console.log(chalk.gray('\n' + '='.repeat(50)));
|
|
528
|
-
console.log(chalk.gray(`Config: ${
|
|
528
|
+
console.log(chalk.gray(`Config: ${ORCHESTRATOR_CONFIG_FILE}`));
|
|
529
529
|
console.log('');
|
|
530
530
|
} catch (error) {
|
|
531
531
|
spinner.fail('Failed to get daemon status');
|
|
@@ -539,13 +539,13 @@ async function stopDaemon(options: {
|
|
|
539
539
|
force?: boolean;
|
|
540
540
|
timeout?: string;
|
|
541
541
|
}): Promise<void> {
|
|
542
|
-
const spinner = ora('Stopping
|
|
542
|
+
const spinner = ora('Stopping Orchestrator Daemon...').start();
|
|
543
543
|
|
|
544
544
|
try {
|
|
545
545
|
const status = await getDaemonStatus();
|
|
546
546
|
|
|
547
547
|
if (!status.running || !status.pid) {
|
|
548
|
-
spinner.info('
|
|
548
|
+
spinner.info('Orchestrator Daemon is not running');
|
|
549
549
|
return;
|
|
550
550
|
}
|
|
551
551
|
|
|
@@ -555,9 +555,9 @@ async function stopDaemon(options: {
|
|
|
555
555
|
if (options.force) {
|
|
556
556
|
spinner.text = 'Force stopping daemon...';
|
|
557
557
|
process.kill(pid, 'SIGKILL');
|
|
558
|
-
await fs.unlink(
|
|
559
|
-
await fs.unlink(path.join(
|
|
560
|
-
spinner.succeed('
|
|
558
|
+
await fs.unlink(ORCHESTRATOR_PID_FILE).catch(() => {});
|
|
559
|
+
await fs.unlink(path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json')).catch(() => {});
|
|
560
|
+
spinner.succeed('Orchestrator Daemon force stopped');
|
|
561
561
|
return;
|
|
562
562
|
}
|
|
563
563
|
|
|
@@ -573,11 +573,11 @@ async function stopDaemon(options: {
|
|
|
573
573
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
574
574
|
} catch {
|
|
575
575
|
// Process exited
|
|
576
|
-
await fs.unlink(
|
|
576
|
+
await fs.unlink(ORCHESTRATOR_PID_FILE).catch(() => {});
|
|
577
577
|
await fs
|
|
578
|
-
.unlink(path.join(
|
|
578
|
+
.unlink(path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json'))
|
|
579
579
|
.catch(() => {});
|
|
580
|
-
spinner.succeed('
|
|
580
|
+
spinner.succeed('Orchestrator Daemon stopped gracefully');
|
|
581
581
|
return;
|
|
582
582
|
}
|
|
583
583
|
}
|
|
@@ -585,11 +585,11 @@ async function stopDaemon(options: {
|
|
|
585
585
|
// Timeout reached, force kill
|
|
586
586
|
spinner.text = 'Graceful shutdown timed out, forcing stop...';
|
|
587
587
|
process.kill(pid, 'SIGKILL');
|
|
588
|
-
await fs.unlink(
|
|
589
|
-
await fs.unlink(path.join(
|
|
590
|
-
spinner.warn('
|
|
588
|
+
await fs.unlink(ORCHESTRATOR_PID_FILE).catch(() => {});
|
|
589
|
+
await fs.unlink(path.join(ORCHESTRATOR_CONFIG_DIR, 'status.json')).catch(() => {});
|
|
590
|
+
spinner.warn('Orchestrator Daemon stopped (forced after timeout)');
|
|
591
591
|
} catch (error) {
|
|
592
|
-
spinner.fail('Failed to stop
|
|
592
|
+
spinner.fail('Failed to stop Orchestrator Daemon');
|
|
593
593
|
console.error(
|
|
594
594
|
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
595
595
|
);
|
|
@@ -600,8 +600,8 @@ async function showConfig(options: { json?: boolean }): Promise<void> {
|
|
|
600
600
|
try {
|
|
601
601
|
const config = await loadConfig();
|
|
602
602
|
|
|
603
|
-
console.log(chalk.cyan('\
|
|
604
|
-
console.log(chalk.gray(`File: ${
|
|
603
|
+
console.log(chalk.cyan('\nOrchestrator Daemon Configuration\n'));
|
|
604
|
+
console.log(chalk.gray(`File: ${ORCHESTRATOR_CONFIG_FILE}`));
|
|
605
605
|
console.log(chalk.gray('='.repeat(50) + '\n'));
|
|
606
606
|
|
|
607
607
|
if (options.json) {
|
|
@@ -623,10 +623,10 @@ async function setConfig(keyValue: string): Promise<void> {
|
|
|
623
623
|
|
|
624
624
|
if (!keyPath || valueStr === undefined) {
|
|
625
625
|
console.error(
|
|
626
|
-
chalk.red('Invalid format. Use: wundr
|
|
626
|
+
chalk.red('Invalid format. Use: wundr orchestrator config set <key>=<value>'),
|
|
627
627
|
);
|
|
628
628
|
console.error(
|
|
629
|
-
chalk.gray('Example: wundr
|
|
629
|
+
chalk.gray('Example: wundr orchestrator config set daemon.port=9000'),
|
|
630
630
|
);
|
|
631
631
|
return;
|
|
632
632
|
}
|
|
@@ -691,7 +691,7 @@ async function resetConfig(options: { force?: boolean }): Promise<void> {
|
|
|
691
691
|
{
|
|
692
692
|
type: 'confirm',
|
|
693
693
|
name: 'confirm',
|
|
694
|
-
message: 'Reset
|
|
694
|
+
message: 'Reset Orchestrator Daemon configuration to defaults?',
|
|
695
695
|
default: false,
|
|
696
696
|
},
|
|
697
697
|
]);
|
|
@@ -706,7 +706,7 @@ async function resetConfig(options: { force?: boolean }): Promise<void> {
|
|
|
706
706
|
await saveConfig(config);
|
|
707
707
|
|
|
708
708
|
console.log(chalk.green('Configuration reset to defaults.'));
|
|
709
|
-
console.log(chalk.gray(`Saved to: ${
|
|
709
|
+
console.log(chalk.gray(`Saved to: ${ORCHESTRATOR_CONFIG_FILE}`));
|
|
710
710
|
} catch (error) {
|
|
711
711
|
console.error(
|
|
712
712
|
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
@@ -720,9 +720,9 @@ async function viewLogs(options: {
|
|
|
720
720
|
}): Promise<void> {
|
|
721
721
|
const lines = parseInt(options.lines ?? '50', 10);
|
|
722
722
|
|
|
723
|
-
if (!existsSync(
|
|
723
|
+
if (!existsSync(ORCHESTRATOR_LOG_FILE)) {
|
|
724
724
|
console.log(chalk.yellow('No log file found.'));
|
|
725
|
-
console.log(chalk.gray(`Expected location: ${
|
|
725
|
+
console.log(chalk.gray(`Expected location: ${ORCHESTRATOR_LOG_FILE}`));
|
|
726
726
|
console.log(
|
|
727
727
|
chalk.gray('Start the daemon with --verbose to enable logging.'),
|
|
728
728
|
);
|
|
@@ -731,11 +731,11 @@ async function viewLogs(options: {
|
|
|
731
731
|
|
|
732
732
|
try {
|
|
733
733
|
if (options.follow) {
|
|
734
|
-
console.log(chalk.cyan(`Following logs from ${
|
|
734
|
+
console.log(chalk.cyan(`Following logs from ${ORCHESTRATOR_LOG_FILE}...`));
|
|
735
735
|
console.log(chalk.gray('Press Ctrl+C to stop.\n'));
|
|
736
736
|
|
|
737
737
|
const { spawn } = await import('child_process');
|
|
738
|
-
const tail = spawn('tail', ['-f', '-n', String(lines),
|
|
738
|
+
const tail = spawn('tail', ['-f', '-n', String(lines), ORCHESTRATOR_LOG_FILE], {
|
|
739
739
|
stdio: 'inherit',
|
|
740
740
|
});
|
|
741
741
|
|
|
@@ -747,11 +747,11 @@ async function viewLogs(options: {
|
|
|
747
747
|
tail.on('close', () => resolve());
|
|
748
748
|
});
|
|
749
749
|
} else {
|
|
750
|
-
const content = await fs.readFile(
|
|
750
|
+
const content = await fs.readFile(ORCHESTRATOR_LOG_FILE, 'utf-8');
|
|
751
751
|
const logLines = content.split('\n');
|
|
752
752
|
const lastLines = logLines.slice(-lines).join('\n');
|
|
753
753
|
|
|
754
|
-
console.log(chalk.cyan(`Last ${lines} lines from ${
|
|
754
|
+
console.log(chalk.cyan(`Last ${lines} lines from ${ORCHESTRATOR_LOG_FILE}:\n`));
|
|
755
755
|
console.log(lastLines);
|
|
756
756
|
}
|
|
757
757
|
} catch (error) {
|