@taskcast/server 0.3.0 → 0.3.1

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/index.d.ts CHANGED
@@ -2,20 +2,44 @@ export { createAuthMiddleware, checkScope } from './auth.js';
2
2
  export type { AuthConfig, AuthContext, JWTConfig } from './auth.js';
3
3
  export { createTasksRouter } from './routes/tasks.js';
4
4
  export { createSSERouter } from './routes/sse.js';
5
- export { createWorkersRouter, WorkerWSHandler } from './routes/workers.js';
5
+ export { createWorkersRouter, WorkerWSHandler, WorkerWSRegistry } from './routes/workers.js';
6
6
  export type { WSLike, TaskSummary } from './routes/workers.js';
7
7
  export { WebhookDelivery } from './webhook.js';
8
- import { Hono } from 'hono';
8
+ export { TaskSchema, TaskEventSchema, WorkerSchema, ErrorSchema, CreateTaskSchema, TransitionSchema, PublishEventSchema, } from './schemas.js';
9
+ import type { Hono } from 'hono';
10
+ import { WorkerWSRegistry } from './routes/worker-ws.js';
9
11
  import type { AuthConfig } from './auth.js';
10
- import type { TaskEngine, WorkerManager } from '@taskcast/core';
12
+ import type { TaskEngine, WorkerManager, ShortTermStore, DisconnectPolicy } from '@taskcast/core';
11
13
  export interface TaskcastServerOptions {
12
14
  engine: TaskEngine;
13
15
  workerManager?: WorkerManager;
16
+ shortTermStore?: ShortTermStore;
14
17
  auth?: AuthConfig;
18
+ scheduler?: {
19
+ enabled?: boolean;
20
+ checkIntervalMs?: number;
21
+ pausedColdAfterMs?: number;
22
+ blockedColdAfterMs?: number;
23
+ };
24
+ heartbeat?: {
25
+ enabled?: boolean;
26
+ checkIntervalMs?: number;
27
+ heartbeatTimeoutMs?: number;
28
+ defaultDisconnectPolicy?: DisconnectPolicy;
29
+ disconnectGraceMs?: number;
30
+ };
31
+ }
32
+ export interface TaskcastApp {
33
+ app: Hono;
34
+ wsRegistry?: WorkerWSRegistry;
35
+ stop(): void;
15
36
  }
16
37
  /**
17
- * Creates a Hono app with all taskcast routes mounted.
38
+ * Creates an OpenAPIHono app with all taskcast routes mounted.
18
39
  * Can be used standalone or mounted into an existing Hono app.
40
+ *
41
+ * Returns a TaskcastApp with `app` (the Hono instance) and `stop()` to
42
+ * clean up scheduler/heartbeat timers.
19
43
  */
20
- export declare function createTaskcastApp(opts: TaskcastServerOptions): Hono;
44
+ export declare function createTaskcastApp(opts: TaskcastServerOptions): TaskcastApp;
21
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1E,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAK3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE/D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAA;IAClB,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAUnE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC5F,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EACL,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EACtD,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,GACvD,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAOhC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,KAAK,EAEV,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AAIvB,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAA;IAClB,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAC5B,CAAA;IACD,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,kBAAkB,CAAC,EAAE,MAAM,CAAA;QAC3B,uBAAuB,CAAC,EAAE,gBAAgB,CAAA;QAC1C,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,IAAI,CAAA;IACT,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,IAAI,IAAI,IAAI,CAAA;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,WAAW,CA0H1E"}
package/dist/index.js CHANGED
@@ -1,26 +1,151 @@
1
1
  export { createAuthMiddleware, checkScope } from './auth.js';
2
2
  export { createTasksRouter } from './routes/tasks.js';
3
3
  export { createSSERouter } from './routes/sse.js';
4
- export { createWorkersRouter, WorkerWSHandler } from './routes/workers.js';
4
+ export { createWorkersRouter, WorkerWSHandler, WorkerWSRegistry } from './routes/workers.js';
5
5
  export { WebhookDelivery } from './webhook.js';
