277 lines
10 KiB
TypeScript
277 lines
10 KiB
TypeScript
import type {Server as HttpServer} from 'node:http';
|
|
|
|
// Token permissions are capabilities, not the complete RPC method list.
|
|
export const RELAY_METHODS = ['bot.snapshot', 'bot.logs', 'bot.subscribe', 'bot.unsubscribe', 'bot.chat', 'execute_function', 'bot.files', 'workflow.control'] as const;
|
|
export const WORKFLOW_ACTIONS = ['run_once', 'trigger_start', 'trigger_stop', 'stop', 'stop_run', 'reload'] as const;
|
|
export const WORKFLOW_METHODS = ['workflow.start', 'workflow.stop', 'workflow.snapshot', 'workflow.catalog',
|
|
'workflow.control', 'workflow.command', 'workflow.runtime'] as const;
|
|
export const BOT_EVENTS = ['status_update', 'log', 'message', 'bot_error'] as const;
|
|
export type RelayMethod = typeof RELAY_METHODS[number];
|
|
export type BotEventName = typeof BOT_EVENTS[number];
|
|
export type BotStatus = 'offline' | 'ready' | 'connecting' | 'online' | 'disconnecting' | 'reconnecting';
|
|
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> = {status: 'success'; data: T} | {
|
|
status: 'error'; data: {code: ForwardErrorCode; message: string; retryable: boolean};
|
|
};
|
|
export type AckCallback<T = unknown> = (result: ForwardAck<T>) => void;
|
|
|
|
export interface SessionScope {
|
|
forward_id: number;
|
|
forward_session_id: string;
|
|
node_session_id: string;
|
|
node_id: number;
|
|
bot_id: number;
|
|
}
|
|
export interface RelayClaims extends SessionScope {
|
|
v: 1;
|
|
role: 'node' | 'web';
|
|
user_id?: number;
|
|
grant_id?: string;
|
|
permissions: RelayMethod[];
|
|
issued_at: number;
|
|
join_before: number;
|
|
expires_at: number;
|
|
jti: string;
|
|
}
|
|
export interface JoinRoomReady extends SessionScope {
|
|
role: 'node' | 'web';
|
|
node_ready: boolean;
|
|
expires_at: number;
|
|
}
|
|
export interface NodeReadyRequest {forward_session_id: string}
|
|
export interface NodeReadyResult {node_ready: true}
|
|
export interface SessionClosed {
|
|
forward_session_id?: string;
|
|
reason: string;
|
|
code: ForwardErrorCode;
|
|
}
|
|
export interface RelayRequest {
|
|
request_id: string;
|
|
method: RelayMethod | keyof WorkflowPayloads | 'get_log_history' | 'tab_complete' | 'get_context_info' | 'get_data_file_list' | 'get_file_data';
|
|
payload: unknown;
|
|
}
|
|
export interface NodeRelayRequest extends RelayRequest {
|
|
correlation_id: string;
|
|
forward_session_id: string;
|
|
deadline_at: number;
|
|
}
|
|
export interface BotSnapshot {
|
|
bot_id: number;
|
|
bot_status: BotStatus;
|
|
context_exists: boolean;
|
|
context_generation: string | null;
|
|
stream_id: string;
|
|
seq: number;
|
|
captured_at: number;
|
|
}
|
|
export interface LogSegment {
|
|
message: string;
|
|
font_color: string;
|
|
font_style: Array<'normal' | 'italic' | 'bold' | 'light' | 'underline' | 'url'>;
|
|
hoverEvent?: {action: 'show_text' | 'show_item' | 'show_entity'; value: string; segments?: Array<Pick<LogSegment, 'message' | 'font_color' | 'font_style'>>};
|
|
clickEvent?: {action: string; value: string};
|
|
insertion?: string;
|
|
}
|
|
export interface BotLogRecord {
|
|
id: string;
|
|
timestamp: number;
|
|
context_generation: string | null;
|
|
seq: number;
|
|
segments: LogSegment[];
|
|
}
|
|
export interface BotLogsResult {
|
|
stream_id: string;
|
|
seq: number;
|
|
records: BotLogRecord[];
|
|
truncated: boolean;
|
|
}
|
|
export interface TabCompleteResult {
|
|
matches: string[];
|
|
start: number;
|
|
length: number;
|
|
}
|
|
export type DataFileNode = {type: 'directory'; name: string; children: DataFileNode[]}
|
|
| {type: 'file'; name: string; id: string; size: number; modified_at: number};
|
|
export interface DataFileListResult {
|
|
listing_id: string;
|
|
expires_at: number;
|
|
partial: boolean;
|
|
children: DataFileNode[];
|
|
}
|
|
export interface FileDataResult {
|
|
id: string;
|
|
name: string;
|
|
encoding: 'base64';
|
|
offset: number;
|
|
next_offset: number;
|
|
total_bytes: number;
|
|
eof: boolean;
|
|
data: string;
|
|
}
|
|
export type JsonValue = null | boolean | number | string | JsonValue[] | {[key: string]: JsonValue};
|
|
export type WorkflowAction = typeof WORKFLOW_ACTIONS[number];
|
|
export interface WorkflowPayloads {
|
|
'workflow.start': {workflow_id: string; trigger?: Record<string, JsonValue>};
|
|
'workflow.stop': {run_id: string};
|
|
'workflow.snapshot': {run_id?: string; include_branches?: boolean};
|
|
'workflow.catalog': {};
|
|
'workflow.control': {command_id: string; context_generation: string; action: WorkflowAction;
|
|
workflow_id?: string; run_id?: string; expected_generation?: number; input?: Record<string, JsonValue>};
|
|
'workflow.command': {command_id: string; context_generation: string};
|
|
'workflow.runtime': {};
|
|
}
|
|
export interface WorkflowStartResult {status: 'started' | 'queued' | 'skipped'; run_id?: string; request_id?: string}
|
|
export interface WorkflowRunSnapshot {
|
|
run_id: string;
|
|
workflow_id: string;
|
|
status: 'running' | 'stopping' | 'completed' | 'failed' | 'cancelled' | 'timed_out';
|
|
steps: number;
|
|
active_nodes: string[];
|
|
started_at: number;
|
|
ended_at: number | null;
|
|
stop_reason: 'manual' | 'disconnect' | 'timeout' | 'disposed' | 'updated' | 'reloaded' | null;
|
|
error: {code: string; message: string; node_id: string} | null;
|
|
// Summary responses omit trigger and branches; legacy responses may omit the marker.
|
|
trigger?: Record<string, JsonValue>;
|
|
trigger_available?: boolean;
|
|
branches?: Array<{
|
|
trigger: Record<string, JsonValue>;
|
|
vars: Record<string, JsonValue>;
|
|
outputs: Record<string, {status: 'success' | 'failure'; output: JsonValue; execution: number;
|
|
error?: NonNullable<WorkflowRunSnapshot['error']>}>;
|
|
last_result: {status: 'success' | 'failure'; output: JsonValue; node_id: string;
|
|
error?: NonNullable<WorkflowRunSnapshot['error']>} | null;
|
|
inputs: Record<string, JsonValue>;
|
|
retry_counts: Record<string, number>;
|
|
}>;
|
|
}
|
|
export interface WorkflowCommand {
|
|
command_id: string;
|
|
action: WorkflowAction;
|
|
workflow_id: string | null;
|
|
state: 'accepted' | 'completed' | 'failed';
|
|
result?: WorkflowStartResult | {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 ContextInfoI18n {
|
|
lang: string; name: string; description: string;
|
|
fields?: Record<string, ContextInfoI18n>;
|
|
values?: Record<string, ContextInfoI18n>;
|
|
}
|
|
export interface ContextInfo {
|
|
name: string;
|
|
i18n: ContextInfoI18n;
|
|
value: JsonValue;
|
|
}
|
|
export interface InfoUpdate {
|
|
forward_session_id: string;
|
|
bot_id: number;
|
|
context_generation: string;
|
|
stream_id: string;
|
|
seq: number;
|
|
emitted_at: number;
|
|
data: {name: string; value: JsonValue};
|
|
}
|
|
export interface BotEventPayloads {
|
|
status_update: {status: BotStatus; context_exists: boolean};
|
|
log: {records: BotLogRecord[]};
|
|
message: {text: string; position: string; json?: unknown};
|
|
bot_error: {code?: string; message: string};
|
|
}
|
|
export type BotEvent = {[K in BotEventName]: {
|
|
forward_session_id: string;
|
|
bot_id: number;
|
|
context_generation: string | null;
|
|
stream_id: string;
|
|
seq: number;
|
|
emitted_at: number;
|
|
name: K;
|
|
payload: BotEventPayloads[K];
|
|
}}[BotEventName];
|
|
export interface OpenSessionCommand extends SessionScope {lease_expires_at: number}
|
|
export interface AuthorizeGrantCommand {
|
|
forward_session_id: string;
|
|
grant_id: string;
|
|
user_id: number;
|
|
permissions: RelayMethod[];
|
|
expires_at: number;
|
|
}
|
|
export interface RevokeSessionCommand {forward_session_id: string; reason: string}
|
|
export interface RevokeGrantCommand extends RevokeSessionCommand {grant_id: string}
|
|
export interface RelayStatus {
|
|
forward_session_id: string;
|
|
node_ready: boolean;
|
|
reason: string;
|
|
}
|
|
export interface ForwardSessionStatus extends RelayStatus, SessionScope {
|
|
state: 'open' | 'ready' | 'unavailable' | 'closed';
|
|
reported_at: number;
|
|
}
|
|
export const CONTROL_EVENTS = ['forward_open_session', 'forward_authorize_grant',
|
|
'forward_revoke_session', 'forward_revoke_grant'] as const;
|
|
export type ControlEvent = typeof CONTROL_EVENTS[number];
|
|
export interface RelayLimits {
|
|
auth_timeout_ms: number;
|
|
close_timeout_ms: number;
|
|
rpc_timeout_ms: number;
|
|
unused_timeout_ms: number;
|
|
idle_timeout_ms: number;
|
|
max_token_age_ms: number;
|
|
join_window_ms: number;
|
|
clock_skew_ms: number;
|
|
max_lease_ms: number;
|
|
max_token_bytes: number;
|
|
max_payload_bytes: number;
|
|
max_chat_length: number;
|
|
max_connections: number;
|
|
max_sessions: number;
|
|
max_grants_per_session: number;
|
|
max_pending_per_web: number;
|
|
max_pending_total: number;
|
|
requests_per_second: number;
|
|
events_per_second: number;
|
|
max_dedupe_entries: number;
|
|
dedupe_ttl_ms: number;
|
|
max_buffered_packets: number;
|
|
}
|
|
export interface RelayLogger {log(input: unknown): void; error(message: unknown): unknown}
|
|
export interface ControlTransport {
|
|
on(event: string, listener: (...args: any[]) => void): unknown;
|
|
off(event: string, listener: (...args: any[]) => void): unknown;
|
|
emit(event: string, ...args: any[]): unknown;
|
|
}
|
|
export interface ForwardServerOptions {
|
|
forward_id: number;
|
|
key: string;
|
|
http_server?: HttpServer;
|
|
port?: number;
|
|
host?: string;
|
|
path?: string;
|
|
namespace?: string;
|
|
allowed_origins: string[];
|
|
allow_no_origin?: boolean;
|
|
control?: ControlTransport;
|
|
logger?: RelayLogger;
|
|
limits?: Partial<RelayLimits>;
|
|
}
|