chore: initial commit
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import type {Bot} from '@wp2508/mineflayer';
|
||||
import type {data_type} from '../instance/bot_runtime.ts';
|
||||
import type {WorkflowManager} from '../workflow/runtime.ts';
|
||||
|
||||
export interface FunctionI18n {
|
||||
lang: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface FunctionArgument {
|
||||
name: string;
|
||||
type: data_type;
|
||||
i18n: FunctionI18n;
|
||||
/** A trailing boolean argument with a default may be omitted by callers. */
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export interface FunctionExecutionContext {
|
||||
workflow?: Pick<WorkflowManager, 'control'>;
|
||||
}
|
||||
|
||||
/** Function implementations declare their metadata and validate arguments before operating on a Bot. */
|
||||
export interface BaseFunction<Args extends unknown[] = unknown[]> {
|
||||
readonly name: string;
|
||||
readonly i18n: FunctionI18n;
|
||||
readonly args: readonly FunctionArgument[];
|
||||
/** Internal dispatch metadata; never included in function discovery responses. */
|
||||
readonly execution?: 'workflow_control';
|
||||
/** Returns true on success and false on execution failure. */
|
||||
run(bot: Bot, args: Args, executionContext?: FunctionExecutionContext): Promise<boolean>;
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
import { ProxyOptions } from "@wp2508/mineflayer";
|
||||
import { supportedVersions } from '@wp2508/minecraft-protocol'
|
||||
import {bot_config_plugin} from "../plugins/plugin.ts";
|
||||
import {workflow_config} from "../workflow/workflow.ts";
|
||||
|
||||
export type bot_status = 'offline' | 'ready' | 'connecting' | 'online'
|
||||
| 'disconnecting' | 'reconnecting';
|
||||
|
||||
export interface bot_config {
|
||||
instance_config: bot_instance_config;
|
||||
plugins: bot_config_plugin<any>[];
|
||||
workflow: workflow_config[];
|
||||
}
|
||||
|
||||
export interface bot_instance_config {
|
||||
username: string;
|
||||
host: string;
|
||||
port?: number | null;
|
||||
version: string;
|
||||
user_id: number;
|
||||
bot_id: number;
|
||||
config_revision?: number;
|
||||
plugins?: bot_config_plugin<any>[];
|
||||
workflow?: workflow_config[];
|
||||
proxy?: ProxyOptions | null;
|
||||
}
|
||||
|
||||
export type data_type = 'string' | 'boolean' | 'number' | 'datetime'|
|
||||
'string-select' | 'number-select' |
|
||||
'string-multi-select' | 'number-multi-select' |
|
||||
'string-array' | 'number-array'
|
||||
@@ -0,0 +1,68 @@
|
||||
import type {data_type} from "../instance/bot_runtime";
|
||||
import {isDeepStrictEqual} from 'node:util';
|
||||
import type {bot_instance_context} from '../../../bot/instance/bot_instance_init.ts';
|
||||
|
||||
export type PluginContext = Pick<bot_instance_context<unknown>,
|
||||
'on' | 'off' | 'log' | 'logger' | 'schedule_reconnect' | 'cancel_reconnect'>;
|
||||
|
||||
/** Constructors only define metadata; resources belong to init/dispose. */
|
||||
export abstract class BasePlugin<Config = Record<string, unknown>, States = unknown> {
|
||||
private states?: States;
|
||||
private readonly state_listeners = new Set<() => void>();
|
||||
abstract readonly name: string;
|
||||
abstract readonly config_template: plugin;
|
||||
abstract init(context: PluginContext, config: Config): Promise<void>;
|
||||
get_status(): States | undefined { return structuredClone(this.states); }
|
||||
protected set_states(states: States): void {
|
||||
if (isDeepStrictEqual(this.states, states)) return;
|
||||
this.states = structuredClone(states);
|
||||
for (const listener of Array.from(this.state_listeners)) {
|
||||
if (!this.state_listeners.has(listener)) continue;
|
||||
try { listener(); } catch {}
|
||||
}
|
||||
}
|
||||
on_states_update(callback: () => void): () => void {
|
||||
const listener = () => callback();
|
||||
this.state_listeners.add(listener);
|
||||
return () => { this.state_listeners.delete(listener); };
|
||||
}
|
||||
abstract dispose(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface bot_config_plugin<T> {
|
||||
name: string;
|
||||
path: string;
|
||||
config: T;
|
||||
}
|
||||
|
||||
export interface plugin {
|
||||
name: string;
|
||||
i18n: {
|
||||
lang: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}[],
|
||||
version: string;
|
||||
config: plugin_config[];
|
||||
}
|
||||
|
||||
export interface plugin_config {
|
||||
name: string;
|
||||
i18n: {
|
||||
lang: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}[]
|
||||
required: boolean;
|
||||
default: any;
|
||||
type: data_type;
|
||||
}
|
||||
|
||||
export interface base_define {
|
||||
path: string;
|
||||
i18n: {
|
||||
lang: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}[]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import type {Json, WorkflowNode, WorkflowStart} from './workflow.ts';
|
||||
|
||||
export interface WorkflowI18n {lang: string; name: string; description: string}
|
||||
export interface WorkflowField {
|
||||
name: string;
|
||||
i18n: WorkflowI18n[];
|
||||
required: boolean;
|
||||
nullable?: boolean;
|
||||
type: 'string' | 'number' | 'boolean' | 'select' | 'object' | 'array' | 'map'
|
||||
| 'value_source' | 'condition' | 'code';
|
||||
default?: Json;
|
||||
min?: number;
|
||||
max?: number;
|
||||
integer?: boolean;
|
||||
max_length?: number;
|
||||
min_length?: number;
|
||||
options?: {value: string | number; i18n: WorkflowI18n[]}[];
|
||||
fields?: WorkflowField[];
|
||||
item?: WorkflowField;
|
||||
provider?: 'function' | 'event' | 'query' | 'info';
|
||||
language?: 'javascript';
|
||||
}
|
||||
export interface WorkflowOutputTemplate {
|
||||
i18n: WorkflowI18n[];
|
||||
type: 'string' | 'number' | 'boolean' | 'null' | 'object' | 'array' | 'json';
|
||||
nullable?: boolean;
|
||||
optional?: boolean;
|
||||
properties?: Record<string, WorkflowOutputTemplate>;
|
||||
items?: WorkflowOutputTemplate;
|
||||
additional_properties?: WorkflowOutputTemplate;
|
||||
}
|
||||
export interface WorkflowPortTemplate {name: string; i18n: WorkflowI18n[]}
|
||||
interface TemplateBase {
|
||||
name: string;
|
||||
version: string;
|
||||
i18n: WorkflowI18n[];
|
||||
config: WorkflowField[];
|
||||
output: WorkflowOutputTemplate;
|
||||
}
|
||||
export interface WorkflowNodeTemplate extends TemplateBase {
|
||||
name: WorkflowNode['type'];
|
||||
ports: WorkflowPortTemplate[];
|
||||
provider?: 'function' | 'query';
|
||||
availability?: {available: boolean; reason?: string};
|
||||
}
|
||||
export interface WorkflowStartTemplate extends TemplateBase {
|
||||
name: WorkflowStart['type'];
|
||||
}
|
||||
export interface WorkflowEventTemplate extends TemplateBase {
|
||||
readiness: 'spawned';
|
||||
custom?: boolean;
|
||||
}
|
||||
export interface WorkflowQueryTemplate extends TemplateBase {provider?: 'info'}
|
||||
export interface WorkflowEventDefinition {
|
||||
config_template: WorkflowEventTemplate;
|
||||
normalize(args: unknown[]): Record<string, Json> | undefined;
|
||||
}
|
||||
export interface WorkflowCatalog {
|
||||
catalog_version: 1;
|
||||
nodes: WorkflowNodeTemplate[];
|
||||
start_nodes: WorkflowStartTemplate[];
|
||||
events: WorkflowEventTemplate[];
|
||||
queries: WorkflowQueryTemplate[];
|
||||
common: {
|
||||
node_fields: WorkflowField[];
|
||||
policy_fields: WorkflowField[];
|
||||
result: WorkflowOutputTemplate;
|
||||
edge_input: WorkflowOutputTemplate;
|
||||
references: {root: string; i18n: WorkflowI18n[]}[];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type {Condition, Json, WorkflowNode, workflow_config} from './workflow.ts';
|
||||
|
||||
export interface WorkflowError {code: string; message: string; node_id: string}
|
||||
export interface NodeResult {
|
||||
status: 'success' | 'failure';
|
||||
output: Json;
|
||||
error?: WorkflowError;
|
||||
}
|
||||
export interface OutputRecord extends NodeResult {execution: number}
|
||||
export interface BranchState {
|
||||
trigger: Record<string, Json>;
|
||||
vars: Record<string, Json>;
|
||||
outputs: Record<string, OutputRecord>;
|
||||
last_result: (NodeResult & {node_id: string}) | null;
|
||||
inputs: Record<string, Json>;
|
||||
retry_counts: Record<string, number>;
|
||||
}
|
||||
export interface NodeReturn {
|
||||
port: string;
|
||||
result: NodeResult;
|
||||
vars?: Record<string, Json>;
|
||||
retry_counts?: Record<string, number>;
|
||||
}
|
||||
export interface WorkflowDependencies {
|
||||
run_function(path: string, name: string, args: Json[], signal: AbortSignal): Promise<boolean>;
|
||||
query(source: string, args: Record<string, Json>, signal: AbortSignal): Promise<Json>;
|
||||
sleep(mode: 'milliseconds' | 'ticks', value: number, signal: AbortSignal): Promise<void>;
|
||||
log(level: 'info' | 'warning' | 'error', message: string): void;
|
||||
execute_code?(source: string, state: BranchState, signal: AbortSignal): Promise<Json>;
|
||||
}
|
||||
export type NodeHandler = (node: WorkflowNode, state: BranchState, signal: AbortSignal,
|
||||
deps: WorkflowDependencies) => Promise<NodeReturn>;
|
||||
export type RunStatus = 'running' | 'stopping' | 'completed' | 'failed' | 'cancelled' | 'timed_out';
|
||||
export type StopReason = 'manual' | 'disconnect' | 'timeout' | 'disposed' | 'updated' | 'reloaded';
|
||||
export type WorkflowAction = 'run_once' | 'trigger_start' | 'trigger_stop' | 'stop' | 'stop_run' | 'reload';
|
||||
export interface WorkflowControl {
|
||||
command_id: string;
|
||||
action: WorkflowAction;
|
||||
workflow_id?: string;
|
||||
run_id?: string;
|
||||
expected_generation?: number;
|
||||
input?: Record<string, Json>;
|
||||
}
|
||||
export interface WorkflowCommand {
|
||||
command_id: string;
|
||||
action: WorkflowAction;
|
||||
workflow_id: string | null;
|
||||
state: 'accepted' | 'completed' | 'failed';
|
||||
result?: StartResult | {stopped: true} | {generation: number};
|
||||
error?: {code: string; message: string};
|
||||
}
|
||||
export interface WorkflowRuntimeState {
|
||||
workflow_id: string;
|
||||
generation: number;
|
||||
enabled: boolean;
|
||||
start_type: 'common' | 'event' | 'timer';
|
||||
trigger_state: 'stopped' | 'listening' | 'scheduled' | 'exhausted' | 'reloading' | 'error';
|
||||
accepted_count: number;
|
||||
max_runs: number | null;
|
||||
next_fire_at: number | null;
|
||||
active_run_ids: string[];
|
||||
queued_count: number;
|
||||
last_error: {code: string; message: string} | null;
|
||||
}
|
||||
export interface WorkflowRuntimeSnapshot {
|
||||
source_config_revision: number;
|
||||
workflows: WorkflowRuntimeState[];
|
||||
commands: WorkflowCommand[];
|
||||
}
|
||||
export interface RunSnapshot {
|
||||
run_id: string;
|
||||
workflow_id: string;
|
||||
status: RunStatus;
|
||||
steps: number;
|
||||
active_nodes: string[];
|
||||
started_at: number;
|
||||
ended_at: number | null;
|
||||
stop_reason: StopReason | null;
|
||||
error: WorkflowError | null;
|
||||
trigger?: Record<string, Json>;
|
||||
trigger_available?: boolean;
|
||||
branches: BranchState[];
|
||||
}
|
||||
export type WorkflowRunEvent = {type: 'run_updated'; snapshot: RunSnapshot}
|
||||
| {type: 'node_started'; run_id: string; node_id: string; execution: number}
|
||||
| {type: 'node_finished'; run_id: string; node_id: string; execution: number; result: NodeResult}
|
||||
| {type: 'runtime_updated'; snapshot: WorkflowRuntimeSnapshot};
|
||||
export interface CompiledWorkflow {
|
||||
definition: workflow_config;
|
||||
nodes: Map<string, WorkflowNode>;
|
||||
outgoing: Map<string, import('./workflow.ts').WorkflowEdge[]>;
|
||||
incoming: Map<string, import('./workflow.ts').WorkflowEdge[]>;
|
||||
join_owners: Map<string, string>;
|
||||
}
|
||||
export interface StartResult {status: 'started' | 'queued' | 'skipped'; run_id?: string; request_id?: string}
|
||||
export interface WorkflowManager {
|
||||
start(workflow_id: string, trigger?: Record<string, Json>): StartResult;
|
||||
stop(run_id: string, reason?: StopReason): Promise<void>;
|
||||
get_snapshot(run_id: string): RunSnapshot | undefined;
|
||||
get_snapshots(): RunSnapshot[];
|
||||
apply(configs: workflow_config[], source_revision?: number): Promise<void>;
|
||||
control(request: WorkflowControl): WorkflowCommand;
|
||||
get_command(command_id: string): WorkflowCommand | undefined;
|
||||
get_runtime_snapshot(): WorkflowRuntimeSnapshot;
|
||||
on_update(callback: (event: WorkflowRunEvent) => void): () => void;
|
||||
dispose(): Promise<void>;
|
||||
}
|
||||
export type {Condition};
|
||||
@@ -0,0 +1,56 @@
|
||||
export type Json = null | boolean | number | string | Json[] | {[key: string]: Json};
|
||||
|
||||
export type ValueSource = {kind: 'literal'; value: Json} | {
|
||||
kind: 'ref';
|
||||
root: 'trigger' | 'vars' | 'outputs' | 'last_result' | 'inputs';
|
||||
path: (string | number)[];
|
||||
};
|
||||
|
||||
export type Condition = {
|
||||
op: 'eq' | 'contains' | 'gt' | 'lt'; left: ValueSource; right: ValueSource;
|
||||
} | {op: 'and' | 'or'; items: Condition[]} | {op: 'not'; item: Condition};
|
||||
|
||||
export type WorkflowStart = {type: 'common'; auto_start: 'never' | 'spawn'}
|
||||
| {type: 'event'; event: string; condition?: Condition; auto_start?: boolean; mode?: 'continuous' | 'once'}
|
||||
| {type: 'timer'; interval_ms: number; auto_start?: boolean; mode?: 'once' | 'repeat'; max_runs?: number | null};
|
||||
|
||||
interface NodeBase {id: string; name?: string; timeout_ms?: number}
|
||||
|
||||
export type WorkflowNode = NodeBase & (
|
||||
{type: 'function'; config: {path: string; name: string; args: ValueSource[]}}
|
||||
| {type: 'query'; config: {source: string; args: Record<string, ValueSource>; save_as?: string}}
|
||||
| {type: 'condition'; config: {condition: Condition}}
|
||||
| {type: 'sleep'; config: {mode: 'milliseconds' | 'ticks'; value: number}}
|
||||
| {type: 'error'; config: {codes?: string[]}}
|
||||
| {type: 'goto'; config: {max_retries: number; delay_ms: number}}
|
||||
| {type: 'merge'; config: {mode: 'all' | 'any'}}
|
||||
| {type: 'log'; config: {level: 'info' | 'warning' | 'error'; message: ValueSource}}
|
||||
| {type: 'code'; config: {source: string}}
|
||||
);
|
||||
|
||||
export interface WorkflowEdge {
|
||||
id: string;
|
||||
source: string;
|
||||
port: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
export interface workflow_config {
|
||||
schema_version: 1;
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
start: WorkflowStart;
|
||||
entry: string;
|
||||
nodes: WorkflowNode[];
|
||||
edges: WorkflowEdge[];
|
||||
initial_vars?: Record<string, Json>;
|
||||
policy: {
|
||||
/** Concurrent instances of this definition; defaults to one. */
|
||||
max_concurrent_runs?: number;
|
||||
on_busy: 'skip' | 'queue';
|
||||
max_queue: number;
|
||||
max_steps: number;
|
||||
timeout_ms: number;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Node owns these wire DTOs independently of the relay and Main implementations.
|
||||
import type {Json} from './bot/workflow/workflow.ts';
|
||||
|
||||
export type ForwardErrorCode = 'INVALID_REQUEST' | 'UNAUTHORIZED' | 'FORBIDDEN'
|
||||
| 'NODE_OFFLINE' | 'RELAY_UNAVAILABLE' | 'SESSION_STALE' | 'TOKEN_EXPIRED'
|
||||
| 'TIMEOUT' | 'CANCELLED' | 'NO_CONTEXT' | 'BOT_NOT_ONLINE' | 'RATE_LIMITED'
|
||||
| 'RESYNC_REQUIRED' | 'INTERNAL_ERROR';
|
||||
|
||||
export type ForwardAck<T = unknown> = {status: 'success'; data: T} | {
|
||||
status: 'error'; data: {code: ForwardErrorCode; message: string; retryable: boolean};
|
||||
};
|
||||
|
||||
export interface EnsureForward {
|
||||
forward_id: number;
|
||||
forward_session_id: string;
|
||||
node_session_id: string;
|
||||
node_id: number;
|
||||
bot_id: number;
|
||||
url: string;
|
||||
path: string;
|
||||
namespace: string;
|
||||
token: string;
|
||||
expires_at: number;
|
||||
deadline_at: number;
|
||||
}
|
||||
|
||||
export type ForwardScope = Pick<EnsureForward,
|
||||
'forward_id' | 'forward_session_id' | 'node_session_id' | 'node_id' | 'bot_id'>;
|
||||
|
||||
export type ForwardReady = ForwardScope & {node_ready: true; lease_expires_at: number};
|
||||
|
||||
export interface JoinReady {
|
||||
forward_id: number;
|
||||
forward_session_id: string;
|
||||
node_session_id: string;
|
||||
node_id: number;
|
||||
bot_id: number;
|
||||
role: 'node';
|
||||
node_ready: boolean;
|
||||
expires_at: number;
|
||||
}
|
||||
|
||||
export type RelayMethod = 'bot.snapshot' | 'bot.logs' | 'bot.chat' | 'get_log_history' | 'tab_complete'
|
||||
| 'get_context_info' | 'execute_function' | 'get_data_file_list' | 'get_file_data' | keyof WorkflowPayloads;
|
||||
|
||||
export interface WorkflowPayloads {
|
||||
'workflow.start': {workflow_id: string; trigger?: Record<string, Json>};
|
||||
'workflow.stop': {run_id: string};
|
||||
'workflow.snapshot': {run_id?: string; include_branches?: boolean};
|
||||
'workflow.catalog': {};
|
||||
'workflow.runtime': {};
|
||||
'workflow.control': import('./bot/workflow/runtime.ts').WorkflowControl & {context_generation: string};
|
||||
'workflow.command': {command_id: string; context_generation: string};
|
||||
}
|
||||
|
||||
export interface RelayRequest {
|
||||
request_id: string;
|
||||
correlation_id: string;
|
||||
forward_session_id: string;
|
||||
deadline_at: number;
|
||||
method: RelayMethod;
|
||||
payload: Record<string, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export type font_style = 'normal' | 'italic' | 'bold' | 'light' |'underline' | 'url';
|
||||
|
||||
export interface log {
|
||||
message: any;
|
||||
font_color: string;
|
||||
font_style: font_style[];
|
||||
hoverEvent?: {action: 'show_text' | 'show_item' | 'show_entity'; value: string; segments?: Array<{message: string; font_color: string; font_style: font_style[]}>};
|
||||
clickEvent?: {action: string; value: string};
|
||||
insertion?: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SocketResponse {
|
||||
status: "success" | "error";
|
||||
data: any;
|
||||
}
|
||||
Reference in New Issue
Block a user