@forestadmin/workflow-executor 1.6.0 → 1.6.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/README.md +25 -121
- package/dist/http/executor-http-server.js +7 -1
- package/dist/stores/database-store.d.ts +1 -0
- package/dist/stores/database-store.js +76 -40
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,142 +6,46 @@ The executor polls the Forest orchestrator for pending steps, runs them locally
|
|
|
6
6
|
(with access to your data via the Forest agent), and reports results back. No
|
|
7
7
|
client data ever leaves your infrastructure.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Prerequisites
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Make sure you are on the latest version of your Forest Admin agent (Node.js/JS or Ruby), then add `workflowExecutorUrl` to your agent config:
|
|
12
12
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| Variable | Required | Default | Description |
|
|
20
|
-
|----------|----------|---------|-------------|
|
|
21
|
-
| `FOREST_ENV_SECRET` | ✓ | — | Forest Admin project environment secret |
|
|
22
|
-
| `FOREST_AUTH_SECRET` | ✓ | — | JWT signing secret (shared with your agent) |
|
|
23
|
-
| `AGENT_URL` | ✓ | — | URL of your running Forest Admin agent |
|
|
24
|
-
| `DATABASE_URL` | ✓* | — | Postgres connection string (*not needed with `--in-memory`) |
|
|
25
|
-
| `DATABASE_SSL` | — | `false` | Set to `true` to connect over TLS (managed databases like RDS often require it). Encrypts without verifying the server certificate. |
|
|
26
|
-
| `HTTP_PORT` | — | `3400` | Port for the executor HTTP server |
|
|
27
|
-
| `FOREST_SERVER_URL` | — | `https://api.forestadmin.com` | Orchestrator URL |
|
|
28
|
-
| `POLLING_INTERVAL_S` | — | `30` | Poll cadence for pending steps |
|
|
29
|
-
| `STOP_TIMEOUT_S` | — | `30` | Graceful shutdown deadline |
|
|
30
|
-
|
|
31
|
-
> **Database schema** — On Postgres, the executor keeps its table and its migration
|
|
32
|
-
> registry in a dedicated `forest` schema (created automatically). It is therefore
|
|
33
|
-
> safe to point `DATABASE_URL` at a database shared with your agent/server: nothing
|
|
34
|
-
> is created in `public`.
|
|
35
|
-
|
|
36
|
-
Optional AI configuration (all-or-nothing — falls back to server AI if any is missing):
|
|
37
|
-
|
|
38
|
-
| Variable | Description |
|
|
39
|
-
|----------|-------------|
|
|
40
|
-
| `AI_PROVIDER` | `anthropic` or `openai` |
|
|
41
|
-
| `AI_MODEL` | Model name (e.g. `claude-sonnet-4-6`) |
|
|
42
|
-
| `AI_API_KEY` | Provider API key |
|
|
43
|
-
|
|
44
|
-
### Run
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
forest-workflow-executor
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
You should see (pretty format when stdout is a TTY):
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
13:33:42 info Workflow executor starting mode="database" forestServerUrl="https://api.forestadmin.com" agentUrl="http://localhost:3351" httpPort=3400 pollingIntervalS=5 aiConfig="server fallback"
|
|
54
|
-
13:33:42 info Workflow executor ready url="http://localhost:3400"
|
|
55
|
-
13:33:47 info Poll cycle completed fetched=0 dispatching=0
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
When stdout is piped, redirected or inside a container, logs are emitted as
|
|
59
|
-
structured JSON instead — ready to be ingested by Datadog, CloudWatch, Loki, etc.:
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
{"message":"Workflow executor ready","timestamp":"2026-04-20T13:33:42.000Z","url":"http://localhost:3400"}
|
|
63
|
-
{"message":"Poll cycle completed","timestamp":"2026-04-20T13:33:47.000Z","fetched":0,"dispatching":0}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Log format overrides
|
|
67
|
-
|
|
68
|
-
| Flag | Behavior |
|
|
69
|
-
|------|----------|
|
|
70
|
-
| `--pretty` | Force colorized human-readable logs |
|
|
71
|
-
| `--json` | Force structured JSON logs |
|
|
72
|
-
| (none) | Auto-detect: pretty when stdout is a TTY, JSON otherwise |
|
|
73
|
-
|
|
74
|
-
Setting `NO_COLOR=1` disables ANSI codes while keeping the pretty format.
|
|
75
|
-
|
|
76
|
-
### Health check
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
curl http://localhost:3400/health
|
|
80
|
-
# → {"state":"running"}
|
|
13
|
+
```js
|
|
14
|
+
createAgent({
|
|
15
|
+
// ...
|
|
16
|
+
workflowExecutorUrl: 'http://localhost:3400',
|
|
17
|
+
})
|
|
81
18
|
```
|
|
82
19
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Send `SIGTERM` or `SIGINT`. The executor drains in-flight steps, closes the HTTP
|
|
86
|
-
server, and exits with code `0`. Steps that don't drain within `STOP_TIMEOUT_S`
|
|
87
|
-
are force-killed and the process exits with code `1`.
|
|
20
|
+
---
|
|
88
21
|
|
|
89
|
-
|
|
22
|
+
## Quick Setup
|
|
90
23
|
|
|
91
|
-
|
|
92
|
-
|------|---------|
|
|
93
|
-
| `0` | Graceful shutdown |
|
|
94
|
-
| `1` | Startup error (missing env, invalid config) or forced shutdown |
|
|
24
|
+
### In-memory (no database)
|
|
95
25
|
|
|
96
|
-
|
|
26
|
+
For testing — state is lost on restart, not for production:
|
|
97
27
|
|
|
98
28
|
```bash
|
|
99
|
-
|
|
29
|
+
FOREST_ENV_SECRET="your-env-secret" \
|
|
30
|
+
FOREST_AUTH_SECRET="your-auth-secret" \
|
|
31
|
+
AGENT_URL="https://your-agent-url" \
|
|
32
|
+
npx @forestadmin/workflow-executor --in-memory
|
|
100
33
|
```
|
|
101
34
|
|
|
102
|
-
|
|
35
|
+
### With a database
|
|
103
36
|
|
|
104
|
-
|
|
37
|
+
For production — requires a Postgres database:
|
|
105
38
|
|
|
106
39
|
```bash
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
If you prefer embedding the executor in your own Node entry point:
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
import { buildDatabaseExecutor } from '@forestadmin/workflow-executor';
|
|
116
|
-
|
|
117
|
-
const executor = buildDatabaseExecutor({
|
|
118
|
-
envSecret: process.env.FOREST_ENV_SECRET!,
|
|
119
|
-
authSecret: process.env.FOREST_AUTH_SECRET!,
|
|
120
|
-
agentUrl: process.env.AGENT_URL!,
|
|
121
|
-
httpPort: 3400,
|
|
122
|
-
database: { uri: process.env.DATABASE_URL! },
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
await executor.start();
|
|
126
|
-
// SIGTERM / SIGINT handling is built in
|
|
40
|
+
FOREST_ENV_SECRET="your-env-secret" \
|
|
41
|
+
FOREST_AUTH_SECRET="your-auth-secret" \
|
|
42
|
+
AGENT_URL="https://your-agent-url" \
|
|
43
|
+
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" \
|
|
44
|
+
npx @forestadmin/workflow-executor
|
|
127
45
|
```
|
|
128
46
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
## Dev with the example scaffold
|
|
132
|
-
|
|
133
|
-
The `example/` folder contains a docker-compose setup with Postgres + a ready
|
|
134
|
-
`index.ts` entrypoint that loads `.env` via `dotenv`. Use it for local development
|
|
135
|
-
only — not for production deployments.
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
cd example
|
|
139
|
-
docker compose up -d
|
|
140
|
-
cp .env.example .env # fill in your secrets
|
|
141
|
-
npx tsx index.ts
|
|
142
|
-
```
|
|
47
|
+
**Where to find your credentials:**
|
|
143
48
|
|
|
144
|
-
|
|
49
|
+
Both values are already in your agent's environment variables. `FOREST_ENV_SECRET` can also be found in [app.forestadmin.com](https://app.forestadmin.com) → **Settings** → **Environments** → click your environment. `FOREST_AUTH_SECRET` is defined on your side only and is not available in the Forest Admin UI.
|
|
145
50
|
|
|
146
|
-
|
|
147
|
-
principles, privacy boundaries, and extension points.
|
|
51
|
+
`AGENT_URL` is the URL where your Forest Admin agent is running (e.g. `http://localhost:3351`).
|
|
@@ -13,12 +13,18 @@ const http_errors_1 = require("./http-errors");
|
|
|
13
13
|
const step_serializer_1 = __importDefault(require("./step-serializer"));
|
|
14
14
|
const console_logger_1 = __importDefault(require("../adapters/console-logger"));
|
|
15
15
|
const errors_1 = require("../errors");
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
|
|
17
|
+
const { version } = require('../../package.json');
|
|
16
18
|
class ExecutorHttpServer {
|
|
17
19
|
constructor(options) {
|
|
18
20
|
this.server = null;
|
|
19
21
|
this.options = options;
|
|
20
22
|
this.logger = options.logger ?? (0, console_logger_1.default)();
|
|
21
23
|
this.app = new koa_1.default();
|
|
24
|
+
this.app.use(async (ctx, next) => {
|
|
25
|
+
ctx.set('X-Executor-Version', version);
|
|
26
|
+
await next();
|
|
27
|
+
});
|
|
22
28
|
// Error-translation middleware — the single place converting thrown errors (typed HTTP
|
|
23
29
|
// errors, domain errors via toHttpError, JWT 401) into HTTP responses. Handlers just throw.
|
|
24
30
|
this.app.use(async (ctx, next) => {
|
|
@@ -157,4 +163,4 @@ class ExecutorHttpServer {
|
|
|
157
163
|
}
|
|
158
164
|
}
|
|
159
165
|
exports.default = ExecutorHttpServer;
|
|
160
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
166
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0b3ItaHR0cC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9leGVjdXRvci1odHRwLXNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUtBLGlFQUF5QztBQUN6Qyx5REFBaUM7QUFDakMsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUN0QixzREFBNkI7QUFFN0IsbURBQXdFO0FBQ3hFLCtDQUt1QjtBQUN2Qix3RUFBcUQ7QUFDckQsZ0ZBQTZEO0FBQzdELHNDQUFnRDtBQUVoRCx5R0FBeUc7QUFDekcsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBd0IsQ0FBQztBQVV6RSxNQUFxQixrQkFBa0I7SUFNckMsWUFBWSxPQUFrQztRQUZ0QyxXQUFNLEdBQWtCLElBQUksQ0FBQztRQUduQyxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBQSx3QkFBbUIsR0FBRSxDQUFDO1FBQ3RELElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztRQUVyQixJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO1lBQy9CLEdBQUcsQ0FBQyxHQUFHLENBQUMsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDdkMsTUFBTSxJQUFJLEVBQUUsQ0FBQztRQUNmLENBQUMsQ0FBQyxDQUFDO1FBRUgsdUZBQXVGO1FBQ3ZGLDRGQUE0RjtRQUM1RixJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO1lBQy9CLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksRUFBRSxDQUFDO1lBQ2YsQ0FBQztZQUFDLE9BQU8sR0FBWSxFQUFFLENBQUM7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
|
|
@@ -14,6 +14,7 @@ export default class DatabaseStore implements RunStore {
|
|
|
14
14
|
private get tableId();
|
|
15
15
|
private get tableReference();
|
|
16
16
|
init(logger?: Logger): Promise<void>;
|
|
17
|
+
private withMigrationLock;
|
|
17
18
|
getStepExecutions(runId: string): Promise<StepExecutionData[]>;
|
|
18
19
|
saveStepExecution(runId: string, stepExecution: StepExecutionData): Promise<void>;
|
|
19
20
|
close(logger?: Logger): Promise<void>;
|
|
@@ -5,6 +5,8 @@ const umzug_1 = require("umzug");
|
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
const TABLE_NAME = 'workflow_step_executions';
|
|
7
7
|
const DEFAULT_SCHEMA = 'forest';
|
|
8
|
+
// Must stay constant across releases, or an old and a new deploy could migrate concurrently.
|
|
9
|
+
const MIGRATION_ADVISORY_LOCK_KEY = 6438071259157;
|
|
8
10
|
class DatabaseStore {
|
|
9
11
|
constructor(options) {
|
|
10
12
|
this.sequelize = options.sequelize;
|
|
@@ -28,43 +30,50 @@ class DatabaseStore {
|
|
|
28
30
|
{
|
|
29
31
|
name: '001_create_workflow_step_executions',
|
|
30
32
|
up: async ({ context }) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
33
|
+
// Atomic (table + indexes) and idempotent so a half-applied or already-applied run
|
|
34
|
+
// can't crash-loop boot.
|
|
35
|
+
await context.sequelize.transaction(async (transaction) => {
|
|
36
|
+
if (await context.tableExists(tableId, { transaction }))
|
|
37
|
+
return;
|
|
38
|
+
await context.createTable(tableId, {
|
|
39
|
+
id: {
|
|
40
|
+
type: sequelize_1.DataTypes.INTEGER,
|
|
41
|
+
primaryKey: true,
|
|
42
|
+
autoIncrement: true,
|
|
43
|
+
},
|
|
44
|
+
runId: {
|
|
45
|
+
type: sequelize_1.DataTypes.STRING(255),
|
|
46
|
+
allowNull: false,
|
|
47
|
+
field: 'run_id',
|
|
48
|
+
},
|
|
49
|
+
stepIndex: {
|
|
50
|
+
type: sequelize_1.DataTypes.INTEGER,
|
|
51
|
+
allowNull: false,
|
|
52
|
+
field: 'step_index',
|
|
53
|
+
},
|
|
54
|
+
data: {
|
|
55
|
+
type: sequelize_1.DataTypes.JSON,
|
|
56
|
+
allowNull: false,
|
|
57
|
+
},
|
|
58
|
+
createdAt: {
|
|
59
|
+
type: sequelize_1.DataTypes.DATE,
|
|
60
|
+
allowNull: false,
|
|
61
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
62
|
+
field: 'created_at',
|
|
63
|
+
},
|
|
64
|
+
updatedAt: {
|
|
65
|
+
type: sequelize_1.DataTypes.DATE,
|
|
66
|
+
allowNull: false,
|
|
67
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
68
|
+
field: 'updated_at',
|
|
69
|
+
},
|
|
70
|
+
}, { transaction });
|
|
71
|
+
await context.addIndex(tableId, ['run_id'], { name: 'idx_run_id', transaction });
|
|
72
|
+
await context.addIndex(tableId, ['run_id', 'step_index'], {
|
|
73
|
+
unique: true,
|
|
74
|
+
name: 'idx_run_id_step_index',
|
|
75
|
+
transaction,
|
|
76
|
+
});
|
|
68
77
|
});
|
|
69
78
|
},
|
|
70
79
|
down: async ({ context }) => {
|
|
@@ -81,10 +90,21 @@ class DatabaseStore {
|
|
|
81
90
|
});
|
|
82
91
|
return this.callPort('init', async () => {
|
|
83
92
|
try {
|
|
93
|
+
if (this.sequelize.getDialect() !== 'postgres') {
|
|
94
|
+
await umzug.up();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// The migration lock holds one pool connection while umzug opens a second.
|
|
98
|
+
const { pool } = this.sequelize.connectionManager;
|
|
99
|
+
const poolMax = pool?.maxSize ?? 1;
|
|
100
|
+
if (poolMax < 2) {
|
|
101
|
+
throw new Error('workflow-executor requires pool.max >= 2 on Postgres: the migration lock holds one connection while migrations run on another');
|
|
102
|
+
}
|
|
103
|
+
// Schema in its own committed transaction so umzug (on other connections) sees it.
|
|
84
104
|
if (schema) {
|
|
85
|
-
await this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"
|
|
105
|
+
await this.withMigrationLock(transaction => this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`, { transaction }));
|
|
86
106
|
}
|
|
87
|
-
await umzug.up();
|
|
107
|
+
await this.withMigrationLock(() => umzug.up());
|
|
88
108
|
}
|
|
89
109
|
catch (error) {
|
|
90
110
|
logger?.('Error', 'Database migration failed', {
|
|
@@ -94,6 +114,22 @@ class DatabaseStore {
|
|
|
94
114
|
}
|
|
95
115
|
});
|
|
96
116
|
}
|
|
117
|
+
// Serializes booting instances via a transaction-scoped advisory lock: auto-releases at commit
|
|
118
|
+
// and is pooler-safe (RDS Proxy / PgBouncer), unlike a session lock which would leak there.
|
|
119
|
+
async withMigrationLock(run) {
|
|
120
|
+
await this.sequelize.transaction(async (transaction) => {
|
|
121
|
+
// Stop a client idle-in-transaction timeout from killing this idle txn mid-migration,
|
|
122
|
+
// which would drop the lock.
|
|
123
|
+
await this.sequelize.query('SET LOCAL idle_in_transaction_session_timeout = 0', {
|
|
124
|
+
transaction,
|
|
125
|
+
});
|
|
126
|
+
await this.sequelize.query('SELECT pg_advisory_xact_lock($1)', {
|
|
127
|
+
bind: [MIGRATION_ADVISORY_LOCK_KEY],
|
|
128
|
+
transaction,
|
|
129
|
+
});
|
|
130
|
+
await run(transaction);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
97
133
|
async getStepExecutions(runId) {
|
|
98
134
|
return this.callPort('getStepExecutions', async () => {
|
|
99
135
|
const [rows] = await this.sequelize.query(`SELECT data FROM ${this.tableReference} WHERE run_id = :runId ORDER BY step_index ASC`, { replacements: { runId } });
|
|
@@ -136,4 +172,4 @@ class DatabaseStore {
|
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
exports.default = DatabaseStore;
|
|
139
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
175
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2Utc3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmVzL2RhdGFiYXNlLXN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBS0EseUNBQXNDO0FBQ3RDLGlDQUFnRDtBQUVoRCxzQ0FBMEY7QUFFMUYsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFDOUMsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDO0FBRWhDLDZGQUE2RjtBQUM3RixNQUFNLDJCQUEyQixHQUFHLGFBQWlCLENBQUM7QUFPdEQsTUFBcUIsYUFBYTtJQUtoQyxZQUFZLE9BQTZCO1FBQ3ZDLElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUNuQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUN6QyxDQUFDO0lBRUQsSUFBWSxNQUFNO1FBQ2hCLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxRQUFRO1lBQUUsT0FBTyxTQUFTLENBQUM7UUFFL0QsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLElBQUksY0FBYyxDQUFDO0lBQ2pELENBQUM7SUFFRCxJQUFZLE9BQU87UUFDakIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDO0lBQ25GLENBQUM7SUFFRCxJQUFZLGNBQWM7UUFDeEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksVUFBVSxHQUFHLENBQUM7SUFDOUUsQ0FBQztJQUVELEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBZTtRQUN4QixNQUFNLEVBQUUsTUFBTSxFQUFFLE9B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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forestadmin/workflow-executor",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"forest-workflow-executor": "dist/cli.js"
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@types/koa": "^2.13.5",
|
|
48
48
|
"@types/koa__router": "^12.0.4",
|
|
49
49
|
"@types/sequelize": "^6.12.0",
|
|
50
|
+
"pg": "^8.8.0",
|
|
50
51
|
"sqlite3": "^6.0.1",
|
|
51
52
|
"supertest": "^7.1.3"
|
|
52
53
|
}
|