bunbase 0.0.9
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 +25 -0
- package/dist/cli/index.js +1530 -0
- package/dist/index.d.ts +543 -0
- package/dist/index.js +164 -0
- package/dist/shared/chunk-k195ahh5.js +79 -0
- package/package.json +86 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
type DatabaseConfig = {
|
|
2
|
+
url: string;
|
|
3
|
+
debug?: boolean;
|
|
4
|
+
};
|
|
5
|
+
type LogLevel = "debug" | "info" | "warn" | "error" | "critical";
|
|
6
|
+
type LoggerOptions = {
|
|
7
|
+
/** Minimum log level (default: 'info') */
|
|
8
|
+
level?: LogLevel;
|
|
9
|
+
/** Show verbose output with metadata details (default: false) */
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type StorageConfig = {};
|
|
13
|
+
import { Database } from "bun:sqlite";
|
|
14
|
+
interface Job {
|
|
15
|
+
id?: number;
|
|
16
|
+
action_name: string;
|
|
17
|
+
trigger_name: string;
|
|
18
|
+
schedule: string;
|
|
19
|
+
status: "active" | "paused" | "completed";
|
|
20
|
+
last_run_at?: string | null;
|
|
21
|
+
next_run_at?: string | null;
|
|
22
|
+
run_count: number;
|
|
23
|
+
last_error?: string | null;
|
|
24
|
+
created_at?: string;
|
|
25
|
+
}
|
|
26
|
+
declare class JobStore {
|
|
27
|
+
private db;
|
|
28
|
+
constructor(db: Database);
|
|
29
|
+
/** Register or update a scheduled job */
|
|
30
|
+
upsert(job: {
|
|
31
|
+
action_name: string;
|
|
32
|
+
trigger_name: string;
|
|
33
|
+
schedule: string;
|
|
34
|
+
}): void;
|
|
35
|
+
/** Record a job execution */
|
|
36
|
+
recordRun(actionName: string, triggerName: string, error?: string): void;
|
|
37
|
+
/** Get all jobs */
|
|
38
|
+
list(status?: string): Job[];
|
|
39
|
+
/** Get a specific job */
|
|
40
|
+
get(actionName: string, triggerName: string): Job | null;
|
|
41
|
+
/** Pause a job */
|
|
42
|
+
pause(actionName: string, triggerName: string): void;
|
|
43
|
+
/** Resume a paused job */
|
|
44
|
+
resume(actionName: string, triggerName: string): void;
|
|
45
|
+
}
|
|
46
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
47
|
+
interface LogEntry {
|
|
48
|
+
id?: number;
|
|
49
|
+
level: string;
|
|
50
|
+
message: string;
|
|
51
|
+
meta?: string | null;
|
|
52
|
+
action_name?: string | null;
|
|
53
|
+
trace_id?: string | null;
|
|
54
|
+
created_at?: string;
|
|
55
|
+
}
|
|
56
|
+
interface LogFilter {
|
|
57
|
+
level?: string;
|
|
58
|
+
action_name?: string;
|
|
59
|
+
trace_id?: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
offset?: number;
|
|
62
|
+
since?: string;
|
|
63
|
+
}
|
|
64
|
+
declare class LogStore {
|
|
65
|
+
private db;
|
|
66
|
+
constructor(db: Database2);
|
|
67
|
+
insert(entry: Omit<LogEntry, "id" | "created_at">): void;
|
|
68
|
+
query(filter?: LogFilter): LogEntry[];
|
|
69
|
+
/** Delete logs older than given number of days */
|
|
70
|
+
cleanup(retentionDays: number): number;
|
|
71
|
+
count(filter?: LogFilter): number;
|
|
72
|
+
}
|
|
73
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
74
|
+
interface ActionRun {
|
|
75
|
+
id?: number;
|
|
76
|
+
trace_id: string;
|
|
77
|
+
action_name: string;
|
|
78
|
+
trigger_name: string;
|
|
79
|
+
trigger_type: string;
|
|
80
|
+
actor_id?: string | null;
|
|
81
|
+
actor_type?: string | null;
|
|
82
|
+
input?: string | null;
|
|
83
|
+
output?: string | null;
|
|
84
|
+
status: "pending" | "success" | "error";
|
|
85
|
+
error_message?: string | null;
|
|
86
|
+
duration_ms?: number | null;
|
|
87
|
+
started_at?: string;
|
|
88
|
+
completed_at?: string | null;
|
|
89
|
+
}
|
|
90
|
+
interface RunFilter {
|
|
91
|
+
action_name?: string;
|
|
92
|
+
trigger_type?: string;
|
|
93
|
+
status?: string;
|
|
94
|
+
limit?: number;
|
|
95
|
+
offset?: number;
|
|
96
|
+
since?: string;
|
|
97
|
+
until?: string;
|
|
98
|
+
}
|
|
99
|
+
declare class RunStore {
|
|
100
|
+
private db;
|
|
101
|
+
constructor(db: Database3);
|
|
102
|
+
/** Record the start of an action execution */
|
|
103
|
+
start(run: {
|
|
104
|
+
trace_id: string;
|
|
105
|
+
action_name: string;
|
|
106
|
+
trigger_name: string;
|
|
107
|
+
trigger_type: string;
|
|
108
|
+
actor_id?: string | null;
|
|
109
|
+
actor_type?: string | null;
|
|
110
|
+
input?: unknown;
|
|
111
|
+
}): void;
|
|
112
|
+
/** Mark an action run as completed successfully */
|
|
113
|
+
complete(traceId: string, output: unknown, durationMs: number): void;
|
|
114
|
+
/** Mark an action run as failed */
|
|
115
|
+
fail(traceId: string, errorMessage: string, durationMs: number): void;
|
|
116
|
+
/** Query action runs with filtering */
|
|
117
|
+
query(filter?: RunFilter): ActionRun[];
|
|
118
|
+
/** Get a single run by trace ID */
|
|
119
|
+
getByTraceId(traceId: string): ActionRun | null;
|
|
120
|
+
/** Get summary stats */
|
|
121
|
+
stats(since?: string): {
|
|
122
|
+
total: number;
|
|
123
|
+
success: number;
|
|
124
|
+
error: number;
|
|
125
|
+
avg_duration_ms: number;
|
|
126
|
+
};
|
|
127
|
+
/** Cleanup old runs */
|
|
128
|
+
cleanup(retentionDays: number): number;
|
|
129
|
+
}
|
|
130
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
131
|
+
interface StoreConfig {
|
|
132
|
+
/** Path to SQLite database file (default: '.gravity/data.db') */
|
|
133
|
+
path?: string;
|
|
134
|
+
/** Days to keep logs before auto-cleanup (default: 30) */
|
|
135
|
+
retention?: number;
|
|
136
|
+
}
|
|
137
|
+
declare class GravityStore {
|
|
138
|
+
readonly db: Database4;
|
|
139
|
+
readonly logs: LogStore;
|
|
140
|
+
readonly runs: RunStore;
|
|
141
|
+
readonly jobs: JobStore;
|
|
142
|
+
readonly retention: number;
|
|
143
|
+
constructor(config?: StoreConfig);
|
|
144
|
+
/** Run retention cleanup on all tables */
|
|
145
|
+
cleanup(): {
|
|
146
|
+
logs: number;
|
|
147
|
+
runs: number;
|
|
148
|
+
};
|
|
149
|
+
/** Close the database connection */
|
|
150
|
+
close(): void;
|
|
151
|
+
}
|
|
152
|
+
interface GravityConfig {
|
|
153
|
+
/**
|
|
154
|
+
* Project name (used for logging, defaults, etc.)
|
|
155
|
+
*/
|
|
156
|
+
name?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Server configuration
|
|
159
|
+
*/
|
|
160
|
+
server?: {
|
|
161
|
+
port?: number;
|
|
162
|
+
host?: string;
|
|
163
|
+
cors?: {
|
|
164
|
+
origin?: string | string[];
|
|
165
|
+
credentials?: boolean;
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Database configuration
|
|
170
|
+
*/
|
|
171
|
+
db?: DatabaseConfig;
|
|
172
|
+
/**
|
|
173
|
+
* Storage configuration
|
|
174
|
+
*/
|
|
175
|
+
storage?: StorageConfig;
|
|
176
|
+
/**
|
|
177
|
+
* Logger configuration
|
|
178
|
+
*/
|
|
179
|
+
logger?: LoggerOptions;
|
|
180
|
+
/**
|
|
181
|
+
* Persistence store configuration (bun:sqlite)
|
|
182
|
+
*/
|
|
183
|
+
store?: StoreConfig;
|
|
184
|
+
/**
|
|
185
|
+
* Actions directory (default: './src/actions' or './actions')
|
|
186
|
+
*/
|
|
187
|
+
actionsDir?: string;
|
|
188
|
+
/**
|
|
189
|
+
* OpenAPI/docs generation
|
|
190
|
+
*/
|
|
191
|
+
docs?: {
|
|
192
|
+
enabled?: boolean;
|
|
193
|
+
path?: string;
|
|
194
|
+
title?: string;
|
|
195
|
+
version?: string;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* MCP server configuration
|
|
199
|
+
*/
|
|
200
|
+
mcp?: {
|
|
201
|
+
enabled?: boolean;
|
|
202
|
+
name?: string;
|
|
203
|
+
version?: string;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Development server options
|
|
207
|
+
*/
|
|
208
|
+
dev?: {
|
|
209
|
+
watch?: boolean;
|
|
210
|
+
hmr?: boolean;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Custom middleware or hooks
|
|
214
|
+
*/
|
|
215
|
+
plugins?: GravityPlugin[];
|
|
216
|
+
}
|
|
217
|
+
interface GravityPlugin {
|
|
218
|
+
name: string;
|
|
219
|
+
setup: (context: GravityPluginContext) => Promise<void> | void;
|
|
220
|
+
}
|
|
221
|
+
interface GravityPluginContext {
|
|
222
|
+
config: GravityConfig;
|
|
223
|
+
}
|
|
224
|
+
declare function loadConfig(): Promise<GravityConfig>;
|
|
225
|
+
declare function defineConfig(config: GravityConfig): GravityConfig;
|
|
226
|
+
import { TSchema as TSchema2 } from "typebox";
|
|
227
|
+
import { Static, TSchema } from "typebox";
|
|
228
|
+
/**
|
|
229
|
+
* A structured logging session with clack-style box-drawing output.
|
|
230
|
+
* Created via `logger.session("title")`.
|
|
231
|
+
*/
|
|
232
|
+
declare class LoggerSession {
|
|
233
|
+
private output;
|
|
234
|
+
private startTime;
|
|
235
|
+
constructor(title: string, output?: NodeJS.WritableStream);
|
|
236
|
+
private write;
|
|
237
|
+
/** Static info line: ◇ label: detail */
|
|
238
|
+
info(label: string, detail?: string): this;
|
|
239
|
+
/** In-progress step: ● label: detail */
|
|
240
|
+
step(label: string, detail?: string): this;
|
|
241
|
+
/** Completed step: ✔ label: detail */
|
|
242
|
+
success(label: string, detail?: string): this;
|
|
243
|
+
/** Warning step: ▲ label: detail */
|
|
244
|
+
warn(label: string, detail?: string): this;
|
|
245
|
+
/** Error step: ✖ label: detail */
|
|
246
|
+
error(label: string, detail?: string): this;
|
|
247
|
+
/** Empty separator bar: │ */
|
|
248
|
+
bar(): this;
|
|
249
|
+
/** Close the session with success: └ message (Xms) */
|
|
250
|
+
end(message?: string): void;
|
|
251
|
+
/** Close the session as error: └ message (Xms) */
|
|
252
|
+
fail(message?: string): void;
|
|
253
|
+
}
|
|
254
|
+
declare const LEVELS: {
|
|
255
|
+
readonly DEBUG: 10;
|
|
256
|
+
readonly INFO: 20;
|
|
257
|
+
readonly WARNING: 30;
|
|
258
|
+
readonly ERROR: 40;
|
|
259
|
+
readonly CRITICAL: 50;
|
|
260
|
+
};
|
|
261
|
+
type LogLevel2 = keyof typeof LEVELS;
|
|
262
|
+
type LogListener = (level: LogLevel2, msg: string, args?: unknown) => void;
|
|
263
|
+
declare class Logger {
|
|
264
|
+
private readonly options;
|
|
265
|
+
private readonly meta;
|
|
266
|
+
private readonly coreListeners;
|
|
267
|
+
private readonly listeners;
|
|
268
|
+
private readonly minLevel;
|
|
269
|
+
private store?;
|
|
270
|
+
constructor(options?: LoggerOptions, meta?: Record<string, unknown>, coreListeners?: LogListener[], store?: GravityStore);
|
|
271
|
+
/** Attach a persistence store for log persistence */
|
|
272
|
+
setStore(store: GravityStore): void;
|
|
273
|
+
/** Create a child logger with additional metadata */
|
|
274
|
+
child(meta: Record<string, unknown>): Logger;
|
|
275
|
+
/** Start a structured logging session with clack-style output */
|
|
276
|
+
session(title: string): LoggerSession;
|
|
277
|
+
private shouldLog;
|
|
278
|
+
private _log;
|
|
279
|
+
info(message: string, args?: unknown): void;
|
|
280
|
+
error(message: string, args?: unknown): void;
|
|
281
|
+
debug(message: string, args?: unknown): void;
|
|
282
|
+
warn(message: string, args?: unknown): void;
|
|
283
|
+
addListener(listener: LogListener): void;
|
|
284
|
+
}
|
|
285
|
+
type TriggerKind = "api" | "cron" | "event" | "agent";
|
|
286
|
+
interface BaseTrigger<TKind extends TriggerKind = TriggerKind> {
|
|
287
|
+
kind: TKind;
|
|
288
|
+
name: string;
|
|
289
|
+
/**
|
|
290
|
+
* Transform source data into action input
|
|
291
|
+
*/
|
|
292
|
+
map?: (source: never) => unknown | Promise<unknown>;
|
|
293
|
+
/**
|
|
294
|
+
* Resolve actor from source data or provide static actor
|
|
295
|
+
*/
|
|
296
|
+
actor?: GravityActor | ((source: never) => GravityActor | Promise<GravityActor>);
|
|
297
|
+
}
|
|
298
|
+
interface ApiTrigger extends BaseTrigger<"api"> {
|
|
299
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
300
|
+
path: string;
|
|
301
|
+
/**
|
|
302
|
+
* Map HTTP request to action input
|
|
303
|
+
* Default: body for POST/PUT/PATCH, query for GET/DELETE
|
|
304
|
+
*/
|
|
305
|
+
map?: (req: ApiRequest) => unknown | Promise<unknown>;
|
|
306
|
+
/**
|
|
307
|
+
* Extract actor from request (session, JWT, API key, etc.)
|
|
308
|
+
*/
|
|
309
|
+
actor?: GravityActor | ((req: ApiRequest) => GravityActor | Promise<GravityActor>);
|
|
310
|
+
}
|
|
311
|
+
interface ApiRequest {
|
|
312
|
+
method: string;
|
|
313
|
+
path: string;
|
|
314
|
+
params: Record<string, string | undefined>;
|
|
315
|
+
query: Record<string, string>;
|
|
316
|
+
body: unknown;
|
|
317
|
+
headers: Record<string, string>;
|
|
318
|
+
raw?: unknown;
|
|
319
|
+
}
|
|
320
|
+
interface CronTrigger extends BaseTrigger<"cron"> {
|
|
321
|
+
/**
|
|
322
|
+
* Cron expression (e.g., '0 * * * *')
|
|
323
|
+
*/
|
|
324
|
+
schedule: string;
|
|
325
|
+
/**
|
|
326
|
+
* Timezone (default: UTC)
|
|
327
|
+
*/
|
|
328
|
+
timezone?: string;
|
|
329
|
+
/**
|
|
330
|
+
* Provide static input for cron jobs
|
|
331
|
+
*/
|
|
332
|
+
map?: () => unknown | Promise<unknown>;
|
|
333
|
+
}
|
|
334
|
+
interface EventTrigger extends BaseTrigger<"event"> {
|
|
335
|
+
/**
|
|
336
|
+
* Event pattern to match (e.g., 'order.created', 'user.*')
|
|
337
|
+
*/
|
|
338
|
+
event: string;
|
|
339
|
+
/**
|
|
340
|
+
* Map event payload to action input
|
|
341
|
+
*/
|
|
342
|
+
map?: (event: GravityEvent) => unknown | Promise<unknown>;
|
|
343
|
+
/**
|
|
344
|
+
* Extract actor from event metadata
|
|
345
|
+
*/
|
|
346
|
+
actor?: GravityActor | ((event: GravityEvent) => GravityActor | Promise<GravityActor>);
|
|
347
|
+
}
|
|
348
|
+
interface GravityEvent<T = unknown> {
|
|
349
|
+
id: string;
|
|
350
|
+
type: string;
|
|
351
|
+
data: T;
|
|
352
|
+
timestamp: Date;
|
|
353
|
+
actor?: GravityActor;
|
|
354
|
+
metadata?: Record<string, unknown>;
|
|
355
|
+
}
|
|
356
|
+
interface AgentTrigger extends BaseTrigger<"agent"> {
|
|
357
|
+
/**
|
|
358
|
+
* MCP tool name
|
|
359
|
+
*/
|
|
360
|
+
tool: string;
|
|
361
|
+
/**
|
|
362
|
+
* Tool description for LLM
|
|
363
|
+
*/
|
|
364
|
+
description: string;
|
|
365
|
+
/**
|
|
366
|
+
* Map MCP call arguments to action input
|
|
367
|
+
*/
|
|
368
|
+
map?: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
369
|
+
/**
|
|
370
|
+
* Extract actor from MCP context
|
|
371
|
+
*/
|
|
372
|
+
actor?: GravityActor | ((context: McpContext) => GravityActor | Promise<GravityActor>);
|
|
373
|
+
}
|
|
374
|
+
interface McpContext {
|
|
375
|
+
toolName: string;
|
|
376
|
+
args: Record<string, unknown>;
|
|
377
|
+
sessionId?: string;
|
|
378
|
+
metadata?: Record<string, unknown>;
|
|
379
|
+
}
|
|
380
|
+
type Trigger = ApiTrigger | CronTrigger | EventTrigger | AgentTrigger;
|
|
381
|
+
type ApiTriggerOptions = {
|
|
382
|
+
map?: (req: ApiRequest) => unknown | Promise<unknown>;
|
|
383
|
+
actor?: GravityActor | ((req: ApiRequest) => GravityActor | Promise<GravityActor>);
|
|
384
|
+
};
|
|
385
|
+
type ApiTriggerBuilder = {
|
|
386
|
+
(method: ApiTrigger["method"], path: string, options?: ApiTriggerOptions): ApiTrigger;
|
|
387
|
+
get: (path: string, options?: Omit<ApiTriggerOptions, never>) => ApiTrigger;
|
|
388
|
+
post: (path: string, options?: Omit<ApiTriggerOptions, never>) => ApiTrigger;
|
|
389
|
+
put: (path: string, options?: Omit<ApiTriggerOptions, never>) => ApiTrigger;
|
|
390
|
+
patch: (path: string, options?: Omit<ApiTriggerOptions, never>) => ApiTrigger;
|
|
391
|
+
delete: (path: string, options?: Omit<ApiTriggerOptions, never>) => ApiTrigger;
|
|
392
|
+
};
|
|
393
|
+
declare const api: ApiTriggerBuilder;
|
|
394
|
+
declare function cron(schedule: string, options?: {
|
|
395
|
+
timezone?: string;
|
|
396
|
+
map?: () => unknown | Promise<unknown>;
|
|
397
|
+
actor?: GravityActor;
|
|
398
|
+
}): CronTrigger;
|
|
399
|
+
declare function event(eventPattern: string, options?: {
|
|
400
|
+
map?: (event: GravityEvent) => unknown | Promise<unknown>;
|
|
401
|
+
actor?: GravityActor | ((event: GravityEvent) => GravityActor | Promise<GravityActor>);
|
|
402
|
+
}): EventTrigger;
|
|
403
|
+
declare function agent(tool: string, description: string, options?: {
|
|
404
|
+
map?: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
405
|
+
actor?: GravityActor | ((context: McpContext) => GravityActor | Promise<GravityActor>);
|
|
406
|
+
}): AgentTrigger;
|
|
407
|
+
/**
|
|
408
|
+
* The Standard Identity Structure for Basalt
|
|
409
|
+
*/
|
|
410
|
+
interface GravityActor {
|
|
411
|
+
type: "user" | "agent" | "system" | "public";
|
|
412
|
+
id?: string;
|
|
413
|
+
name?: string;
|
|
414
|
+
roles?: string[];
|
|
415
|
+
metadata?: Record<string, unknown>;
|
|
416
|
+
}
|
|
417
|
+
type Actor = GravityActor;
|
|
418
|
+
/**
|
|
419
|
+
* The Unified Trigger Contract
|
|
420
|
+
*/
|
|
421
|
+
/**
|
|
422
|
+
* Auth context resolved by the auth provider.
|
|
423
|
+
* Augmented via .gravity/types.d.ts module augmentation.
|
|
424
|
+
*/
|
|
425
|
+
type GravityAuthContext = {};
|
|
426
|
+
interface Organization {
|
|
427
|
+
id: string;
|
|
428
|
+
plan?: string;
|
|
429
|
+
[key: string]: any;
|
|
430
|
+
}
|
|
431
|
+
interface JobOptions {
|
|
432
|
+
delay?: string | number;
|
|
433
|
+
}
|
|
434
|
+
type BaseActionContext = {
|
|
435
|
+
db: any;
|
|
436
|
+
storage: any;
|
|
437
|
+
enqueue: (event: string, input: any) => void | Promise<void>;
|
|
438
|
+
schedule: <
|
|
439
|
+
TInput extends TSchema,
|
|
440
|
+
TOutput extends TSchema
|
|
441
|
+
>(when: number | Date | string, action: Action<TInput, TOutput>, input: Static<TInput>) => void | Promise<void>;
|
|
442
|
+
logger: Logger;
|
|
443
|
+
};
|
|
444
|
+
type ExtendActionContext = {};
|
|
445
|
+
type ActionContext = {
|
|
446
|
+
actor: GravityActor;
|
|
447
|
+
org?: Organization;
|
|
448
|
+
triggerName: string;
|
|
449
|
+
triggerType: TriggerKind;
|
|
450
|
+
traceId: string;
|
|
451
|
+
} & BaseActionContext & ExtendActionContext;
|
|
452
|
+
interface RateLimitRule {
|
|
453
|
+
/** How many requests */
|
|
454
|
+
limit: number;
|
|
455
|
+
/** Time window, e.g., "1s", "1m", "1h" */
|
|
456
|
+
window: string;
|
|
457
|
+
/** What to group by: 'actor' (user), 'org' (tenant), or 'ip' (guest) */
|
|
458
|
+
by: "actor" | "org" | "ip";
|
|
459
|
+
/** Optional: custom identifier, e.g., 'ai-budget' */
|
|
460
|
+
name?: string;
|
|
461
|
+
}
|
|
462
|
+
type RateLimitConfig = RateLimitRule | RateLimitRule[];
|
|
463
|
+
type ActionRateLimit = RateLimitConfig | {
|
|
464
|
+
common?: RateLimitConfig;
|
|
465
|
+
api?: RateLimitConfig;
|
|
466
|
+
agent?: RateLimitConfig;
|
|
467
|
+
[key: string]: RateLimitConfig | undefined;
|
|
468
|
+
};
|
|
469
|
+
interface AccessConfig {
|
|
470
|
+
common?: {
|
|
471
|
+
authenticated?: boolean;
|
|
472
|
+
};
|
|
473
|
+
api?: {
|
|
474
|
+
roles?: string[];
|
|
475
|
+
};
|
|
476
|
+
agent?: {
|
|
477
|
+
authenticated?: boolean;
|
|
478
|
+
};
|
|
479
|
+
authenticated?: boolean;
|
|
480
|
+
roles?: string[];
|
|
481
|
+
}
|
|
482
|
+
type GuardFunction<TInput> = (input: TInput, ctx: ActionContext) => boolean | Promise<boolean>;
|
|
483
|
+
interface GuardConfig<TInput> {
|
|
484
|
+
api?: GuardFunction<TInput>;
|
|
485
|
+
event?: GuardFunction<TInput>;
|
|
486
|
+
cron?: GuardFunction<TInput>;
|
|
487
|
+
agent?: GuardFunction<TInput>;
|
|
488
|
+
[key: string]: GuardFunction<TInput> | undefined;
|
|
489
|
+
}
|
|
490
|
+
interface ActionConfig<
|
|
491
|
+
TInput extends TSchema = TSchema,
|
|
492
|
+
TOutput extends TSchema = TSchema
|
|
493
|
+
> {
|
|
494
|
+
name: string;
|
|
495
|
+
description?: string;
|
|
496
|
+
input?: TInput;
|
|
497
|
+
output?: TOutput;
|
|
498
|
+
triggers?: Trigger[];
|
|
499
|
+
access?: AccessConfig;
|
|
500
|
+
guard?: GuardConfig<Static<TInput>>;
|
|
501
|
+
rateLimit?: ActionRateLimit;
|
|
502
|
+
}
|
|
503
|
+
type ActionHandler<
|
|
504
|
+
TInput extends TSchema,
|
|
505
|
+
TOutput extends TSchema
|
|
506
|
+
> = (input: Static<TInput>, ctx: ActionContext) => Promise<Static<TOutput>>;
|
|
507
|
+
interface Action<
|
|
508
|
+
TInput extends TSchema = TSchema,
|
|
509
|
+
TOutput extends TSchema = TSchema
|
|
510
|
+
> {
|
|
511
|
+
config: ActionConfig<TInput, TOutput>;
|
|
512
|
+
handler: ActionHandler<TInput, TOutput>;
|
|
513
|
+
(input: Static<TInput>, ctx: ActionContext): Promise<Static<TOutput>>;
|
|
514
|
+
}
|
|
515
|
+
declare function action<
|
|
516
|
+
TInput extends TSchema2 = TSchema2,
|
|
517
|
+
TOutput extends TSchema2 = TSchema2
|
|
518
|
+
>(config: ActionConfig<TInput, TOutput>, handler: ActionHandler<TInput, TOutput>): Action<TInput, TOutput>;
|
|
519
|
+
declare class GravityError extends Error {
|
|
520
|
+
code: string;
|
|
521
|
+
status: number;
|
|
522
|
+
constructor(message: string, code: string, status: number);
|
|
523
|
+
}
|
|
524
|
+
declare class NotFoundError extends GravityError {
|
|
525
|
+
constructor(message?: string);
|
|
526
|
+
}
|
|
527
|
+
declare class UnauthorizedError extends GravityError {
|
|
528
|
+
constructor(message?: string);
|
|
529
|
+
}
|
|
530
|
+
declare class ForbiddenError extends GravityError {
|
|
531
|
+
constructor(message?: string);
|
|
532
|
+
}
|
|
533
|
+
declare class BadRequestError extends GravityError {
|
|
534
|
+
constructor(message?: string);
|
|
535
|
+
}
|
|
536
|
+
declare class InternalServerError extends GravityError {
|
|
537
|
+
constructor(message?: string);
|
|
538
|
+
}
|
|
539
|
+
declare class NonRetriableError extends GravityError {
|
|
540
|
+
constructor(message: string);
|
|
541
|
+
}
|
|
542
|
+
import { Type } from "typebox";
|
|
543
|
+
export { Type as t, loadConfig, event, defineConfig, cron, api, agent, action, UnauthorizedError, TriggerKind, Trigger, RateLimitRule, RateLimitConfig, Organization, NotFoundError, NonRetriableError, McpContext, JobOptions, InternalServerError, GuardFunction, GuardConfig, GravityEvent, GravityError, GravityAuthContext, GravityActor, ForbiddenError, ExtendActionContext, EventTrigger, CronTrigger, BaseTrigger, BaseActionContext, BadRequestError, ApiTrigger, ApiRequest, AgentTrigger, Actor, ActionRateLimit, ActionHandler, ActionContext, ActionConfig, Action, AccessConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BadRequestError,
|
|
3
|
+
ForbiddenError,
|
|
4
|
+
GravityError,
|
|
5
|
+
InternalServerError,
|
|
6
|
+
NonRetriableError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
UnauthorizedError,
|
|
9
|
+
defineConfig,
|
|
10
|
+
loadConfig
|
|
11
|
+
} from "./shared/chunk-k195ahh5.js";
|
|
12
|
+
// src/core/action.ts
|
|
13
|
+
import { Value } from "typebox/value";
|
|
14
|
+
function resolveRateLimitRules(config, triggerType) {
|
|
15
|
+
if (!config)
|
|
16
|
+
return [];
|
|
17
|
+
let rules;
|
|
18
|
+
if ("limit" in config || Array.isArray(config)) {
|
|
19
|
+
rules = config;
|
|
20
|
+
} else {
|
|
21
|
+
const structured = config;
|
|
22
|
+
const specific = structured[triggerType];
|
|
23
|
+
const common = structured.common;
|
|
24
|
+
if (specific && common) {
|
|
25
|
+
const sRules = Array.isArray(specific) ? specific : [specific];
|
|
26
|
+
const cRules = Array.isArray(common) ? common : [common];
|
|
27
|
+
return [...cRules, ...sRules];
|
|
28
|
+
}
|
|
29
|
+
rules = specific || common;
|
|
30
|
+
}
|
|
31
|
+
if (!rules)
|
|
32
|
+
return [];
|
|
33
|
+
return Array.isArray(rules) ? rules : [rules];
|
|
34
|
+
}
|
|
35
|
+
async function checkRateLimit(ctx, config) {
|
|
36
|
+
if (!config.rateLimit)
|
|
37
|
+
return;
|
|
38
|
+
const rules = resolveRateLimitRules(config.rateLimit, ctx.triggerType);
|
|
39
|
+
if (rules.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
for (const rule of rules) {}
|
|
42
|
+
}
|
|
43
|
+
async function checkAccess(ctx, access) {
|
|
44
|
+
if (ctx.actor.type === "system")
|
|
45
|
+
return;
|
|
46
|
+
let config = { ...access.common };
|
|
47
|
+
if (ctx.triggerType === "api" && access.api) {
|
|
48
|
+
config = { ...config, ...access.api };
|
|
49
|
+
} else if (ctx.triggerType === "agent" && access.agent) {
|
|
50
|
+
config = { ...config, ...access.agent };
|
|
51
|
+
}
|
|
52
|
+
if (Object.keys(config).length === 0 && (access.authenticated !== undefined || access.roles)) {
|
|
53
|
+
config = {
|
|
54
|
+
authenticated: access.authenticated,
|
|
55
|
+
roles: access.roles
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (config.authenticated) {
|
|
59
|
+
if (ctx.actor.type === "public") {
|
|
60
|
+
throw new UnauthorizedError("Authentication required");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (config.roles && config.roles.length > 0) {
|
|
64
|
+
if (ctx.actor.type === "public") {
|
|
65
|
+
throw new UnauthorizedError("Authentication required");
|
|
66
|
+
}
|
|
67
|
+
const userRoles = ctx.actor.roles || [];
|
|
68
|
+
const hasRole = config.roles.some((role) => userRoles.includes(role));
|
|
69
|
+
if (!hasRole) {
|
|
70
|
+
throw new ForbiddenError(`User lacks required role(s): ${config.roles.join(", ")}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function checkGuard(ctx, input, config) {
|
|
75
|
+
if (!config.guard)
|
|
76
|
+
return;
|
|
77
|
+
const guardFn = config.guard[ctx.triggerName] || config.guard[ctx.triggerType];
|
|
78
|
+
if (guardFn) {
|
|
79
|
+
const allowed = await guardFn(input, ctx);
|
|
80
|
+
if (!allowed) {
|
|
81
|
+
throw new ForbiddenError("Guard check failed");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function action(config, handler) {
|
|
86
|
+
const wrapped = async (input, ctx) => {
|
|
87
|
+
try {
|
|
88
|
+
if (config.input && !Value.Check(config.input, input)) {
|
|
89
|
+
const errors = [...Value.Errors(config.input, input)];
|
|
90
|
+
throw new BadRequestError(`Validation Error: ${JSON.stringify(errors)}`);
|
|
91
|
+
}
|
|
92
|
+
await checkRateLimit(ctx, config);
|
|
93
|
+
if (config.access) {
|
|
94
|
+
await checkAccess(ctx, config.access);
|
|
95
|
+
}
|
|
96
|
+
await checkGuard(ctx, input, config);
|
|
97
|
+
return await handler(input, ctx);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
wrapped.config = config;
|
|
103
|
+
wrapped.handler = handler;
|
|
104
|
+
return wrapped;
|
|
105
|
+
}
|
|
106
|
+
// src/triggers/index.ts
|
|
107
|
+
var api = (method, path, options) => {
|
|
108
|
+
return {
|
|
109
|
+
kind: "api",
|
|
110
|
+
name: `api:${method}:${path}`,
|
|
111
|
+
method,
|
|
112
|
+
path,
|
|
113
|
+
...options
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
api.get = (path, options) => api("GET", path, options);
|
|
117
|
+
api.post = (path, options) => api("POST", path, options);
|
|
118
|
+
api.put = (path, options) => api("PUT", path, options);
|
|
119
|
+
api.patch = (path, options) => api("PATCH", path, options);
|
|
120
|
+
api.delete = (path, options) => api("DELETE", path, options);
|
|
121
|
+
function cron(schedule, options) {
|
|
122
|
+
return {
|
|
123
|
+
kind: "cron",
|
|
124
|
+
name: `cron:${schedule}`,
|
|
125
|
+
schedule,
|
|
126
|
+
...options
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function event(eventPattern, options) {
|
|
130
|
+
return {
|
|
131
|
+
kind: "event",
|
|
132
|
+
name: `event:${eventPattern}`,
|
|
133
|
+
event: eventPattern,
|
|
134
|
+
...options
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function agent(tool, description, options) {
|
|
138
|
+
return {
|
|
139
|
+
kind: "agent",
|
|
140
|
+
name: `agent:${tool}`,
|
|
141
|
+
tool,
|
|
142
|
+
description,
|
|
143
|
+
...options
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// src/utils/typebox.ts
|
|
147
|
+
import { Type } from "typebox";
|
|
148
|
+
export {
|
|
149
|
+
Type as t,
|
|
150
|
+
loadConfig,
|
|
151
|
+
event,
|
|
152
|
+
defineConfig,
|
|
153
|
+
cron,
|
|
154
|
+
api,
|
|
155
|
+
agent,
|
|
156
|
+
action,
|
|
157
|
+
UnauthorizedError,
|
|
158
|
+
NotFoundError,
|
|
159
|
+
NonRetriableError,
|
|
160
|
+
InternalServerError,
|
|
161
|
+
GravityError,
|
|
162
|
+
ForbiddenError,
|
|
163
|
+
BadRequestError
|
|
164
|
+
};
|