luxlabs 1.0.16 ā 1.0.17
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/commands/webview.js +36 -0
- package/commands/workflows.js +234 -0
- package/package.json +1 -1
package/commands/webview.js
CHANGED
|
@@ -233,6 +233,27 @@ async function wait(interfaceId, ms) {
|
|
|
233
233
|
return result;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Refresh the webview (soft or hard refresh)
|
|
238
|
+
*/
|
|
239
|
+
async function refresh(interfaceId, hard = false) {
|
|
240
|
+
const id = generateId();
|
|
241
|
+
const result = await sendCommand({
|
|
242
|
+
id,
|
|
243
|
+
type: 'refresh',
|
|
244
|
+
appId: interfaceId,
|
|
245
|
+
payload: { hard },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (result.success) {
|
|
249
|
+
console.log(chalk.green(hard ? 'Hard refresh completed!' : 'Refresh completed!'));
|
|
250
|
+
} else {
|
|
251
|
+
console.log(chalk.red('Refresh failed:'), result.error);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
|
|
236
257
|
/**
|
|
237
258
|
* Start the interface preview in Lux Studio
|
|
238
259
|
*/
|
|
@@ -265,6 +286,7 @@ function showHelp() {
|
|
|
265
286
|
console.log(' lux test url <interface-id> Get current URL');
|
|
266
287
|
console.log(' lux test navigate <interface-id> <url> Navigate to URL');
|
|
267
288
|
console.log(' lux test wait <interface-id> <ms> Wait for duration');
|
|
289
|
+
console.log(' lux test refresh <interface-id> [--hard] Refresh the page');
|
|
268
290
|
console.log('');
|
|
269
291
|
console.log(chalk.cyan('Examples:'));
|
|
270
292
|
console.log(' lux test screenshot my-interface ./screenshot.png');
|
|
@@ -272,6 +294,7 @@ function showHelp() {
|
|
|
272
294
|
console.log(' lux test type my-interface "#email" "user@example.com"');
|
|
273
295
|
console.log(' lux test eval my-interface "document.title"');
|
|
274
296
|
console.log(' lux test navigate my-interface "http://localhost:3000/login"');
|
|
297
|
+
console.log(' lux test refresh my-interface --hard');
|
|
275
298
|
console.log('');
|
|
276
299
|
console.log(chalk.dim('Note: <interface-id> is the interface ID or name from "lux servers"'));
|
|
277
300
|
console.log('');
|
|
@@ -370,6 +393,18 @@ async function handleTest(args = []) {
|
|
|
370
393
|
break;
|
|
371
394
|
}
|
|
372
395
|
|
|
396
|
+
case 'refresh': {
|
|
397
|
+
const [interfaceId, ...flags] = rest;
|
|
398
|
+
if (!interfaceId) {
|
|
399
|
+
console.log(chalk.red('Error: interface-id is required'));
|
|
400
|
+
showHelp();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const hard = flags.includes('--hard') || flags.includes('-h');
|
|
404
|
+
await refresh(interfaceId, hard);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
|
|
373
408
|
default:
|
|
374
409
|
console.log(chalk.red(`Unknown test command: ${subcommand}`));
|
|
375
410
|
showHelp();
|
|
@@ -388,6 +423,7 @@ module.exports = {
|
|
|
388
423
|
getUrl,
|
|
389
424
|
navigate,
|
|
390
425
|
wait,
|
|
426
|
+
refresh,
|
|
391
427
|
startPreview,
|
|
392
428
|
sendCommand,
|
|
393
429
|
};
|
package/commands/workflows.js
CHANGED
|
@@ -102,6 +102,11 @@ ${chalk.bold('Commands:')}
|
|
|
102
102
|
delete <id> Delete a local workflow
|
|
103
103
|
diff <id> Show local vs published differences
|
|
104
104
|
|
|
105
|
+
${chalk.bold('Execution History:')}
|
|
106
|
+
executions <flow-id> [--limit N] List execution history for a flow
|
|
107
|
+
execution <flow-id> <exec-id> Get full execution details
|
|
108
|
+
node <flow-id> <exec-id> <node-id> Get specific node execution details
|
|
109
|
+
|
|
105
110
|
${chalk.bold('Sync Status:')}
|
|
106
111
|
draft - Never published, local only
|
|
107
112
|
synced - Matches published version
|
|
@@ -127,6 +132,12 @@ ${chalk.bold('Examples:')}
|
|
|
127
132
|
lux workflows publish flow_123
|
|
128
133
|
lux workflows diff flow_123
|
|
129
134
|
|
|
135
|
+
${chalk.bold('Execution history:')}
|
|
136
|
+
lux flow executions my-flow-id
|
|
137
|
+
lux flow executions my-flow-id --limit 50
|
|
138
|
+
lux flow execution my-flow-id exec_abc123
|
|
139
|
+
lux flow node my-flow-id exec_abc123 node-1
|
|
140
|
+
|
|
130
141
|
${chalk.bold('Webhook workflow:')}
|
|
131
142
|
lux flow webhook-url flow_123
|
|
132
143
|
lux flow webhook-listen flow_123
|
|
@@ -851,6 +862,229 @@ ${chalk.bold('Examples:')}
|
|
|
851
862
|
break;
|
|
852
863
|
}
|
|
853
864
|
|
|
865
|
+
// ============ EXECUTION HISTORY COMMANDS ============
|
|
866
|
+
|
|
867
|
+
case 'executions': {
|
|
868
|
+
requireArgs(args.slice(1), 1, 'lux flow executions <flow-id> [--limit N]');
|
|
869
|
+
const flowId = args[1];
|
|
870
|
+
|
|
871
|
+
// Parse --limit flag
|
|
872
|
+
const limitIndex = args.indexOf('--limit');
|
|
873
|
+
const limit = limitIndex !== -1 && args[limitIndex + 1] ? parseInt(args[limitIndex + 1], 10) : 20;
|
|
874
|
+
|
|
875
|
+
info(`Fetching execution history for: ${flowId}`);
|
|
876
|
+
|
|
877
|
+
const { data } = await axios.get(
|
|
878
|
+
`${apiUrl}/api/flows/${flowId}/executions?limit=${limit}`,
|
|
879
|
+
{ headers: getAuthHeaders() }
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
if (!data.executions || data.executions.length === 0) {
|
|
883
|
+
console.log(chalk.dim('\nNo executions found for this flow.\n'));
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
console.log(`\nš Execution History for: ${flowId}\n`);
|
|
888
|
+
|
|
889
|
+
// Status colors
|
|
890
|
+
const statusColors = {
|
|
891
|
+
completed: chalk.green,
|
|
892
|
+
running: chalk.cyan,
|
|
893
|
+
failed: chalk.red,
|
|
894
|
+
cancelled: chalk.yellow,
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const formatted = data.executions.map((exec) => {
|
|
898
|
+
const statusColor = statusColors[exec.status] || chalk.white;
|
|
899
|
+
const duration = exec.durationMs ? `${exec.durationMs}ms` : '-';
|
|
900
|
+
const startedAt = exec.startedAt ? new Date(exec.startedAt).toLocaleString() : '-';
|
|
901
|
+
const nodeCount = exec.nodeExecutions?.length || 0;
|
|
902
|
+
|
|
903
|
+
return {
|
|
904
|
+
id: exec.id.substring(0, 16) + '...',
|
|
905
|
+
status: statusColor(exec.status),
|
|
906
|
+
trigger: exec.triggerType || '-',
|
|
907
|
+
duration: duration,
|
|
908
|
+
nodes: nodeCount,
|
|
909
|
+
started: startedAt,
|
|
910
|
+
test: exec.isTest ? chalk.yellow('test') : '',
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
formatTable(formatted);
|
|
915
|
+
|
|
916
|
+
if (data.pagination?.hasMore) {
|
|
917
|
+
console.log(chalk.dim(`\nShowing ${data.executions.length} of ${data.pagination.total} executions.`));
|
|
918
|
+
console.log(chalk.dim(`Use --limit N to see more.\n`));
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
console.log(chalk.gray(`\nTo see full details: lux flow execution ${flowId} <execution-id>\n`));
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
case 'execution': {
|
|
926
|
+
requireArgs(args.slice(1), 2, 'lux flow execution <flow-id> <execution-id>');
|
|
927
|
+
const flowId = args[1];
|
|
928
|
+
const executionId = args[2];
|
|
929
|
+
|
|
930
|
+
info(`Fetching execution: ${executionId}`);
|
|
931
|
+
|
|
932
|
+
const { data } = await axios.get(
|
|
933
|
+
`${apiUrl}/api/flows/${flowId}/executions/${executionId}`,
|
|
934
|
+
{ headers: getAuthHeaders() }
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
if (!data.execution) {
|
|
938
|
+
error(`Execution not found: ${executionId}`);
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const exec = data.execution;
|
|
943
|
+
const statusColors = {
|
|
944
|
+
completed: chalk.green,
|
|
945
|
+
running: chalk.cyan,
|
|
946
|
+
failed: chalk.red,
|
|
947
|
+
cancelled: chalk.yellow,
|
|
948
|
+
};
|
|
949
|
+
const statusColor = statusColors[exec.status] || chalk.white;
|
|
950
|
+
|
|
951
|
+
console.log(`\nš Execution Details\n`);
|
|
952
|
+
console.log(` ID: ${exec.id}`);
|
|
953
|
+
console.log(` Flow: ${exec.flowId}`);
|
|
954
|
+
console.log(` Status: ${statusColor(exec.status)}`);
|
|
955
|
+
console.log(` Trigger: ${exec.triggerType || '-'}`);
|
|
956
|
+
console.log(` Duration: ${exec.durationMs ? exec.durationMs + 'ms' : '-'}`);
|
|
957
|
+
console.log(` Started: ${exec.startedAt ? new Date(exec.startedAt).toLocaleString() : '-'}`);
|
|
958
|
+
console.log(` Completed: ${exec.completedAt ? new Date(exec.completedAt).toLocaleString() : '-'}`);
|
|
959
|
+
console.log(` Version: ${exec.flowVersion || '-'}`);
|
|
960
|
+
console.log(` Test Run: ${exec.isTest ? chalk.yellow('Yes') : 'No'}`);
|
|
961
|
+
|
|
962
|
+
if (exec.error) {
|
|
963
|
+
console.log(`\n${chalk.red('ā Error:')}`);
|
|
964
|
+
console.log(` ${exec.error}`);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Show input data
|
|
968
|
+
if (exec.inputData && Object.keys(exec.inputData).length > 0) {
|
|
969
|
+
console.log(`\n${chalk.cyan('š„ Input Data:')}`);
|
|
970
|
+
console.log(formatJson(exec.inputData));
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Show output data
|
|
974
|
+
if (exec.outputData && Object.keys(exec.outputData).length > 0) {
|
|
975
|
+
console.log(`\n${chalk.cyan('š¤ Output Data:')}`);
|
|
976
|
+
console.log(formatJson(exec.outputData));
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Show node executions summary
|
|
980
|
+
if (exec.nodeExecutions && exec.nodeExecutions.length > 0) {
|
|
981
|
+
console.log(`\n${chalk.cyan('š Node Executions:')} (${exec.nodeExecutions.length} nodes)\n`);
|
|
982
|
+
|
|
983
|
+
const nodeFormatted = exec.nodeExecutions.map((node) => {
|
|
984
|
+
const nodeStatus = node.status || 'unknown';
|
|
985
|
+
const nodeStatusColor = statusColors[nodeStatus] || chalk.white;
|
|
986
|
+
const nodeDuration = node.durationMs ? `${node.durationMs}ms` : '-';
|
|
987
|
+
|
|
988
|
+
return {
|
|
989
|
+
node_id: node.nodeId || node.id,
|
|
990
|
+
type: node.nodeType || '-',
|
|
991
|
+
status: nodeStatusColor(nodeStatus),
|
|
992
|
+
duration: nodeDuration,
|
|
993
|
+
error: node.error ? chalk.red('ā ') : '',
|
|
994
|
+
};
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
formatTable(nodeFormatted);
|
|
998
|
+
|
|
999
|
+
console.log(chalk.gray(`\nTo see node details: lux flow node ${flowId} ${executionId} <node-id>\n`));
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
case 'node': {
|
|
1006
|
+
requireArgs(args.slice(1), 3, 'lux flow node <flow-id> <execution-id> <node-id>');
|
|
1007
|
+
const flowId = args[1];
|
|
1008
|
+
const executionId = args[2];
|
|
1009
|
+
const nodeId = args[3];
|
|
1010
|
+
|
|
1011
|
+
info(`Fetching node execution: ${nodeId}`);
|
|
1012
|
+
|
|
1013
|
+
const { data } = await axios.get(
|
|
1014
|
+
`${apiUrl}/api/flows/${flowId}/executions/${executionId}`,
|
|
1015
|
+
{ headers: getAuthHeaders() }
|
|
1016
|
+
);
|
|
1017
|
+
|
|
1018
|
+
if (!data.execution) {
|
|
1019
|
+
error(`Execution not found: ${executionId}`);
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const exec = data.execution;
|
|
1024
|
+
const nodeExec = exec.nodeExecutions?.find(
|
|
1025
|
+
(n) => n.nodeId === nodeId || n.id === nodeId
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
if (!nodeExec) {
|
|
1029
|
+
error(`Node not found in execution: ${nodeId}`);
|
|
1030
|
+
console.log(chalk.dim(`\nAvailable nodes:`));
|
|
1031
|
+
exec.nodeExecutions?.forEach((n) => {
|
|
1032
|
+
console.log(chalk.dim(` - ${n.nodeId || n.id} (${n.nodeType || 'unknown'})`));
|
|
1033
|
+
});
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const statusColors = {
|
|
1038
|
+
completed: chalk.green,
|
|
1039
|
+
running: chalk.cyan,
|
|
1040
|
+
failed: chalk.red,
|
|
1041
|
+
cancelled: chalk.yellow,
|
|
1042
|
+
skipped: chalk.gray,
|
|
1043
|
+
};
|
|
1044
|
+
const statusColor = statusColors[nodeExec.status] || chalk.white;
|
|
1045
|
+
|
|
1046
|
+
console.log(`\nš Node Execution Details\n`);
|
|
1047
|
+
console.log(` Node ID: ${nodeExec.nodeId || nodeExec.id}`);
|
|
1048
|
+
console.log(` Type: ${nodeExec.nodeType || '-'}`);
|
|
1049
|
+
console.log(` Label: ${nodeExec.label || '-'}`);
|
|
1050
|
+
console.log(` Status: ${statusColor(nodeExec.status || 'unknown')}`);
|
|
1051
|
+
console.log(` Duration: ${nodeExec.durationMs ? nodeExec.durationMs + 'ms' : '-'}`);
|
|
1052
|
+
console.log(` Started: ${nodeExec.startedAt ? new Date(nodeExec.startedAt).toLocaleString() : '-'}`);
|
|
1053
|
+
console.log(` Completed: ${nodeExec.completedAt ? new Date(nodeExec.completedAt).toLocaleString() : '-'}`);
|
|
1054
|
+
|
|
1055
|
+
if (nodeExec.error) {
|
|
1056
|
+
console.log(`\n${chalk.red('ā Error:')}`);
|
|
1057
|
+
console.log(` ${nodeExec.error}`);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Show node config
|
|
1061
|
+
if (nodeExec.config && Object.keys(nodeExec.config).length > 0) {
|
|
1062
|
+
console.log(`\n${chalk.cyan('āļø Node Config:')}`);
|
|
1063
|
+
console.log(formatJson(nodeExec.config));
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Show input
|
|
1067
|
+
if (nodeExec.input !== undefined) {
|
|
1068
|
+
console.log(`\n${chalk.cyan('š„ Input:')}`);
|
|
1069
|
+
console.log(formatJson(nodeExec.input));
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Show output
|
|
1073
|
+
if (nodeExec.output !== undefined) {
|
|
1074
|
+
console.log(`\n${chalk.cyan('š¤ Output:')}`);
|
|
1075
|
+
console.log(formatJson(nodeExec.output));
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// Show raw data if nothing else
|
|
1079
|
+
if (!nodeExec.input && !nodeExec.output && !nodeExec.config) {
|
|
1080
|
+
console.log(`\n${chalk.cyan('š Raw Node Data:')}`);
|
|
1081
|
+
console.log(formatJson(nodeExec));
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
console.log('');
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
854
1088
|
default:
|
|
855
1089
|
error(
|
|
856
1090
|
`Unknown command: ${command}\n\nRun 'lux workflows' to see available commands`
|
package/package.json
CHANGED