diff --git a/packages/bash-types/src/bash.ts b/packages/bash-types/src/bash.ts index 860b99b..2232b72 100644 --- a/packages/bash-types/src/bash.ts +++ b/packages/bash-types/src/bash.ts @@ -1,3 +1,4 @@ +import { CommandHandler, CustomCommand } from "./command"; import type { BaseRuntime } from "./runtime"; export interface BashOptions { @@ -6,6 +7,11 @@ export interface BashOptions { */ runtime: BaseRuntime; + /** + * Array with custom commands added at runtime + */ + customCommands?: CustomCommand[]; + /** * The host workspace directory */ diff --git a/packages/bash-types/src/command.ts b/packages/bash-types/src/command.ts index 8860055..86c0db1 100644 --- a/packages/bash-types/src/command.ts +++ b/packages/bash-types/src/command.ts @@ -4,32 +4,26 @@ import { State } from "./state"; /** * The context of a command execution */ -export type CommandContext = { +export interface CommandContext { opts: CommandOptions; stdin: string; state: State; runtime: BaseRuntime; }; -/** - * The handler of a command execution - */ -export type CommandHandler = (ctx: CommandContext) => Promise; - /** * The result of a command execution */ -export type CommandResult = { +export interface CommandResult { stdout: string; stderr: string; exitCode: number; }; - /** * The options of a command execution */ -export type CommandOptions = { +export interface CommandOptions { raw: string[]; flags: Set; options: Map; @@ -40,10 +34,32 @@ export type CommandOptions = { /** * The manual of a command */ -export type CommandManual = { +export interface CommandManual { name: string; description: string; usage: string; options?: Record; }; +/** + * The custom command creation result + */ +export interface CustomCommand { + name: string; + handler: CommandHandler; + manual?: CommandManual; +} + +/** + * The handler of a command execution + */ +export type CommandHandler = (ctx: CommandContext) => Promise; + + +/** + * The builder to create a custom command + */ +export type CreateCustomCommand = (name: string, handler: CommandHandler, manual?: CommandManual) => CustomCommand; + + + diff --git a/packages/bash-types/src/index.ts b/packages/bash-types/src/index.ts index 0aa52f6..12c9885 100644 --- a/packages/bash-types/src/index.ts +++ b/packages/bash-types/src/index.ts @@ -1,4 +1,4 @@ export { State } from "./state"; export { BaseRuntime, RuntimeResult } from "./runtime"; export { BashOptions } from "./bash"; -export { CommandResult, CommandHandler, CommandContext, CommandOptions, CommandManual } from "./command"; +export { CommandResult, CommandHandler, CommandContext, CommandOptions, CommandManual, CustomCommand, CreateCustomCommand } from "./command"; diff --git a/packages/bash/src/core/bash.ts b/packages/bash/src/core/bash.ts index 051125c..aa4e0ae 100644 --- a/packages/bash/src/core/bash.ts +++ b/packages/bash/src/core/bash.ts @@ -1,4 +1,4 @@ -import type { BaseRuntime, BashOptions, CommandResult } from "@capsule-run/bash-types"; +import type { BaseRuntime, BashOptions, CommandHandler, CommandResult, CustomCommand } from "@capsule-run/bash-types"; import { StateManager } from "./stateManager"; import { Filesystem } from "./filesystem"; import { Parser } from "./parser"; @@ -9,16 +9,19 @@ export class Bash { private filesystem: Filesystem; private parser: Parser; private executor: Executor; + private customCommands: CustomCommand[]; public readonly stateManager: StateManager; - constructor({ runtime, hostWorkspace = ".capsule/session/workspace", initialCwd = "workspace" }: BashOptions) { + constructor({ runtime, customCommands = [], hostWorkspace = ".capsule/session/workspace", initialCwd = "workspace" }: BashOptions) { this.runtime = runtime; this.runtime.hostWorkspace = hostWorkspace; + this.customCommands = customCommands; + this.stateManager = new StateManager(runtime, initialCwd); this.filesystem = new Filesystem(hostWorkspace); this.parser = new Parser(); - this.executor = new Executor(runtime, this.stateManager.state); + this.executor = new Executor(runtime, this.customCommands, this.stateManager.state); this.filesystem.init(); } diff --git a/packages/bash/src/core/executor.ts b/packages/bash/src/core/executor.ts index 3baa7e1..b82cd8c 100644 --- a/packages/bash/src/core/executor.ts +++ b/packages/bash/src/core/executor.ts @@ -2,7 +2,7 @@ import path from 'path'; import { parsedCommandOptions } from '../helpers/commandOptions'; import { displayCommandManual } from '../helpers/commandManual'; -import type { BaseRuntime, CommandHandler, CommandManual, CommandResult, State } from '@capsule-run/bash-types'; +import type { BaseRuntime, CommandHandler, CommandManual, CommandResult, CustomCommand, State } from '@capsule-run/bash-types'; import type { ASTNode, CommandNode } from './parser'; @@ -10,6 +10,7 @@ export class Executor { constructor( private readonly runtime: BaseRuntime, + private readonly customCommands: CustomCommand[], private readonly state: State, ) {} @@ -138,6 +139,11 @@ export class Executor { const commandsDir = path.resolve(__dirname, '../commands'); const handlerPath = path.join(commandsDir, name, 'handler'); + const customCommand = this.customCommands.find(cmd => cmd.name === name); + if (customCommand) { + return { handler: customCommand.handler, manual: customCommand.manual }; + } + try { const mod = require(handlerPath); return { handler: mod.handler as CommandHandler, manual: mod.manual as CommandManual }; diff --git a/packages/bash/src/index.ts b/packages/bash/src/index.ts index 8767807..e470b67 100644 --- a/packages/bash/src/index.ts +++ b/packages/bash/src/index.ts @@ -1 +1,2 @@ export { Bash } from "./core/bash"; +export { createCommand } from "./utilities/createCommand"; diff --git a/packages/bash/src/utilities/createCommand.ts b/packages/bash/src/utilities/createCommand.ts new file mode 100644 index 0000000..fdd5c26 --- /dev/null +++ b/packages/bash/src/utilities/createCommand.ts @@ -0,0 +1,5 @@ +import { CreateCustomCommand } from "@capsule-run/bash-types"; + +export const createCommand: CreateCustomCommand = (name, handler, manual) => { + return { name, handler, manual }; +}