6
- import { Hono } from 'hono';
6
+ export { TaskSchema, TaskEventSchema, WorkerSchema, ErrorSchema, CreateTaskSchema, TransitionSchema, PublishEventSchema, } from './schemas.js';
7
+ import { OpenAPIHono } from '@hono/zod-openapi';
8
+ import { apiReference } from '@scalar/hono-api-reference';
7
9
  import { createAuthMiddleware } from './auth.js';
8
10
  import { createTasksRouter } from './routes/tasks.js';
9
11
  import { createSSERouter } from './routes/sse.js';
10
12
  import { createWorkersRouter } from './routes/workers.js';
13
+ import { WorkerWSRegistry } from './routes/worker-ws.js';
14
+ import { isTerminal, matchesWorkerRule } from '@taskcast/core';
15
+ import { TaskScheduler } from '@taskcast/core';
16
+ import { HeartbeatMonitor } from '@taskcast/core';
11
17
  /**
12
- * Creates a Hono app with all taskcast routes mounted.
18
+ * Creates an OpenAPIHono app with all taskcast routes mounted.
13
19
  * Can be used standalone or mounted into an existing Hono app.
20
+ *
21
+ * Returns a TaskcastApp with `app` (the Hono instance) and `stop()` to
22
+ * clean up scheduler/heartbeat timers.
14
23
  */
