mirror of
https://github.com/HeyPuter/puter
synced 2024-11-15 06:15:47 +00:00
Merge pull request #421 from AtkinsSJ/exit-status
Add exit status codes to `puter.exit()`, and an `exit` builtin to Phoenix
This commit is contained in:
commit
d57980c6cb
@ -160,6 +160,7 @@ export class ANSIShell extends EventTarget {
|
|||||||
}
|
}
|
||||||
this.ctx.externs.out.write('error: ' + e.message + '\n');
|
this.ctx.externs.out.write('error: ' + e.message + '\n');
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
this.ctx.locals.exit = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,6 +226,13 @@ export class ANSIShell extends EventTarget {
|
|||||||
const pipeline = await Pipeline.createFromAST(executionCtx, ast);
|
const pipeline = await Pipeline.createFromAST(executionCtx, ast);
|
||||||
|
|
||||||
await pipeline.execute(executionCtx);
|
await pipeline.execute(executionCtx);
|
||||||
|
|
||||||
|
// Store exit code for the next pipeline
|
||||||
|
// TODO: This feels like a hacky way of doing this.
|
||||||
|
this.ctx.locals.exit = executionCtx.locals.exit;
|
||||||
|
if ( this.ctx.locals.exit ) {
|
||||||
|
this.ctx.externs.out.write(`Exited with code ${this.ctx.locals.exit}\n`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expandPromptString (str) {
|
expandPromptString (str) {
|
||||||
|
@ -247,14 +247,14 @@ export class PreparedCommand {
|
|||||||
if ( ! ctx.cmdExecState.valid ) {
|
if ( ! ctx.cmdExecState.valid ) {
|
||||||
ctx.locals.exit = -1;
|
ctx.locals.exit = -1;
|
||||||
await ctx.externs.out.close();
|
await ctx.externs.out.close();
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ctx.cmdExecState.printHelpAndExit ) {
|
if ( ctx.cmdExecState.printHelpAndExit ) {
|
||||||
ctx.locals.exit = 0;
|
ctx.locals.exit = 0;
|
||||||
await printUsage(command, ctx.externs.out, ctx.vars);
|
await printUsage(command, ctx.externs.out, ctx.vars);
|
||||||
await ctx.externs.out.close();
|
await ctx.externs.out.close();
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let execute = command.execute.bind(command);
|
let execute = command.execute.bind(command);
|
||||||
@ -291,13 +291,14 @@ export class PreparedCommand {
|
|||||||
command.name + ': ' +
|
command.name + ': ' +
|
||||||
e.message + '\x1B[0m\n'
|
e.message + '\x1B[0m\n'
|
||||||
);
|
);
|
||||||
|
exit_code = -1;
|
||||||
} else {
|
} else {
|
||||||
await ctx.externs.err.write(
|
await ctx.externs.err.write(
|
||||||
'\x1B[31;1m' +
|
'\x1B[31;1m' +
|
||||||
command.name + ': ' +
|
command.name + ': ' +
|
||||||
e.toString() + '\x1B[0m\n'
|
e.toString() + '\x1B[0m\n'
|
||||||
);
|
);
|
||||||
ctx.locals.exit = -1;
|
exit_code = -1;
|
||||||
}
|
}
|
||||||
if ( ! (e instanceof Exit) ) console.error(e);
|
if ( ! (e instanceof Exit) ) console.error(e);
|
||||||
}
|
}
|
||||||
@ -316,6 +317,8 @@ export class PreparedCommand {
|
|||||||
|
|
||||||
await filesystem.write(path, outputMemWriters[i].getAsBlob());
|
await filesystem.write(path, outputMemWriters[i].getAsBlob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +351,7 @@ export class Pipeline {
|
|||||||
const valve = new Coupler(nextIn, pipeline_input_pipe.in);
|
const valve = new Coupler(nextIn, pipeline_input_pipe.in);
|
||||||
nextIn = pipeline_input_pipe.out;
|
nextIn = pipeline_input_pipe.out;
|
||||||
|
|
||||||
// TOOD: this will eventually defer piping of certain
|
// TODO: this will eventually defer piping of certain
|
||||||
// sub-pipelines to the Puter Shell.
|
// sub-pipelines to the Puter Shell.
|
||||||
|
|
||||||
for ( let i=0 ; i < preparedCommands.length ; i++ ) {
|
for ( let i=0 ; i < preparedCommands.length ; i++ ) {
|
||||||
@ -381,9 +384,11 @@ export class Pipeline {
|
|||||||
const command = preparedCommands[i];
|
const command = preparedCommands[i];
|
||||||
commandPromises.push(command.execute());
|
commandPromises.push(command.execute());
|
||||||
}
|
}
|
||||||
await Promise.all(commandPromises);
|
const results = await Promise.all(commandPromises);
|
||||||
|
// TODO: Consider what to do about intermediate exit codes
|
||||||
|
ctx.locals.exit = results[results.length-1];
|
||||||
await coupler.isDone;
|
await coupler.isDone;
|
||||||
|
|
||||||
valve.close();
|
valve.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import { launchPuterShell } from './puter-shell/main.js';
|
|||||||
import { NodeStdioPTT } from './pty/NodeStdioPTT.js';
|
import { NodeStdioPTT } from './pty/NodeStdioPTT.js';
|
||||||
import { CreateFilesystemProvider } from './platform/node/filesystem.js';
|
import { CreateFilesystemProvider } from './platform/node/filesystem.js';
|
||||||
import { CreateEnvProvider } from './platform/node/env.js';
|
import { CreateEnvProvider } from './platform/node/env.js';
|
||||||
|
import { CreateSystemProvider } from './platform/node/system.js';
|
||||||
import { parseArgs } from '@pkgjs/parseargs';
|
import { parseArgs } from '@pkgjs/parseargs';
|
||||||
import capcon from 'capture-console';
|
import capcon from 'capture-console';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@ -64,6 +65,7 @@ const ctx = new Context({
|
|||||||
name: 'node',
|
name: 'node',
|
||||||
filesystem: CreateFilesystemProvider(),
|
filesystem: CreateFilesystemProvider(),
|
||||||
env: CreateEnvProvider(),
|
env: CreateEnvProvider(),
|
||||||
|
system: CreateSystemProvider(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import { CreateFilesystemProvider } from './platform/puter/filesystem.js';
|
|||||||
import { CreateDriversProvider } from './platform/puter/drivers.js';
|
import { CreateDriversProvider } from './platform/puter/drivers.js';
|
||||||
import { XDocumentPTT } from './pty/XDocumentPTT.js';
|
import { XDocumentPTT } from './pty/XDocumentPTT.js';
|
||||||
import { CreateEnvProvider } from './platform/puter/env.js';
|
import { CreateEnvProvider } from './platform/puter/env.js';
|
||||||
|
import { CreateSystemProvider } from './platform/puter/system.js';
|
||||||
|
|
||||||
window.main_shell = async () => {
|
window.main_shell = async () => {
|
||||||
const config = {};
|
const config = {};
|
||||||
@ -73,6 +74,7 @@ window.main_shell = async () => {
|
|||||||
filesystem: CreateFilesystemProvider({ puterSDK }),
|
filesystem: CreateFilesystemProvider({ puterSDK }),
|
||||||
drivers: CreateDriversProvider({ puterSDK }),
|
drivers: CreateDriversProvider({ puterSDK }),
|
||||||
env: CreateEnvProvider({ config }),
|
env: CreateEnvProvider({ config }),
|
||||||
|
system: CreateSystemProvider({ puterSDK })
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
9
packages/phoenix/src/platform/node/system.js
Normal file
9
packages/phoenix/src/platform/node/system.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import process from 'node:process';
|
||||||
|
|
||||||
|
export const CreateSystemProvider = () => {
|
||||||
|
return {
|
||||||
|
exit: (code) => {
|
||||||
|
process.exit(code);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
7
packages/phoenix/src/platform/puter/system.js
Normal file
7
packages/phoenix/src/platform/puter/system.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export const CreateSystemProvider = ({ puterSDK }) => {
|
||||||
|
return {
|
||||||
|
exit: (code) => {
|
||||||
|
puterSDK.exit(code);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ import module_dirname from './dirname.js'
|
|||||||
import module_echo from './echo.js'
|
import module_echo from './echo.js'
|
||||||
import module_env from './env.js'
|
import module_env from './env.js'
|
||||||
import module_errno from './errno.js'
|
import module_errno from './errno.js'
|
||||||
|
import module_exit from './exit.js'
|
||||||
import module_false from './false.js'
|
import module_false from './false.js'
|
||||||
import module_grep from './grep.js'
|
import module_grep from './grep.js'
|
||||||
import module_head from './head.js'
|
import module_head from './head.js'
|
||||||
@ -75,6 +76,7 @@ export default {
|
|||||||
"echo": module_echo,
|
"echo": module_echo,
|
||||||
"env": module_env,
|
"env": module_env,
|
||||||
"errno": module_errno,
|
"errno": module_errno,
|
||||||
|
"exit": module_exit,
|
||||||
"false": module_false,
|
"false": module_false,
|
||||||
"grep": module_grep,
|
"grep": module_grep,
|
||||||
"head": module_head,
|
"head": module_head,
|
||||||
|
48
packages/phoenix/src/puter-shell/coreutils/exit.js
Normal file
48
packages/phoenix/src/puter-shell/coreutils/exit.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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 './coreutil_lib/exit.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'exit',
|
||||||
|
usage: 'exit [CODE]',
|
||||||
|
description: 'Exit the shell and return the given CODE. If no argument is given, uses the most recent return code.',
|
||||||
|
args: {
|
||||||
|
$: 'simple-parser',
|
||||||
|
allowPositionals: true
|
||||||
|
},
|
||||||
|
execute: async ctx => {
|
||||||
|
const { positionals, exit } = ctx.locals;
|
||||||
|
|
||||||
|
let status_code = 0;
|
||||||
|
|
||||||
|
if (positionals.length === 0) {
|
||||||
|
status_code = exit;
|
||||||
|
} else if (positionals.length === 1) {
|
||||||
|
const maybe_number = Number(positionals[0]);
|
||||||
|
if (Number.isInteger(maybe_number)) {
|
||||||
|
status_code = maybe_number;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await ctx.externs.err.write('exit: Too many arguments');
|
||||||
|
throw new Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.platform.system.exit(status_code);
|
||||||
|
}
|
||||||
|
};
|
@ -62,9 +62,12 @@ export class PuterAppCommandProvider {
|
|||||||
|
|
||||||
// Wait for app to close.
|
// Wait for app to close.
|
||||||
const app_close_promise = new Promise((resolve, reject) => {
|
const app_close_promise = new Promise((resolve, reject) => {
|
||||||
child.on('close', () => {
|
child.on('close', (data) => {
|
||||||
// TODO: Exit codes for apps
|
if ((data.statusCode ?? 0) != 0) {
|
||||||
resolve({ done: true });
|
reject(new Exit(data.statusCode));
|
||||||
|
} else {
|
||||||
|
resolve({ done: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -262,10 +262,16 @@ window.puter = (function() {
|
|||||||
this.updateSubmodules();
|
this.updateSubmodules();
|
||||||
}
|
}
|
||||||
|
|
||||||
exit = function() {
|
exit = function(statusCode = 0) {
|
||||||
|
if (statusCode && (typeof statusCode !== 'number')) {
|
||||||
|
console.warn('puter.exit() requires status code to be a number. Treating it as 1');
|
||||||
|
statusCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
msg: "exit",
|
msg: "exit",
|
||||||
appInstanceID: this.appInstanceID,
|
appInstanceID: this.appInstanceID,
|
||||||
|
statusCode,
|
||||||
}, '*');
|
}, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ class AppConnection extends EventListener {
|
|||||||
this.#isOpen = false;
|
this.#isOpen = false;
|
||||||
this.emit('close', {
|
this.emit('close', {
|
||||||
appInstanceID: this.targetAppInstanceID,
|
appInstanceID: this.targetAppInstanceID,
|
||||||
|
statusCode: event.data.statusCode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
13
src/IPC.js
13
src/IPC.js
@ -1201,6 +1201,15 @@ window.addEventListener('message', async (event) => {
|
|||||||
// exit
|
// exit
|
||||||
//--------------------------------------------------------
|
//--------------------------------------------------------
|
||||||
else if(event.data.msg === 'exit'){
|
else if(event.data.msg === 'exit'){
|
||||||
$(window.window_for_app_instance(event.data.appInstanceID)).close({bypass_iframe_messaging: true});
|
// Ensure status code is a number. Convert any truthy non-numbers to 1.
|
||||||
|
let status_code = event.data.statusCode ?? 0;
|
||||||
|
if (status_code && (typeof status_code !== 'number')) {
|
||||||
|
status_code = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(window.window_for_app_instance(event.data.appInstanceID)).close({
|
||||||
|
bypass_iframe_messaging: true,
|
||||||
|
status_code,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2887,7 +2887,7 @@ $.fn.close = async function(options) {
|
|||||||
$(`.window[data-parent_uuid="${window_uuid}"]`).close();
|
$(`.window[data-parent_uuid="${window_uuid}"]`).close();
|
||||||
|
|
||||||
// notify other apps that we're closing
|
// notify other apps that we're closing
|
||||||
window.report_app_closed(window_uuid);
|
window.report_app_closed(window_uuid, options.status_code ?? 0);
|
||||||
|
|
||||||
// remove backdrop
|
// remove backdrop
|
||||||
$(this).closest('.window-backdrop').remove();
|
$(this).closest('.window-backdrop').remove();
|
||||||
|
@ -3511,7 +3511,7 @@ window.report_app_launched = (instance_id, { uses_sdk = true }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Run any callbacks to say that the app has closed
|
// Run any callbacks to say that the app has closed
|
||||||
window.report_app_closed = (instance_id) => {
|
window.report_app_closed = (instance_id, status_code) => {
|
||||||
const el_window = window.window_for_app_instance(instance_id);
|
const el_window = window.window_for_app_instance(instance_id);
|
||||||
|
|
||||||
// notify parent app, if we have one, that we're closing
|
// notify parent app, if we have one, that we're closing
|
||||||
@ -3521,6 +3521,7 @@ window.report_app_closed = (instance_id) => {
|
|||||||
parent.contentWindow.postMessage({
|
parent.contentWindow.postMessage({
|
||||||
msg: 'appClosed',
|
msg: 'appClosed',
|
||||||
appInstanceID: instance_id,
|
appInstanceID: instance_id,
|
||||||
|
statusCode: status_code ?? 0,
|
||||||
}, '*');
|
}, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3530,6 +3531,7 @@ window.report_app_closed = (instance_id) => {
|
|||||||
child.contentWindow.postMessage({
|
child.contentWindow.postMessage({
|
||||||
msg: 'appClosed',
|
msg: 'appClosed',
|
||||||
appInstanceID: instance_id,
|
appInstanceID: instance_id,
|
||||||
|
statusCode: status_code ?? 0,
|
||||||
}, '*');
|
}, '*');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user