Skip to content

Commit

Permalink
PDE command provider
Browse files Browse the repository at this point in the history
  • Loading branch information
ProgrammerIn-wonderland authored and KernelDeimos committed Feb 11, 2025
1 parent 5169d4b commit fd38759
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/phoenix/src/puter-shell/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { CompositeCommandProvider } from './providers/CompositeCommandProvider.j
import { ScriptCommandProvider } from './providers/ScriptCommandProvider.js';
import { PuterAppCommandProvider } from './providers/PuterAppCommandProvider.js';
import { EmuCommandProvider } from './providers/EmuCommandProvider.js';
import { PDECommandProvider } from './providers/PDECommandProvider.js';

const argparser_registry = {
[SimpleArgParser.name]: SimpleArgParser
Expand Down Expand Up @@ -90,6 +91,7 @@ export const launchPuterShell = async (ctx) => {
...(ctx.platform.name === 'puter' ? [new PuterAppCommandProvider()] : []),
new ScriptCommandProvider(),
new EmuCommandProvider(),
new PDECommandProvider()
]);

ctx = ctx.sub({
Expand Down
187 changes: 187 additions & 0 deletions src/phoenix/src/puter-shell/providers/PDECommandProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright (C) 2024-present Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Exit } from '../coreutils/coreutil_lib/exit.js';
import { signals } from '../../ansi-shell/signals.js';

const BUILT_IN_APPS = [
'explorer',
];

const lookup_app = async (id) => {
// if (BUILT_IN_APPS.includes(id)) {
// return { success: true, path: null };
// }

const request = await fetch(`${puter.APIOrigin}/drivers/call`, {
"headers": {
"Content-Type": "application/json",
"Authorization": `Bearer ${puter.authToken}`,
},
"body": JSON.stringify({ interface: 'puter-apps', method: 'read', args: { id: { name: id } } }),
"method": "POST",
});

const { success, result } = await request.json();
return { success, path: result?.index_url };
};

export class PDECommandProvider {

async lookup (id) {
try {
await puter.fs.stat("/admin/Public/bin/" + id + ".pde")
} catch (e) {
return false;
}
const { success, path } = await lookup_app("pderunner");

return {
name: id,
path: path ?? 'Built-in Puter app',
// TODO: Let apps expose option/positional definitions like builtins do, and parse them here?
async execute(ctx) {
console.log(ctx)
const args = {
command_line: {
args: ["/admin/Public/bin/" + id + ".pde" , ...ctx.locals.args],
},
env: {...ctx.env},
};
console.log(args)
const child = await puter.ui.launchApp("pderunner", args);

const resize_listener = evt => {
child.postMessage({
$: 'ioctl.set',
windowSize: {
rows: evt.detail.rows,
cols: evt.detail.cols,
}
});
};
ctx.shell.addEventListener('signal.window-resize', resize_listener);

// Wait for app to close.
// console.log('waiting for app to close (phoenix)', window, {
// child_appid: child.targetAppInstanceID,
// phoen_appid: puter.appInstanceID,
// });
const app_close_promise = new Promise((resolve, reject) => {
child.on('close', (data) => {
if ((data.statusCode ?? 0) != 0) {
reject(new Exit(data.statusCode));
} else {
resolve({ done: true });
}
});
});

// Wait for SIGINT
const sigint_promise = new Promise((resolve, reject) => {
ctx.externs.sig.on((signal) => {
if (signal === signals.SIGINT) {
child.close();
reject(new Exit(130));
}
});
});

// We don't connect stdio to non-SDK apps, because they won't make use of it.
if (child.usesSDK) {
const decoder = new TextDecoder();
child.on('message', message => {
if (message.$ === 'stdout') {
ctx.externs.out.write(decoder.decode(message.data));
}
if (message.$ === 'chtermios') {
if ( message.termios.echo !== undefined ) {
if ( message.termios.echo ) {
ctx.externs.echo.on();
} else {
ctx.externs.echo.off();
}
}
}
});

// Repeatedly copy data from stdin to the child, while it's running.
// DRY: Initially copied from PathCommandProvider
let data, done;
const next_data = async () => {
({ value: data, done } = await Promise.race([
app_close_promise, sigint_promise, ctx.externs.in_.read(),
]));
if (data) {
child.postMessage({
$: 'stdin',
data: data,
});
if (!done) setTimeout(next_data, 0);
}
};
setTimeout(next_data, 0);
}

// TODO: propagate sigint to the app
const exit = await Promise.race([ app_close_promise, sigint_promise ]);
ctx.shell.removeEventListener('signal.window-resize', resize_listener);
return exit;
}
};
}

// Only a single Puter app can match a given name
async lookupAll (...a) {
const result = await this.lookup(...a);
if ( result ) {
return [ result ];
}
return undefined;
}

async complete (query, { ctx }) {
if (query === '') return [];

const results = [];

// for (const app_name of BUILT_IN_APPS) {
// if (app_name.startsWith(query)) {
// results.push(app_name);
// }
// }

// const request = await fetch(`${puter.APIOrigin}/drivers/call`, {
// "headers": {
// "Content-Type": "application/json",
// "Authorization": `Bearer ${puter.authToken}`,
// },
// "body": JSON.stringify({ interface: 'puter-apps', method: 'select', args: { predicate: [ 'name-like', query + '%' ] } }),
// "method": "POST",
// });

// const json = await request.json();
// if (json.success) {
// for (const app of json.result) {
// results.push(app.name);
// }
// }

return results;
}
}

0 comments on commit fd38759

Please sign in to comment.