15
24
  export function createTaskcastApp(opts) {
16
- const app = new Hono();
25
+ const app = new OpenAPIHono();
17
26
  app.get('/health', (c) => c.json({ ok: true }));
18
27
  app.use('*', createAuthMiddleware(opts.auth ?? { mode: 'none' }));
19
28
  app.route('/tasks', createTasksRouter(opts.engine));
20
29
  app.route('/tasks', createSSERouter(opts.engine));
30
+ const cleanups = [];
31
+ // Wire scheduler
32
+ let scheduler;
33
+ if (opts.scheduler?.enabled !== false && opts.shortTermStore) {
34
+ const schedulerOpts = {
35
+ engine: opts.engine,
36
+ shortTermStore: opts.shortTermStore,
37
+ };
38
+ if (opts.scheduler?.checkIntervalMs !== undefined)
39
+ schedulerOpts.checkIntervalMs = opts.scheduler.checkIntervalMs;
40
+ if (opts.scheduler?.pausedColdAfterMs !== undefined)
41
+ schedulerOpts.pausedColdAfterMs = opts.scheduler.pausedColdAfterMs;
42
+ if (opts.scheduler?.blockedColdAfterMs !== undefined)
43
+ schedulerOpts.blockedColdAfterMs = opts.scheduler.blockedColdAfterMs;
44
+ scheduler = new TaskScheduler(schedulerOpts);
45
+ scheduler.start();
46
+ cleanups.push(() => scheduler.stop());
47
+ }
48
+ // Wire worker manager
49
+ let wsRegistry;
21
50
  if (opts.workerManager) {
51
+ const wm = opts.workerManager;
52
+ wsRegistry = new WorkerWSRegistry();
53
+ // Auto-release capacity on terminal transitions
54
+ opts.engine.addTransitionListener((_task, _from, to) => {
55
+ if (isTerminal(to)) {
56
+ wm.releaseTask(_task.id).catch(() => { });
57
+ }
58
+ });
59
+ // Wire ws-offer/ws-race dispatch on pending transitions
60
+ async function dispatchToWS(task) {
61
+ if (task.assignMode === 'ws-offer') {
62
+ const result = await wm.dispatchTask(task.id);
63
+ if (result.matched && result.workerId) {
64
+ const handler = wsRegistry.get(result.workerId);
65
+ if (handler)
66
+ handler.offerTask(task);
67
+ }
68
+ }
69
+ else if (task.assignMode === 'ws-race') {
70
+ const workers = await wm.listWorkers({ status: ['idle', 'busy'] });
71
+ for (const worker of workers) {
72
+ if (worker.connectionMode !== 'websocket')
73
+ continue;
74
+ if (!matchesWorkerRule(task, worker.matchRule))
75
+ continue;
76
+ const cost = task.cost ?? 1;
77
+ if (worker.usedSlots + cost > worker.capacity)
78
+ continue;
79
+ const handler = wsRegistry.get(worker.id);
80
+ if (handler)
81
+ handler.broadcastAvailable(task);
82
+ }
83
+ }
84
+ }
85
+ // Dispatch on initial task creation
86
+ opts.engine.addCreationListener((task) => {
87
+ if (!task.assignMode || (task.assignMode !== 'ws-offer' && task.assignMode !== 'ws-race'))
88
+ return;
89
+ dispatchToWS(task).catch(() => { });
90
+ });
91
+ // Re-dispatch when task transitions back to pending (e.g. after decline)
92
+ opts.engine.addTransitionListener((task, _from, to) => {
93
+ if (to !== 'pending')
94
+ return;
95
+ if (!task.assignMode || (task.assignMode !== 'ws-offer' && task.assignMode !== 'ws-race'))
96
+ return;
97
+ // Fire-and-forget async dispatch
98
+ dispatchToWS(task).catch(() => { });
99
+ });
22
100
  app.route('/workers', createWorkersRouter(opts.workerManager, opts.engine));
101
+ // Wire heartbeat monitor
102
+ if (opts.heartbeat?.enabled !== false && opts.shortTermStore) {
103
+ const monitorOpts = {
104
+ workerManager: wm,
105
+ engine: opts.engine,
106
+ shortTermStore: opts.shortTermStore,
107
+ };
108
+ if (opts.heartbeat?.checkIntervalMs !== undefined)
109
+ monitorOpts.checkIntervalMs = opts.heartbeat.checkIntervalMs;
110
+ if (opts.heartbeat?.heartbeatTimeoutMs !== undefined)
111
+ monitorOpts.heartbeatTimeoutMs = opts.heartbeat.heartbeatTimeoutMs;
112
+ if (opts.heartbeat?.defaultDisconnectPolicy !== undefined)
113
+ monitorOpts.defaultDisconnectPolicy = opts.heartbeat.defaultDisconnectPolicy;
114
+ if (opts.heartbeat?.disconnectGraceMs !== undefined)
115
+ monitorOpts.disconnectGraceMs = opts.heartbeat.disconnectGraceMs;
116
+ const monitor = new HeartbeatMonitor(monitorOpts);
117
+ monitor.start();
118
+ cleanups.push(() => monitor.stop());
119
+ }
23
120
  }
24
- return app;
121
+ // Register security scheme
122
+ app.openAPIRegistry.registerComponent('securitySchemes', 'Bearer', {
123
+ type: 'http',
124
+ scheme: 'bearer',
125
+ bearerFormat: 'JWT',
126
+ description: 'JWT Bearer token. Required scopes vary per endpoint.',
127
+ });
128
+ // OpenAPI spec endpoint
129
+ app.doc('/openapi.json', {
130
+ openapi: '3.1.0',
131
+ info: {
132
+ title: 'Taskcast API',
133
+ version: '0.3.0',
134
+ description: 'Unified long-lifecycle task tracking service for LLM streaming, agents, and async workloads.',
135
+ },
136
+ security: [{ Bearer: [] }],
137
+ });
138
+ // API documentation UI
139
+ app.get('/docs', apiReference({
140
+ url: '/openapi.json',
141
+ }));
142
+ return {
143
+ app: app,
144
+ ...(wsRegistry !== undefined && { wsRegistry }),
145
+ stop() {
146
+ for (const fn of cleanups)
147
+ fn();
148
+ },
149
+ };
25
150
  }
26
151
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAUzD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAA2B;IAC3D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC/C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IACjE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACjD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,mBAAmB,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Hono } from 'hono';
1
+ import type { Hono } from 'hono';
2
2
  import type { TaskEngine } from '@taskcast/core';
3
- export declare function createSSERouter(engine: TaskEngine): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export declare function createSSERouter(engine: TaskEngine): Hono;
4
4
  //# sourceMappingURL=sse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/routes/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,gBAAgB,CAAA;AAkDhG,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,8EAyEjD"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/routes/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAOhC,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,gBAAgB,CAAA;AAsFhG,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CA0ExD"}
@@ -1,7 +1,35 @@
1
- import { Hono } from 'hono';
1
+ import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
2
2
  import { streamSSE } from 'hono/streaming';
