@rolealpha/n8n-nodes-rolealpha 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +339 -2
- package/dist/credentials/ApprovalSealApi.credentials.d.ts +7 -0
- package/dist/credentials/ApprovalSealApi.credentials.js +29 -0
- package/dist/credentials/ApprovalSealApi.credentials.js.map +1 -0
- package/dist/credentials/RoleAlphaAgentKeysApi.credentials.d.ts +11 -0
- package/dist/credentials/RoleAlphaAgentKeysApi.credentials.js +35 -0
- package/dist/credentials/RoleAlphaAgentKeysApi.credentials.js.map +1 -0
- package/dist/credentials/RoleAlphaWebhookApi.credentials.d.ts +8 -0
- package/dist/credentials/RoleAlphaWebhookApi.credentials.js +23 -0
- package/dist/credentials/RoleAlphaWebhookApi.credentials.js.map +1 -0
- package/dist/lib/agentRun.d.ts +73 -0
- package/dist/lib/agentRun.js +170 -0
- package/dist/lib/agentRun.js.map +1 -0
- package/dist/lib/agents.d.ts +19 -0
- package/dist/lib/agents.js +91 -0
- package/dist/lib/agents.js.map +1 -0
- package/dist/lib/graph.d.ts +155 -0
- package/dist/lib/graph.js +380 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/keys.d.ts +12 -0
- package/dist/lib/keys.js +53 -0
- package/dist/lib/keys.js.map +1 -0
- package/dist/lib/mcp.d.ts +52 -0
- package/dist/lib/mcp.js +201 -0
- package/dist/lib/mcp.js.map +1 -0
- package/dist/lib/prompt.d.ts +52 -0
- package/dist/lib/prompt.js +180 -0
- package/dist/lib/prompt.js.map +1 -0
- package/dist/lib/schedule.d.ts +37 -0
- package/dist/lib/schedule.js +106 -0
- package/dist/lib/schedule.js.map +1 -0
- package/dist/lib/seal.d.ts +22 -0
- package/dist/lib/seal.js +79 -0
- package/dist/lib/seal.js.map +1 -0
- package/dist/lib/settings.d.ts +55 -0
- package/dist/lib/settings.js +105 -0
- package/dist/lib/settings.js.map +1 -0
- package/dist/lib/share.d.ts +156 -0
- package/dist/lib/share.js +208 -0
- package/dist/lib/share.js.map +1 -0
- package/dist/lib/waits.d.ts +142 -0
- package/dist/lib/waits.js +339 -0
- package/dist/lib/waits.js.map +1 -0
- package/dist/lib/webhook.d.ts +12 -0
- package/dist/lib/webhook.js +50 -0
- package/dist/lib/webhook.js.map +1 -0
- package/dist/nodes/ApprovalSeal/ApprovalSeal.node.d.ts +9 -0
- package/dist/nodes/ApprovalSeal/ApprovalSeal.node.js +141 -0
- package/dist/nodes/ApprovalSeal/ApprovalSeal.node.js.map +1 -0
- package/dist/nodes/ApprovalSeal/approvalSeal.svg +1 -0
- package/dist/nodes/RoleAlpha/RoleAlpha.node.d.ts +5 -0
- package/dist/nodes/RoleAlpha/RoleAlpha.node.js +1099 -0
- package/dist/nodes/RoleAlpha/RoleAlpha.node.js.map +1 -0
- package/dist/nodes/RoleAlpha/roleAlpha.svg +1 -0
- package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.d.ts +9 -0
- package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.js +61 -0
- package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.js.map +1 -0
- package/dist/nodes/RoleAlphaTrigger/roleAlpha.svg +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting within a step. Parts of one step can depend on each other: an agent whose part
|
|
3
|
+
* needs the result of another role does what it can, records a wait and ends. When a run
|
|
4
|
+
* of another role of the same step ends with a result, the waiting agents are started again.
|
|
5
|
+
*
|
|
6
|
+
* The rows live in a store of the workflow (e.g. an n8n data table); these functions only
|
|
7
|
+
* decide, so the same rules hold wherever the rows are kept.
|
|
8
|
+
*
|
|
9
|
+
* state "waiting": agent_uuid waits on step_uuid for the roles in waits_for (empty = any other role)
|
|
10
|
+
* state "done": a run of agent_uuid with the roles in roles ended with a result at "at"
|
|
11
|
+
* state "running": lease of a run that is in progress; one live lease per agent, step and records
|
|
12
|
+
* state "queued": a start that arrived while the agent was busy; runs when the busy run ends
|
|
13
|
+
* state "escalated": a wait that reached the attempt limit and was reported to a person
|
|
14
|
+
*
|
|
15
|
+
* Safety rules:
|
|
16
|
+
* - Every wake-up names one row (claimId). The workflow deletes that row first and wakes only
|
|
17
|
+
* if the delete really removed it, so two runs that plan at once never wake the same agent twice.
|
|
18
|
+
* - Attempts are counted per wait; at the limit the wait is escalated, never woken again by itself.
|
|
19
|
+
* - Text that one agent left for another (reason, event) is cut to fixed sizes and passed as data.
|
|
20
|
+
*/
|
|
21
|
+
export interface WaitRow {
|
|
22
|
+
id?: number | string;
|
|
23
|
+
state: string;
|
|
24
|
+
step_uuid: string;
|
|
25
|
+
step_custom_id?: string | null;
|
|
26
|
+
agent_uuid: string;
|
|
27
|
+
agent_name?: string | null;
|
|
28
|
+
roles?: string | null;
|
|
29
|
+
waits_for?: string | null;
|
|
30
|
+
reason?: string | null;
|
|
31
|
+
record_keys?: string | null;
|
|
32
|
+
event?: string | null;
|
|
33
|
+
run_id?: string | null;
|
|
34
|
+
attempts?: number | string | null;
|
|
35
|
+
progress?: boolean | string | null;
|
|
36
|
+
at?: string | null;
|
|
37
|
+
createdAt?: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface FinishedRun {
|
|
40
|
+
runId: string;
|
|
41
|
+
stepUuid: string;
|
|
42
|
+
stepCustomId?: string | null;
|
|
43
|
+
agentUuid: string;
|
|
44
|
+
agentName?: string | null;
|
|
45
|
+
roleUuids: string[];
|
|
46
|
+
recordKeys?: unknown;
|
|
47
|
+
startedAt: string;
|
|
48
|
+
event?: unknown;
|
|
49
|
+
/** 0 for a first run, n for the n-th wake-up. */
|
|
50
|
+
attempt?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface WakeUp {
|
|
53
|
+
/** Row to delete before waking; wake only if that delete removed it. */
|
|
54
|
+
claimId: number | string | null;
|
|
55
|
+
agentUuid: string;
|
|
56
|
+
agentName: string;
|
|
57
|
+
stepUuid: string;
|
|
58
|
+
stepCustomId: string;
|
|
59
|
+
recordKeys: string;
|
|
60
|
+
attempt: number;
|
|
61
|
+
event: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
export interface AfterRunPlan {
|
|
64
|
+
/** The agent recorded a wait in this run. */
|
|
65
|
+
waited: boolean;
|
|
66
|
+
/** The run produced a result (no wait, or a wait after progress); only then are others woken. */
|
|
67
|
+
produced: boolean;
|
|
68
|
+
/** Row to add for this run, or null. */
|
|
69
|
+
doneRow: WaitRow | null;
|
|
70
|
+
wake: WakeUp[];
|
|
71
|
+
/** Row ids to delete: older duplicates of woken waits, replaced or expired done rows, this run's lease. */
|
|
72
|
+
remove: Array<number | string>;
|
|
73
|
+
/** Waits that reached the attempt limit; the scheduler escalates them. */
|
|
74
|
+
stuck: WaitRow[];
|
|
75
|
+
}
|
|
76
|
+
export interface Limits {
|
|
77
|
+
maxAttempts?: number;
|
|
78
|
+
keepDoneHours?: number;
|
|
79
|
+
maxWaitMinutes?: number;
|
|
80
|
+
runTimeoutMinutes?: number;
|
|
81
|
+
}
|
|
82
|
+
export interface WaitOptions extends Limits {
|
|
83
|
+
now?: Date;
|
|
84
|
+
/** Limits per agent and step (e.g. from Runner Settings); they win over the plain options. */
|
|
85
|
+
limitsFor?: (target: {
|
|
86
|
+
agentUuid: string;
|
|
87
|
+
stepUuid: string;
|
|
88
|
+
stepCustomId: string;
|
|
89
|
+
}) => Limits;
|
|
90
|
+
}
|
|
91
|
+
export declare const MAX_REASON_CHARS = 500;
|
|
92
|
+
export declare const MAX_EVENT_CHARS = 20000;
|
|
93
|
+
export declare function uuidSet(value: unknown): string[];
|
|
94
|
+
/** Record keys of an event as one stable text, e.g. "12,7" → "12,7" sorted. */
|
|
95
|
+
export declare function recordKey(keys: unknown): string;
|
|
96
|
+
/** Two runs concern the same records when both have none or they share one. */
|
|
97
|
+
export declare function sameRecords(a: unknown, b: unknown): boolean;
|
|
98
|
+
/** Cuts text to a fixed size, so one agent cannot flood the next one's prompt. */
|
|
99
|
+
export declare function clip(value: unknown, max: number): string;
|
|
100
|
+
export declare function parseEvent(value: unknown): Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* Decides what happens after a run of one agent on one step.
|
|
103
|
+
* rows: the stored rows of this step (other steps are ignored).
|
|
104
|
+
*/
|
|
105
|
+
export declare function planAfterRun(rows: WaitRow[], run: FinishedRun, opts?: WaitOptions): AfterRunPlan;
|
|
106
|
+
export interface StartCheck {
|
|
107
|
+
/** Whether this run may go on. */
|
|
108
|
+
start: boolean;
|
|
109
|
+
reason: string;
|
|
110
|
+
/** The live lease of another run that blocks this one. */
|
|
111
|
+
blockedBy: string | null;
|
|
112
|
+
/** Rows to delete: this run's own lease when it may not start, and dead leases. */
|
|
113
|
+
remove: Array<number | string>;
|
|
114
|
+
/** When blocked: a row to add so the start runs after the busy run (state "queued"). */
|
|
115
|
+
queueRow: WaitRow | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Duplicate-run guard. The workflow first adds a lease row for this run (state "running",
|
|
119
|
+
* run_id), then reads the leases of the step and asks here. Of the live leases of the same
|
|
120
|
+
* agent and records the oldest wins; a later one does not start and is queued instead, so
|
|
121
|
+
* nothing is lost and nothing runs twice. Leases older than the run timeout count as dead.
|
|
122
|
+
*/
|
|
123
|
+
export declare function checkStart(rows: WaitRow[], run: Omit<FinishedRun, 'roleUuids' | 'startedAt'> & {
|
|
124
|
+
roleUuids?: string[];
|
|
125
|
+
startedAt?: string;
|
|
126
|
+
}, opts?: WaitOptions): StartCheck;
|
|
127
|
+
export interface StaleResult {
|
|
128
|
+
wake: WakeUp[];
|
|
129
|
+
remove: Array<number | string>;
|
|
130
|
+
/** Waits at the attempt limit (already escalated ones excluded). */
|
|
131
|
+
stuck: WaitRow[];
|
|
132
|
+
/** Row ids to set to state "escalated" once the report went out. */
|
|
133
|
+
escalate: Array<number | string>;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* For a scheduler: waits nobody answered within maxWaitMinutes (e.g. because a person fills
|
|
137
|
+
* the awaited role), queued starts whose busy run died, dead leases and expired rows.
|
|
138
|
+
* Waits at the attempt limit are returned once for escalation, never woken again by themselves.
|
|
139
|
+
*/
|
|
140
|
+
export declare function findStaleWaits(rows: WaitRow[], opts?: WaitOptions & {
|
|
141
|
+
olderThanMinutes?: number;
|
|
142
|
+
}): StaleResult;
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Waiting within a step. Parts of one step can depend on each other: an agent whose part
|
|
4
|
+
* needs the result of another role does what it can, records a wait and ends. When a run
|
|
5
|
+
* of another role of the same step ends with a result, the waiting agents are started again.
|
|
6
|
+
*
|
|
7
|
+
* The rows live in a store of the workflow (e.g. an n8n data table); these functions only
|
|
8
|
+
* decide, so the same rules hold wherever the rows are kept.
|
|
9
|
+
*
|
|
10
|
+
* state "waiting": agent_uuid waits on step_uuid for the roles in waits_for (empty = any other role)
|
|
11
|
+
* state "done": a run of agent_uuid with the roles in roles ended with a result at "at"
|
|
12
|
+
* state "running": lease of a run that is in progress; one live lease per agent, step and records
|
|
13
|
+
* state "queued": a start that arrived while the agent was busy; runs when the busy run ends
|
|
14
|
+
* state "escalated": a wait that reached the attempt limit and was reported to a person
|
|
15
|
+
*
|
|
16
|
+
* Safety rules:
|
|
17
|
+
* - Every wake-up names one row (claimId). The workflow deletes that row first and wakes only
|
|
18
|
+
* if the delete really removed it, so two runs that plan at once never wake the same agent twice.
|
|
19
|
+
* - Attempts are counted per wait; at the limit the wait is escalated, never woken again by itself.
|
|
20
|
+
* - Text that one agent left for another (reason, event) is cut to fixed sizes and passed as data.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.MAX_EVENT_CHARS = exports.MAX_REASON_CHARS = void 0;
|
|
24
|
+
exports.uuidSet = uuidSet;
|
|
25
|
+
exports.recordKey = recordKey;
|
|
26
|
+
exports.sameRecords = sameRecords;
|
|
27
|
+
exports.clip = clip;
|
|
28
|
+
exports.parseEvent = parseEvent;
|
|
29
|
+
exports.planAfterRun = planAfterRun;
|
|
30
|
+
exports.checkStart = checkStart;
|
|
31
|
+
exports.findStaleWaits = findStaleWaits;
|
|
32
|
+
exports.MAX_REASON_CHARS = 500;
|
|
33
|
+
exports.MAX_EVENT_CHARS = 20_000;
|
|
34
|
+
const lower = (v) => String(v ?? '').trim().toLowerCase();
|
|
35
|
+
function uuidSet(value) {
|
|
36
|
+
const raw = Array.isArray(value) ? value : String(value ?? '').split(',');
|
|
37
|
+
return Array.from(new Set(raw.map(lower).filter(Boolean)));
|
|
38
|
+
}
|
|
39
|
+
/** Record keys of an event as one stable text, e.g. "12,7" → "12,7" sorted. */
|
|
40
|
+
function recordKey(keys) {
|
|
41
|
+
const raw = Array.isArray(keys) ? keys : keys === undefined || keys === null || keys === '' ? [] : String(keys).split(',');
|
|
42
|
+
return Array.from(new Set(raw.map((k) => String(k).trim()).filter(Boolean))).sort().join(',');
|
|
43
|
+
}
|
|
44
|
+
/** Two runs concern the same records when both have none or they share one. */
|
|
45
|
+
function sameRecords(a, b) {
|
|
46
|
+
const ka = recordKey(a);
|
|
47
|
+
const kb = recordKey(b);
|
|
48
|
+
if (ka === '' || kb === '')
|
|
49
|
+
return ka === kb;
|
|
50
|
+
const set = new Set(ka.split(','));
|
|
51
|
+
return kb.split(',').some((k) => set.has(k));
|
|
52
|
+
}
|
|
53
|
+
/** Cuts text to a fixed size, so one agent cannot flood the next one's prompt. */
|
|
54
|
+
function clip(value, max) {
|
|
55
|
+
const s = String(value ?? '');
|
|
56
|
+
return s.length > max ? `${s.slice(0, max)} …[cut]` : s;
|
|
57
|
+
}
|
|
58
|
+
function truthy(v) {
|
|
59
|
+
return v === true || lower(v) === 'true' || v === 1 || v === '1';
|
|
60
|
+
}
|
|
61
|
+
function attemptsOf(row) {
|
|
62
|
+
const n = Number(row.attempts);
|
|
63
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
64
|
+
}
|
|
65
|
+
function timeOf(row) {
|
|
66
|
+
const t = Date.parse(String(row.at ?? row.createdAt ?? ''));
|
|
67
|
+
return Number.isFinite(t) ? t : 0;
|
|
68
|
+
}
|
|
69
|
+
function idOrder(a, b) {
|
|
70
|
+
const na = Number(a.id);
|
|
71
|
+
const nb = Number(b.id);
|
|
72
|
+
if (Number.isFinite(na) && Number.isFinite(nb))
|
|
73
|
+
return na - nb;
|
|
74
|
+
return String(a.id ?? '').localeCompare(String(b.id ?? ''));
|
|
75
|
+
}
|
|
76
|
+
function parseEvent(value) {
|
|
77
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
78
|
+
const json = JSON.stringify(value);
|
|
79
|
+
return json.length > exports.MAX_EVENT_CHARS ? { text: clip(json, exports.MAX_EVENT_CHARS) } : { ...value };
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === 'string' && value.trim()) {
|
|
82
|
+
if (value.length <= exports.MAX_EVENT_CHARS) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(value);
|
|
85
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* keep as text */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { text: clip(value, exports.MAX_EVENT_CHARS) };
|
|
93
|
+
}
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
function waitsOn(row, roleUuids) {
|
|
97
|
+
const wanted = uuidSet(row.waits_for);
|
|
98
|
+
return wanted.length === 0 || wanted.some((r) => roleUuids.includes(r));
|
|
99
|
+
}
|
|
100
|
+
function wakeFor(row, by, claimId) {
|
|
101
|
+
const attempt = attemptsOf(row) + 1;
|
|
102
|
+
const event = parseEvent(row.event);
|
|
103
|
+
delete event.wake;
|
|
104
|
+
event.wake = { attempt, wokenBy: clip(by.agentName, 200), wokenByRoles: by.roles, waitedFor: clip(row.reason, exports.MAX_REASON_CHARS) };
|
|
105
|
+
return {
|
|
106
|
+
claimId,
|
|
107
|
+
agentUuid: lower(row.agent_uuid),
|
|
108
|
+
agentName: clip(row.agent_name, 200),
|
|
109
|
+
stepUuid: lower(row.step_uuid),
|
|
110
|
+
stepCustomId: String(row.step_custom_id ?? ''),
|
|
111
|
+
recordKeys: recordKey(row.record_keys),
|
|
112
|
+
attempt,
|
|
113
|
+
event,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** The newest row per agent and records; the others are removed with it. */
|
|
117
|
+
function groupWaits(rows) {
|
|
118
|
+
const groups = new Map();
|
|
119
|
+
for (const row of rows) {
|
|
120
|
+
const key = `${lower(row.agent_uuid)}|${recordKey(row.record_keys)}`;
|
|
121
|
+
const g = groups.get(key);
|
|
122
|
+
if (!g)
|
|
123
|
+
groups.set(key, { row, ids: row.id !== undefined ? [row.id] : [] });
|
|
124
|
+
else {
|
|
125
|
+
if (row.id !== undefined)
|
|
126
|
+
g.ids.push(row.id);
|
|
127
|
+
if (timeOf(row) > timeOf(g.row) || (timeOf(row) === timeOf(g.row) && attemptsOf(row) > attemptsOf(g.row)))
|
|
128
|
+
g.row = row;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return Array.from(groups.values());
|
|
132
|
+
}
|
|
133
|
+
function limitsOf(opts, row) {
|
|
134
|
+
const own = opts.limitsFor?.({ agentUuid: lower(row.agent_uuid), stepUuid: lower(row.step_uuid), stepCustomId: String(row.step_custom_id ?? '') }) ?? {};
|
|
135
|
+
return {
|
|
136
|
+
maxAttempts: own.maxAttempts ?? opts.maxAttempts ?? 5,
|
|
137
|
+
keepDoneHours: own.keepDoneHours ?? opts.keepDoneHours ?? 168,
|
|
138
|
+
maxWaitMinutes: own.maxWaitMinutes ?? opts.maxWaitMinutes ?? 60,
|
|
139
|
+
runTimeoutMinutes: own.runTimeoutMinutes ?? opts.runTimeoutMinutes ?? 15,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Adds a group as a wake-up with a claim, or as stuck at the attempt limit. */
|
|
143
|
+
function wakeOrStick(g, by, maxAttempts, out) {
|
|
144
|
+
if (attemptsOf(g.row) >= maxAttempts) {
|
|
145
|
+
out.stuck.push(g.row);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const claimId = g.row.id ?? null;
|
|
149
|
+
out.remove.push(...g.ids.filter((id) => id !== claimId));
|
|
150
|
+
out.wake.push(wakeFor(g.row, by, claimId));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Decides what happens after a run of one agent on one step.
|
|
154
|
+
* rows: the stored rows of this step (other steps are ignored).
|
|
155
|
+
*/
|
|
156
|
+
function planAfterRun(rows, run, opts = {}) {
|
|
157
|
+
const now = opts.now ?? new Date();
|
|
158
|
+
const stepUuid = lower(run.stepUuid);
|
|
159
|
+
const agentUuid = lower(run.agentUuid);
|
|
160
|
+
const roles = uuidSet(run.roleUuids);
|
|
161
|
+
const keys = recordKey(run.recordKeys ?? parseEvent(run.event).keys);
|
|
162
|
+
const started = Date.parse(run.startedAt);
|
|
163
|
+
const onStep = rows.filter((r) => lower(r.step_uuid) === stepUuid);
|
|
164
|
+
const state = (s) => onStep.filter((r) => lower(r.state) === s);
|
|
165
|
+
const waiting = state('waiting');
|
|
166
|
+
const done = state('done');
|
|
167
|
+
const own = limitsOf(opts, { agent_uuid: agentUuid, step_uuid: stepUuid, step_custom_id: run.stepCustomId });
|
|
168
|
+
const myWaits = waiting.filter((r) => lower(r.agent_uuid) === agentUuid && String(r.run_id ?? '') === String(run.runId));
|
|
169
|
+
const waited = myWaits.length > 0;
|
|
170
|
+
const produced = !waited || myWaits.some((r) => truthy(r.progress));
|
|
171
|
+
const out = { wake: [], remove: [], stuck: [] };
|
|
172
|
+
const by = { agentName: String(run.agentName ?? ''), roles };
|
|
173
|
+
// This run's lease ends with it.
|
|
174
|
+
for (const r of state('running'))
|
|
175
|
+
if (r.id !== undefined && String(r.run_id ?? '') === String(run.runId))
|
|
176
|
+
out.remove.push(r.id);
|
|
177
|
+
// Done and escalated rows: one done row per agent and records; expired ones go.
|
|
178
|
+
for (const r of [...done, ...state('escalated')]) {
|
|
179
|
+
if (r.id === undefined)
|
|
180
|
+
continue;
|
|
181
|
+
const keepMs = limitsOf(opts, r).keepDoneHours * 3600_000;
|
|
182
|
+
const replaced = lower(r.state) === 'done' && produced && lower(r.agent_uuid) === agentUuid && recordKey(r.record_keys) === keys;
|
|
183
|
+
if (replaced || now.getTime() - timeOf(r) > keepMs)
|
|
184
|
+
out.remove.push(r.id);
|
|
185
|
+
}
|
|
186
|
+
const doneRow = produced
|
|
187
|
+
? {
|
|
188
|
+
state: 'done',
|
|
189
|
+
step_uuid: stepUuid,
|
|
190
|
+
step_custom_id: run.stepCustomId ?? '',
|
|
191
|
+
agent_uuid: agentUuid,
|
|
192
|
+
agent_name: clip(run.agentName, 200),
|
|
193
|
+
roles: roles.join(','),
|
|
194
|
+
waits_for: '',
|
|
195
|
+
reason: '',
|
|
196
|
+
record_keys: keys,
|
|
197
|
+
event: '',
|
|
198
|
+
run_id: String(run.runId),
|
|
199
|
+
attempts: run.attempt ?? 0,
|
|
200
|
+
progress: true,
|
|
201
|
+
at: now.toISOString(),
|
|
202
|
+
}
|
|
203
|
+
: null;
|
|
204
|
+
// Wake the agents of this step that wait on one of the roles of this run.
|
|
205
|
+
if (produced) {
|
|
206
|
+
const candidates = waiting.filter((r) => lower(r.agent_uuid) !== agentUuid && sameRecords(r.record_keys, keys) && waitsOn(r, roles));
|
|
207
|
+
for (const g of groupWaits(candidates))
|
|
208
|
+
wakeOrStick(g, by, limitsOf(opts, g.row).maxAttempts, out);
|
|
209
|
+
}
|
|
210
|
+
// A result of an awaited role may have arrived while this agent was still checking: then go again now.
|
|
211
|
+
if (waited) {
|
|
212
|
+
const mine = groupWaits(myWaits)[0];
|
|
213
|
+
const arrived = done.some((d) => lower(d.agent_uuid) !== agentUuid &&
|
|
214
|
+
sameRecords(d.record_keys, keys) &&
|
|
215
|
+
timeOf(d) >= (Number.isFinite(started) ? started : 0) &&
|
|
216
|
+
waitsOn(mine.row, uuidSet(d.roles)));
|
|
217
|
+
if (arrived)
|
|
218
|
+
wakeOrStick(mine, { agentName: 'arrived while waiting', roles: [] }, own.maxAttempts, out);
|
|
219
|
+
}
|
|
220
|
+
// Starts that came in while this run was busy run now, newest event first.
|
|
221
|
+
const queued = state('queued').filter((r) => lower(r.agent_uuid) === agentUuid && sameRecords(r.record_keys, keys));
|
|
222
|
+
for (const g of groupWaits(queued))
|
|
223
|
+
wakeOrStick(g, { agentName: 'queued while running', roles: [] }, own.maxAttempts, out);
|
|
224
|
+
return { waited, produced, doneRow, wake: out.wake, remove: Array.from(new Set(out.remove)), stuck: out.stuck };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Duplicate-run guard. The workflow first adds a lease row for this run (state "running",
|
|
228
|
+
* run_id), then reads the leases of the step and asks here. Of the live leases of the same
|
|
229
|
+
* agent and records the oldest wins; a later one does not start and is queued instead, so
|
|
230
|
+
* nothing is lost and nothing runs twice. Leases older than the run timeout count as dead.
|
|
231
|
+
*/
|
|
232
|
+
function checkStart(rows, run, opts = {}) {
|
|
233
|
+
const now = (opts.now ?? new Date()).getTime();
|
|
234
|
+
const agentUuid = lower(run.agentUuid);
|
|
235
|
+
const stepUuid = lower(run.stepUuid);
|
|
236
|
+
const keys = recordKey(run.recordKeys ?? parseEvent(run.event).keys);
|
|
237
|
+
const timeoutMs = limitsOf(opts, { agent_uuid: agentUuid, step_uuid: stepUuid, step_custom_id: run.stepCustomId }).runTimeoutMinutes * 60_000;
|
|
238
|
+
const leases = rows.filter((r) => lower(r.state) === 'running' && lower(r.step_uuid) === stepUuid && lower(r.agent_uuid) === agentUuid);
|
|
239
|
+
const remove = [];
|
|
240
|
+
const live = [];
|
|
241
|
+
for (const r of leases) {
|
|
242
|
+
if (now - timeOf(r) > timeoutMs) {
|
|
243
|
+
if (r.id !== undefined)
|
|
244
|
+
remove.push(r.id);
|
|
245
|
+
}
|
|
246
|
+
else if (sameRecords(r.record_keys, keys))
|
|
247
|
+
live.push(r);
|
|
248
|
+
}
|
|
249
|
+
const mine = live.filter((r) => String(r.run_id ?? '') === String(run.runId));
|
|
250
|
+
if (mine.length === 0) {
|
|
251
|
+
return { start: false, reason: 'No lease of this run was found; refusing to start', blockedBy: null, remove, queueRow: null };
|
|
252
|
+
}
|
|
253
|
+
live.sort((a, b) => timeOf(a) - timeOf(b) || idOrder(a, b));
|
|
254
|
+
const winner = live[0];
|
|
255
|
+
if (String(winner.run_id ?? '') === String(run.runId)) {
|
|
256
|
+
// Own extra lease rows (e.g. a retried insert) go as well.
|
|
257
|
+
remove.push(...mine.filter((r) => r !== winner && r.id !== undefined).map((r) => r.id));
|
|
258
|
+
return { start: true, reason: 'No other run of this agent is busy with these records', blockedBy: null, remove, queueRow: null };
|
|
259
|
+
}
|
|
260
|
+
remove.push(...mine.filter((r) => r.id !== undefined).map((r) => r.id));
|
|
261
|
+
const event = parseEvent(run.event);
|
|
262
|
+
return {
|
|
263
|
+
start: false,
|
|
264
|
+
reason: `Agent is busy with the same records in run ${winner.run_id}; this start is queued`,
|
|
265
|
+
blockedBy: String(winner.run_id ?? ''),
|
|
266
|
+
remove,
|
|
267
|
+
queueRow: {
|
|
268
|
+
state: 'queued',
|
|
269
|
+
step_uuid: stepUuid,
|
|
270
|
+
step_custom_id: run.stepCustomId ?? '',
|
|
271
|
+
agent_uuid: agentUuid,
|
|
272
|
+
agent_name: clip(run.agentName, 200),
|
|
273
|
+
roles: '',
|
|
274
|
+
waits_for: '',
|
|
275
|
+
reason: 'Started while the agent was busy with the same records',
|
|
276
|
+
record_keys: keys,
|
|
277
|
+
event: JSON.stringify(event),
|
|
278
|
+
run_id: String(winner.run_id ?? ''),
|
|
279
|
+
attempts: Math.max(0, Math.floor(Number(run.attempt) || 0)),
|
|
280
|
+
progress: '',
|
|
281
|
+
at: new Date(now).toISOString(),
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* For a scheduler: waits nobody answered within maxWaitMinutes (e.g. because a person fills
|
|
287
|
+
* the awaited role), queued starts whose busy run died, dead leases and expired rows.
|
|
288
|
+
* Waits at the attempt limit are returned once for escalation, never woken again by themselves.
|
|
289
|
+
*/
|
|
290
|
+
function findStaleWaits(rows, opts = {}) {
|
|
291
|
+
const now = (opts.now ?? new Date()).getTime();
|
|
292
|
+
const base = { ...opts, maxWaitMinutes: opts.olderThanMinutes ?? opts.maxWaitMinutes };
|
|
293
|
+
const out = { wake: [], remove: [], stuck: [] };
|
|
294
|
+
const escalate = [];
|
|
295
|
+
const liveLeases = [];
|
|
296
|
+
for (const r of rows) {
|
|
297
|
+
const s = lower(r.state);
|
|
298
|
+
const lim = limitsOf(base, r);
|
|
299
|
+
if (s === 'running') {
|
|
300
|
+
if (now - timeOf(r) > lim.runTimeoutMinutes * 60_000) {
|
|
301
|
+
if (r.id !== undefined)
|
|
302
|
+
out.remove.push(r.id);
|
|
303
|
+
}
|
|
304
|
+
else
|
|
305
|
+
liveLeases.push(r);
|
|
306
|
+
}
|
|
307
|
+
else if ((s === 'done' || s === 'escalated') && r.id !== undefined && now - timeOf(r) > lim.keepDoneHours * 3600_000) {
|
|
308
|
+
out.remove.push(r.id);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const busy = (r) => liveLeases.some((l) => lower(l.agent_uuid) === lower(r.agent_uuid) && lower(l.step_uuid) === lower(r.step_uuid) && sameRecords(l.record_keys, r.record_keys));
|
|
312
|
+
const byStep = new Map();
|
|
313
|
+
for (const r of rows) {
|
|
314
|
+
const s = lower(r.state);
|
|
315
|
+
if (s !== 'waiting' && s !== 'queued')
|
|
316
|
+
continue;
|
|
317
|
+
const key = `${s}|${lower(r.step_uuid)}`;
|
|
318
|
+
byStep.set(key, [...(byStep.get(key) ?? []), r]);
|
|
319
|
+
}
|
|
320
|
+
for (const [key, stepRows] of byStep) {
|
|
321
|
+
const queued = key.startsWith('queued|');
|
|
322
|
+
for (const g of groupWaits(stepRows)) {
|
|
323
|
+
const lim = limitsOf(base, g.row);
|
|
324
|
+
// A queued start waits for its busy run; only when that run is gone does the scheduler start it.
|
|
325
|
+
if (queued ? busy(g.row) : now - timeOf(g.row) < lim.maxWaitMinutes * 60_000)
|
|
326
|
+
continue;
|
|
327
|
+
if (!queued && busy(g.row))
|
|
328
|
+
continue;
|
|
329
|
+
const before = out.stuck.length;
|
|
330
|
+
wakeOrStick(g, { agentName: 'scheduler', roles: [] }, lim.maxAttempts, out);
|
|
331
|
+
if (out.stuck.length > before && g.row.id !== undefined) {
|
|
332
|
+
escalate.push(g.row.id);
|
|
333
|
+
out.remove.push(...g.ids.filter((id) => id !== g.row.id));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { wake: out.wake, remove: Array.from(new Set(out.remove)), stuck: out.stuck, escalate };
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=waits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waits.js","sourceRoot":"","sources":["../../lib/waits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Verification of roleALPHA context webhooks (`X-RA-Signature: sha256=<hex>`). */
|
|
2
|
+
export interface ContextChangedEvent {
|
|
3
|
+
event: 'context.changed';
|
|
4
|
+
agentUuid: string;
|
|
5
|
+
contextVersion: string;
|
|
6
|
+
changedAt: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function signBody(secret: string, rawBody: Buffer | string): string;
|
|
9
|
+
export declare function verifySignature(secrets: string[], rawBody: Buffer | string, signature: unknown): boolean;
|
|
10
|
+
/** Secrets may be one string, a JSON list, or a JSON map agentUuid → secret. */
|
|
11
|
+
export declare function parseSecrets(raw: string, agentUuid?: string): string[];
|
|
12
|
+
export declare function parseEvent(body: unknown): ContextChangedEvent | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Verification of roleALPHA context webhooks (`X-RA-Signature: sha256=<hex>`). */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.signBody = signBody;
|
|
5
|
+
exports.verifySignature = verifySignature;
|
|
6
|
+
exports.parseSecrets = parseSecrets;
|
|
7
|
+
exports.parseEvent = parseEvent;
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
function signBody(secret, rawBody) {
|
|
10
|
+
return `sha256=${(0, crypto_1.createHmac)('sha256', secret).update(rawBody).digest('hex')}`;
|
|
11
|
+
}
|
|
12
|
+
function verifySignature(secrets, rawBody, signature) {
|
|
13
|
+
const given = typeof signature === 'string' ? signature.trim().toLowerCase() : '';
|
|
14
|
+
if (!/^sha256=[0-9a-f]{64}$/.test(given))
|
|
15
|
+
return false;
|
|
16
|
+
const actual = Buffer.from(given, 'utf8');
|
|
17
|
+
return secrets
|
|
18
|
+
.filter((s) => typeof s === 'string' && s !== '')
|
|
19
|
+
.some((secret) => {
|
|
20
|
+
const expected = Buffer.from(signBody(secret, rawBody), 'utf8');
|
|
21
|
+
return expected.length === actual.length && (0, crypto_1.timingSafeEqual)(expected, actual);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Secrets may be one string, a JSON list, or a JSON map agentUuid → secret. */
|
|
25
|
+
function parseSecrets(raw, agentUuid) {
|
|
26
|
+
const text = (raw ?? '').trim();
|
|
27
|
+
if (text === '')
|
|
28
|
+
return [];
|
|
29
|
+
if (!text.startsWith('{') && !text.startsWith('['))
|
|
30
|
+
return [text];
|
|
31
|
+
const parsed = JSON.parse(text);
|
|
32
|
+
if (Array.isArray(parsed))
|
|
33
|
+
return parsed.filter((s) => typeof s === 'string');
|
|
34
|
+
const map = parsed;
|
|
35
|
+
if (agentUuid && typeof map[agentUuid] === 'string')
|
|
36
|
+
return [map[agentUuid]];
|
|
37
|
+
return agentUuid ? [] : Object.values(map).filter((s) => typeof s === 'string');
|
|
38
|
+
}
|
|
39
|
+
function parseEvent(body) {
|
|
40
|
+
const o = (body && typeof body === 'object' ? body : {});
|
|
41
|
+
if (o.event !== 'context.changed' || typeof o.agentUuid !== 'string')
|
|
42
|
+
return null;
|
|
43
|
+
return {
|
|
44
|
+
event: 'context.changed',
|
|
45
|
+
agentUuid: o.agentUuid,
|
|
46
|
+
contextVersion: String(o.contextVersion ?? ''),
|
|
47
|
+
changedAt: String(o.changedAt ?? ''),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../lib/webhook.ts"],"names":[],"mappings":";AAAA,mFAAmF;;AAWnF,4BAEC;AAED,0CAUC;AAGD,oCASC;AAED,gCASC;AA9CD,mCAAqD;AASrD,SAAgB,QAAQ,CAAC,MAAc,EAAE,OAAwB;IAChE,OAAO,UAAU,IAAA,mBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,SAAgB,eAAe,CAAC,OAAiB,EAAE,OAAwB,EAAE,SAAkB;IAC9F,MAAM,KAAK,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,OAAO;SACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;SAChD,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QAChE,OAAO,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,IAAA,wBAAe,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,SAAgB,YAAY,CAAC,GAAW,EAAE,SAAkB;IAC3D,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC3B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IAC3C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC3F,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,IAAI,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAW,CAAC,CAAC;IACvF,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC9F,CAAC;AAED,SAAgB,UAAU,CAAC,IAAa;IACvC,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;IACpF,IAAI,CAAC,CAAC,KAAK,KAAK,iBAAiB,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAClF,OAAO;QACN,KAAK,EAAE,iBAAiB;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,IAAI,EAAE,CAAC;QAC9C,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;KACpC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Seals the exact version a person approved and checks it again before anything is sent.
|
|
4
|
+
* The key lives only in the credential; the seal can be stored next to the record.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApprovalSeal implements INodeType {
|
|
7
|
+
description: INodeTypeDescription;
|
|
8
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
9
|
+
}
|