Skip to content

Commit 62634b0

Browse files
committed
feat: add connectToInstance method to puter.ui
1 parent e2cb619 commit 62634b0

File tree

7 files changed

+132
-0
lines changed

7 files changed

+132
-0
lines changed

src/emulator/src/main.js

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class WispClient {
2323
}
2424
}
2525

26+
puter.ui.on('connection', event => {
27+
console.log('emulator got connection event', event);
28+
});
29+
2630
window.onload = async function()
2731
{
2832
const resp = await fetch(

src/gui/src/definitions.js

+13
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export class Process extends AdvancedBase{
8585
this._signal(sig);
8686
}
8787

88+
handle_connection (other_process) {
89+
throw new Error('Not implemented');
90+
}
91+
8892
get type () {
8993
const _to_type_name = (name) => {
9094
return name.replace(/Process$/, '').toLowerCase();
@@ -139,6 +143,15 @@ export class PortalProcess extends Process {
139143
const target = this.references.iframe.contentWindow;
140144
// NEXT: ...
141145
}
146+
147+
handle_connection (other_process, args) {
148+
const target = this.references.iframe.contentWindow;
149+
target.postMessage({
150+
msg: 'connection',
151+
appInstanceID: other_process.uuid,
152+
args,
153+
});
154+
}
142155
};
143156
export class PseudoProcess extends Process {
144157
_construct () { this.type_ = 'ui' }

src/gui/src/services/ExecService.js

+33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ export class ExecService extends Service {
1111
svc_ipc.register_ipc_handler('launchApp', {
1212
handler: this.launchApp.bind(this),
1313
});
14+
svc_ipc.register_ipc_handler('connectToInstance', {
15+
handler: this.connectToInstance.bind(this),
16+
});
1417
}
1518

1619
// This method is exposed to apps via IPCService.
@@ -70,4 +73,34 @@ export class ExecService extends Service {
7073
usesSDK: true,
7174
};
7275
}
76+
77+
async connectToInstance ({ app_name, args }, { ipc_context, msg_id } = {}) {
78+
const caller_process = ipc_context?.caller?.process;
79+
if ( ! caller_process ) {
80+
throw new Error('Caller process not found');
81+
}
82+
83+
console.log(
84+
caller_process.name,
85+
app_name,
86+
);
87+
// TODO: permissions integration; for now it's hardcoded
88+
if ( caller_process.name !== 'phoenix' ) {
89+
throw new Error('Connection not allowed.');
90+
}
91+
if ( app_name !== 'test-emu' ) {
92+
throw new Error('Connection not allowed.');
93+
}
94+
95+
const svc_process = this.services.get('process');
96+
const options = svc_process.select_by_name(app_name);
97+
const process = options[0];
98+
99+
await process.handle_connection(caller_process, args);
100+
101+
return {
102+
appInstanceID: process.uuid,
103+
response,
104+
};
105+
}
73106
}

src/gui/src/services/ProcessService.js

+10
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ export class ProcessService extends Service {
6666
return this.uuid_to_treelist.get(uuid);
6767
}
6868

69+
select_by_name (name) {
70+
const list = [];
71+
for ( const process of this.processes ) {
72+
if ( process.name === name ) {
73+
list.push(process);
74+
}
75+
}
76+
return list;
77+
}
78+
6979
register (process) {
7080
this.register_(process);
7181
this.attach_to_parent_(process);

src/phoenix/src/puter-shell/main.js

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { MultiWriter } from '../ansi-shell/ioutil/MultiWriter.js';
3535
import { CompositeCommandProvider } from './providers/CompositeCommandProvider.js';
3636
import { ScriptCommandProvider } from './providers/ScriptCommandProvider.js';
3737
import { PuterAppCommandProvider } from './providers/PuterAppCommandProvider.js';
38+
import { EmuCommandProvider } from './providers/EmuCommandProvider.js';
3839

3940
const argparser_registry = {
4041
[SimpleArgParser.name]: SimpleArgParser
@@ -92,6 +93,7 @@ export const launchPuterShell = async (ctx) => {
9293
// PuterAppCommandProvider is only usable on Puter
9394
...(ctx.platform.name === 'puter' ? [new PuterAppCommandProvider()] : []),
9495
new ScriptCommandProvider(),
96+
new EmuCommandProvider(),
9597
]);
9698

9799
ctx = ctx.sub({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Exit } from "../coreutils/coreutil_lib/exit";
2+
3+
export class EmuCommandProvider {
4+
static AVAILABLE = [
5+
'bash',
6+
'htop',
7+
];
8+
9+
static EMU_APP_NAME = 'test-emu';
10+
11+
constructor () {
12+
this.available = this.constructor.AVAILABLE;
13+
this.emulator = null;
14+
}
15+
16+
async aquire_emulator () {
17+
if ( this.emulator ) return this.emulator;
18+
19+
// FUTURE: when we have a way to query instances
20+
// without exposing the real instance id
21+
/*
22+
const instances = await puter.ui.queryInstances();
23+
if ( instances.length < 0 ) {
24+
return;
25+
}
26+
const instance = instances[0];
27+
*/
28+
29+
const conn = await puter.ui.connectToInstance(this.constructor.EMU_APP_NAME);
30+
return this.emulator = conn;
31+
}
32+
33+
async lookup (id, { ctx }) {
34+
if ( ! this.available.includes(id) ) {
35+
return;
36+
}
37+
38+
const emu = await this.aquire_emulator();
39+
if ( ! emu ) {
40+
ctx.externs.out.write('No emulator available.\n');
41+
return new Exit(1);
42+
}
43+
44+
ctx.externs.out.write(`Launching ${id} in emulator ${emu.appInstanceID}\n`);
45+
}
46+
}

src/puter-js/src/modules/UI.js

+24
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ class UI extends EventListener {
203203
const eventNames = [
204204
'localeChanged',
205205
'themeChanged',
206+
'connection',
206207
];
207208
super(eventNames);
208209
this.#eventNames = eventNames;
@@ -460,6 +461,15 @@ class UI extends EventListener {
460461
this.emit(name, data);
461462
this.#lastBroadcastValue.set(name, data);
462463
}
464+
else if ( e.data.msg === 'connection' ) {
465+
const conn = AppConnection.from(e.data, {
466+
appInstanceID: this.appInstanceID,
467+
messageTarget: window.parent,
468+
});
469+
this.emit('connection', {
470+
conn
471+
});
472+
}
463473
});
464474

465475
// We need to send the mouse position to the host environment
@@ -951,6 +961,20 @@ class UI extends EventListener {
951961
});
952962
}
953963

964+
connectToInstance = async function connectToInstance (app_name) {
965+
const app_info = await this.#ipc_stub({
966+
method: 'connectToInstance',
967+
parameters: {
968+
app_name,
969+
}
970+
});
971+
972+
return AppConnection.from(app_info, {
973+
appInstanceID: this.appInstanceID,
974+
messageTarget: this.messageTarget,
975+
});
976+
}
977+
954978
parentApp() {
955979
return this.#parentAppConnection;
956980
}

0 commit comments

Comments
 (0)