3
3
  import { applyFilteredIndex, matchesFilter, TERMINAL_STATUSES } from '@taskcast/core';
4
4
  import { checkScope } from '../auth.js';
5
+ import { ErrorSchema } from '../schemas.js';
6
+ // ─── Route Definition ──────────────────────────────────────────────────────
7
+ const sseRoute = createRoute({
8
+ method: 'get',
9
+ path: '/{taskId}/events',
10
+ tags: ['Events'],
11
+ summary: 'Subscribe to task events via SSE',
12
+ description: 'Server-Sent Events stream. Replays history then streams live. Closes on terminal status.',
13
+ security: [{ Bearer: [] }],
14
+ request: {
15
+ params: z.object({ taskId: z.string() }),
16
+ query: z.object({
17
+ types: z.string().optional().openapi({ description: 'Comma-separated type filter with wildcard support' }),
18
+ levels: z.string().optional().openapi({ description: 'Comma-separated level filter' }),
19
+ includeStatus: z.string().optional().openapi({ description: 'Include taskcast:status events (default: true)' }),
20
+ wrap: z.string().optional().openapi({ description: 'Wrap in SSEEnvelope (default: true)' }),
21
+ 'since.id': z.string().optional(),
22
+ 'since.index': z.string().optional(),
23
+ 'since.timestamp': z.string().optional(),
24
+ }),
25
+ },
26
+ responses: {
27
+ 200: { description: 'SSE event stream (text/event-stream)' },
28
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
29
+ 404: { description: 'Task not found', content: { 'application/json': { schema: ErrorSchema } } },
30
+ },
31
+ });
32
+ // ─── Helpers ───────────────────────────────────────────────────────────────
5
33
  function parseFilter(query) {
6
34
  const get = (k) => query[k];
7
35
  const filter = {};
@@ -47,13 +75,16 @@ function toEnvelope(event, filteredIndex) {
47
75
  env.seriesId = event.seriesId;
48
76
  if (event.seriesMode !== undefined)
49
77
  env.seriesMode = event.seriesMode;
78
+ if (event.seriesAccField !== undefined)
79
+ env.seriesAccField = event.seriesAccField;
50
80
  return env;
51
81
  }
52
82
  const TERMINAL = new Set(TERMINAL_STATUSES);
53
83
  export function createSSERouter(engine) {
54
- const router = new Hono();
55
- router.get('/:taskId/events', async (c) => {
56
- const { taskId } = c.req.param();
84
+ const router = new OpenAPIHono();
85
+ const register = router.openapi.bind(router);
86
+ register(sseRoute, async (c) => {
87
+ const taskId = c.req.param('taskId');
57
88
  const auth = c.get('auth');
58
89
  if (!checkScope(auth, 'event:subscribe', taskId))
59
90
  return c.json({ error: 'Forbidden' }, 403);
@@ -1 +1 @@
1
- {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/routes/sse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/routes/sse.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Hono } from 'hono';
1
+ import type { Hono } from 'hono';
2
2
  import type { TaskEngine } from '@taskcast/core';
3
- export declare function createTasksRouter(engine: TaskEngine): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export declare function createTasksRouter(engine: TaskEngine): Hono;
4
4
  //# sourceMappingURL=tasks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,UAAU,EAA8D,MAAM,gBAAgB,CAAA;AAwB5G,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,8EA6InD"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAYhC,OAAO,KAAK,EAAE,UAAU,EAA8E,MAAM,gBAAgB,CAAA;AA0G5H,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAwL1D"}
@@ -1,29 +1,98 @@
1
- import { Hono } from 'hono';
2
- import { z } from 'zod';
1
+ import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
3
2
  import { checkScope } from '../auth.js';
4
- const CreateTaskSchema = z.object({
5
- id: z.string().optional(),
6
- type: z.string().optional(),
7
- params: z.record(z.unknown()).optional(),
8
- metadata: z.record(z.unknown()).optional(),
9
- ttl: z.number().int().positive().optional(),
10
- webhooks: z.array(z.unknown()).optional(),
11
- cleanup: z.object({ rules: z.array(z.unknown()) }).optional(),
12
- tags: z.array(z.string()).optional(),
13
- assignMode: z.enum(['external', 'pull', 'ws-offer', 'ws-race']).optional(),
14
- cost: z.number().int().positive().optional(),
15
- disconnectPolicy: z.enum(['reassign', 'mark', 'fail']).optional(),
3
+ import { CreateTaskSchema, PublishEventSchema, TransitionSchema, TaskSchema, TaskEventSchema, ErrorSchema, } from '../schemas.js';
4
+ // ─── Route Definitions ─────────────────────────────────────────────────────
5
+ const createTaskRoute = createRoute({
6
+ method: 'post',
7
+ path: '/',
8
+ tags: ['Tasks'],
9
+ summary: 'Create a new task',
10
+ security: [{ Bearer: [] }],
11
+ request: {
12
+ body: { content: { 'application/json': { schema: CreateTaskSchema } } },
13
+ },
14
+ responses: {
15
+ 201: { description: 'Task created', content: { 'application/json': { schema: TaskSchema } } },
16
+ 400: { description: 'Validation error', content: { 'application/json': { schema: ErrorSchema } } },
17
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
18
+ },
16
19
  });
17
- const PublishEventSchema = z.object({
18
- type: z.string(),
19
- level: z.enum(['debug', 'info', 'warn', 'error']),
20
- data: z.unknown(),
21
- seriesId: z.string().optional(),
22
- seriesMode: z.enum(['keep-all', 'accumulate', 'latest']).optional(),
20
+ const getTaskRoute = createRoute({
21
+ method: 'get',
22
+ path: '/{taskId}',
23
+ tags: ['Tasks'],
24
+ summary: 'Get task by ID',
25
+ security: [{ Bearer: [] }],
26
+ request: {
27
+ params: z.object({ taskId: z.string() }),
28
+ },
29
+ responses: {
30
+ 200: { description: 'Task details', content: { 'application/json': { schema: TaskSchema } } },
31
+ 404: { description: 'Task not found', content: { 'application/json': { schema: ErrorSchema } } },
32
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
33
+ },
34
+ });
35
+ const transitionRoute = createRoute({
36
+ method: 'patch',
37
+ path: '/{taskId}/status',
38
+ tags: ['Tasks'],
39
+ summary: 'Transition task status',
40
+ security: [{ Bearer: [] }],
41
+ request: {
42
+ params: z.object({ taskId: z.string() }),
43
+ body: { content: { 'application/json': { schema: TransitionSchema } } },
44
+ },
45
+ responses: {
46
+ 200: { description: 'Updated task', content: { 'application/json': { schema: TaskSchema } } },
47
+ 400: { description: 'Invalid transition', content: { 'application/json': { schema: ErrorSchema } } },
48
+ 404: { description: 'Task not found', content: { 'application/json': { schema: ErrorSchema } } },
49
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
50
+ },
51
+ });
52
+ const publishEventsRoute = createRoute({
53
+ method: 'post',
54
+ path: '/{taskId}/events',
55
+ tags: ['Events'],
56
+ summary: 'Publish events to a task',
57
+ description: 'Supports single event or batch (array) publishing.',
58
+ security: [{ Bearer: [] }],
59
+ request: {
60
+ params: z.object({ taskId: z.string() }),
61
+ body: { content: { 'application/json': { schema: z.union([PublishEventSchema, z.array(PublishEventSchema)]) } } },
62
+ },
63
+ responses: {
64
+ 201: { description: 'Events published', content: { 'application/json': { schema: z.union([TaskEventSchema, z.array(TaskEventSchema)]) } } },
65
+ 400: { description: 'Validation error', content: { 'application/json': { schema: ErrorSchema } } },
66
+ 404: { description: 'Task not found', content: { 'application/json': { schema: ErrorSchema } } },
67
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
68
+ },
69
+ });
70
+ const eventHistoryRoute = createRoute({
71
+ method: 'get',
72
+ path: '/{taskId}/events/history',
73
+ tags: ['Events'],
74
+ summary: 'Query event history',
75
+ security: [{ Bearer: [] }],
76
+ request: {
77
+ params: z.object({ taskId: z.string() }),
78
+ query: z.object({
79
+ 'since.id': z.string().optional(),
80
+ 'since.index': z.string().optional(),
81
+ 'since.timestamp': z.string().optional(),
82
+ types: z.string().optional(),
83
+ levels: z.string().optional(),
84
+ }),
85
+ },
86
+ responses: {
87
+ 200: { description: 'Event list', content: { 'application/json': { schema: z.array(TaskEventSchema) } } },
88
+ 404: { description: 'Task not found', content: { 'application/json': { schema: ErrorSchema } } },
89
+ 403: { description: 'Forbidden', content: { 'application/json': { schema: ErrorSchema } } },
90
+ },
23
91
  });
24
92
  export function createTasksRouter(engine) {
25
- const router = new Hono();
26
- router.post('/', async (c) => {
93
+ const router = new OpenAPIHono();
94
+ const register = router.openapi.bind(router);
95
+ register(createTaskRoute, async (c) => {
27
96
  const auth = c.get('auth');
28
97
  if (!checkScope(auth, 'task:create'))
29
98
  return c.json({ error: 'Forbidden' }, 403);
@@ -51,11 +120,17 @@ export function createTasksRouter(engine) {
51
120
  input.cost = d.cost;
52
121
  if (d.disconnectPolicy !== undefined)
53
122
  input.disconnectPolicy = d.disconnectPolicy;
123
+ if (d.webhooks !== undefined)
124
+ input.webhooks = d.webhooks;
125
+ if (d.cleanup !== undefined)
126
+ input.cleanup = d.cleanup;
127
+ if (d.authConfig !== undefined)
128
+ input.authConfig = d.authConfig;
54
129
  const task = await engine.createTask(input);
55
130
  return c.json(task, 201);
56
131
  });
57
- router.get('/:taskId', async (c) => {
58
- const { taskId } = c.req.param();
132
+ register(getTaskRoute, async (c) => {
133
+ const taskId = c.req.param('taskId');
59
134
  const auth = c.get('auth');
60
135
  if (!checkScope(auth, 'event:subscribe', taskId))
61
136
  return c.json({ error: 'Forbidden' }, 403);
@@ -64,25 +139,13 @@ export function createTasksRouter(engine) {
64
139
  return c.json({ error: 'Task not found' }, 404);
65
140
  return c.json(task);
66
141
  });
67
- router.patch('/:taskId/status', async (c) => {
68
- const { taskId } = c.req.param();
142
+ register(transitionRoute, async (c) => {
143
+ const taskId = c.req.param('taskId');
69
144
  const auth = c.get('auth');
70
145
  if (!checkScope(auth, 'task:manage', taskId))
71
146
  return c.json({ error: 'Forbidden' }, 403);
72
147
  const body = await c.req.json();
73
- const schema = z.object({
74
- status: z.enum(['pending', 'assigned', 'running', 'paused', 'blocked', 'completed', 'failed', 'timeout', 'cancelled']),
75
- result: z.record(z.unknown()).optional(),
76
- error: z.object({
77
- code: z.string().optional(),
78
- message: z.string(),
79
- details: z.record(z.unknown()).optional(),
80
- }).optional(),
81
- reason: z.string().optional(),
82
- ttl: z.number().int().positive().optional(),
83
- resumeAfterMs: z.number().int().positive().optional(),
84
- });
85
- const parsed = schema.safeParse(body);
148
+ const parsed = TransitionSchema.safeParse(body);
86
149
  if (!parsed.success)
87
150
  return c.json({ error: parsed.error.flatten() }, 400);
88
151
  try {
@@ -104,6 +167,12 @@ export function createTasksRouter(engine) {
104
167
  payload.ttl = parsed.data.ttl;
105
168
  if (parsed.data.resumeAfterMs !== undefined)
106
169
  payload.resumeAfterMs = parsed.data.resumeAfterMs;
170
+ if (parsed.data.blockedRequest !== undefined) {
171
+ payload.blockedRequest = {
172
+ type: parsed.data.blockedRequest.type,
173
+ data: parsed.data.blockedRequest.data,
174
+ };
175
+ }
107
176
  const task = await engine.transitionTask(taskId, parsed.data.status, payload);
108
177
  return c.json(task);
109
178
  }
@@ -114,8 +183,8 @@ export function createTasksRouter(engine) {
114
183
  return c.json({ error: msg }, 400);
115
184
  }
116
185
  });
117
- router.post('/:taskId/events', async (c) => {
118
- const { taskId } = c.req.param();
186
+ register(publishEventsRoute, async (c) => {
187
+ const taskId = c.req.param('taskId');
119
188
  const auth = c.get('auth');
120
189
  if (!checkScope(auth, 'event:publish', taskId))
121
190
  return c.json({ error: 'Forbidden' }, 403);
@@ -134,6 +203,8 @@ export function createTasksRouter(engine) {
134
203
  eventInput.seriesId = d.seriesId;
135
204
  if (d.seriesMode !== undefined)
136
205
  eventInput.seriesMode = d.seriesMode;
206
+ if (d.seriesAccField !== undefined)
207
+ eventInput.seriesAccField = d.seriesAccField;
137
208
  const event = await engine.publishEvent(taskId, eventInput);
138
209
  events.push(event);
139
210
  }
@@ -146,8 +217,8 @@ export function createTasksRouter(engine) {
146
217
  }
147
218
  return c.json(isBatch ? events : events[0], 201);
148
219
  });
149
- router.get('/:taskId/events/history', async (c) => {
150
- const { taskId } = c.req.param();
220
+ register(eventHistoryRoute, async (c) => {
221
+ const taskId = c.req.param('taskId');
151
222
  const auth = c.get('auth');
152
223
  if (!checkScope(auth, 'event:history', taskId))
153
224
  return c.json({ error: 'Forbidden' }, 403);
@@ -170,6 +241,49 @@ export function createTasksRouter(engine) {
170
241
  const events = await engine.getEvents(taskId, since !== undefined ? { since } : undefined);
171
242
  return c.json(events);
172
243
  });
244
+ // ─── POST /tasks/:taskId/resolve — Resolve a blocked task ─────────────────
245
+ router.post('/:taskId/resolve', async (c) => {
246
+ const taskId = c.req.param('taskId');
247
+ const auth = c.get('auth');
248
+ if (!checkScope(auth, 'task:resolve', taskId))
249
+ return c.json({ error: 'Forbidden' }, 403);
250
+ const task = await engine.getTask(taskId);
251
+ if (!task)
252
+ return c.json({ error: 'Task not found' }, 404);
253
+ if (task.status !== 'blocked')
254
+ return c.json({ error: 'Task is not blocked' }, 400);
255
+ const body = await c.req.json();
256
+ const schema = z.object({ data: z.unknown() });
257
+ const parsed = schema.safeParse(body);
258
+ if (!parsed.success)
259
+ return c.json({ error: parsed.error.flatten() }, 400);
260
+ try {
261
+ const updated = await engine.transitionTask(taskId, 'running', {
262
+ result: typeof parsed.data.data === 'object' && parsed.data.data !== null
263
+ ? parsed.data.data
264
+ : { resolution: parsed.data.data },
265
+ });
266
+ return c.json(updated);
267
+ }
268
+ catch (err) {
269
+ const msg = err instanceof Error ? err.message : String(err);
270
+ return c.json({ error: msg }, 400);
271
+ }
272
+ });
273
+ // ─── GET /tasks/:taskId/request — Get blocked request ────────────────────
274
+ router.get('/:taskId/request', async (c) => {
275
+ const taskId = c.req.param('taskId');
276
+ const auth = c.get('auth');
277
+ if (!checkScope(auth, 'task:resolve', taskId))
278
+ return c.json({ error: 'Forbidden' }, 403);
279
+ const task = await engine.getTask(taskId);
280
+ if (!task)
281
+ return c.json({ error: 'Task not found' }, 404);
282
+ if (task.status !== 'blocked' || !task.blockedRequest) {
283
+ return c.json({ error: 'No blocked request' }, 404);
284
+ }
285
+ return c.json(task.blockedRequest);
286
+ });
173
287
  return router;
174
288
  }
175
289
  //# sourceMappingURL=tasks.js.map