Skip to content

Commit 975e202

Browse files
committed
Fix RPA import portability and local script execution
1 parent 35d8b15 commit 975e202

3 files changed

Lines changed: 145 additions & 53 deletions

File tree

plugins/pages/rpa-workflow/src/components/task-editor/workflow-mapper.ts

Lines changed: 124 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,30 @@ interface EditorState {
1818
workflow: RpaWorkflowSchema;
1919
}
2020

21-
interface StoredWorkflowStep {
22-
id?: string;
23-
type?: string;
24-
name?: string;
25-
enabled?: boolean;
26-
next?: string | null;
27-
branches?: Record<string, string>;
28-
config?: Record<string, unknown>;
29-
ui?: {
30-
source_type?: string;
21+
interface StoredWorkflowStep {
22+
id?: string;
23+
type?: string;
24+
name?: string;
25+
enabled?: boolean;
26+
next?: string | null;
27+
branches?: Record<string, string>;
28+
click_type?: string;
29+
wait_for_element?: boolean;
30+
clear_first?: boolean;
31+
type_slowly?: boolean;
32+
duration_ms?: number;
33+
timeout_ms?: number;
34+
output?: string;
35+
script?: string;
36+
url?: string;
37+
value?: string;
38+
target?: {
39+
by?: string;
40+
value?: string;
41+
};
42+
config?: Record<string, unknown>;
43+
ui?: {
44+
source_type?: string;
3145
position?: {
3246
x?: number;
3347
y?: number;
@@ -330,18 +344,69 @@ function mapStoredGlobalVariables(
330344
.filter((variable) => variable.name.trim());
331345
}
332346

333-
function buildFlowStepFromStoredStep(step: StoredWorkflowStep): FlowStep {
334-
const uiType = typeof step.ui?.source_type === 'string' ? step.ui.source_type : step.type;
335-
const position = step.ui?.position;
336-
const parentLoopId = typeof step.ui?.parent_id === 'string' ? step.ui.parent_id : null;
337-
const config =
338-
step.config && typeof step.config === 'object'
339-
? { ...(step.config as Record<string, unknown>) }
340-
: {};
341-
342-
if (step.branches && typeof step.branches === 'object') {
343-
config.branches = step.branches;
344-
}
347+
function buildFlowStepFromStoredStep(step: StoredWorkflowStep): FlowStep {
348+
const uiType = typeof step.ui?.source_type === 'string' ? step.ui.source_type : step.type;
349+
const position = step.ui?.position;
350+
const parentLoopId = typeof step.ui?.parent_id === 'string' ? step.ui.parent_id : null;
351+
const config =
352+
step.config && typeof step.config === 'object'
353+
? { ...(step.config as Record<string, unknown>) }
354+
: {};
355+
const selector = typeof step.target?.value === 'string' ? step.target.value : '';
356+
357+
if (!config.selector && selector) {
358+
config.selector = selector;
359+
}
360+
361+
if (!config.url && typeof step.url === 'string') {
362+
config.url = step.url;
363+
}
364+
365+
if (step.type === 'click') {
366+
if (!config.clickType && typeof step.click_type === 'string') {
367+
config.clickType = step.click_type;
368+
}
369+
if (!Object.prototype.hasOwnProperty.call(config, 'waitForElement')) {
370+
config.waitForElement = step.wait_for_element !== false;
371+
}
372+
}
373+
374+
if (step.type === 'fill') {
375+
if (!config.text && typeof step.value === 'string') {
376+
config.text = step.value;
377+
}
378+
if (!Object.prototype.hasOwnProperty.call(config, 'clearFirst')) {
379+
config.clearFirst = step.clear_first !== false;
380+
}
381+
if (!Object.prototype.hasOwnProperty.call(config, 'typeSlowly')) {
382+
config.typeSlowly = step.type_slowly === true;
383+
}
384+
}
385+
386+
if (step.type === 'delay') {
387+
if (!config.duration && typeof step.duration_ms === 'number') {
388+
config.duration = step.duration_ms;
389+
}
390+
if (!config.waitType) {
391+
config.waitType = 'time';
392+
}
393+
}
394+
395+
if (step.type === 'wait_for' && !config.timeout && typeof step.timeout_ms === 'number') {
396+
config.timeout = step.timeout_ms;
397+
}
398+
399+
if (!config.outputKey && typeof step.output === 'string') {
400+
config.outputKey = step.output;
401+
}
402+
403+
if (!config.script && typeof step.script === 'string') {
404+
config.script = step.script;
405+
}
406+
407+
if (step.branches && typeof step.branches === 'object') {
408+
config.branches = step.branches;
409+
}
345410

346411
return {
347412
id: String(step.id),
@@ -548,31 +613,43 @@ export function mapPortableTaskToEditorState(document: PortableRpaTaskDocument):
548613
})?.config?.workflow_meta as StoredWorkflowMeta | undefined;
549614
const importedIdMap = new Map<string, string>();
550615
sortedSteps.forEach((step) => {
551-
importedIdMap.set(step.uuid, crypto.randomUUID());
616+
const nextId = crypto.randomUUID();
617+
importedIdMap.set(step.uuid, nextId);
618+
619+
const storedStep = step.config?.workflow_step;
620+
if (storedStep && typeof storedStep === 'object' && typeof storedStep.id === 'string') {
621+
importedIdMap.set(storedStep.id, nextId);
622+
}
552623
});
553624
const storedStartStepId =
554625
typeof storedWorkflowMeta?.start_step_id === 'string'
555626
? importedIdMap.get(storedWorkflowMeta.start_step_id) ?? null
556627
: null;
557628

558629
const steps = sortedSteps.map((step, index) => {
559-
const legacyStep = buildLegacyFlowStep(
560-
{
561-
uuid: step.uuid,
562-
step_type: step.step_type,
563-
name: step.name,
564-
config: step.config,
565-
enabled: step.enabled ?? true,
566-
position_x: step.position_x,
567-
position_y: step.position_y,
568-
sort_order: step.sort_order,
569-
next_step_uuid: step.next_step_uuid,
570-
branch_config: step.branch_config,
571-
},
572-
step.next_step_uuid ?? sortedSteps[index + 1]?.uuid ?? null
573-
);
574-
575-
const currentBranches = (legacyStep.config.branches as Record<string, unknown> | undefined) ?? {};
630+
const storedStep =
631+
step.config?.workflow_step && typeof step.config.workflow_step === 'object'
632+
? (step.config.workflow_step as StoredWorkflowStep)
633+
: null;
634+
const baseStep = storedStep
635+
? buildFlowStepFromStoredStep(storedStep)
636+
: buildLegacyFlowStep(
637+
{
638+
uuid: step.uuid,
639+
step_type: step.step_type,
640+
name: step.name,
641+
config: step.config,
642+
enabled: step.enabled ?? true,
643+
position_x: step.position_x,
644+
position_y: step.position_y,
645+
sort_order: step.sort_order,
646+
next_step_uuid: step.next_step_uuid,
647+
branch_config: step.branch_config,
648+
},
649+
step.next_step_uuid ?? sortedSteps[index + 1]?.uuid ?? null
650+
);
651+
const sourceId = storedStep?.id ?? step.uuid;
652+
const currentBranches = (baseStep.config.branches as Record<string, unknown> | undefined) ?? {};
576653
const mappedBranches = Object.fromEntries(
577654
Object.entries(currentBranches).map(([key, value]) => [
578655
key,
@@ -581,17 +658,18 @@ export function mapPortableTaskToEditorState(document: PortableRpaTaskDocument):
581658
);
582659

583660
return {
584-
...legacyStep,
585-
id: importedIdMap.get(step.uuid) ?? legacyStep.id,
586-
nextStepId: step.next_step_uuid ? importedIdMap.get(step.next_step_uuid) ?? null : null,
587-
isStart: storedStartStepId ? importedIdMap.get(step.uuid) === storedStartStepId : index === 0,
661+
...baseStep,
662+
id: importedIdMap.get(sourceId) ?? baseStep.id,
663+
nextStepId: baseStep.nextStepId ? importedIdMap.get(baseStep.nextStepId) ?? null : null,
664+
parentLoopId: baseStep.parentLoopId ? importedIdMap.get(baseStep.parentLoopId) ?? null : null,
665+
isStart: storedStartStepId ? importedIdMap.get(sourceId) === storedStartStepId : index === 0,
588666
config:
589667
Object.keys(mappedBranches).length > 0
590668
? {
591-
...legacyStep.config,
669+
...baseStep.config,
592670
branches: mappedBranches,
593671
}
594-
: legacyStep.config,
672+
: baseStep.config,
595673
};
596674
});
597675

plugins/pages/rpa-workflow/src/lib/rpa-transfer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function buildPortableRpaTask(detail: RpaTaskDetailDto): PortableRpaTaskD
7171
stop_on_error: detail.task.stop_on_error,
7272
notify_on_complete: detail.task.notify_on_complete,
7373
notify_on_error: detail.task.notify_on_error,
74-
environment_uuids: detail.environment_uuids,
74+
environment_uuids: [],
7575
steps: detail.steps.map((step) => ({
7676
uuid: step.uuid,
7777
step_type: step.step_type,

src-tauri/src/commands/rpa.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ pub async fn execute_local_rpa_script(
3434
.await
3535
.map_err(|_| "LOCAL_SCRIPT_PREPARE_FAILED".to_string())?;
3636

37-
let execution = timeout(
38-
Duration::from_secs(60),
39-
Command::new("node").arg(&script_path).output(),
40-
)
41-
.await;
37+
let mut command = Command::new("node");
38+
command.arg(&script_path);
39+
apply_hidden_window_flags(&mut command);
40+
41+
let execution = timeout(Duration::from_secs(60), command.output()).await;
4242

4343
let cleanup_result = fs::remove_file(&script_path).await;
4444
if cleanup_result.is_err() {
@@ -90,7 +90,11 @@ pub async fn execute_local_rpa_script(
9090
}
9191

9292
async fn ensure_node_runtime() -> std::result::Result<(), String> {
93-
match Command::new("node").arg("--version").output().await {
93+
let mut command = Command::new("node");
94+
command.arg("--version");
95+
apply_hidden_window_flags(&mut command);
96+
97+
match command.output().await {
9498
Ok(output) if output.status.success() => Ok(()),
9599
Ok(_) => Err("NODE_RUNTIME_NOT_FOUND".to_string()),
96100
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
@@ -104,6 +108,16 @@ fn build_temp_script_path() -> PathBuf {
104108
env::temp_dir().join(format!("simprint-rpa-script-{}.cjs", Uuid::new_v4()))
105109
}
106110

111+
fn apply_hidden_window_flags(command: &mut Command) {
112+
#[cfg(target_os = "windows")]
113+
{
114+
use std::os::windows::process::CommandExt;
115+
116+
const CREATE_NO_WINDOW: u32 = 0x08000000;
117+
command.creation_flags(CREATE_NO_WINDOW);
118+
}
119+
}
120+
107121
fn build_runner_script(script: &str, variables: &Value) -> std::result::Result<String, String> {
108122
let script_body =
109123
serde_json::to_string(script).map_err(|_| "LOCAL_SCRIPT_PREPARE_FAILED".to_string())?;

0 commit comments

Comments
 (0)