This commit is contained in:
Francesco Renzi
2026-01-22 00:12:27 +00:00
committed by GitHub
parent 1bba60b475
commit d334ab3f0a
8 changed files with 1446 additions and 1183 deletions

View File

@@ -28,6 +28,14 @@ function escapeHtml(text) {
return div.innerHTML;
}
/**
* Quote a string for use in command, escaping as needed
*/
function quoteString(str) {
// Escape backslashes and quotes, wrap in quotes
return `"${str.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
}
/**
* Strip result indicator suffix from step name
* e.g., "Run tests [running]" -> "Run tests"
@@ -510,7 +518,7 @@ function renderStepList(steps) {
async function loadSteps() {
try {
const response = await sendDapRequest('evaluate', {
expression: JSON.stringify({ cmd: 'step.list', verbose: false }),
expression: 'steps list --output json',
frameId: currentFrameId,
context: 'repl',
});
@@ -553,13 +561,122 @@ function enableStepControls(enabled) {
}
/**
* Send step command via JSON API
* Build REPL command string from action and options
*/
async function sendStepCommand(cmd, options = {}) {
const command = { cmd, ...options };
function buildStepCommand(action, options) {
let cmd;
switch (action) {
case 'step.list':
cmd = options.verbose ? 'steps list --verbose' : 'steps list';
break;
case 'step.add':
cmd = buildAddStepCommand(options);
break;
case 'step.edit':
cmd = buildEditStepCommand(options);
break;
case 'step.remove':
cmd = `steps remove ${options.index}`;
break;
case 'step.move':
cmd = buildMoveStepCommand(options);
break;
case 'step.export':
cmd = buildExportCommand(options);
break;
default:
throw new Error(`Unknown step command: ${action}`);
}
// Always request JSON output for programmatic use
return cmd + ' --output json';
}
/**
* Build add step command string
*/
function buildAddStepCommand(options) {
let cmd = 'steps add';
if (options.type === 'run') {
cmd += ` run ${quoteString(options.script)}`;
if (options.shell) cmd += ` --shell ${options.shell}`;
if (options.workingDirectory) cmd += ` --working-directory ${quoteString(options.workingDirectory)}`;
} else if (options.type === 'uses') {
cmd += ` uses ${options.action}`;
if (options.with) {
for (const [key, value] of Object.entries(options.with)) {
cmd += ` --with ${key}=${value}`;
}
}
}
if (options.name) cmd += ` --name ${quoteString(options.name)}`;
if (options.if) cmd += ` --if ${quoteString(options.if)}`;
if (options.env) {
for (const [key, value] of Object.entries(options.env)) {
cmd += ` --env ${key}=${value}`;
}
}
if (options.continueOnError) cmd += ' --continue-on-error';
if (options.timeout) cmd += ` --timeout ${options.timeout}`;
// Position
if (options.position) {
if (options.position.after !== undefined) cmd += ` --after ${options.position.after}`;
else if (options.position.before !== undefined) cmd += ` --before ${options.position.before}`;
else if (options.position.at !== undefined) cmd += ` --at ${options.position.at}`;
else if (options.position.first) cmd += ' --first';
// --last is default, no need to specify
}
return cmd;
}
/**
* Build edit step command string
*/
function buildEditStepCommand(options) {
let cmd = `steps edit ${options.index}`;
if (options.name) cmd += ` --name ${quoteString(options.name)}`;
if (options.script) cmd += ` --script ${quoteString(options.script)}`;
if (options.if) cmd += ` --if ${quoteString(options.if)}`;
if (options.shell) cmd += ` --shell ${options.shell}`;
if (options.workingDirectory) cmd += ` --working-directory ${quoteString(options.workingDirectory)}`;
return cmd;
}
/**
* Build move step command string
*/
function buildMoveStepCommand(options) {
let cmd = `steps move ${options.from}`;
const pos = options.position;
if (pos.after !== undefined) cmd += ` --after ${pos.after}`;
else if (pos.before !== undefined) cmd += ` --before ${pos.before}`;
else if (pos.at !== undefined) cmd += ` --to ${pos.at}`;
else if (pos.first) cmd += ' --first';
else if (pos.last) cmd += ' --last';
return cmd;
}
/**
* Build export command string
*/
function buildExportCommand(options) {
let cmd = 'steps export';
if (options.changesOnly) cmd += ' --changes-only';
if (options.withComments) cmd += ' --with-comments';
return cmd;
}
/**
* Send step command via REPL format
*/
async function sendStepCommand(action, options = {}) {
const expression = buildStepCommand(action, options);
try {
const response = await sendDapRequest('evaluate', {
expression: JSON.stringify(command),
expression,
frameId: currentFrameId,
context: 'repl',
});
@@ -568,7 +685,8 @@ async function sendStepCommand(cmd, options = {}) {
try {
return JSON.parse(response.result);
} catch (e) {
return { Success: false, Error: 'PARSE_ERROR', Message: response.result };
// Response might be plain text for non-JSON output
return { Success: true, Message: response.result };
}
}
return { Success: false, Error: 'NO_RESPONSE', Message: 'No response from server' };
@@ -1141,7 +1259,8 @@ async function handleReplKeydown(e) {
const response = await sendDapRequest('evaluate', {
expression: command,
frameId: currentFrameId,
context: command.startsWith('!') ? 'repl' : 'watch',
// Use 'repl' context for shell commands (!) and step commands
context: (command.startsWith('!') || command.startsWith('steps')) ? 'repl' : 'watch',
});
// Only show result if it's NOT an exit code summary
// (shell command output is already streamed via output events)