From 8f18a841b3c67301c1835b74e3aee6cdad0bfcfa Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Sun, 3 Aug 2025 17:15:46 -0700 Subject: [PATCH] Add file column to worker table and enable file path launching --- src/dev-center/index.html | 1 + src/dev-center/js/{libs => }/html-entities.js | 0 .../js/{libs => }/jquery-3.6.0.min.js | 0 .../js/{libs => }/jquery.dragster.js | 0 src/dev-center/js/{libs => }/slugify.js | 0 src/dev-center/js/workers.js | 16 +++++ src/gui/src/services/ExecService.js | 65 +++++++++++++++++-- src/puter-js/src/modules/UI.js | 15 ++++- 8 files changed, 92 insertions(+), 5 deletions(-) rename src/dev-center/js/{libs => }/html-entities.js (100%) rename src/dev-center/js/{libs => }/jquery-3.6.0.min.js (100%) rename src/dev-center/js/{libs => }/jquery.dragster.js (100%) rename src/dev-center/js/{libs => }/slugify.js (100%) diff --git a/src/dev-center/index.html b/src/dev-center/index.html index 984744512..e38a34d18 100644 --- a/src/dev-center/index.html +++ b/src/dev-center/index.html @@ -285,6 +285,7 @@ Worker + File Created diff --git a/src/dev-center/js/libs/html-entities.js b/src/dev-center/js/html-entities.js similarity index 100% rename from src/dev-center/js/libs/html-entities.js rename to src/dev-center/js/html-entities.js diff --git a/src/dev-center/js/libs/jquery-3.6.0.min.js b/src/dev-center/js/jquery-3.6.0.min.js similarity index 100% rename from src/dev-center/js/libs/jquery-3.6.0.min.js rename to src/dev-center/js/jquery-3.6.0.min.js diff --git a/src/dev-center/js/libs/jquery.dragster.js b/src/dev-center/js/jquery.dragster.js similarity index 100% rename from src/dev-center/js/libs/jquery.dragster.js rename to src/dev-center/js/jquery.dragster.js diff --git a/src/dev-center/js/libs/slugify.js b/src/dev-center/js/slugify.js similarity index 100% rename from src/dev-center/js/libs/slugify.js rename to src/dev-center/js/slugify.js diff --git a/src/dev-center/js/workers.js b/src/dev-center/js/workers.js index 016edf1b8..5b5592b81 100644 --- a/src/dev-center/js/workers.js +++ b/src/dev-center/js/workers.js @@ -220,6 +220,7 @@ function generate_worker_card(worker) { ${worker.name} + ${worker.file_path} ${worker.created_at} @@ -373,4 +374,19 @@ async function attempt_delete_worker(worker_name) { } } +$(document).on('click', '.file-path', function (e) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + + const file_path = $(this).attr('data-file-path'); + + if(file_path){ + puter.ui.launchApp({ + name: 'editor', + file_paths: [file_path], + }); + } +}) + export default init_workers; \ No newline at end of file diff --git a/src/gui/src/services/ExecService.js b/src/gui/src/services/ExecService.js index 16aee4791..a2c310284 100644 --- a/src/gui/src/services/ExecService.js +++ b/src/gui/src/services/ExecService.js @@ -48,7 +48,8 @@ export class ExecService extends Service { } // This method is exposed to apps via IPCService. - async launchApp ({ app_name, args, pseudonym }, { ipc_context, msg_id } = {}) { + async launchApp ({ app_name, args, pseudonym, file_paths }, { ipc_context, msg_id } = {}) { + console.log('launchApp', { app_name, args, pseudonym, file_paths }); const app = ipc_context?.caller?.app; const process = ipc_context?.caller?.process; @@ -68,8 +69,8 @@ export class ExecService extends Service { Object.assign(params, provider()); } - // The "body" of this method is in a separate file - const child_process = await launch_app({ + // Handle file paths if provided and caller is in godmode + let launch_options = { launched_by_exec_service: true, name: app_name, pseudonym, @@ -80,7 +81,63 @@ export class ExecService extends Service { ...(connection ? { parent_pseudo_id: connection.backward.uuid, } : {}), - }); + }; + + // Check if file_paths are provided and caller has godmode permissions + if (file_paths && Array.isArray(file_paths) && file_paths.length > 0 && process) { + console.log('file_paths', file_paths); + try { + // Get caller app info to check godmode status + const caller_app_name = process.name; + const caller_app_info = await window.get_apps(caller_app_name); + + // Check if caller is in godmode + if (caller_app_info && caller_app_info.godmode === 1) { + this.log.info(`⚠️ GODMODE app ${caller_app_name} launching ${app_name} with files:`, file_paths); + + // Get target app info to create file signatures + const target_app_info = await puter.apps.get(app_name); + + // For the first file, create a file signature and set it up like opening a file + if (file_paths.length > 0) { + const first_file_path = file_paths[0]; + + try { + // Get file stats to verify it exists + const file_stat = await puter.fs.stat(first_file_path); + + // Create file signature for the target app + const file_signature_result = await puter.fs.sign(target_app_info.uuid, { + path: first_file_path, + action: 'write' + }); + + // Set up launch options with file information + launch_options.file_signature = file_signature_result.items; + launch_options.file_path = first_file_path; + launch_options.token = file_signature_result.token; + + // Add all file paths to args for the target app + launch_options.args.file_paths = file_paths; + + } catch (file_error) { + this.log.warn(`Failed to process file ${first_file_path}:`, file_error); + // Continue with launch but without file signature + } + } + + } else { + console.log(`⚠️ App ${caller_app_name} attempted to launch ${app_name} with files but does not have godmode permissions`); + // Continue with normal launch, ignoring file_paths + } + } catch (error) { + console.log('Error checking godmode permissions:', error); + // Continue with normal launch + } + } + + // The "body" of this method is in a separate file + const child_process = await launch_app(launch_options); const send_child_launched_msg = (...a) => { if ( ! process ) return; diff --git a/src/puter-js/src/modules/UI.js b/src/puter-js/src/modules/UI.js index ebc0995fb..b32c7239e 100644 --- a/src/puter-js/src/modules/UI.js +++ b/src/puter-js/src/modules/UI.js @@ -1047,7 +1047,19 @@ class UI extends EventListener { // Returns a Promise launchApp = async function launchApp(app_name, args, callback) { let pseudonym = undefined; - if ( app_name.includes('#(as)') ) { + let file_paths = undefined; + + // Handle case where app_name is an options object + if (typeof app_name === 'object' && app_name !== null) { + const options = app_name; + app_name = options.name || options.app_name; + file_paths = options.file_paths; + args = args || options.args; + callback = callback || options.callback; + pseudonym = options.pseudonym; + } + + if ( app_name && app_name.includes('#(as)') ) { [app_name, pseudonym] = app_name.split('#(as)'); } const app_info = await this.#ipc_stub({ @@ -1055,6 +1067,7 @@ class UI extends EventListener { callback, parameters: { app_name, + file_paths, pseudonym, args, },