@graphorin/server 0.6.1 → 0.7.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/CHANGELOG.md +71 -0
- package/README.md +3 -3
- package/dist/app-audit-binding.d.ts +15 -0
- package/dist/app-audit-binding.d.ts.map +1 -0
- package/dist/app-audit-binding.js +136 -0
- package/dist/app-audit-binding.js.map +1 -0
- package/dist/app-daemons.d.ts +29 -0
- package/dist/app-daemons.d.ts.map +1 -0
- package/dist/app-daemons.js +27 -0
- package/dist/app-daemons.js.map +1 -0
- package/dist/app-lifecycle.js +272 -0
- package/dist/app-lifecycle.js.map +1 -0
- package/dist/app-metrics.js +79 -0
- package/dist/app-metrics.js.map +1 -0
- package/dist/app-middleware.js +92 -0
- package/dist/app-middleware.js.map +1 -0
- package/dist/app-routes.js +131 -0
- package/dist/app-routes.js.map +1 -0
- package/dist/app-ws.js +65 -0
- package/dist/app-ws.js.map +1 -0
- package/dist/app.d.ts +16 -16
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +35 -607
- package/dist/app.js.map +1 -1
- package/dist/commentary/built-in-patterns.d.ts +1 -1
- package/dist/commentary/built-in-patterns.js +1 -1
- package/dist/commentary/built-in-patterns.js.map +1 -1
- package/dist/config.d.ts +100 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -2
- package/dist/config.js.map +1 -1
- package/dist/health/checks.d.ts +15 -1
- package/dist/health/checks.d.ts.map +1 -1
- package/dist/health/checks.js +21 -0
- package/dist/health/checks.js.map +1 -1
- package/dist/health/routes.js +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -24
- package/dist/index.js.map +1 -1
- package/dist/internal/context.d.ts +2 -2
- package/dist/internal/wire-error.js +20 -0
- package/dist/internal/wire-error.js.map +1 -0
- package/dist/lifecycle/pre-bind.d.ts +1 -1
- package/dist/metrics/catalog.d.ts.map +1 -1
- package/dist/metrics/catalog.js.map +1 -1
- package/dist/metrics/tools-bridge.d.ts +15 -0
- package/dist/metrics/tools-bridge.d.ts.map +1 -0
- package/dist/metrics/tools-bridge.js +103 -0
- package/dist/metrics/tools-bridge.js.map +1 -0
- package/dist/middleware/audit.d.ts +1 -1
- package/dist/middleware/auth.js +1 -1
- package/dist/middleware/csrf.js +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/scope.d.ts.map +1 -1
- package/dist/middleware/scope.js +44 -4
- package/dist/middleware/scope.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/dist/registry/index.d.ts +33 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js.map +1 -1
- package/dist/replay/routes.d.ts +1 -1
- package/dist/replay/routes.js +2 -2
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +64 -18
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/auth.js +2 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/sessions.js +5 -5
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/tokens.d.ts +1 -1
- package/dist/routes/tokens.d.ts.map +1 -1
- package/dist/routes/tokens.js +21 -1
- package/dist/routes/tokens.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +165 -19
- package/dist/routes/workflows.js.map +1 -1
- package/dist/runtime/retention.d.ts +105 -0
- package/dist/runtime/retention.d.ts.map +1 -0
- package/dist/runtime/retention.js +154 -0
- package/dist/runtime/retention.js.map +1 -0
- package/dist/runtime/run-state.d.ts +2 -0
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +34 -2
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/sse/events.js +3 -4
- package/dist/sse/events.js.map +1 -1
- package/dist/tools-audit-bridge.d.ts +29 -0
- package/dist/tools-audit-bridge.d.ts.map +1 -0
- package/dist/tools-audit-bridge.js +103 -0
- package/dist/tools-audit-bridge.js.map +1 -0
- package/dist/workflows/timer-daemon.d.ts +69 -0
- package/dist/workflows/timer-daemon.d.ts.map +1 -0
- package/dist/workflows/timer-daemon.js +37 -0
- package/dist/workflows/timer-daemon.js.map +1 -0
- package/dist/ws/dispatcher.d.ts +1 -1
- package/dist/ws/dispatcher.js +19 -12
- package/dist/ws/dispatcher.js.map +1 -1
- package/dist/ws/index.d.ts +2 -2
- package/dist/ws/index.js +3 -3
- package/dist/ws/replay-buffer.d.ts +35 -1
- package/dist/ws/replay-buffer.d.ts.map +1 -1
- package/dist/ws/replay-buffer.js +35 -3
- package/dist/ws/replay-buffer.js.map +1 -1
- package/dist/ws/upgrade.d.ts.map +1 -1
- package/dist/ws/upgrade.js +24 -18
- package/dist/ws/upgrade.js.map +1 -1
- package/package.json +30 -29
- package/src/app-audit-binding.ts +227 -0
- package/src/app-daemons.ts +73 -0
- package/src/app-lifecycle.ts +476 -0
- package/src/app-metrics.ts +144 -0
- package/src/app-middleware.ts +149 -0
- package/src/app-routes.ts +305 -0
- package/src/app-ws.ts +111 -0
- package/src/app.ts +317 -0
- package/src/commentary/audit-bridge.ts +135 -0
- package/src/commentary/built-in-patterns.ts +79 -0
- package/src/commentary/index.ts +32 -0
- package/src/commentary/sanitizer.ts +238 -0
- package/src/commentary/types.ts +130 -0
- package/src/config.ts +472 -0
- package/src/consolidator/daemon.ts +141 -0
- package/src/consolidator/index.ts +14 -0
- package/src/errors/index.ts +247 -0
- package/src/health/checks.ts +322 -0
- package/src/health/index.ts +34 -0
- package/src/health/routes.ts +154 -0
- package/src/index.ts +243 -0
- package/src/internal/context.ts +77 -0
- package/src/internal/ids.ts +17 -0
- package/src/internal/json.ts +47 -0
- package/src/internal/wire-error.ts +44 -0
- package/src/lifecycle/hooks.ts +66 -0
- package/src/lifecycle/index.ts +16 -0
- package/src/lifecycle/pre-bind.ts +138 -0
- package/src/metrics/catalog.ts +112 -0
- package/src/metrics/index.ts +12 -0
- package/src/metrics/registry.ts +337 -0
- package/src/metrics/tools-bridge.ts +124 -0
- package/src/middleware/audit.ts +114 -0
- package/src/middleware/auth.ts +195 -0
- package/src/middleware/cors.ts +72 -0
- package/src/middleware/csrf.ts +98 -0
- package/src/middleware/idempotency.ts +336 -0
- package/src/middleware/index.ts +38 -0
- package/src/middleware/rate-limit.ts +71 -0
- package/src/middleware/request-state.ts +79 -0
- package/src/middleware/scope.ts +161 -0
- package/src/registry/index.ts +278 -0
- package/src/replay/index.ts +14 -0
- package/src/replay/routes.ts +255 -0
- package/src/routes/agents.ts +363 -0
- package/src/routes/audit.ts +207 -0
- package/src/routes/auth.ts +80 -0
- package/src/routes/health.ts +42 -0
- package/src/routes/index.ts +16 -0
- package/src/routes/mcp.ts +97 -0
- package/src/routes/memory.ts +152 -0
- package/src/routes/sessions.ts +250 -0
- package/src/routes/skills.ts +91 -0
- package/src/routes/tokens.ts +166 -0
- package/src/routes/workflows.ts +616 -0
- package/src/runtime/retention.ts +284 -0
- package/src/runtime/run-state.ts +422 -0
- package/src/sse/events.ts +303 -0
- package/src/sse/index.ts +7 -0
- package/src/tools-audit-bridge.ts +120 -0
- package/src/triggers/daemon.ts +198 -0
- package/src/triggers/index.ts +16 -0
- package/src/triggers/routes.ts +139 -0
- package/src/workflows/timer-daemon.ts +102 -0
- package/src/ws/dispatcher.ts +537 -0
- package/src/ws/index.ts +45 -0
- package/src/ws/replay-buffer.ts +209 -0
- package/src/ws/subjects.ts +162 -0
- package/src/ws/ticket.ts +174 -0
- package/src/ws/upgrade.ts +507 -0
- package/dist/lifecycle/index.js +0 -3
- package/dist/routes/index.js +0 -12
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST routes for the `/v1/triggers` family. Mounted by the server
|
|
3
|
+
* factory when a {@link TriggersDaemon} is wired in.
|
|
4
|
+
*
|
|
5
|
+
* GET /triggers (scope `triggers:read`)
|
|
6
|
+
* GET /triggers/:id (scope `triggers:read`)
|
|
7
|
+
* POST /triggers/:id/fire (scope `triggers:fire`)
|
|
8
|
+
* POST /triggers/:id/disable (scope `triggers:disable`) - flag flip
|
|
9
|
+
* POST /triggers/:id/enable (scope `triggers:disable`) - flag flip
|
|
10
|
+
* DELETE /triggers/:id (scope `triggers:disable`) - unregister
|
|
11
|
+
* POST /triggers/prune (scope `triggers:disable`)
|
|
12
|
+
*
|
|
13
|
+
* Response shapes mirror the persisted `TriggerState` rows so CLI +
|
|
14
|
+
* dashboard consumers can render them without re-mapping.
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { TriggerState } from '@graphorin/core/contracts';
|
|
20
|
+
import type { Scheduler } from '@graphorin/triggers';
|
|
21
|
+
import { Hono } from 'hono';
|
|
22
|
+
|
|
23
|
+
import type { ServerVariables } from '../internal/context.js';
|
|
24
|
+
import { createScopeMiddleware } from '../middleware/scope.js';
|
|
25
|
+
import type { TriggersDaemon } from './daemon.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @stable
|
|
29
|
+
*/
|
|
30
|
+
export interface TriggersRoutesDeps {
|
|
31
|
+
readonly daemon: TriggersDaemon;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @stable
|
|
36
|
+
*/
|
|
37
|
+
export function createTriggersRoutes(
|
|
38
|
+
deps: TriggersRoutesDeps,
|
|
39
|
+
): Hono<{ Variables: ServerVariables }> {
|
|
40
|
+
const app = new Hono<{ Variables: ServerVariables }>();
|
|
41
|
+
const scheduler: Scheduler = deps.daemon.scheduler;
|
|
42
|
+
|
|
43
|
+
app.get('/', createScopeMiddleware('triggers:read'), async (c) => {
|
|
44
|
+
const all = await scheduler.list();
|
|
45
|
+
return c.json({ triggers: all.map(serializeTrigger) });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
app.get('/:id', createScopeMiddleware('triggers:read'), async (c) => {
|
|
49
|
+
const id = c.req.param('id');
|
|
50
|
+
const all = await scheduler.list();
|
|
51
|
+
const found = all.find((t) => t.id === id);
|
|
52
|
+
if (found === undefined) {
|
|
53
|
+
return c.json({ error: 'not-found', message: `Trigger '${id}' not found.` }, 404);
|
|
54
|
+
}
|
|
55
|
+
return c.json({ trigger: serializeTrigger(found) });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
app.post('/:id/fire', createScopeMiddleware('triggers:fire'), async (c) => {
|
|
59
|
+
const id = c.req.param('id');
|
|
60
|
+
try {
|
|
61
|
+
await scheduler.fire(id);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return c.json(
|
|
64
|
+
{
|
|
65
|
+
error: 'not-found',
|
|
66
|
+
message: err instanceof Error ? err.message : `Trigger '${id}' not found.`,
|
|
67
|
+
},
|
|
68
|
+
404,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return c.json({ ok: true, fired: id });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// IP-17: disable/enable are non-destructive flag flips on the
|
|
75
|
+
// persistent TriggerState (in agreement with the CLI); the
|
|
76
|
+
// destructive removal is DELETE /:id.
|
|
77
|
+
app.post('/:id/disable', createScopeMiddleware('triggers:disable'), async (c) => {
|
|
78
|
+
const id = c.req.param('id');
|
|
79
|
+
try {
|
|
80
|
+
const updated = await scheduler.setDisabled(id, true);
|
|
81
|
+
return c.json({ ok: true, trigger: serializeTrigger(updated) });
|
|
82
|
+
} catch {
|
|
83
|
+
return c.json({ error: 'not-found', message: `Trigger '${id}' not found.` }, 404);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
app.post('/:id/enable', createScopeMiddleware('triggers:disable'), async (c) => {
|
|
88
|
+
const id = c.req.param('id');
|
|
89
|
+
try {
|
|
90
|
+
const updated = await scheduler.setDisabled(id, false);
|
|
91
|
+
return c.json({ ok: true, trigger: serializeTrigger(updated) });
|
|
92
|
+
} catch {
|
|
93
|
+
return c.json({ error: 'not-found', message: `Trigger '${id}' not found.` }, 404);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
app.delete('/:id', createScopeMiddleware('triggers:disable'), async (c) => {
|
|
98
|
+
const id = c.req.param('id');
|
|
99
|
+
const all = await scheduler.list();
|
|
100
|
+
if (!all.some((t) => t.id === id)) {
|
|
101
|
+
return c.json({ error: 'not-found', message: `Trigger '${id}' not found.` }, 404);
|
|
102
|
+
}
|
|
103
|
+
await scheduler.unregister(id);
|
|
104
|
+
return c.json({ ok: true, removed: id });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
app.post('/prune', createScopeMiddleware('triggers:disable'), async (c) => {
|
|
108
|
+
const all = await scheduler.list();
|
|
109
|
+
const removedIds: string[] = [];
|
|
110
|
+
for (const trigger of all) {
|
|
111
|
+
if (trigger.disabled) {
|
|
112
|
+
await scheduler.unregister(trigger.id);
|
|
113
|
+
removedIds.push(trigger.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return c.json({ ok: true, removed: removedIds, count: removedIds.length });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return app;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function serializeTrigger(state: TriggerState): Record<string, unknown> {
|
|
123
|
+
return {
|
|
124
|
+
id: state.id,
|
|
125
|
+
kind: state.kind,
|
|
126
|
+
spec: state.spec,
|
|
127
|
+
callbackRef: state.callbackRef,
|
|
128
|
+
...(state.nextFireAt !== undefined ? { nextFireAt: state.nextFireAt } : {}),
|
|
129
|
+
...(state.lastFiredAt !== undefined ? { lastFiredAt: state.lastFiredAt } : {}),
|
|
130
|
+
missedFires: state.missedFires,
|
|
131
|
+
disabled: state.disabled,
|
|
132
|
+
catchupPolicy: state.catchupPolicy,
|
|
133
|
+
maxCatchupRuns: state.maxCatchupRuns,
|
|
134
|
+
catchupWindowMs: state.catchupWindowMs,
|
|
135
|
+
...(state.tags !== undefined ? { tags: [...state.tags] } : {}),
|
|
136
|
+
createdAt: state.createdAt,
|
|
137
|
+
...(state.updatedAt !== undefined ? { updatedAt: state.updatedAt } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W-032: durable-timer driver integration with the server lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the triggers daemon: the operator builds a
|
|
5
|
+
* `createTimerDriver(...)` (from `@graphorin/workflow`) over their
|
|
6
|
+
* workflows + checkpoint stores and hands it to `createServer({
|
|
7
|
+
* workflowTimers: { driver } })`; the daemon binds `start()`/`stop()`
|
|
8
|
+
* to the lifecycle hooks and surfaces `status()` through `/v1/health`.
|
|
9
|
+
* The driver is typed STRUCTURALLY so `@graphorin/server` takes no
|
|
10
|
+
* dependency on `@graphorin/workflow`.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Structural slice of `@graphorin/workflow`'s `TimerDriver` the daemon
|
|
17
|
+
* consumes.
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
export interface WorkflowTimerDriverLike {
|
|
22
|
+
start(): void;
|
|
23
|
+
stop(): void;
|
|
24
|
+
status(): {
|
|
25
|
+
readonly running: boolean;
|
|
26
|
+
readonly sweeps: number;
|
|
27
|
+
readonly fired: number;
|
|
28
|
+
readonly errors: number;
|
|
29
|
+
readonly lastSweepAt?: number;
|
|
30
|
+
readonly nextWakeAt?: number;
|
|
31
|
+
};
|
|
32
|
+
sweep(): Promise<number>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Snapshot exposed via {@link WorkflowTimerDaemon.status} + the
|
|
37
|
+
* `/v1/health` aggregator.
|
|
38
|
+
*
|
|
39
|
+
* @stable
|
|
40
|
+
*/
|
|
41
|
+
export interface WorkflowTimerDaemonStatus {
|
|
42
|
+
readonly running: boolean;
|
|
43
|
+
readonly sweeps: number;
|
|
44
|
+
readonly fired: number;
|
|
45
|
+
readonly errors: number;
|
|
46
|
+
readonly lastSweepAt?: string;
|
|
47
|
+
readonly nextWakeAt?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @stable */
|
|
51
|
+
export interface CreateWorkflowTimerDaemonOptions {
|
|
52
|
+
readonly driver: WorkflowTimerDriverLike;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Stateful handle returned by {@link createWorkflowTimerDaemon}.
|
|
57
|
+
*
|
|
58
|
+
* @stable
|
|
59
|
+
*/
|
|
60
|
+
export interface WorkflowTimerDaemon {
|
|
61
|
+
start(): Promise<void>;
|
|
62
|
+
stop(): Promise<void>;
|
|
63
|
+
status(): Promise<WorkflowTimerDaemonStatus>;
|
|
64
|
+
readonly driver: WorkflowTimerDriverLike;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @stable
|
|
69
|
+
*/
|
|
70
|
+
export function createWorkflowTimerDaemon(
|
|
71
|
+
options: CreateWorkflowTimerDaemonOptions,
|
|
72
|
+
): WorkflowTimerDaemon {
|
|
73
|
+
let running = false;
|
|
74
|
+
return {
|
|
75
|
+
get driver() {
|
|
76
|
+
return options.driver;
|
|
77
|
+
},
|
|
78
|
+
async start() {
|
|
79
|
+
if (running) return;
|
|
80
|
+
running = true;
|
|
81
|
+
options.driver.start();
|
|
82
|
+
},
|
|
83
|
+
async stop() {
|
|
84
|
+
if (!running) return;
|
|
85
|
+
running = false;
|
|
86
|
+
options.driver.stop();
|
|
87
|
+
},
|
|
88
|
+
async status() {
|
|
89
|
+
const s = options.driver.status();
|
|
90
|
+
return Object.freeze({
|
|
91
|
+
running: s.running,
|
|
92
|
+
sweeps: s.sweeps,
|
|
93
|
+
fired: s.fired,
|
|
94
|
+
errors: s.errors,
|
|
95
|
+
...(s.lastSweepAt !== undefined
|
|
96
|
+
? { lastSweepAt: new Date(s.lastSweepAt).toISOString() }
|
|
97
|
+
: {}),
|
|
98
|
+
...(s.nextWakeAt !== undefined ? { nextWakeAt: new Date(s.nextWakeAt).toISOString() } : {}),
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|