loopengine 0.3.0 → 0.3.2
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/dist/core/agent-config.d.ts +2 -0
- package/dist/core/http-notifier.js +14 -3
- package/dist/core/http-notify-triggers/telegram.d.ts +20 -1
- package/dist/core/http-notify-triggers/telegram.js +63 -17
- package/dist/core/messaging/connector.d.ts +7 -0
- package/dist/core/messaging/connector.js +1 -1
- package/dist/core/messaging/connectors/slack/events.d.ts +2 -1
- package/dist/core/messaging/connectors/slack/events.js +6 -2
- package/dist/core/messaging/connectors/slack/setup.d.ts +2 -1
- package/dist/core/messaging/connectors/slack/setup.js +10 -5
- package/dist/core/messaging/connectors/telegram/format.d.ts +6 -0
- package/dist/core/messaging/connectors/telegram/format.js +57 -0
- package/dist/core/messaging/connectors/telegram/index.d.ts +39 -0
- package/dist/core/messaging/connectors/telegram/index.js +136 -0
- package/dist/core/messaging/connectors/telegram/store.d.ts +31 -0
- package/dist/core/messaging/connectors/telegram/store.js +69 -0
- package/dist/core/messaging/connectors/telegram/updates.d.ts +40 -0
- package/dist/core/messaging/connectors/telegram/updates.js +112 -0
- package/dist/core/messaging/jobs.d.ts +44 -0
- package/dist/core/messaging/jobs.js +100 -0
- package/dist/core/messaging/threads.d.ts +12 -0
- package/dist/core/messaging/threads.js +36 -0
- package/dist/core/notifier-file.d.ts +13 -5
- package/dist/core/notifier-file.js +41 -6
- package/dist/core/run-callbacks.d.ts +39 -0
- package/dist/core/run-callbacks.js +125 -0
- package/dist/core/system-agents/workflow-planner.d.ts +37 -4
- package/dist/core/system-agents/workflow-planner.js +79 -5
- package/dist/core/workflow-runs.d.ts +131 -0
- package/dist/core/workflow-runs.js +229 -0
- package/dist/core/workflow-triggers.d.ts +140 -0
- package/dist/core/workflow-triggers.js +378 -0
- package/dist/web/agents-config-page.js +168 -50
- package/dist/web/notifier-admin.d.ts +69 -13
- package/dist/web/notifier-admin.js +127 -31
- package/dist/web/workflow-admin.d.ts +10 -0
- package/dist/web/workflow-admin.js +42 -3
- package/dist/web/workflow-page.js +93 -2
- package/package.json +7 -1
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Workflow runs: started by hand or by an app (Run now in the console, or
|
|
2
|
+
// POST /agents/:name/workflows/:id/run), or by a schedule that runs the
|
|
3
|
+
// workflow. Each is a record of its own here, so a workflow has one
|
|
4
|
+
// history of every run whatever started it, the Schedules tab only lists
|
|
5
|
+
// what is set to run, and each run can be looked up by its id afterwards
|
|
6
|
+
// (GET /agents/:name/runs/:id).
|
|
7
|
+
//
|
|
8
|
+
// A run executes the same way a scheduled task fires (supplied by the
|
|
9
|
+
// server, adapters/http.ts): an unattended agent turn whose approvals and
|
|
10
|
+
// questions go to the agent's messaging app and wait durably. A run that
|
|
11
|
+
// pauses there is settled by settleFromResume once someone decides.
|
|
12
|
+
//
|
|
13
|
+
// Stored like scheduled tasks: one JSON file per run under
|
|
14
|
+
// `<LOOPENGINE_SCHEDULER_DIR or .scheduler>/runs/`. Each agent keeps its
|
|
15
|
+
// latest MAX_FINISHED_PER_AGENT finished runs; older ones are removed as
|
|
16
|
+
// new runs start.
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { KeyedMutex } from './session-store.js';
|
|
21
|
+
const MAX_FINISHED_PER_AGENT = 500;
|
|
22
|
+
const SESSION_PREFIX = 'run-';
|
|
23
|
+
/** A scheduled task's sessions (core/scheduler.ts). */
|
|
24
|
+
const SCHEDULE_SESSION_PREFIX = 'sched-';
|
|
25
|
+
export class RunError extends Error {
|
|
26
|
+
}
|
|
27
|
+
const fileMutex = new KeyedMutex();
|
|
28
|
+
/** One JSON file per run under `<dir>/runs/` — one server process. */
|
|
29
|
+
export class FileRunStore {
|
|
30
|
+
dir;
|
|
31
|
+
constructor(dir) {
|
|
32
|
+
this.dir = path.join(dir, 'runs');
|
|
33
|
+
}
|
|
34
|
+
file(runId) {
|
|
35
|
+
if (!/^[A-Za-z0-9_-]+$/.test(runId))
|
|
36
|
+
throw new RunError(`invalid run id "${runId}"`);
|
|
37
|
+
return path.join(this.dir, `${runId}.json`);
|
|
38
|
+
}
|
|
39
|
+
async list() {
|
|
40
|
+
if (!existsSync(this.dir))
|
|
41
|
+
return [];
|
|
42
|
+
const runs = [];
|
|
43
|
+
for (const name of readdirSync(this.dir)) {
|
|
44
|
+
if (!name.endsWith('.json'))
|
|
45
|
+
continue;
|
|
46
|
+
try {
|
|
47
|
+
runs.push(JSON.parse(readFileSync(path.join(this.dir, name), 'utf8')));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// A file being written, or not a run: skip it.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return runs.sort((a, b) => b.started_at.localeCompare(a.started_at));
|
|
54
|
+
}
|
|
55
|
+
async get(runId) {
|
|
56
|
+
const file = this.file(runId);
|
|
57
|
+
return existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : undefined;
|
|
58
|
+
}
|
|
59
|
+
async put(run) {
|
|
60
|
+
mkdirSync(this.dir, { recursive: true });
|
|
61
|
+
const file = this.file(run.run_id);
|
|
62
|
+
writeFileSync(file + '.tmp', JSON.stringify(run, null, 2));
|
|
63
|
+
renameSync(file + '.tmp', file);
|
|
64
|
+
}
|
|
65
|
+
async remove(runId) {
|
|
66
|
+
rmSync(this.file(runId), { force: true });
|
|
67
|
+
}
|
|
68
|
+
async update(runId, fn) {
|
|
69
|
+
return fileMutex.run(runId, async () => {
|
|
70
|
+
const next = fn(await this.get(runId));
|
|
71
|
+
if (next)
|
|
72
|
+
await this.put(next);
|
|
73
|
+
return next;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let store;
|
|
78
|
+
/** The process-wide store: `runs/` in the scheduler's folder. */
|
|
79
|
+
export function getRunStore() {
|
|
80
|
+
store ??= new FileRunStore(process.env.LOOPENGINE_SCHEDULER_DIR || path.join(process.cwd(), '.scheduler'));
|
|
81
|
+
return store;
|
|
82
|
+
}
|
|
83
|
+
export function setRunStore(next) {
|
|
84
|
+
store = next;
|
|
85
|
+
}
|
|
86
|
+
// ---------- reading ----------
|
|
87
|
+
/** An agent's runs, newest first; of one workflow when `workflowId` is given. */
|
|
88
|
+
export async function listRuns(agent, filter = {}) {
|
|
89
|
+
const runs = (await getRunStore().list()).filter((r) => r.agent === agent && (!filter.workflowId || r.workflow_id === filter.workflowId));
|
|
90
|
+
return filter.limit ? runs.slice(0, filter.limit) : runs;
|
|
91
|
+
}
|
|
92
|
+
/** One of an agent's runs; undefined for another agent's. */
|
|
93
|
+
export async function getRun(agent, runId) {
|
|
94
|
+
if (!/^[A-Za-z0-9_-]+$/.test(runId))
|
|
95
|
+
return undefined;
|
|
96
|
+
const run = await getRunStore().get(runId);
|
|
97
|
+
return run && run.agent === agent ? run : undefined;
|
|
98
|
+
}
|
|
99
|
+
export function isUnderWay(run) {
|
|
100
|
+
return run.status === 'running' || run.status === 'awaiting_approval';
|
|
101
|
+
}
|
|
102
|
+
export class WorkflowRunner {
|
|
103
|
+
execute;
|
|
104
|
+
maxFinishedPerAgent;
|
|
105
|
+
onChange;
|
|
106
|
+
inFlight = new Set();
|
|
107
|
+
/** `onChange`: called with the run each time it finishes, fails or
|
|
108
|
+
* stops for a decision (the server sends its notify and callback). */
|
|
109
|
+
constructor(options) {
|
|
110
|
+
this.execute = options.execute;
|
|
111
|
+
this.maxFinishedPerAgent = options.maxFinishedPerAgent ?? MAX_FINISHED_PER_AGENT;
|
|
112
|
+
this.onChange = options.onChange;
|
|
113
|
+
}
|
|
114
|
+
changed(run) {
|
|
115
|
+
if (!run || !this.onChange)
|
|
116
|
+
return;
|
|
117
|
+
void Promise.resolve()
|
|
118
|
+
.then(() => this.onChange(run))
|
|
119
|
+
.catch((err) => console.error(`[loopengine] reporting run ${run.run_id} failed:`, err));
|
|
120
|
+
}
|
|
121
|
+
/** Records a run and starts it; returns at once with the record. */
|
|
122
|
+
async start(input) {
|
|
123
|
+
const runId = randomUUID();
|
|
124
|
+
const run = {
|
|
125
|
+
run_id: runId,
|
|
126
|
+
agent: input.agent,
|
|
127
|
+
workflow_id: input.workflow_id,
|
|
128
|
+
...(input.params && Object.keys(input.params).length ? { params: input.params } : {}),
|
|
129
|
+
source: input.source,
|
|
130
|
+
...(input.notify ? { notify: true } : {}),
|
|
131
|
+
...(input.callback_url ? { callback_url: input.callback_url } : {}),
|
|
132
|
+
...(input.trigger_id ? { trigger_id: input.trigger_id } : {}),
|
|
133
|
+
status: 'running',
|
|
134
|
+
started_at: new Date().toISOString(),
|
|
135
|
+
message: input.message,
|
|
136
|
+
session_id: `${SESSION_PREFIX}${runId.slice(0, 8)}-${Date.now().toString(36)}`,
|
|
137
|
+
};
|
|
138
|
+
await getRunStore().put(run);
|
|
139
|
+
await this.prune(input.agent);
|
|
140
|
+
const job = this.run(run).finally(() => this.inFlight.delete(job));
|
|
141
|
+
this.inFlight.add(job);
|
|
142
|
+
return run;
|
|
143
|
+
}
|
|
144
|
+
/** A schedule firing one of its agent's workflows (the scheduler's fire,
|
|
145
|
+
* for a workflow task): recorded as a run of the workflow, then run the
|
|
146
|
+
* usual way. Returns the outcome for the scheduler, and throws what the
|
|
147
|
+
* run threw, so the schedule's own history records it too. */
|
|
148
|
+
async runForSchedule(input) {
|
|
149
|
+
const run = {
|
|
150
|
+
run_id: randomUUID(),
|
|
151
|
+
agent: input.agent,
|
|
152
|
+
workflow_id: input.workflow_id,
|
|
153
|
+
...(input.params && Object.keys(input.params).length ? { params: input.params } : {}),
|
|
154
|
+
source: 'schedule',
|
|
155
|
+
task_id: input.task_id,
|
|
156
|
+
...(input.task_name ? { task_name: input.task_name } : {}),
|
|
157
|
+
status: 'running',
|
|
158
|
+
started_at: new Date().toISOString(),
|
|
159
|
+
message: input.message,
|
|
160
|
+
session_id: input.session_id,
|
|
161
|
+
};
|
|
162
|
+
await getRunStore().put(run);
|
|
163
|
+
await this.prune(input.agent);
|
|
164
|
+
try {
|
|
165
|
+
const outcome = await this.execute(run.agent, run.message, run.session_id);
|
|
166
|
+
if (outcome.kind === 'pending')
|
|
167
|
+
this.changed(await getRunStore().update(run.run_id, (r) => r && { ...r, status: 'awaiting_approval', pending_ids: outcome.pendingIds }));
|
|
168
|
+
else
|
|
169
|
+
await this.finish(run.run_id, { status: 'done', result: outcome.text });
|
|
170
|
+
return outcome;
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
await this.finish(run.run_id, { status: 'error', error: err instanceof Error ? err.message : String(err) });
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Waits for the runs this process has started (tests, shutdown). */
|
|
178
|
+
async idle() {
|
|
179
|
+
await Promise.all([...this.inFlight]);
|
|
180
|
+
}
|
|
181
|
+
/** How many runs are executing right now (for idle-shutdown checks). */
|
|
182
|
+
get running() {
|
|
183
|
+
return this.inFlight.size;
|
|
184
|
+
}
|
|
185
|
+
async run(run) {
|
|
186
|
+
try {
|
|
187
|
+
const outcome = await this.execute(run.agent, run.message, run.session_id);
|
|
188
|
+
if (outcome.kind === 'pending') {
|
|
189
|
+
this.changed(await getRunStore().update(run.run_id, (r) => r && { ...r, status: 'awaiting_approval', pending_ids: outcome.pendingIds }));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
await this.finish(run.run_id, { status: 'done', result: outcome.text });
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
await this.finish(run.run_id, { status: 'error', error: err instanceof Error ? err.message : String(err) });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async finish(runId, end) {
|
|
199
|
+
this.changed(await getRunStore().update(runId, (r) => r && { ...r, ...end, pending_ids: undefined, finished_at: new Date().toISOString() }));
|
|
200
|
+
}
|
|
201
|
+
/** A paused run resumed after a decision (the server calls this from
|
|
202
|
+
* its resolve path, for every resumed session): settle it, or record
|
|
203
|
+
* what it's waiting on now. */
|
|
204
|
+
async settleFromResume(sessionId, outcome) {
|
|
205
|
+
if (!sessionId.startsWith(SESSION_PREFIX) && !sessionId.startsWith(SCHEDULE_SESSION_PREFIX))
|
|
206
|
+
return;
|
|
207
|
+
const run = (await getRunStore().list()).find((r) => r.session_id === sessionId);
|
|
208
|
+
if (!run)
|
|
209
|
+
return;
|
|
210
|
+
if (outcome.pendingIds?.length) {
|
|
211
|
+
this.changed(await getRunStore().update(run.run_id, (r) => r && { ...r, status: 'awaiting_approval', pending_ids: outcome.pendingIds }));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
await this.finish(run.run_id, { status: 'done', result: outcome.text });
|
|
215
|
+
}
|
|
216
|
+
/** On startup: a run a restart cut off can't continue, so it's an
|
|
217
|
+
* error. One waiting on a decision is kept: its checkpoint survives. */
|
|
218
|
+
async recover() {
|
|
219
|
+
for (const run of await getRunStore().list()) {
|
|
220
|
+
if (run.status === 'running')
|
|
221
|
+
await this.finish(run.run_id, { status: 'error', error: 'The server restarted while this run was in progress.' });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async prune(agent) {
|
|
225
|
+
const finished = (await listRuns(agent)).filter((r) => !isUnderWay(r));
|
|
226
|
+
for (const old of finished.slice(this.maxFinishedPerAgent))
|
|
227
|
+
await getRunStore().remove(old.run_id);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { Workflow } from './workflows.js';
|
|
2
|
+
/** Whose signature scheme a trigger checks. "standard" is Standard
|
|
3
|
+
* Webhooks (Svix: used by Clerk, Resend, OpenAI and others); "custom" is
|
|
4
|
+
* an HMAC of the raw body in a header the sender names (Typeform, Linear,
|
|
5
|
+
* Sentry, Intercom and most others that sign the body). */
|
|
6
|
+
export type TriggerSender = 'stripe' | 'github' | 'shopify' | 'standard' | 'custom';
|
|
7
|
+
export type SignatureAlgorithm = 'sha256' | 'sha1' | 'sha512';
|
|
8
|
+
export interface TriggerVerify {
|
|
9
|
+
sender: TriggerSender;
|
|
10
|
+
/** The signing secret the sender gives (Stripe's whsec_…, GitHub's,
|
|
11
|
+
* Shopify's, a Standard Webhooks whsec_…, or a custom one). */
|
|
12
|
+
secret: string;
|
|
13
|
+
/** custom only: the header the signature is in, e.g. "Linear-Signature". */
|
|
14
|
+
header?: string;
|
|
15
|
+
/** custom only: the HMAC's hash (default sha256). */
|
|
16
|
+
algorithm?: SignatureAlgorithm;
|
|
17
|
+
/** custom only: how the signature is written (default hex). */
|
|
18
|
+
encoding?: 'hex' | 'base64';
|
|
19
|
+
/** custom only: text before the signature, e.g. "sha256=". */
|
|
20
|
+
prefix?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface TriggerFilter {
|
|
23
|
+
/** Where in the payload, as a dot path: "type", "action", "data.object.status". */
|
|
24
|
+
path: string;
|
|
25
|
+
/** Run only when the value there is one of these. */
|
|
26
|
+
values: string[];
|
|
27
|
+
}
|
|
28
|
+
export type TriggerOutcome = 'started' | 'filtered' | 'duplicate' | 'busy' | 'rejected' | 'invalid' | 'error';
|
|
29
|
+
export interface TriggerDelivery {
|
|
30
|
+
at: string;
|
|
31
|
+
outcome: TriggerOutcome;
|
|
32
|
+
delivery_id?: string;
|
|
33
|
+
run_id?: string;
|
|
34
|
+
detail?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface WorkflowTrigger {
|
|
37
|
+
trigger_id: string;
|
|
38
|
+
agent: string;
|
|
39
|
+
workflow_id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
/** SHA-256 of the URL's token. */
|
|
42
|
+
token_hash: string;
|
|
43
|
+
/** The token's start, to tell triggers apart: "whk_8f3k…". */
|
|
44
|
+
token_hint: string;
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
created_at: string;
|
|
47
|
+
verify?: TriggerVerify;
|
|
48
|
+
filter?: TriggerFilter;
|
|
49
|
+
/** Workflow parameter → dot path in the payload. */
|
|
50
|
+
mapping?: Record<string, string>;
|
|
51
|
+
/** A parameter that gets the whole payload, as JSON. */
|
|
52
|
+
payload_param?: string;
|
|
53
|
+
notify?: boolean;
|
|
54
|
+
callback_url?: string;
|
|
55
|
+
/** The latest deliveries, newest first (20). */
|
|
56
|
+
deliveries: TriggerDelivery[];
|
|
57
|
+
/** Delivery ids already handled (the latest 200), for retries. */
|
|
58
|
+
seen_ids: string[];
|
|
59
|
+
}
|
|
60
|
+
/** A trigger as the console and API show it: no token hash, no secret,
|
|
61
|
+
* only whether one is set. */
|
|
62
|
+
export type PublicTrigger = Omit<WorkflowTrigger, 'token_hash' | 'verify' | 'seen_ids'> & {
|
|
63
|
+
verify?: Omit<TriggerVerify, 'secret'> & {
|
|
64
|
+
secret_set: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
export declare class TriggerError extends Error {
|
|
68
|
+
}
|
|
69
|
+
export interface TriggerStore {
|
|
70
|
+
list(): Promise<WorkflowTrigger[]>;
|
|
71
|
+
get(id: string): Promise<WorkflowTrigger | undefined>;
|
|
72
|
+
put(trigger: WorkflowTrigger): Promise<void>;
|
|
73
|
+
remove(id: string): Promise<void>;
|
|
74
|
+
update(id: string, fn: (t: WorkflowTrigger | undefined) => WorkflowTrigger | undefined): Promise<WorkflowTrigger | undefined>;
|
|
75
|
+
}
|
|
76
|
+
/** One JSON file per trigger under `<dir>/triggers/` — one server process. */
|
|
77
|
+
export declare class FileTriggerStore implements TriggerStore {
|
|
78
|
+
private readonly dir;
|
|
79
|
+
constructor(dir: string);
|
|
80
|
+
private file;
|
|
81
|
+
list(): Promise<WorkflowTrigger[]>;
|
|
82
|
+
get(id: string): Promise<WorkflowTrigger | undefined>;
|
|
83
|
+
put(trigger: WorkflowTrigger): Promise<void>;
|
|
84
|
+
remove(id: string): Promise<void>;
|
|
85
|
+
update(id: string, fn: (t: WorkflowTrigger | undefined) => WorkflowTrigger | undefined): Promise<WorkflowTrigger | undefined>;
|
|
86
|
+
}
|
|
87
|
+
export declare function getTriggerStore(): TriggerStore;
|
|
88
|
+
export declare function setTriggerStore(next: TriggerStore): void;
|
|
89
|
+
export declare function publicTrigger(t: WorkflowTrigger): PublicTrigger;
|
|
90
|
+
/** The settings a trigger can have, checked against its workflow. */
|
|
91
|
+
export interface TriggerSettings {
|
|
92
|
+
name?: unknown;
|
|
93
|
+
enabled?: unknown;
|
|
94
|
+
verify?: unknown;
|
|
95
|
+
filter?: unknown;
|
|
96
|
+
mapping?: unknown;
|
|
97
|
+
payload_param?: unknown;
|
|
98
|
+
notify?: unknown;
|
|
99
|
+
callback_url?: unknown;
|
|
100
|
+
}
|
|
101
|
+
/** Checks settings for a trigger of `workflow`; throws TriggerError saying
|
|
102
|
+
* what's wrong. With `existing` (a change), a setting left out keeps its
|
|
103
|
+
* value and null removes it, and a sender's secret is kept when only the
|
|
104
|
+
* sender is given again (the console never sees the secret).
|
|
105
|
+
*
|
|
106
|
+
* A trigger that checks a signature can be made turned off without its
|
|
107
|
+
* secret yet: the workflow planner makes one that way, for the person to
|
|
108
|
+
* add the secret the sender shows them. It can't be turned on until the
|
|
109
|
+
* secret is there. */
|
|
110
|
+
export declare function checkTriggerSettings(input: TriggerSettings, workflow: Workflow, existing?: WorkflowTrigger): Pick<WorkflowTrigger, 'name' | 'enabled' | 'verify' | 'filter' | 'mapping' | 'payload_param' | 'notify' | 'callback_url'>;
|
|
111
|
+
/** Makes a trigger for a workflow; returns it with its token (shown once). */
|
|
112
|
+
export declare function createTrigger(agent: string, workflow: Workflow, input: TriggerSettings): Promise<{
|
|
113
|
+
trigger: WorkflowTrigger;
|
|
114
|
+
token: string;
|
|
115
|
+
}>;
|
|
116
|
+
export declare function updateTrigger(id: string, workflow: Workflow, input: TriggerSettings): Promise<WorkflowTrigger>;
|
|
117
|
+
/** A new URL for a trigger; the old one stops working. */
|
|
118
|
+
export declare function rotateTrigger(id: string): Promise<{
|
|
119
|
+
trigger: WorkflowTrigger;
|
|
120
|
+
token: string;
|
|
121
|
+
}>;
|
|
122
|
+
export declare function listTriggers(agent: string, workflowId?: string): Promise<WorkflowTrigger[]>;
|
|
123
|
+
/** The trigger a URL's token belongs to. */
|
|
124
|
+
export declare function findTriggerByToken(token: string): Promise<WorkflowTrigger | undefined>;
|
|
125
|
+
/** Whether the sender's own signature on the raw body is right. Never
|
|
126
|
+
* with no secret: anyone can sign with an empty key. */
|
|
127
|
+
export declare function verifySenderSignature(verify: TriggerVerify, headers: Record<string, string | string[] | undefined>, raw: Buffer, nowS?: number): boolean;
|
|
128
|
+
/** The payload: JSON, a form, or plain text (as { body }). */
|
|
129
|
+
export declare function parsePayload(raw: Buffer, contentType: string | undefined): unknown;
|
|
130
|
+
/** The value at a dot path: "data.object.id", "items.0.sku". */
|
|
131
|
+
export declare function valueAt(payload: unknown, dotPath: string): unknown;
|
|
132
|
+
/** The sender's id for this delivery, if it gives one: what a retry
|
|
133
|
+
* repeats. */
|
|
134
|
+
export declare function deliveryId(headers: Record<string, string | string[] | undefined>, payload: unknown): string | undefined;
|
|
135
|
+
/** Whether the filter keeps this payload. */
|
|
136
|
+
export declare function passesFilter(filter: TriggerFilter | undefined, payload: unknown): boolean;
|
|
137
|
+
/** The workflow's parameters from the payload. */
|
|
138
|
+
export declare function paramsFromPayload(trigger: Pick<WorkflowTrigger, 'mapping' | 'payload_param'>, payload: unknown): Record<string, string>;
|
|
139
|
+
/** Notes a delivery on its trigger, and its id as handled. */
|
|
140
|
+
export declare function recordDelivery(id: string, delivery: TriggerDelivery, seenId?: string): Promise<void>;
|