feat(cli/daemon/processmonitor): Add flexible target resolution and search command; improve restart/backoff and error handling
This commit is contained in:
@@ -22,13 +22,14 @@ export function registerDefaultCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
);
|
||||
console.log(' disable Disable TSPM system service');
|
||||
console.log('\nProcess Commands:');
|
||||
console.log(' start <script> Start a process');
|
||||
console.log(' start <id|id:N|name:LBL> Start a process');
|
||||
console.log(' list List all processes');
|
||||
console.log(' stop <id> Stop a process');
|
||||
console.log(' restart <id> Restart a process');
|
||||
console.log(' delete <id> Delete a process');
|
||||
console.log(' describe <id> Show details for a process');
|
||||
console.log(' logs <id> Show logs for a process');
|
||||
console.log(' stop <id|id:N|name:LBL> Stop a process');
|
||||
console.log(' restart <id|id:N|name:LBL> Restart a process');
|
||||
console.log(' delete <id|id:N|name:LBL> Delete a process');
|
||||
console.log(' describe <id|id:N|name:LBL> Show details for a process');
|
||||
console.log(' logs <id|id:N|name:LBL> Show logs for a process');
|
||||
console.log(' search <query> Find processes by id/name');
|
||||
console.log(' start-all Start all saved processes');
|
||||
console.log(' stop-all Stop all processes');
|
||||
console.log(' restart-all Restart all processes');
|
||||
|
@@ -8,23 +8,25 @@ export function registerDeleteCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
['delete', 'remove'],
|
||||
async (argvArg: CliArguments) => {
|
||||
const id = argvArg._[1];
|
||||
if (!id) {
|
||||
console.error('Error: Please provide a process ID');
|
||||
console.log('Usage: tspm delete <id> | tspm remove <id>');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target');
|
||||
console.log('Usage: tspm delete <id|id:N|name:LABEL> | tspm remove <id|id:N|name:LABEL>');
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if command was 'remove' to use the new IPC route, otherwise 'delete'
|
||||
const cmd = String(argvArg._[0]);
|
||||
const useRemove = cmd === 'remove';
|
||||
console.log(`${useRemove ? 'Removing' : 'Deleting'} process: ${id}`);
|
||||
const response = await tspmIpcClient.request(useRemove ? 'remove' : 'delete', { id } as any);
|
||||
const isRemoveAlias = cmd === 'remove';
|
||||
console.log(`${isRemoveAlias ? 'Removing' : 'Deleting'} process: ${target}`);
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
// Always call daemon 'delete'; 'remove' is CLI alias only
|
||||
const response = await tspmIpcClient.request('delete', { id: resolved.id } as any);
|
||||
|
||||
if (response.success) {
|
||||
console.log(`✓ ${response.message || (useRemove ? 'Removed successfully' : 'Deleted successfully')}`);
|
||||
console.log(`✓ ${response.message || (isRemoveAlias ? 'Removed successfully' : 'Deleted successfully')}`);
|
||||
} else {
|
||||
console.error(`✗ Failed to ${useRemove ? 'remove' : 'delete'} process: ${response.message}`);
|
||||
console.error(`✗ Failed to ${isRemoveAlias ? 'remove' : 'delete'} process: ${response.message}`);
|
||||
}
|
||||
},
|
||||
{ actionLabel: 'delete/remove process' },
|
||||
|
@@ -9,16 +9,17 @@ export function registerDescribeCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
'describe',
|
||||
async (argvArg: CliArguments) => {
|
||||
const id = argvArg._[1];
|
||||
if (!id) {
|
||||
console.error('Error: Please provide a process ID');
|
||||
console.log('Usage: tspm describe <id>');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target');
|
||||
console.log('Usage: tspm describe <id | id:N | name:LABEL>');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await tspmIpcClient.request('describe', { id });
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
const response = await tspmIpcClient.request('describe', { id: resolved.id });
|
||||
|
||||
console.log(`Process Details: ${id}`);
|
||||
console.log(`Process Details: ${response.config.name || resolved.id}`);
|
||||
console.log('─'.repeat(40));
|
||||
console.log(`Status: ${response.processInfo.status}`);
|
||||
console.log(`PID: ${response.processInfo.pid || 'N/A'}`);
|
||||
|
@@ -9,17 +9,16 @@ export function registerEditCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
'edit',
|
||||
async (argvArg: CliArguments) => {
|
||||
const idRaw = argvArg._[1];
|
||||
if (!idRaw) {
|
||||
console.error('Error: Please provide a process ID to edit');
|
||||
console.log('Usage: tspm edit <id>');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target to edit');
|
||||
console.log('Usage: tspm edit <id | id:N | name:LABEL>');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = idRaw;
|
||||
|
||||
// Load current config
|
||||
const { config } = await tspmIpcClient.request('describe', { id });
|
||||
// Resolve and load current config
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
const { config } = await tspmIpcClient.request('describe', { id: resolved.id });
|
||||
|
||||
// Interactive editing is temporarily disabled - needs smartinteract API update
|
||||
console.log('Interactive editing is temporarily disabled.');
|
||||
@@ -63,7 +62,7 @@ export function registerEditCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
};
|
||||
|
||||
const updateResponse = await tspmIpcClient.request('update', {
|
||||
id,
|
||||
id: resolved.id,
|
||||
updates,
|
||||
});
|
||||
|
||||
@@ -73,4 +72,3 @@ export function registerEditCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
{ actionLabel: 'edit process config' },
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -11,10 +11,10 @@ export function registerLogsCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
'logs',
|
||||
async (argvArg: CliArguments) => {
|
||||
const id = argvArg._[1];
|
||||
if (!id) {
|
||||
console.error('Error: Please provide a process ID');
|
||||
console.log('Usage: tspm logs <id> [options]');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target');
|
||||
console.log('Usage: tspm logs <id | id:N | name:LABEL> [options]');
|
||||
console.log('\nOptions:');
|
||||
console.log(' --lines <n> Number of lines to show (default: 50)');
|
||||
console.log(' --follow Stream logs in real-time (like tail -f)');
|
||||
@@ -24,6 +24,8 @@ export function registerLogsCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
const lines = getNumber(argvArg, 'lines', 50);
|
||||
const follow = getBool(argvArg, 'follow', 'f');
|
||||
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
const id = resolved.id;
|
||||
const response = await tspmIpcClient.request('getLogs', { id, lines });
|
||||
|
||||
if (!follow) {
|
||||
@@ -44,7 +46,7 @@ export function registerLogsCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
}
|
||||
|
||||
// Streaming mode
|
||||
console.log(`Logs for process: ${id} (streaming...)`);
|
||||
console.log(`Logs for process: ${resolved.name || id} (streaming...)`);
|
||||
console.log('─'.repeat(60));
|
||||
|
||||
let lastSeq = 0;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import { tspmIpcClient } from '../../../client/tspm.ipcclient.js';
|
||||
import { toProcessId } from '../../../shared/protocol/id.js';
|
||||
import type { CliArguments } from '../../types.js';
|
||||
import { registerIpcCommand } from '../../registration/index.js';
|
||||
|
||||
@@ -11,9 +10,9 @@ export function registerRestartCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
async (argvArg: CliArguments) => {
|
||||
const arg = argvArg._[1];
|
||||
if (!arg) {
|
||||
console.error('Error: Please provide a process ID or "all"');
|
||||
console.error('Error: Please provide a process target or "all"');
|
||||
console.log('Usage:');
|
||||
console.log(' tspm restart <id>');
|
||||
console.log(' tspm restart <id | id:N | name:LABEL>');
|
||||
console.log(' tspm restart all');
|
||||
return;
|
||||
}
|
||||
@@ -33,12 +32,13 @@ export function registerRestartCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = String(arg);
|
||||
console.log(`Restarting process: ${id}`);
|
||||
const response = await tspmIpcClient.request('restart', { id: toProcessId(id) });
|
||||
const target = String(arg);
|
||||
console.log(`Restarting process: ${target}`);
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target });
|
||||
const response = await tspmIpcClient.request('restart', { id: resolved.id });
|
||||
|
||||
console.log(`✓ Process restarted successfully`);
|
||||
console.log(` ID: ${response.processId}`);
|
||||
console.log(` ID: ${response.processId}${resolved.name ? ` (name: ${resolved.name})` : ''}`);
|
||||
console.log(` PID: ${response.pid || 'N/A'}`);
|
||||
console.log(` Status: ${response.status}`);
|
||||
},
|
||||
|
62
ts/cli/commands/process/search.ts
Normal file
62
ts/cli/commands/process/search.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import { tspmIpcClient } from '../../../client/tspm.ipcclient.js';
|
||||
import type { CliArguments } from '../../types.js';
|
||||
import { registerIpcCommand } from '../../registration/index.js';
|
||||
|
||||
export function registerSearchCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
registerIpcCommand(
|
||||
smartcli,
|
||||
'search',
|
||||
async (argvArg: CliArguments) => {
|
||||
const query = String(argvArg._[1] || '').trim();
|
||||
if (!query) {
|
||||
console.error('Error: Please provide a search query');
|
||||
console.log('Usage: tspm search <name-fragment | id-fragment>');
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch list of processes, then enrich with names via describe
|
||||
const listRes = await tspmIpcClient.request('list', {});
|
||||
const processes = listRes.processes;
|
||||
|
||||
// If there are no processes, short-circuit
|
||||
if (processes.length === 0) {
|
||||
console.log('No processes found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const lowerQ = query.toLowerCase();
|
||||
const matches: Array<{ id: number; name?: string }> = [];
|
||||
|
||||
// Collect describe calls to obtain names
|
||||
for (const proc of processes) {
|
||||
try {
|
||||
const desc = await tspmIpcClient.request('describe', { id: proc.id });
|
||||
const name = desc.config.name || '';
|
||||
const idStr = String(proc.id);
|
||||
if (name.toLowerCase().includes(lowerQ) || idStr.includes(query)) {
|
||||
matches.push({ id: proc.id, name });
|
||||
}
|
||||
} catch {
|
||||
// Ignore describe errors for individual processes
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.length === 0) {
|
||||
console.log(`No matches for "${query}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Matches for "${query}":`);
|
||||
for (const m of matches) {
|
||||
if (m.name) {
|
||||
console.log(`- id:${m.id}\tname:${m.name}`);
|
||||
} else {
|
||||
console.log(`- id:${m.id}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ actionLabel: 'search processes' },
|
||||
);
|
||||
}
|
||||
|
@@ -10,17 +10,18 @@ export function registerStartCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
'start',
|
||||
async (argvArg: CliArguments) => {
|
||||
const id = argvArg._[1];
|
||||
if (!id) {
|
||||
console.error('Error: Please provide a process ID to start');
|
||||
console.log('Usage: tspm start <id>');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target to start');
|
||||
console.log('Usage: tspm start <id | id:N | name:LABEL>');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Starting process id ${id}...`);
|
||||
const response = await tspmIpcClient.request('startById', { id });
|
||||
console.log(`Starting process: ${target}...`);
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
const response = await tspmIpcClient.request('startById', { id: resolved.id });
|
||||
console.log('✓ Process started');
|
||||
console.log(` ID: ${response.processId}`);
|
||||
console.log(` ID: ${response.processId}${resolved.name ? ` (name: ${resolved.name})` : ''}`);
|
||||
console.log(` PID: ${response.pid || 'N/A'}`);
|
||||
console.log(` Status: ${response.status}`);
|
||||
},
|
||||
|
@@ -8,15 +8,16 @@ export function registerStopCommand(smartcli: plugins.smartcli.Smartcli) {
|
||||
smartcli,
|
||||
'stop',
|
||||
async (argvArg: CliArguments) => {
|
||||
const id = argvArg._[1];
|
||||
if (!id) {
|
||||
console.error('Error: Please provide a process ID');
|
||||
console.log('Usage: tspm stop <id>');
|
||||
const target = argvArg._[1];
|
||||
if (!target) {
|
||||
console.error('Error: Please provide a process target');
|
||||
console.log('Usage: tspm stop <id | id:N | name:LABEL>');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Stopping process: ${id}`);
|
||||
const response = await tspmIpcClient.request('stop', { id });
|
||||
console.log(`Stopping process: ${target}`);
|
||||
const resolved = await tspmIpcClient.request('resolveTarget', { target: String(target) });
|
||||
const response = await tspmIpcClient.request('stop', { id: resolved.id });
|
||||
|
||||
if (response.success) {
|
||||
console.log(`✓ ${response.message}`);
|
||||
|
@@ -10,6 +10,7 @@ import { registerAddCommand } from './commands/process/add.js';
|
||||
import { registerStopCommand } from './commands/process/stop.js';
|
||||
import { registerRestartCommand } from './commands/process/restart.js';
|
||||
import { registerDeleteCommand } from './commands/process/delete.js';
|
||||
import { registerSearchCommand } from './commands/process/search.js';
|
||||
import { registerListCommand } from './commands/process/list.js';
|
||||
import { registerDescribeCommand } from './commands/process/describe.js';
|
||||
import { registerLogsCommand } from './commands/process/logs.js';
|
||||
@@ -74,6 +75,7 @@ export const run = async (): Promise<void> => {
|
||||
registerDescribeCommand(smartcliInstance);
|
||||
registerLogsCommand(smartcliInstance);
|
||||
registerEditCommand(smartcliInstance);
|
||||
registerSearchCommand(smartcliInstance);
|
||||
|
||||
// Batch commands
|
||||
registerStartAllCommand(smartcliInstance);
|
||||
|
Reference in New Issue
Block a user