feat: add connectToInstance method to puter.ui

This commit is contained in:
KernelDeimos 2024-09-09 18:25:33 -04:00
parent e2cb6194bb
commit 62634b0afe
7 changed files with 132 additions and 0 deletions

View File

@ -23,6 +23,10 @@ class WispClient {
} }
} }
puter.ui.on('connection', event => {
console.log('emulator got connection event', event);
});
window.onload = async function() window.onload = async function()
{ {
const resp = await fetch( const resp = await fetch(

View File

@ -85,6 +85,10 @@ export class Process extends AdvancedBase{
this._signal(sig); this._signal(sig);
} }
handle_connection (other_process) {
throw new Error('Not implemented');
}
get type () { get type () {
const _to_type_name = (name) => { const _to_type_name = (name) => {
return name.replace(/Process$/, '').toLowerCase(); return name.replace(/Process$/, '').toLowerCase();
@ -139,6 +143,15 @@ export class PortalProcess extends Process {
const target = this.references.iframe.contentWindow; const target = this.references.iframe.contentWindow;
// NEXT: ... // NEXT: ...
} }
handle_connection (other_process, args) {
const target = this.references.iframe.contentWindow;
target.postMessage({
msg: 'connection',
appInstanceID: other_process.uuid,
args,
});
}
}; };
export class PseudoProcess extends Process { export class PseudoProcess extends Process {
_construct () { this.type_ = 'ui' } _construct () { this.type_ = 'ui' }

View File

@ -11,6 +11,9 @@ export class ExecService extends Service {
svc_ipc.register_ipc_handler('launchApp', { svc_ipc.register_ipc_handler('launchApp', {
handler: this.launchApp.bind(this), handler: this.launchApp.bind(this),
}); });
svc_ipc.register_ipc_handler('connectToInstance', {
handler: this.connectToInstance.bind(this),
});
} }
// This method is exposed to apps via IPCService. // This method is exposed to apps via IPCService.
@ -70,4 +73,34 @@ export class ExecService extends Service {
usesSDK: true, usesSDK: true,
}; };
} }
async connectToInstance ({ app_name, args }, { ipc_context, msg_id } = {}) {
const caller_process = ipc_context?.caller?.process;
if ( ! caller_process ) {
throw new Error('Caller process not found');
}
console.log(
caller_process.name,
app_name,
);
// TODO: permissions integration; for now it's hardcoded
if ( caller_process.name !== 'phoenix' ) {
throw new Error('Connection not allowed.');
}
if ( app_name !== 'test-emu' ) {
throw new Error('Connection not allowed.');
}
const svc_process = this.services.get('process');
const options = svc_process.select_by_name(app_name);
const process = options[0];
await process.handle_connection(caller_process, args);
return {
appInstanceID: process.uuid,
response,
};
}
} }

View File

@ -66,6 +66,16 @@ export class ProcessService extends Service {
return this.uuid_to_treelist.get(uuid); return this.uuid_to_treelist.get(uuid);
} }
select_by_name (name) {
const list = [];
for ( const process of this.processes ) {
if ( process.name === name ) {
list.push(process);
}
}
return list;
}
register (process) { register (process) {
this.register_(process); this.register_(process);
this.attach_to_parent_(process); this.attach_to_parent_(process);

View File

@ -35,6 +35,7 @@ import { MultiWriter } from '../ansi-shell/ioutil/MultiWriter.js';
import { CompositeCommandProvider } from './providers/CompositeCommandProvider.js'; import { CompositeCommandProvider } from './providers/CompositeCommandProvider.js';
import { ScriptCommandProvider } from './providers/ScriptCommandProvider.js'; import { ScriptCommandProvider } from './providers/ScriptCommandProvider.js';
import { PuterAppCommandProvider } from './providers/PuterAppCommandProvider.js'; import { PuterAppCommandProvider } from './providers/PuterAppCommandProvider.js';
import { EmuCommandProvider } from './providers/EmuCommandProvider.js';
const argparser_registry = { const argparser_registry = {
[SimpleArgParser.name]: SimpleArgParser [SimpleArgParser.name]: SimpleArgParser
@ -92,6 +93,7 @@ export const launchPuterShell = async (ctx) => {
// PuterAppCommandProvider is only usable on Puter // PuterAppCommandProvider is only usable on Puter
...(ctx.platform.name === 'puter' ? [new PuterAppCommandProvider()] : []), ...(ctx.platform.name === 'puter' ? [new PuterAppCommandProvider()] : []),
new ScriptCommandProvider(), new ScriptCommandProvider(),
new EmuCommandProvider(),
]); ]);
ctx = ctx.sub({ ctx = ctx.sub({

View File

@ -0,0 +1,46 @@
import { Exit } from "../coreutils/coreutil_lib/exit";
export class EmuCommandProvider {
static AVAILABLE = [
'bash',
'htop',
];
static EMU_APP_NAME = 'test-emu';
constructor () {
this.available = this.constructor.AVAILABLE;
this.emulator = null;
}
async aquire_emulator () {
if ( this.emulator ) return this.emulator;
// FUTURE: when we have a way to query instances
// without exposing the real instance id
/*
const instances = await puter.ui.queryInstances();
if ( instances.length < 0 ) {
return;
}
const instance = instances[0];
*/
const conn = await puter.ui.connectToInstance(this.constructor.EMU_APP_NAME);
return this.emulator = conn;
}
async lookup (id, { ctx }) {
if ( ! this.available.includes(id) ) {
return;
}
const emu = await this.aquire_emulator();
if ( ! emu ) {
ctx.externs.out.write('No emulator available.\n');
return new Exit(1);
}
ctx.externs.out.write(`Launching ${id} in emulator ${emu.appInstanceID}\n`);
}
}

View File

@ -203,6 +203,7 @@ class UI extends EventListener {
const eventNames = [ const eventNames = [
'localeChanged', 'localeChanged',
'themeChanged', 'themeChanged',
'connection',
]; ];
super(eventNames); super(eventNames);
this.#eventNames = eventNames; this.#eventNames = eventNames;
@ -460,6 +461,15 @@ class UI extends EventListener {
this.emit(name, data); this.emit(name, data);
this.#lastBroadcastValue.set(name, data); this.#lastBroadcastValue.set(name, data);
} }
else if ( e.data.msg === 'connection' ) {
const conn = AppConnection.from(e.data, {
appInstanceID: this.appInstanceID,
messageTarget: window.parent,
});
this.emit('connection', {
conn
});
}
}); });
// We need to send the mouse position to the host environment // We need to send the mouse position to the host environment
@ -951,6 +961,20 @@ class UI extends EventListener {
}); });
} }
connectToInstance = async function connectToInstance (app_name) {
const app_info = await this.#ipc_stub({
method: 'connectToInstance',
parameters: {
app_name,
}
});
return AppConnection.from(app_info, {
appInstanceID: this.appInstanceID,
messageTarget: this.messageTarget,
});
}
parentApp() { parentApp() {
return this.#parentAppConnection; return this.#parentAppConnection;
} }