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,
},