chore: initial commit

This commit is contained in:
2026-10-22 17:07:08 +08:00
commit 0acef64f48
200 changed files with 35546 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
import type {WorkflowCatalog} from '../../types/bot/workflow/metadata.ts';
import {node_registry, query_registry, start_registry} from './registry.ts';
import {event_registry} from './node_action/event_node/registry.ts';
import {describe, edge_input_output, result_output} from './metadata.ts';
import {node_fields, policy_fields} from './common_template.ts';
import {assert_json} from './runtime/value.ts';
import {validate_fields, validate_i18n, validate_output} from './runtime/template.ts';
export function validate_workflow_catalog(catalog: WorkflowCatalog): void {
assert_json(catalog);
if (catalog.catalog_version !== 1) throw new Error('Invalid workflow catalog version');
for (const group of [catalog.nodes, catalog.start_nodes, catalog.events, catalog.queries]) {
const names = new Set<string>();
for (const item of group) {
if (!/^[a-zA-Z0-9_-]{1,100}$/.test(item.name) || names.has(item.name) || !/^\d+\.\d+\.\d+$/.test(item.version)) {
throw new Error('Invalid or duplicate workflow catalog entry');
}
names.add(item.name); validate_i18n(item.i18n); validate_fields(item.config); validate_output(item.output);
}
}
for (const node of catalog.nodes) {
const ports = new Set<string>();
for (const port of node.ports) {
if (!/^[a-zA-Z0-9_-]+$/.test(port.name) || ports.has(port.name)) throw new Error('Invalid workflow port');
ports.add(port.name); validate_i18n(port.i18n);
}
if (!ports.has('failure')) throw new Error('Workflow node needs failure port');
}
validate_fields(catalog.common.node_fields); validate_fields(catalog.common.policy_fields);
validate_output(catalog.common.result); validate_output(catalog.common.edge_input);
for (const reference of catalog.common.references) validate_i18n(reference.i18n);
}
/** Pure catalog inspection: no Bot, actor, event subscriptions or user code execution. */
export function get_workflow_catalog(options: {allow_code?: boolean} = {}): WorkflowCatalog {
const catalog: WorkflowCatalog = structuredClone({
catalog_version: 1,
nodes: Object.values(node_registry).map(module => module.config_template),
start_nodes: Object.values(start_registry).map(module => module.config_template),
events: Object.values(event_registry).map(module => module.config_template),
queries: Object.values(query_registry).map(module => module.config_template),
common: {
node_fields, policy_fields, result: result_output, edge_input: edge_input_output,
references: [
{root: 'trigger', i18n: describe('触发参数', '本次运行的事件、手动输入或定时触发数据。')},
{root: 'vars', i18n: describe('运行变量', '当前分支变量,合并后包含无冲突的分支更新。')},
{root: 'outputs', i18n: describe('上游节点结果', '沿已执行连线传入的节点结果;条件分支和 any 合并不保证所有祖先都存在。')},
{root: 'last_result', i18n: describe('最近结果', '当前路径最近一个节点的执行结果和节点 ID。')},
{root: 'inputs', i18n: describe('当前入线', '以连线 ID 为键的直接上游结果;all 合并包含全部匹配入线。')},
],
},
});
const code = catalog.nodes.find(node => node.name === 'code');
if (code) code.availability = options.allow_code
? {available: true} : {available: false, reason: 'CODE_EXECUTION_DISABLED'};
validate_workflow_catalog(catalog);
return catalog;
}