@forestadmin/workflow-executor 1.12.0 → 1.14.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/README.md +4 -0
- package/dist/adapters/agent-client-agent-port.d.ts +1 -1
- package/dist/adapters/agent-client-agent-port.js +3 -3
- package/dist/adapters/ai-client-adapter.d.ts +5 -1
- package/dist/adapters/ai-client-adapter.js +4 -1
- package/dist/adapters/always-error-ai-model-port.d.ts +5 -1
- package/dist/adapters/always-error-ai-model-port.js +6 -1
- package/dist/adapters/server-ai-adapter.d.ts +5 -1
- package/dist/adapters/server-ai-adapter.js +4 -1
- package/dist/adapters/step-outcome-to-update-step-mapper.js +4 -1
- package/dist/build-workflow-executor.js +87 -2
- package/dist/crypto/credential-encryption.d.ts +10 -0
- package/dist/crypto/credential-encryption.js +75 -0
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +5 -2
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +50 -2
- package/dist/executors/mcp-step-executor.d.ts +7 -2
- package/dist/executors/mcp-step-executor.js +78 -14
- package/dist/executors/step-executor-factory.d.ts +1 -1
- package/dist/executors/step-executor-factory.js +24 -3
- package/dist/executors/trigger-record-action-step-executor.js +10 -4
- package/dist/http/executor-http-server.d.ts +12 -0
- package/dist/http/executor-http-server.js +90 -1
- package/dist/http/mcp-oauth-credentials.d.ts +24 -0
- package/dist/http/mcp-oauth-credentials.js +77 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +10 -3
- package/dist/oauth/keyed-mutex.d.ts +5 -0
- package/dist/oauth/keyed-mutex.js +27 -0
- package/dist/oauth/refresh-grant.d.ts +15 -0
- package/dist/oauth/refresh-grant.js +112 -0
- package/dist/oauth/token-endpoint-url.d.ts +2 -0
- package/dist/oauth/token-endpoint-url.js +103 -0
- package/dist/oauth/token-service.d.ts +34 -0
- package/dist/oauth/token-service.js +172 -0
- package/dist/ports/agent-port.d.ts +1 -0
- package/dist/ports/ai-model-port.d.ts +5 -1
- package/dist/ports/mcp-oauth-credentials-store.d.ts +24 -0
- package/dist/ports/mcp-oauth-credentials-store.js +3 -0
- package/dist/ports/run-store.d.ts +1 -0
- package/dist/remote-tool-fetcher.d.ts +7 -2
- package/dist/remote-tool-fetcher.js +53 -6
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +3 -3
- package/dist/stores/database-mcp-oauth-credentials-store.d.ts +22 -0
- package/dist/stores/database-mcp-oauth-credentials-store.js +188 -0
- package/dist/stores/database-store.d.ts +1 -1
- package/dist/stores/database-store.js +17 -50
- package/dist/stores/in-memory-mcp-oauth-credentials-store.d.ts +13 -0
- package/dist/stores/in-memory-mcp-oauth-credentials-store.js +44 -0
- package/dist/stores/in-memory-store.d.ts +1 -0
- package/dist/stores/in-memory-store.js +6 -1
- package/dist/stores/schema-migrations.d.ts +19 -0
- package/dist/stores/schema-migrations.js +69 -0
- package/dist/types/validated/execution.d.ts +6 -0
- package/dist/types/validated/step-outcome.d.ts +10 -0
- package/dist/types/validated/step-outcome.js +7 -2
- package/package.json +4 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Logger } from '../ports/logger-port';
|
|
2
|
+
import type { McpOAuthCredentialInput, McpOAuthCredentialsStore, StoredMcpOAuthCredential } from '../ports/mcp-oauth-credentials-store';
|
|
3
|
+
import type { Sequelize } from 'sequelize';
|
|
4
|
+
export interface DatabaseMcpOAuthCredentialsStoreOptions {
|
|
5
|
+
sequelize: Sequelize;
|
|
6
|
+
schema?: string;
|
|
7
|
+
}
|
|
8
|
+
export default class DatabaseMcpOAuthCredentialsStore implements McpOAuthCredentialsStore {
|
|
9
|
+
private readonly sequelize;
|
|
10
|
+
private readonly configuredSchema?;
|
|
11
|
+
constructor(options: DatabaseMcpOAuthCredentialsStoreOptions);
|
|
12
|
+
private get schema();
|
|
13
|
+
private get tableReference();
|
|
14
|
+
init(logger?: Logger): Promise<void>;
|
|
15
|
+
get(userId: number, mcpServerId: string): Promise<StoredMcpOAuthCredential | null>;
|
|
16
|
+
upsert(credential: McpOAuthCredentialInput): Promise<void>;
|
|
17
|
+
updateIfPresent(id: number, credential: McpOAuthCredentialInput): Promise<void>;
|
|
18
|
+
delete(userId: number, mcpServerId: string): Promise<void>;
|
|
19
|
+
close(logger?: Logger): Promise<void>;
|
|
20
|
+
private static toCredential;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=database-mcp-oauth-credentials-store.d.ts.map
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const sequelize_1 = require("sequelize");
|
|
4
|
+
const umzug_1 = require("umzug");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const schema_migrations_1 = require("./schema-migrations");
|
|
7
|
+
const TABLE_NAME = 'ai_mcp_oauth_credentials';
|
|
8
|
+
class DatabaseMcpOAuthCredentialsStore {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.sequelize = options.sequelize;
|
|
11
|
+
this.configuredSchema = options.schema;
|
|
12
|
+
}
|
|
13
|
+
get schema() {
|
|
14
|
+
return (0, schema_migrations_1.resolveSchema)(this.sequelize, this.configuredSchema);
|
|
15
|
+
}
|
|
16
|
+
get tableReference() {
|
|
17
|
+
return (0, schema_migrations_1.tableReference)(this.schema, TABLE_NAME);
|
|
18
|
+
}
|
|
19
|
+
async init(logger) {
|
|
20
|
+
const { schema } = this;
|
|
21
|
+
const tableId = (0, schema_migrations_1.tableId)(schema, TABLE_NAME);
|
|
22
|
+
const umzug = new umzug_1.Umzug({
|
|
23
|
+
migrations: [
|
|
24
|
+
{
|
|
25
|
+
name: '002_create_mcp_oauth_credentials',
|
|
26
|
+
up: async ({ context }) => {
|
|
27
|
+
// Atomic (table + index) and idempotent so a half-applied or already-applied run can't
|
|
28
|
+
// crash-loop boot.
|
|
29
|
+
await context.sequelize.transaction(async (transaction) => {
|
|
30
|
+
if (await context.tableExists(tableId, { transaction }))
|
|
31
|
+
return;
|
|
32
|
+
await context.createTable(tableId, {
|
|
33
|
+
id: { type: sequelize_1.DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
34
|
+
userId: { type: sequelize_1.DataTypes.INTEGER, allowNull: false, field: 'user_id' },
|
|
35
|
+
mcpServerId: {
|
|
36
|
+
type: sequelize_1.DataTypes.STRING(255),
|
|
37
|
+
allowNull: false,
|
|
38
|
+
field: 'mcp_server_id',
|
|
39
|
+
},
|
|
40
|
+
refreshTokenEnc: {
|
|
41
|
+
type: sequelize_1.DataTypes.BLOB,
|
|
42
|
+
allowNull: false,
|
|
43
|
+
field: 'refresh_token_enc',
|
|
44
|
+
},
|
|
45
|
+
clientId: { type: sequelize_1.DataTypes.STRING(255), allowNull: true, field: 'client_id' },
|
|
46
|
+
clientSecretEnc: {
|
|
47
|
+
type: sequelize_1.DataTypes.BLOB,
|
|
48
|
+
allowNull: true,
|
|
49
|
+
field: 'client_secret_enc',
|
|
50
|
+
},
|
|
51
|
+
clientSecretExpiresAt: {
|
|
52
|
+
type: sequelize_1.DataTypes.DATE,
|
|
53
|
+
allowNull: true,
|
|
54
|
+
field: 'client_secret_expires_at',
|
|
55
|
+
},
|
|
56
|
+
tokenEndpoint: {
|
|
57
|
+
type: sequelize_1.DataTypes.STRING(2048),
|
|
58
|
+
allowNull: false,
|
|
59
|
+
field: 'token_endpoint',
|
|
60
|
+
},
|
|
61
|
+
tokenEndpointAuthMethod: {
|
|
62
|
+
type: sequelize_1.DataTypes.STRING(64),
|
|
63
|
+
allowNull: true,
|
|
64
|
+
field: 'token_endpoint_auth_method',
|
|
65
|
+
},
|
|
66
|
+
scopes: { type: sequelize_1.DataTypes.STRING(2048), allowNull: true, field: 'scopes' },
|
|
67
|
+
createdAt: {
|
|
68
|
+
type: sequelize_1.DataTypes.DATE,
|
|
69
|
+
allowNull: false,
|
|
70
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
71
|
+
field: 'created_at',
|
|
72
|
+
},
|
|
73
|
+
updatedAt: {
|
|
74
|
+
type: sequelize_1.DataTypes.DATE,
|
|
75
|
+
allowNull: false,
|
|
76
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
77
|
+
field: 'updated_at',
|
|
78
|
+
},
|
|
79
|
+
}, { transaction });
|
|
80
|
+
await context.addIndex(tableId, ['user_id', 'mcp_server_id'], {
|
|
81
|
+
unique: true,
|
|
82
|
+
name: 'idx_user_id_mcp_server_id',
|
|
83
|
+
transaction,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
down: async ({ context }) => {
|
|
88
|
+
await context.dropTable(tableId);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
context: this.sequelize.getQueryInterface(),
|
|
93
|
+
storage: new umzug_1.SequelizeStorage({
|
|
94
|
+
sequelize: this.sequelize,
|
|
95
|
+
...(schema ? { schema } : {}),
|
|
96
|
+
}),
|
|
97
|
+
logger: undefined,
|
|
98
|
+
});
|
|
99
|
+
await (0, schema_migrations_1.runMigrations)({
|
|
100
|
+
sequelize: this.sequelize,
|
|
101
|
+
umzug,
|
|
102
|
+
schema,
|
|
103
|
+
logger,
|
|
104
|
+
failMessage: 'MCP OAuth credentials migration failed',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async get(userId, mcpServerId) {
|
|
108
|
+
const [rows] = await this.sequelize.query(`SELECT * FROM ${this.tableReference} WHERE user_id = :userId AND mcp_server_id = :mcpServerId`, { replacements: { userId, mcpServerId } });
|
|
109
|
+
const row = rows[0];
|
|
110
|
+
return row ? DatabaseMcpOAuthCredentialsStore.toCredential(row) : null;
|
|
111
|
+
}
|
|
112
|
+
async upsert(credential) {
|
|
113
|
+
await this.sequelize.transaction(async (transaction) => {
|
|
114
|
+
const now = new Date();
|
|
115
|
+
const replacements = {
|
|
116
|
+
userId: credential.userId,
|
|
117
|
+
mcpServerId: credential.mcpServerId,
|
|
118
|
+
refreshTokenEnc: credential.refreshTokenEnc,
|
|
119
|
+
clientId: credential.clientId ?? null,
|
|
120
|
+
clientSecretEnc: credential.clientSecretEnc ?? null,
|
|
121
|
+
clientSecretExpiresAt: credential.clientSecretExpiresAt ?? null,
|
|
122
|
+
tokenEndpoint: credential.tokenEndpoint,
|
|
123
|
+
tokenEndpointAuthMethod: credential.tokenEndpointAuthMethod ?? null,
|
|
124
|
+
scopes: credential.scopes ?? null,
|
|
125
|
+
now,
|
|
126
|
+
};
|
|
127
|
+
// Delete + insert in transaction: dialect-agnostic upsert (avoids ON CONFLICT / ON DUPLICATE).
|
|
128
|
+
await this.sequelize.query(`DELETE FROM ${this.tableReference} WHERE user_id = :userId AND mcp_server_id = :mcpServerId`, { replacements, transaction });
|
|
129
|
+
await this.sequelize.query(`INSERT INTO ${this.tableReference} ` +
|
|
130
|
+
'(user_id, mcp_server_id, refresh_token_enc, client_id, client_secret_enc, ' +
|
|
131
|
+
'client_secret_expires_at, token_endpoint, token_endpoint_auth_method, scopes, ' +
|
|
132
|
+
'created_at, updated_at) VALUES ' +
|
|
133
|
+
'(:userId, :mcpServerId, :refreshTokenEnc, :clientId, :clientSecretEnc, ' +
|
|
134
|
+
':clientSecretExpiresAt, :tokenEndpoint, :tokenEndpointAuthMethod, :scopes, ' +
|
|
135
|
+
':now, :now)', { replacements, transaction });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async updateIfPresent(id, credential) {
|
|
139
|
+
// Single atomic UPDATE … WHERE id — affects zero rows if that row was deleted or re-created.
|
|
140
|
+
await this.sequelize.query(`UPDATE ${this.tableReference} SET ` +
|
|
141
|
+
'refresh_token_enc = :refreshTokenEnc, client_id = :clientId, ' +
|
|
142
|
+
'client_secret_enc = :clientSecretEnc, client_secret_expires_at = :clientSecretExpiresAt, ' +
|
|
143
|
+
'token_endpoint = :tokenEndpoint, token_endpoint_auth_method = :tokenEndpointAuthMethod, ' +
|
|
144
|
+
'scopes = :scopes, updated_at = :now ' +
|
|
145
|
+
'WHERE id = :id', {
|
|
146
|
+
replacements: {
|
|
147
|
+
id,
|
|
148
|
+
refreshTokenEnc: credential.refreshTokenEnc,
|
|
149
|
+
clientId: credential.clientId ?? null,
|
|
150
|
+
clientSecretEnc: credential.clientSecretEnc ?? null,
|
|
151
|
+
clientSecretExpiresAt: credential.clientSecretExpiresAt ?? null,
|
|
152
|
+
tokenEndpoint: credential.tokenEndpoint,
|
|
153
|
+
tokenEndpointAuthMethod: credential.tokenEndpointAuthMethod ?? null,
|
|
154
|
+
scopes: credential.scopes ?? null,
|
|
155
|
+
now: new Date(),
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async delete(userId, mcpServerId) {
|
|
160
|
+
await this.sequelize.query(`DELETE FROM ${this.tableReference} WHERE user_id = :userId AND mcp_server_id = :mcpServerId`, { replacements: { userId, mcpServerId } });
|
|
161
|
+
}
|
|
162
|
+
async close(logger) {
|
|
163
|
+
try {
|
|
164
|
+
await this.sequelize.close();
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
logger?.('Error', 'Failed to close database connection', {
|
|
168
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
static toCredential(row) {
|
|
173
|
+
return {
|
|
174
|
+
id: Number(row.id),
|
|
175
|
+
userId: Number(row.user_id),
|
|
176
|
+
mcpServerId: row.mcp_server_id,
|
|
177
|
+
refreshTokenEnc: row.refresh_token_enc,
|
|
178
|
+
clientId: row.client_id ?? null,
|
|
179
|
+
clientSecretEnc: row.client_secret_enc ?? null,
|
|
180
|
+
clientSecretExpiresAt: row.client_secret_expires_at == null ? null : new Date(row.client_secret_expires_at),
|
|
181
|
+
tokenEndpoint: row.token_endpoint,
|
|
182
|
+
tokenEndpointAuthMethod: row.token_endpoint_auth_method ?? null,
|
|
183
|
+
scopes: row.scopes ?? null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.default = DatabaseMcpOAuthCredentialsStore;
|
|
188
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2UtbWNwLW9hdXRoLWNyZWRlbnRpYWxzLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9kYXRhYmFzZS1tY3Atb2F1dGgtY3JlZGVudGlhbHMtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFRQSx5Q0FBc0M7QUFDdEMsaUNBQWdEO0FBRWhELHNDQUFnRDtBQUNoRCwyREFLNkI7QUFFN0IsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFvQjlDLE1BQXFCLGdDQUFnQztJQUtuRCxZQUFZLE9BQWdEO1FBQzFELElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUNuQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUN6QyxDQUFDO0lBRUQsSUFBWSxNQUFNO1FBQ2hCLE9BQU8sSUFBQSxpQ0FBYSxFQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVELElBQVksY0FBYztRQUN4QixPQUFPLElBQUEsa0NBQWdCLEVBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFlO1FBQ3hCLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBQSwyQkFBUyxFQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztRQUU5QyxNQUFNLEtBQUssR0FBRyxJQUFJLGFBQUssQ0FBQztZQUN0QixVQUFVLEVBQUU7Z0JBQ1Y7b0JBQ0UsSUFBSSxFQUFFLGtDQUFrQztvQkFDeEMsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLE9BQU8sRUFBK0IsRUFBRSxFQUFFO3dCQUNyRCx1RkFBdUY7d0JBQ3ZGLG1CQUFtQjt3QkFDbkIsTUFBTSxPQUFP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
|
|
@@ -14,9 +14,9 @@ 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;
|
|
18
17
|
getStepExecutions(runId: string): Promise<StepExecutionData[]>;
|
|
19
18
|
saveStepExecution(runId: string, stepExecution: StepExecutionData): Promise<void>;
|
|
19
|
+
deleteStepExecution(runId: string, stepIndex: number): Promise<void>;
|
|
20
20
|
close(logger?: Logger): Promise<void>;
|
|
21
21
|
private callPort;
|
|
22
22
|
}
|
|
@@ -3,25 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const sequelize_1 = require("sequelize");
|
|
4
4
|
const umzug_1 = require("umzug");
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
|
+
const schema_migrations_1 = require("./schema-migrations");
|
|
6
7
|
const TABLE_NAME = 'workflow_step_executions';
|
|
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;
|
|
10
8
|
class DatabaseStore {
|
|
11
9
|
constructor(options) {
|
|
12
10
|
this.sequelize = options.sequelize;
|
|
13
11
|
this.configuredSchema = options.schema;
|
|
14
12
|
}
|
|
15
13
|
get schema() {
|
|
16
|
-
|
|
17
|
-
return undefined;
|
|
18
|
-
return this.configuredSchema || DEFAULT_SCHEMA;
|
|
14
|
+
return (0, schema_migrations_1.resolveSchema)(this.sequelize, this.configuredSchema);
|
|
19
15
|
}
|
|
20
16
|
get tableId() {
|
|
21
|
-
return
|
|
17
|
+
return (0, schema_migrations_1.tableId)(this.schema, TABLE_NAME);
|
|
22
18
|
}
|
|
23
19
|
get tableReference() {
|
|
24
|
-
return
|
|
20
|
+
return (0, schema_migrations_1.tableReference)(this.schema, TABLE_NAME);
|
|
25
21
|
}
|
|
26
22
|
async init(logger) {
|
|
27
23
|
const { schema, tableId } = this;
|
|
@@ -88,47 +84,13 @@ class DatabaseStore {
|
|
|
88
84
|
}),
|
|
89
85
|
logger: undefined,
|
|
90
86
|
});
|
|
91
|
-
return this.callPort('init',
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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.
|
|
104
|
-
if (schema) {
|
|
105
|
-
await this.withMigrationLock(transaction => this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`, { transaction }));
|
|
106
|
-
}
|
|
107
|
-
await this.withMigrationLock(() => umzug.up());
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
logger?.('Error', 'Database migration failed', {
|
|
111
|
-
error: (0, errors_1.extractErrorMessage)(error),
|
|
112
|
-
});
|
|
113
|
-
throw error;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
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
|
-
});
|
|
87
|
+
return this.callPort('init', () => (0, schema_migrations_1.runMigrations)({
|
|
88
|
+
sequelize: this.sequelize,
|
|
89
|
+
umzug,
|
|
90
|
+
schema,
|
|
91
|
+
logger,
|
|
92
|
+
failMessage: 'Database migration failed',
|
|
93
|
+
}));
|
|
132
94
|
}
|
|
133
95
|
async getStepExecutions(runId) {
|
|
134
96
|
return this.callPort('getStepExecutions', async () => {
|
|
@@ -148,6 +110,11 @@ class DatabaseStore {
|
|
|
148
110
|
});
|
|
149
111
|
});
|
|
150
112
|
}
|
|
113
|
+
async deleteStepExecution(runId, stepIndex) {
|
|
114
|
+
return this.callPort('deleteStepExecution', async () => {
|
|
115
|
+
await this.sequelize.query(`DELETE FROM ${this.tableReference} WHERE run_id = :runId AND step_index = :stepIndex`, { replacements: { runId, stepIndex } });
|
|
116
|
+
});
|
|
117
|
+
}
|
|
151
118
|
async close(logger) {
|
|
152
119
|
return this.callPort('close', async () => {
|
|
153
120
|
try {
|
|
@@ -172,4 +139,4 @@ class DatabaseStore {
|
|
|
172
139
|
}
|
|
173
140
|
}
|
|
174
141
|
exports.default = DatabaseStore;
|
|
175
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2Utc3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmVzL2RhdGFiYXNlLXN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBS0EseUNBQXNDO0FBQ3RDLGlDQUFnRDtBQUVoRCxzQ0FBMEY7QUFDMUYsMkRBSzZCO0FBRTdCLE1BQU0sVUFBVSxHQUFHLDBCQUEwQixDQUFDO0FBTzlDLE1BQXFCLGFBQWE7SUFLaEMsWUFBWSxPQUE2QjtRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUM7UUFDbkMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUM7SUFDekMsQ0FBQztJQUVELElBQVksTUFBTTtRQUNoQixPQUFPLElBQUEsaUNBQWEsRUFBQyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRCxJQUFZLE9BQU87UUFDakIsT0FBTyxJQUFBLDJCQUFTLEVBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsSUFBWSxjQUFjO1FBQ3hCLE9BQU8sSUFBQSxrQ0FBZ0IsRUFBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQWU7UUFDeEIsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFFakMsTUFBTSxLQUFLLEdBQUcsSUFBSSxhQUFLLENBQUM7WUFDdEIsVUFBVSxFQUFFO2dCQUNWO29CQUNFLElBQUksRUFBRSxxQ0FBcUM7b0JBQzNDLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQStCLEVBQUUsRUFBRTt3QkFDckQsbUZBQW1GO3dCQUNuRix5QkFBeUI7d0JBQ3pCLE1BQU0sT0FBTyxDQUFDLFNBQVMs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
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { McpOAuthCredentialInput, McpOAuthCredentialsStore, StoredMcpOAuthCredential } from '../ports/mcp-oauth-credentials-store';
|
|
2
|
+
export default class InMemoryMcpOAuthCredentialsStore implements McpOAuthCredentialsStore {
|
|
3
|
+
private readonly data;
|
|
4
|
+
private nextId;
|
|
5
|
+
init(): Promise<void>;
|
|
6
|
+
close(): Promise<void>;
|
|
7
|
+
get(userId: number, mcpServerId: string): Promise<StoredMcpOAuthCredential | null>;
|
|
8
|
+
upsert(credential: McpOAuthCredentialInput): Promise<void>;
|
|
9
|
+
updateIfPresent(id: number, credential: McpOAuthCredentialInput): Promise<void>;
|
|
10
|
+
delete(userId: number, mcpServerId: string): Promise<void>;
|
|
11
|
+
private static key;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=in-memory-mcp-oauth-credentials-store.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// In-memory MCP OAuth credentials store for --in-memory / dev. Same throwaway semantics as
|
|
4
|
+
// InMemoryStore (the run store): state is lost on restart. It holds the already-encrypted Buffers
|
|
5
|
+
// produced by buildMcpOAuthCredentialInput — there is no crypto here, just a Map keyed by
|
|
6
|
+
// (userId, mcpServerId), mirroring the DB store's one-row-per-key contract.
|
|
7
|
+
class InMemoryMcpOAuthCredentialsStore {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.data = new Map();
|
|
10
|
+
this.nextId = 1;
|
|
11
|
+
}
|
|
12
|
+
async init() {
|
|
13
|
+
// No-op: nothing to migrate for an in-memory store.
|
|
14
|
+
}
|
|
15
|
+
async close() {
|
|
16
|
+
// No-op: nothing to close.
|
|
17
|
+
}
|
|
18
|
+
async get(userId, mcpServerId) {
|
|
19
|
+
return this.data.get(InMemoryMcpOAuthCredentialsStore.key(userId, mcpServerId)) ?? null;
|
|
20
|
+
}
|
|
21
|
+
async upsert(credential) {
|
|
22
|
+
// Overwrite in place — one row per (userId, mcpServerId). A fresh id each time mirrors the DB
|
|
23
|
+
// store's delete-then-insert; nothing relies on id stability.
|
|
24
|
+
const key = InMemoryMcpOAuthCredentialsStore.key(credential.userId, credential.mcpServerId);
|
|
25
|
+
this.data.set(key, { ...credential, id: this.nextId });
|
|
26
|
+
this.nextId += 1;
|
|
27
|
+
}
|
|
28
|
+
async updateIfPresent(id, credential) {
|
|
29
|
+
const key = InMemoryMcpOAuthCredentialsStore.key(credential.userId, credential.mcpServerId);
|
|
30
|
+
const existing = this.data.get(key);
|
|
31
|
+
// Only update the exact row the caller read; a re-created row has a new id, so skip it.
|
|
32
|
+
if (!existing || existing.id !== id)
|
|
33
|
+
return;
|
|
34
|
+
this.data.set(key, { ...credential, id });
|
|
35
|
+
}
|
|
36
|
+
async delete(userId, mcpServerId) {
|
|
37
|
+
this.data.delete(InMemoryMcpOAuthCredentialsStore.key(userId, mcpServerId));
|
|
38
|
+
}
|
|
39
|
+
static key(userId, mcpServerId) {
|
|
40
|
+
return `${userId}:${mcpServerId}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.default = InMemoryMcpOAuthCredentialsStore;
|
|
44
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tbWVtb3J5LW1jcC1vYXV0aC1jcmVkZW50aWFscy1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZXMvaW4tbWVtb3J5LW1jcC1vYXV0aC1jcmVkZW50aWFscy1zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQU1BLDJGQUEyRjtBQUMzRixrR0FBa0c7QUFDbEcsMEZBQTBGO0FBQzFGLDRFQUE0RTtBQUM1RSxNQUFxQixnQ0FBZ0M7SUFBckQ7UUFDbUIsU0FBSSxHQUFHLElBQUksR0FBRyxFQUFvQyxDQUFDO1FBQzVELFdBQU0sR0FBRyxDQUFDLENBQUM7SUFzQ3JCLENBQUM7SUFwQ0MsS0FBSyxDQUFDLElBQUk7UUFDUixvREFBb0Q7SUFDdEQsQ0FBQztJQUVELEtBQUssQ0FBQyxLQUFLO1FBQ1QsMkJBQTJCO0lBQzdCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLE1BQWMsRUFBRSxXQUFtQjtRQUMzQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLGdDQUFnQyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDMUYsQ0FBQztJQUVELEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBbUM7UUFDOUMsOEZBQThGO1FBQzlGLDhEQUE4RDtRQUM5RCxNQUFNLEdBQUcsR0FBRyxnQ0FBZ0MsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDNUYsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxVQUFVLEVBQUUsRUFBRSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZELElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDO0lBQ25CLENBQUM7SUFFRCxLQUFLLENBQUMsZUFBZSxDQUFDLEVBQVUsRUFBRSxVQUFtQztRQUNuRSxNQUFNLEdBQUcsR0FBRyxnQ0FBZ0MsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDNUYsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDcEMsd0ZBQXdGO1FBQ3hGLElBQUksQ0FBQyxRQUFRLElBQUksUUFBUSxDQUFDLEVBQUUsS0FBSyxFQUFFO1lBQUUsT0FBTztRQUU1QyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxHQUFHLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFRCxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQWMsRUFBRSxXQUFtQjtRQUM5QyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxnQ0FBZ0MsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUVPLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBYyxFQUFFLFdBQW1CO1FBQ3BELE9BQU8sR0FBRyxNQUFNLElBQUksV0FBVyxFQUFFLENBQUM7SUFDcEMsQ0FBQztDQUNGO0FBeENELG1EQXdDQyJ9
|
|
@@ -6,6 +6,7 @@ export default class InMemoryStore implements RunStore {
|
|
|
6
6
|
close(): Promise<void>;
|
|
7
7
|
getStepExecutions(runId: string): Promise<StepExecutionData[]>;
|
|
8
8
|
saveStepExecution(runId: string, stepExecution: StepExecutionData): Promise<void>;
|
|
9
|
+
deleteStepExecution(runId: string, stepIndex: number): Promise<void>;
|
|
9
10
|
private callPort;
|
|
10
11
|
}
|
|
11
12
|
//# sourceMappingURL=in-memory-store.d.ts.map
|
|
@@ -33,6 +33,11 @@ class InMemoryStore {
|
|
|
33
33
|
runData.set(stepExecution.stepIndex, stepExecution);
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
async deleteStepExecution(runId, stepIndex) {
|
|
37
|
+
return this.callPort('deleteStepExecution', async () => {
|
|
38
|
+
this.data.get(runId)?.delete(stepIndex);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
36
41
|
async callPort(operation, fn) {
|
|
37
42
|
try {
|
|
38
43
|
return await fn();
|
|
@@ -45,4 +50,4 @@ class InMemoryStore {
|
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
exports.default = InMemoryStore;
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
53
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tbWVtb3J5LXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9pbi1tZW1vcnktc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFHQSxzQ0FBcUU7QUFFckUsTUFBcUIsYUFBYTtJQUFsQztRQUNtQixTQUFJLEdBQUcsSUFBSSxHQUFHLEVBQTBDLENBQUM7SUFtRDVFLENBQUM7SUFqREMsS0FBSyxDQUFDLElBQUk7UUFDUixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLEtBQUssSUFBSSxFQUFFO1lBQ3RDLG9EQUFvRDtRQUN0RCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSztRQUNULE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDdkMsNkJBQTZCO1FBQy9CLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxLQUFhO1FBQ25DLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsRUFBRSxLQUFLLElBQUksRUFBRTtZQUNuRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUVyQyxJQUFJLENBQUMsT0FBTztnQkFBRSxPQUFPLEVBQUUsQ0FBQztZQUV4QixPQUFPLENBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN6RSxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxLQUFLLENBQUMsaUJBQWlCLENBQUMsS0FBYSxFQUFFLGFBQWdDO1FBQ3JFLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsRUFBRSxLQUFLLElBQUksRUFBRTtZQUNuRCxJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUVuQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2IsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7Z0JBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNoQyxDQUFDO1lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsU0FBUyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBQ3RELENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxLQUFhLEVBQUUsU0FBaUI7UUFDeEQsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLEtBQUssSUFBSSxFQUFFO1lBQ3JELElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMxQyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxLQUFLLENBQUMsUUFBUSxDQUFJLFNBQWlCLEVBQUUsRUFBb0I7UUFDL0QsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLEVBQUUsRUFBRSxDQUFDO1FBQ3BCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxLQUFLLFlBQVksOEJBQXFCO2dCQUFFLE1BQU0sS0FBSyxDQUFDO1lBQ3hELE1BQU0sSUFBSSwwQkFBaUIsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDaEQsQ0FBQztJQUNILENBQUM7Q0FDRjtBQXBERCxnQ0FvREMifQ==
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Logger } from '../ports/logger-port';
|
|
2
|
+
import type { Sequelize } from 'sequelize';
|
|
3
|
+
export declare const DEFAULT_SCHEMA = "forest";
|
|
4
|
+
export declare function resolveSchema(sequelize: Sequelize, configured?: string): string | undefined;
|
|
5
|
+
export declare function tableId(schema: string | undefined, tableName: string): string | {
|
|
6
|
+
tableName: string;
|
|
7
|
+
schema: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function tableReference(schema: string | undefined, tableName: string): string;
|
|
10
|
+
export declare function runMigrations({ sequelize, umzug, schema, logger, failMessage, }: {
|
|
11
|
+
sequelize: Sequelize;
|
|
12
|
+
umzug: {
|
|
13
|
+
up: () => Promise<unknown>;
|
|
14
|
+
};
|
|
15
|
+
schema: string | undefined;
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
failMessage: string;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=schema-migrations.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_SCHEMA = void 0;
|
|
4
|
+
exports.resolveSchema = resolveSchema;
|
|
5
|
+
exports.tableId = tableId;
|
|
6
|
+
exports.tableReference = tableReference;
|
|
7
|
+
exports.runMigrations = runMigrations;
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
// The schema both executor tables live under, so a shared customer database stays safe: the executor
|
|
10
|
+
// never touches `public`, and its umzug `SequelizeMeta` registries can't collide with the
|
|
11
|
+
// agent/server's own. Skipped on SQLite (test suite), which has no schema support.
|
|
12
|
+
exports.DEFAULT_SCHEMA = 'forest';
|
|
13
|
+
// Must stay constant across releases, or an old and a new deploy could migrate concurrently. Shared
|
|
14
|
+
// by every executor migration runner so all of them serialize against one another on cold start.
|
|
15
|
+
const MIGRATION_ADVISORY_LOCK_KEY = 6438071259157;
|
|
16
|
+
function resolveSchema(sequelize, configured) {
|
|
17
|
+
if (sequelize.getDialect() === 'sqlite')
|
|
18
|
+
return undefined;
|
|
19
|
+
return configured || exports.DEFAULT_SCHEMA;
|
|
20
|
+
}
|
|
21
|
+
function tableId(schema, tableName) {
|
|
22
|
+
return schema ? { tableName, schema } : tableName;
|
|
23
|
+
}
|
|
24
|
+
function tableReference(schema, tableName) {
|
|
25
|
+
return schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
26
|
+
}
|
|
27
|
+
// Serializes booting instances via a transaction-scoped advisory lock: auto-releases at commit and
|
|
28
|
+
// is pooler-safe (RDS Proxy / PgBouncer), unlike a session lock which would leak there.
|
|
29
|
+
async function withMigrationLock(sequelize, run) {
|
|
30
|
+
await sequelize.transaction(async (transaction) => {
|
|
31
|
+
// Stop a client idle-in-transaction timeout from killing this idle txn mid-migration, which
|
|
32
|
+
// would drop the lock.
|
|
33
|
+
await sequelize.query('SET LOCAL idle_in_transaction_session_timeout = 0', { transaction });
|
|
34
|
+
await sequelize.query('SELECT pg_advisory_xact_lock($1)', {
|
|
35
|
+
bind: [MIGRATION_ADVISORY_LOCK_KEY],
|
|
36
|
+
transaction,
|
|
37
|
+
});
|
|
38
|
+
await run(transaction);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Runs an umzug migration set under the executor's shared-database safety rules: on Postgres the
|
|
42
|
+
// `forest` schema is created and migrations run behind the advisory lock (one writer across booting
|
|
43
|
+
// replicas); on other dialects (SQLite) it just runs. Logs `failMessage` and rethrows on failure.
|
|
44
|
+
async function runMigrations({ sequelize, umzug, schema, logger, failMessage, }) {
|
|
45
|
+
try {
|
|
46
|
+
if (sequelize.getDialect() !== 'postgres') {
|
|
47
|
+
await umzug.up();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// The migration lock holds one pool connection while umzug opens a second.
|
|
51
|
+
const { pool } = sequelize.connectionManager;
|
|
52
|
+
// With replication configured, `pool` splits into write/read sub-pools and `maxSize` is
|
|
53
|
+
// undefined; the migration lock runs on the write pool, so size the check against it.
|
|
54
|
+
const poolMax = pool?.maxSize ?? pool?.write?.maxSize ?? pool?.read?.maxSize ?? 1;
|
|
55
|
+
if (poolMax < 2) {
|
|
56
|
+
throw new Error('workflow-executor requires pool.max >= 2 on Postgres: the migration lock holds one connection while migrations run on another');
|
|
57
|
+
}
|
|
58
|
+
// Schema in its own committed transaction so umzug (on other connections) sees it.
|
|
59
|
+
if (schema) {
|
|
60
|
+
await withMigrationLock(sequelize, transaction => sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`, { transaction }));
|
|
61
|
+
}
|
|
62
|
+
await withMigrationLock(sequelize, () => umzug.up());
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger?.('Error', failMessage, { error: (0, errors_1.extractErrorMessage)(error) });
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLW1pZ3JhdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmVzL3NjaGVtYS1taWdyYXRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQWNBLHNDQUlDO0FBRUQsMEJBS0M7QUFFRCx3Q0FFQztBQXVCRCxzQ0E4Q0M7QUEvRkQsc0NBQWdEO0FBRWhELHFHQUFxRztBQUNyRywwRkFBMEY7QUFDMUYsbUZBQW1GO0FBQ3RFLFFBQUEsY0FBYyxHQUFHLFFBQVEsQ0FBQztBQUV2QyxvR0FBb0c7QUFDcEcsaUdBQWlHO0FBQ2pHLE1BQU0sMkJBQTJCLEdBQUcsYUFBaUIsQ0FBQztBQUV0RCxTQUFnQixhQUFhLENBQUMsU0FBb0IsRUFBRSxVQUFtQjtJQUNyRSxJQUFJLFNBQVMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxRQUFRO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFMUQsT0FBTyxVQUFVLElBQUksc0JBQWMsQ0FBQztBQUN0QyxDQUFDO0FBRUQsU0FBZ0IsT0FBTyxDQUNyQixNQUEwQixFQUMxQixTQUFpQjtJQUVqQixPQUFPLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNwRCxDQUFDO0FBRUQsU0FBZ0IsY0FBYyxDQUFDLE1BQTBCLEVBQUUsU0FBaUI7SUFDMUUsT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksTUFBTSxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLFNBQVMsR0FBRyxDQUFDO0FBQ2xFLENBQUM7QUFFRCxtR0FBbUc7QUFDbkcsd0ZBQXdGO0FBQ3hGLEtBQUssVUFBVSxpQkFBaUIsQ0FDOUIsU0FBb0IsRUFDcEIsR0FBbUQ7SUFFbkQsTUFBTSxTQUFTLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBQyxXQUFXLEVBQUMsRUFBRTtRQUM5Qyw0RkFBNEY7UUFDNUYsdUJBQXVCO1FBQ3ZCLE1BQU0sU0FBUyxDQUFDLEtBQUssQ0FBQyxtREFBbUQsRUFBRSxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUM7UUFDNUYsTUFBTSxTQUFTLENBQUMsS0FBSyxDQUFDLGtDQUFrQyxFQUFFO1lBQ3hELElBQUksRUFBRSxDQUFDLDJCQUEyQixDQUFDO1lBQ25DLFdBQVc7U0FDWixDQUFDLENBQUM7UUFDSCxNQUFNLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUN6QixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxpR0FBaUc7QUFDakcsb0dBQW9HO0FBQ3BHLGtHQUFrRztBQUMzRixLQUFLLFVBQVUsYUFBYSxDQUFDLEVBQ2xDLFNBQVMsRUFDVCxLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixXQUFXLEdBT1o7SUFDQyxJQUFJLENBQUM7UUFDSCxJQUFJLFNBQVMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUMxQyxNQUFNLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUVqQixPQUFPO1FBQ1QsQ0FBQztRQUVELDJFQUEyRTtRQUMzRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsU0FBUyxDQUFDLGlCQUUxQixDQUFDO1FBQ0Ysd0ZBQXdGO1FBQ3hGLHNGQUFzRjtRQUN0RixNQUFNLE9BQU8sR0FBRyxJQUFJLEVBQUUsT0FBTyxJQUFJLElBQUksRUFBRSxLQUFLLEVBQUUsT0FBTyxJQUFJLElBQUksRUFBRSxJQUFJLEVBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQztRQUVsRixJQUFJLE9BQU8sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNoQixNQUFNLElBQUksS0FBSyxDQUNiLCtIQUErSCxDQUNoSSxDQUFDO1FBQ0osQ0FBQztRQUVELG1GQUFtRjtRQUNuRixJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsTUFBTSxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLEVBQUUsQ0FDL0MsU0FBUyxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsTUFBTSxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUM1RSxDQUFDO1FBQ0osQ0FBQztRQUVELE1BQU0saUJBQWlCLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxFQUFFLENBQUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFBLDRCQUFtQixFQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0RSxNQUFNLEtBQUssQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDIn0=
|
|
@@ -122,6 +122,9 @@ export declare const StepSchema: z.ZodObject<{
|
|
|
122
122
|
error: "error";
|
|
123
123
|
"awaiting-input": "awaiting-input";
|
|
124
124
|
}>;
|
|
125
|
+
awaitingInputReason: z.ZodOptional<z.ZodEnum<{
|
|
126
|
+
"needs-oauth-reauth": "needs-oauth-reauth";
|
|
127
|
+
}>>;
|
|
125
128
|
stepId: z.ZodString;
|
|
126
129
|
stepIndex: z.ZodNumber;
|
|
127
130
|
error: z.ZodOptional<z.ZodString>;
|
|
@@ -339,6 +342,9 @@ export declare const AvailableStepExecutionSchema: z.ZodObject<{
|
|
|
339
342
|
error: "error";
|
|
340
343
|
"awaiting-input": "awaiting-input";
|
|
341
344
|
}>;
|
|
345
|
+
awaitingInputReason: z.ZodOptional<z.ZodEnum<{
|
|
346
|
+
"needs-oauth-reauth": "needs-oauth-reauth";
|
|
347
|
+
}>>;
|
|
342
348
|
stepId: z.ZodString;
|
|
343
349
|
stepIndex: z.ZodNumber;
|
|
344
350
|
error: z.ZodOptional<z.ZodString>;
|
|
@@ -11,6 +11,10 @@ export declare const RecordStepStatusSchema: z.ZodEnum<{
|
|
|
11
11
|
"awaiting-input": "awaiting-input";
|
|
12
12
|
}>;
|
|
13
13
|
export type RecordStepStatus = z.infer<typeof RecordStepStatusSchema>;
|
|
14
|
+
export declare const AwaitingInputReasonSchema: z.ZodEnum<{
|
|
15
|
+
"needs-oauth-reauth": "needs-oauth-reauth";
|
|
16
|
+
}>;
|
|
17
|
+
export type AwaitingInputReason = z.infer<typeof AwaitingInputReasonSchema>;
|
|
14
18
|
export type StepStatus = BaseStepStatus | RecordStepStatus;
|
|
15
19
|
export declare const ConditionStepOutcomeSchema: z.ZodObject<{
|
|
16
20
|
type: z.ZodLiteral<"condition">;
|
|
@@ -47,6 +51,9 @@ export declare const McpStepOutcomeSchema: z.ZodObject<{
|
|
|
47
51
|
error: "error";
|
|
48
52
|
"awaiting-input": "awaiting-input";
|
|
49
53
|
}>;
|
|
54
|
+
awaitingInputReason: z.ZodOptional<z.ZodEnum<{
|
|
55
|
+
"needs-oauth-reauth": "needs-oauth-reauth";
|
|
56
|
+
}>>;
|
|
50
57
|
stepId: z.ZodString;
|
|
51
58
|
stepIndex: z.ZodNumber;
|
|
52
59
|
error: z.ZodOptional<z.ZodString>;
|
|
@@ -95,6 +102,9 @@ export declare const StepOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
95
102
|
error: "error";
|
|
96
103
|
"awaiting-input": "awaiting-input";
|
|
97
104
|
}>;
|
|
105
|
+
awaitingInputReason: z.ZodOptional<z.ZodEnum<{
|
|
106
|
+
"needs-oauth-reauth": "needs-oauth-reauth";
|
|
107
|
+
}>>;
|
|
98
108
|
stepId: z.ZodString;
|
|
99
109
|
stepIndex: z.ZodNumber;
|
|
100
110
|
error: z.ZodOptional<z.ZodString>;
|