123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- /*
- * Copyright (C) 2024 Puter Technologies Inc.
- *
- * This file is part of Phoenix Shell.
- *
- * Phoenix Shell 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 PuterAppCommandProvider {
- async lookup (id) {
- const { success, path } = await lookup_app(id);
- if (!success) return;
- 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) {
- const args = {
- command_line: {
- args: ctx.locals.args,
- },
- env: {...ctx.env},
- };
- const child = await puter.ui.launchApp(id, args);
- // Wait for app to close.
- const app_close_promise = new Promise((resolve, reject) => {
- child.on('close', () => {
- // TODO: Exit codes for apps
- 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));
- }
- });
- // 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);
- }
- return Promise.race([ app_close_promise, sigint_promise ]);
- }
- };
- }
- // 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;
- }
- }
|