@forestadmin/workflow-executor 1.1.5 → 1.3.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.
@@ -8,6 +8,8 @@ const router_1 = __importDefault(require("@koa/router"));
8
8
  const http_1 = __importDefault(require("http"));
9
9
  const koa_1 = __importDefault(require("koa"));
10
10
  const koa_jwt_1 = __importDefault(require("koa-jwt"));
11
+ const bearer_claims_1 = require("./bearer-claims");
12
+ const http_errors_1 = require("./http-errors");
11
13
  const step_serializer_1 = __importDefault(require("./step-serializer"));
12
14
  const console_logger_1 = __importDefault(require("../adapters/console-logger"));
13
15
  const errors_1 = require("../errors");
@@ -15,28 +17,41 @@ class ExecutorHttpServer {
15
17
  constructor(options) {
16
18
  this.server = null;
17
19
  this.options = options;
18
- this.logger = options.logger ?? new console_logger_1.default();
20
+ this.logger = options.logger ?? (0, console_logger_1.default)();
19
21
  this.app = new koa_1.default();
20
- // Error middleware — catches all errors (including JWT 401) and returns structured JSON
22
+ // Error-translation middleware — the single place converting thrown errors (typed HTTP
23
+ // errors, domain errors via toHttpError, JWT 401) into HTTP responses. Handlers just throw.
21
24
  this.app.use(async (ctx, next) => {
22
25
  try {
23
26
  await next();
24
27
  }
25
28
  catch (err) {
26
- const { status } = err;
27
- if (status === 401) {
28
- ctx.status = 401;
29
- ctx.body = { error: 'Unauthorized' };
29
+ const httpError = (0, http_errors_1.toHttpError)(err);
30
+ if (!httpError) {
31
+ this.logger('Error', 'Unhandled HTTP error', {
32
+ method: ctx.method,
33
+ path: ctx.path,
34
+ error: (0, errors_1.extractErrorMessage)(err),
35
+ stack: err instanceof Error ? err.stack : undefined,
36
+ });
37
+ ctx.status = 500;
38
+ ctx.body = { error: 'Internal server error' };
30
39
  return;
31
40
  }
32
- this.logger.error('Unhandled HTTP error', {
33
- method: ctx.method,
34
- path: ctx.path,
35
- error: (0, errors_1.extractErrorMessage)(err),
36
- stack: err instanceof Error ? err.stack : undefined,
37
- });
38
- ctx.status = 500;
39
- ctx.body = { error: 'Internal server error' };
41
+ if (httpError.log) {
42
+ this.logger('Error', 'HTTP request failed', {
43
+ method: ctx.method,
44
+ path: ctx.path,
45
+ status: httpError.status,
46
+ error: (0, errors_1.extractErrorMessage)(httpError.cause ?? httpError),
47
+ // Prefer the cause's stack (points at the real fault site); fall back to the HTTP
48
+ // error's own stack so a log:true error without a cause never logs an empty stack.
49
+ stack: (httpError.cause instanceof Error ? httpError.cause.stack : undefined) ??
50
+ httpError.stack,
51
+ });
52
+ }
53
+ ctx.status = httpError.status;
54
+ ctx.body = { error: httpError.userMessage };
40
55
  }
41
56
  });
42
57
  // Health endpoint — before JWT so it's publicly accessible (infra probes don't send tokens)
@@ -53,6 +68,24 @@ class ExecutorHttpServer {
53
68
  // JWT middleware — validates Bearer token using authSecret
54
69
  // tokenKey: 'rawToken' exposes the raw token string on ctx.state.rawToken for downstream use
55
70
  this.app.use((0, koa_jwt_1.default)({ secret: options.authSecret, cookie: 'forest_session_token', tokenKey: 'rawToken' }));
71
+ // koa-jwt only validates the token's signature/expiry, not its payload shape. Validate the
72
+ // claims once, here, so every handler downstream gets a user with a guaranteed numeric id.
73
+ this.app.use(async (ctx, next) => {
74
+ const claims = bearer_claims_1.BearerClaimsSchema.safeParse(ctx.state.user);
75
+ if (!claims.success) {
76
+ // A token koa-jwt accepted (valid signature) but whose payload is malformed is rare and
77
+ // high-signal (token-issuance regression / version skew / forgery probe) — log it, unlike
78
+ // ordinary expired-token churn. Only the issue paths/codes, never the payload (PII).
79
+ this.logger('Warn', 'Bearer token has invalid claims', {
80
+ method: ctx.method,
81
+ path: ctx.path,
82
+ issues: claims.error.issues.map(issue => ({ path: issue.path, code: issue.code })),
83
+ });
84
+ throw new http_errors_1.UnauthorizedHttpError();
85
+ }
86
+ ctx.state.user = { ...ctx.state.user, ...claims.data };
87
+ await next();
88
+ });
56
89
  const router = new router_1.default();
57
90
  // hasRunAccess authorization — only on GET (read-only route).
58
91
  // Trigger handles its own authz by comparing bearer user with step.user.
@@ -90,26 +123,23 @@ class ExecutorHttpServer {
90
123
  }
91
124
  async hasRunAccessMiddleware(ctx, next) {
92
125
  const user = ctx.state.user;
126
+ let allowed;
93
127
  try {
94
- const allowed = await this.options.workflowPort.hasRunAccess(ctx.params.runId, user);
95
- if (!allowed) {
96
- ctx.status = 403;
97
- ctx.body = { error: 'Forbidden' };
98
- return;
99
- }
128
+ allowed = await this.options.workflowPort.hasRunAccess(ctx.params.runId, user);
100
129
  }
101
130
  catch (err) {
102
- this.logger.error('Failed to check run access', {
131
+ this.logger('Error', 'Failed to check run access', {
103
132
  runId: ctx.params.runId,
104
133
  method: ctx.method,
105
134
  path: ctx.path,
106
135
  error: (0, errors_1.extractErrorMessage)(err),
107
136
  stack: err instanceof Error ? err.stack : undefined,
108
137
  });
109
- ctx.status = 503;
110
- ctx.body = { error: 'Service unavailable' };
111
- return;
138
+ // log:false — already logged above with the richer runId context.
139
+ throw new http_errors_1.ServiceUnavailableHttpError('Service unavailable', { cause: err });
112
140
  }
141
+ if (!allowed)
142
+ throw new http_errors_1.ForbiddenHttpError();
113
143
  await next();
114
144
  }
115
145
  async handleGetRun(ctx) {
@@ -118,53 +148,13 @@ class ExecutorHttpServer {
118
148
  }
119
149
  async handleTrigger(ctx) {
120
150
  const { runId } = ctx.params;
121
- const rawId = ctx.state.user?.id;
122
- const bearerUserId = typeof rawId === 'number' ? rawId : Number(rawId);
123
- if (!Number.isFinite(bearerUserId)) {
124
- ctx.status = 400;
125
- ctx.body = { error: 'Missing or invalid user id in token' };
126
- return;
127
- }
151
+ // Guaranteed a number by the bearer-claims middleware.
152
+ const bearerUserId = ctx.state.user.id;
128
153
  const pendingData = ctx.request.body?.pendingData;
129
- try {
130
- await this.options.runner.triggerPoll(runId, {
131
- pendingData,
132
- bearerUserId,
133
- });
134
- }
135
- catch (err) {
136
- if (err instanceof errors_1.RunNotFoundError) {
137
- ctx.status = 404;
138
- ctx.body = { error: 'Run not found or unavailable' };
139
- return;
140
- }
141
- if (err instanceof errors_1.RunAlreadyInFlightError) {
142
- ctx.status = 400;
143
- ctx.body = { error: err.message };
144
- return;
145
- }
146
- if (err instanceof errors_1.UserMismatchError) {
147
- this.logger.error('User mismatch on trigger', { runId, bearerUserId });
148
- ctx.status = 403;
149
- ctx.body = { error: 'Forbidden' };
150
- return;
151
- }
152
- if (err instanceof errors_1.WorkflowExecutorError) {
153
- this.logger.error('Malformed run on trigger', {
154
- runId,
155
- bearerUserId,
156
- error: (0, errors_1.extractErrorMessage)(err),
157
- stack: err.stack,
158
- });
159
- ctx.status = 400;
160
- ctx.body = { error: err.userMessage };
161
- return;
162
- }
163
- throw err;
164
- }
154
+ await this.options.runner.triggerPoll(runId, { pendingData, bearerUserId });
165
155
  ctx.status = 200;
166
156
  ctx.body = { triggered: true };
167
157
  }
168
158
  }
169
159
  exports.default = ExecutorHttpServer;
170
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0b3ItaHR0cC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9leGVjdXRvci1odHRwLXNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQU1BLGlFQUF5QztBQUN6Qyx5REFBaUM7QUFDakMsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUN0QixzREFBNkI7QUFFN0Isd0VBQXFEO0FBQ3JELGdGQUF1RDtBQUN2RCxzQ0FNbUI7QUFVbkIsTUFBcUIsa0JBQWtCO0lBTXJDLFlBQVksT0FBa0M7UUFGdEMsV0FBTSxHQUFrQixJQUFJLENBQUM7UUFHbkMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxJQUFJLElBQUksd0JBQWEsRUFBRSxDQUFDO1FBQ3BELElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztRQUVyQix3RkFBd0Y7UUFDeEYsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtZQUMvQixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLEVBQUUsQ0FBQztZQUNmLENBQUM7WUFBQyxPQUFPLEdBQVksRUFBRSxDQUFDO2dCQUN0QixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsR0FBMEIsQ0FBQztnQkFFOUMsSUFBSSxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUM7b0JBQ25CLEdBQUcsQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDO29CQUNqQixHQUFHLENBQUMsSUFBSSxHQUFHLEVBQUUsS0FBSyxFQUFFLGNBQWMsRUFBRSxDQUFDO29CQUVyQyxPQUFPO2dCQUNULENBQUM7Z0JBRUQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEVBQUU7b0JBQ3hDLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtvQkFD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
160
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0b3ItaHR0cC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9leGVjdXRvci1odHRwLXNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUtBLGlFQUF5QztBQUN6Qyx5REFBaUM7QUFDakMsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUN0QixzREFBNkI7QUFFN0IsbURBQXdFO0FBQ3hFLCtDQUt1QjtBQUN2Qix3RUFBcUQ7QUFDckQsZ0ZBQTZEO0FBQzdELHNDQUFnRDtBQVVoRCxNQUFxQixrQkFBa0I7SUFNckMsWUFBWSxPQUFrQztRQUZ0QyxXQUFNLEdBQWtCLElBQUksQ0FBQztRQUduQyxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBQSx3QkFBbUIsR0FBRSxDQUFDO1FBQ3RELElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztRQUVyQix1RkFBdUY7UUFDdkYsNEZBQTRGO1FBQzVGLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDL0IsSUFBSSxDQUFDO2dCQUNILE1BQU0sSUFBSSxFQUFFLENBQUM7WUFDZixDQUFDO1lBQUMsT0FBTyxHQUFZLEVBQUUsQ0FBQztnQkFDdEIsTUFBTSxTQUFTLEdBQUcsSUFBQSx5QkFBVyxFQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUVuQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7b0JBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsc0JBQXNCLEVBQUU7d0JBQzNDLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTt3QkFDbEIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO3dCQUNkLEtBQUssRUFBRSxJQUFBLDRCQUFtQixFQUFDLEdBQUcsQ0FBQzt3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
@@ -0,0 +1,39 @@
1
+ export declare abstract class BaseHttpError extends Error {
2
+ readonly status: number;
3
+ readonly userMessage: string;
4
+ readonly log: boolean;
5
+ cause?: unknown;
6
+ constructor(status: number, userMessage: string, options?: {
7
+ log?: boolean;
8
+ cause?: unknown;
9
+ });
10
+ }
11
+ export declare class BadRequestHttpError extends BaseHttpError {
12
+ constructor(userMessage: string, options?: {
13
+ log?: boolean;
14
+ cause?: unknown;
15
+ });
16
+ }
17
+ export declare class UnauthorizedHttpError extends BaseHttpError {
18
+ constructor();
19
+ }
20
+ export declare class ForbiddenHttpError extends BaseHttpError {
21
+ constructor(options?: {
22
+ log?: boolean;
23
+ cause?: unknown;
24
+ });
25
+ }
26
+ export declare class NotFoundHttpError extends BaseHttpError {
27
+ constructor(userMessage: string, options?: {
28
+ log?: boolean;
29
+ cause?: unknown;
30
+ });
31
+ }
32
+ export declare class ServiceUnavailableHttpError extends BaseHttpError {
33
+ constructor(userMessage: string, options?: {
34
+ log?: boolean;
35
+ cause?: unknown;
36
+ });
37
+ }
38
+ export declare function toHttpError(err: unknown): BaseHttpError | null;
39
+ //# sourceMappingURL=http-errors.d.ts.map
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceUnavailableHttpError = exports.NotFoundHttpError = exports.ForbiddenHttpError = exports.UnauthorizedHttpError = exports.BadRequestHttpError = exports.BaseHttpError = void 0;
4
+ exports.toHttpError = toHttpError;
5
+ /* eslint-disable max-classes-per-file */
6
+ const errors_1 = require("../errors");
7
+ // HTTP-typed errors: each carries its response semantics (status + user-facing body) so the
8
+ // error-translation middleware can render any thrown error without per-handler branching. One
9
+ // concrete class per status; handlers throw them directly, and toHttpError maps domain error
10
+ // categories onto them.
11
+ class BaseHttpError extends Error {
12
+ constructor(status, userMessage, options = {}) {
13
+ super(userMessage);
14
+ this.name = this.constructor.name;
15
+ this.status = status;
16
+ this.userMessage = userMessage;
17
+ this.log = options.log ?? false;
18
+ if (options.cause !== undefined)
19
+ this.cause = options.cause;
20
+ }
21
+ }
22
+ exports.BaseHttpError = BaseHttpError;
23
+ class BadRequestHttpError extends BaseHttpError {
24
+ constructor(userMessage, options) {
25
+ super(400, userMessage, options);
26
+ }
27
+ }
28
+ exports.BadRequestHttpError = BadRequestHttpError;
29
+ class UnauthorizedHttpError extends BaseHttpError {
30
+ constructor() {
31
+ super(401, 'Unauthorized');
32
+ }
33
+ }
34
+ exports.UnauthorizedHttpError = UnauthorizedHttpError;
35
+ class ForbiddenHttpError extends BaseHttpError {
36
+ // 403 bodies stay opaque ('Forbidden') on purpose — never echo why access was denied.
37
+ constructor(options) {
38
+ super(403, 'Forbidden', options);
39
+ }
40
+ }
41
+ exports.ForbiddenHttpError = ForbiddenHttpError;
42
+ class NotFoundHttpError extends BaseHttpError {
43
+ constructor(userMessage, options) {
44
+ super(404, userMessage, options);
45
+ }
46
+ }
47
+ exports.NotFoundHttpError = NotFoundHttpError;
48
+ class ServiceUnavailableHttpError extends BaseHttpError {
49
+ constructor(userMessage, options) {
50
+ super(503, userMessage, options);
51
+ }
52
+ }
53
+ exports.ServiceUnavailableHttpError = ServiceUnavailableHttpError;
54
+ // The single domain→HTTP mapping. Maps by domain category, not concrete class, so a new
55
+ // NotFoundError/ConflictError/… is translated correctly without touching this function. Returns
56
+ // null when the error has no HTTP translation — the middleware then responds 500 without leaking
57
+ // internals.
58
+ function toHttpError(err) {
59
+ if (err instanceof BaseHttpError)
60
+ return err;
61
+ // koa-jwt rejects with an error object carrying status 401.
62
+ if (err?.status === 401)
63
+ return new UnauthorizedHttpError();
64
+ // Category branches MUST precede the WorkflowExecutorError catch-all: every category extends it.
65
+ if (err instanceof errors_1.NotFoundError)
66
+ return new NotFoundHttpError(err.userMessage, { cause: err });
67
+ if (err instanceof errors_1.AccessDeniedError)
68
+ return new ForbiddenHttpError({ log: true, cause: err });
69
+ // Expected client churn (a double trigger): 400, not logged. Precedes the catch-all, which logs.
70
+ if (err instanceof errors_1.RunAlreadyInFlightError) {
71
+ return new BadRequestHttpError(err.userMessage, { cause: err });
72
+ }
73
+ if (err instanceof errors_1.UnavailableError) {
74
+ return new ServiceUnavailableHttpError(err.userMessage, { log: true, cause: err });
75
+ }
76
+ // Uncategorized domain error: 400 with its userMessage (safe default — never a silent 500).
77
+ if (err instanceof errors_1.WorkflowExecutorError) {
78
+ return new BadRequestHttpError(err.userMessage, { log: true, cause: err });
79
+ }
80
+ return null;
81
+ }
82
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1lcnJvcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9odHRwLWVycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFzRUEsa0NBeUJDO0FBL0ZELHlDQUF5QztBQUN6QyxzQ0FNbUI7QUFFbkIsNEZBQTRGO0FBQzVGLDhGQUE4RjtBQUM5Riw2RkFBNkY7QUFDN0Ysd0JBQXdCO0FBQ3hCLE1BQXNCLGFBQWMsU0FBUSxLQUFLO0lBUS9DLFlBQ0UsTUFBYyxFQUNkLFdBQW1CLEVBQ25CLFVBQThDLEVBQUU7UUFFaEQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ25CLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUM7UUFDbEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFDL0IsSUFBSSxDQUFDLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxJQUFJLEtBQUssQ0FBQztRQUNoQyxJQUFJLE9BQU8sQ0FBQyxLQUFLLEtBQUssU0FBUztZQUFFLElBQUksQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztJQUM5RCxDQUFDO0NBQ0Y7QUFwQkQsc0NBb0JDO0FBRUQsTUFBYSxtQkFBb0IsU0FBUSxhQUFhO0lBQ3BELFlBQVksV0FBbUIsRUFBRSxPQUE0QztRQUMzRSxLQUFLLENBQUMsR0FBRyxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNuQyxDQUFDO0NBQ0Y7QUFKRCxrREFJQztBQUVELE1BQWEscUJBQXNCLFNBQVEsYUFBYTtJQUN0RDtRQUNFLEtBQUssQ0FBQyxHQUFHLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFDN0IsQ0FBQztDQUNGO0FBSkQsc0RBSUM7QUFFRCxNQUFhLGtCQUFtQixTQUFRLGFBQWE7SUFDbkQsc0ZBQXNGO0lBQ3RGLFlBQVksT0FBNEM7UUFDdEQsS0FBSyxDQUFDLEdBQUcsRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDbkMsQ0FBQztDQUNGO0FBTEQsZ0RBS0M7QUFFRCxNQUFhLGlCQUFrQixTQUFRLGFBQWE7SUFDbEQsWUFBWSxXQUFtQixFQUFFLE9BQTRDO1FBQzNFLEtBQUssQ0FBQyxHQUFHLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ25DLENBQUM7Q0FDRjtBQUpELDhDQUlDO0FBRUQsTUFBYSwyQkFBNEIsU0FBUSxhQUFhO0lBQzVELFlBQVksV0FBbUIsRUFBRSxPQUE0QztRQUMzRSxLQUFLLENBQUMsR0FBRyxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNuQyxDQUFDO0NBQ0Y7QUFKRCxrRUFJQztBQUVELHdGQUF3RjtBQUN4RixnR0FBZ0c7QUFDaEcsaUdBQWlHO0FBQ2pHLGFBQWE7QUFDYixTQUFnQixXQUFXLENBQUMsR0FBWTtJQUN0QyxJQUFJLEdBQUcsWUFBWSxhQUFhO1FBQUUsT0FBTyxHQUFHLENBQUM7SUFFN0MsNERBQTREO0lBQzVELElBQUssR0FBMkIsRUFBRSxNQUFNLEtBQUssR0FBRztRQUFFLE9BQU8sSUFBSSxxQkFBcUIsRUFBRSxDQUFDO0lBRXJGLGlHQUFpRztJQUNqRyxJQUFJLEdBQUcsWUFBWSxzQkFBYTtRQUFFLE9BQU8sSUFBSSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDaEcsSUFBSSxHQUFHLFlBQVksMEJBQWlCO1FBQUUsT0FBTyxJQUFJLGtCQUFrQixDQUFDLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUUvRixpR0FBaUc7SUFDakcsSUFBSSxHQUFHLFlBQVksZ0NBQXVCLEVBQUUsQ0FBQztRQUMzQyxPQUFPLElBQUksbUJBQW1CLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFFRCxJQUFJLEdBQUcsWUFBWSx5QkFBZ0IsRUFBRSxDQUFDO1FBQ3BDLE9BQU8sSUFBSSwyQkFBMkIsQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUNyRixDQUFDO0lBRUQsNEZBQTRGO0lBQzVGLElBQUksR0FBRyxZQUFZLDhCQUFxQixFQUFFLENBQUM7UUFDekMsT0FBTyxJQUFJLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUMifQ==
@@ -1,6 +1,3 @@
1
- export interface Logger {
2
- error(message: string, context: Record<string, unknown>): void;
3
- warn(message: string, context: Record<string, unknown>): void;
4
- info(message: string, context: Record<string, unknown>): void;
5
- }
1
+ export type LoggerLevel = 'Debug' | 'Info' | 'Warn' | 'Error';
2
+ export type Logger = (level: LoggerLevel, message: string, context?: Record<string, unknown>) => void;
6
3
  //# sourceMappingURL=logger-port.d.ts.map
@@ -1,4 +1,4 @@
1
- import type { AvailableStepExecution, StepUser } from '../types/execution-context';
1
+ import type { AvailableStepExecution } from '../types/execution-context';
2
2
  import type { CollectionSchema } from '../types/validated/collection';
3
3
  import type { StepOutcome } from '../types/validated/step-outcome';
4
4
  import type { ToolConfig } from '@forestadmin/ai-proxy';
@@ -25,6 +25,8 @@ export interface WorkflowPort {
25
25
  updateStepExecution(runId: string, stepOutcome: StepOutcome): Promise<AvailableRunDispatch | null>;
26
26
  getCollectionSchema(collectionName: string, runId: string): Promise<CollectionSchema>;
27
27
  getMcpServerConfigs(): Promise<Record<string, ToolConfig>>;
28
- hasRunAccess(runId: string, user: StepUser): Promise<boolean>;
28
+ hasRunAccess(runId: string, user: {
29
+ id: number;
30
+ }): Promise<boolean>;
29
31
  }
30
32
  //# sourceMappingURL=workflow-port.d.ts.map
@@ -31,7 +31,7 @@ class RemoteToolFetcher {
31
31
  const availableMcpServerIds = Object.values(configs)
32
32
  .map(cfg => cfg.id)
33
33
  .filter((id) => Boolean(id));
34
- this.logger.warn(Object.keys(configs).length === 0
34
+ this.logger('Warn', Object.keys(configs).length === 0
35
35
  ? 'MCP step targets a server but orchestrator returned no MCP configs'
36
36
  : 'MCP step targets a server not advertised by the orchestrator', { requestedMcpServerId: mcpServerId, mcpServerName, availableMcpServerIds });
37
37
  }
@@ -45,7 +45,7 @@ class RemoteToolFetcher {
45
45
  .map(([name]) => name);
46
46
  if (failedConfigNames.length === 0)
47
47
  return;
48
- this.logger.error('MCP servers failed to load tools', {
48
+ this.logger('Error', 'MCP servers failed to load tools', {
49
49
  requestedMcpServerId: mcpServerId,
50
50
  mcpServerName,
51
51
  failedConfigNames,
@@ -53,4 +53,4 @@ class RemoteToolFetcher {
53
53
  }
54
54
  }
55
55
  exports.default = RemoteToolFetcher;
56
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3RlLXRvb2wtZmV0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9yZW1vdGUtdG9vbC1mZXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBTUEsb0RBS0M7QUFORCxrRkFBa0Y7QUFDbEYsU0FBZ0Isb0JBQW9CLENBQ2xDLE9BQW1DLEVBQ25DLFdBQW1CO0lBRW5CLE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBQ2pHLENBQUM7QUFPRCxNQUFxQixpQkFBaUI7SUFLcEMsWUFBWSxZQUEwQixFQUFFLFdBQXdCLEVBQUUsTUFBYztRQUM5RSxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQUssQ0FBQyxXQUFtQjtRQUM3QixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5RCxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUQsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFNUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLGFBQWEsRUFBRSxDQUFDO1FBRTFFLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLHlCQUF5QixDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVELDZGQUE2RjtJQUM3RiwyRkFBMkY7SUFDM0Ysb0JBQW9CO0lBQ1osdUJBQXVCLENBQzdCLE9BQW1DLEVBQ25DLE1BQWtDLEVBQ2xDLFdBQW1CLEVBQ25CLGFBQWlDO1FBRWpDLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU87UUFFM0MsTUFBTSxxQkFBcUIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQzthQUNqRCxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2FBQ2xCLE1BQU0sQ0FBQyxDQUFDLEVBQUUsRUFBZ0IsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRTdDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUNkLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDL0IsQ0FBQyxDQUFDLG9FQUFvRTtZQUN0RSxDQUFDLENBQUMsOERBQThELEVBQ2xFLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxxQkFBcUIsRUFBRSxDQUM1RSxDQUFDO0lBQ0osQ0FBQztJQUVELDRGQUE0RjtJQUM1Riw0RkFBNEY7SUFDNUYseUZBQXlGO0lBQ2pGLHlCQUF5QixDQUMvQixNQUFrQyxFQUNsQyxLQUFtQixFQUNuQixXQUFtQixFQUNuQixhQUFpQztRQUVqQyxNQUFNLGtCQUFrQixHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztRQUNsRSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO2FBQzdDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQ3BELEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXpCLElBQUksaUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPO1FBRTNDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLGtDQUFrQyxFQUFFO1lBQ3BELG9CQUFvQixFQUFFLFdBQVc7WUFDakMsYUFBYTtZQUNiLGlCQUFpQjtTQUNsQixDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUF4RUQsb0NBd0VDIn0=
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3RlLXRvb2wtZmV0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9yZW1vdGUtdG9vbC1mZXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBTUEsb0RBS0M7QUFORCxrRkFBa0Y7QUFDbEYsU0FBZ0Isb0JBQW9CLENBQ2xDLE9BQW1DLEVBQ25DLFdBQW1CO0lBRW5CLE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBQ2pHLENBQUM7QUFPRCxNQUFxQixpQkFBaUI7SUFLcEMsWUFBWSxZQUEwQixFQUFFLFdBQXdCLEVBQUUsTUFBYztRQUM5RSxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQUssQ0FBQyxXQUFtQjtRQUM3QixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5RCxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUQsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFNUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLGFBQWEsRUFBRSxDQUFDO1FBRTFFLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLHlCQUF5QixDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVELDZGQUE2RjtJQUM3RiwyRkFBMkY7SUFDM0Ysb0JBQW9CO0lBQ1osdUJBQXVCLENBQzdCLE9BQW1DLEVBQ25DLE1BQWtDLEVBQ2xDLFdBQW1CLEVBQ25CLGFBQWlDO1FBRWpDLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU87UUFFM0MsTUFBTSxxQkFBcUIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQzthQUNqRCxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2FBQ2xCLE1BQU0sQ0FBQyxDQUFDLEVBQUUsRUFBZ0IsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRTdDLElBQUksQ0FBQyxNQUFNLENBQ1QsTUFBTSxFQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDL0IsQ0FBQyxDQUFDLG9FQUFvRTtZQUN0RSxDQUFDLENBQUMsOERBQThELEVBQ2xFLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxxQkFBcUIsRUFBRSxDQUM1RSxDQUFDO0lBQ0osQ0FBQztJQUVELDRGQUE0RjtJQUM1Riw0RkFBNEY7SUFDNUYseUZBQXlGO0lBQ2pGLHlCQUF5QixDQUMvQixNQUFrQyxFQUNsQyxLQUFtQixFQUNuQixXQUFtQixFQUNuQixhQUFpQztRQUVqQyxNQUFNLGtCQUFrQixHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztRQUNsRSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO2FBQzdDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQ3BELEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXpCLElBQUksaUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPO1FBRTNDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLGtDQUFrQyxFQUFFO1lBQ3ZELG9CQUFvQixFQUFFLFdBQVc7WUFDakMsYUFBYTtZQUNiLGlCQUFpQjtTQUNsQixDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUF6RUQsb0NBeUVDIn0=