@bpinhosilva/agent-orchestrator 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.1.2](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.1.1...v1.1.2) (2026-04-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add migration checks for pending database migrations and correct FK in recurrent_task_execs ([9978f4e](https://github.com/bpinhosilva/agent-orchestrator/commit/9978f4eef9af69449326f97422cc857aa3d7a0e3))
7
+
1
8
  ## [1.1.1](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.1.0...v1.1.1) (2026-04-19)
2
9
 
3
10
 
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerRunCommand = registerRunCommand;
4
4
  const utils_1 = require("../utils");
5
5
  const process_manager_1 = require("../process-manager");
6
+ const migration_state_1 = require("../../database/migration-state");
6
7
  const constants_1 = require("../constants");
7
8
  const VALID_LOG_LEVELS = [
8
9
  'fatal',
@@ -25,6 +26,18 @@ function registerRunCommand(program) {
25
26
  throw new Error(`Invalid log level "${options.logLevel}". Valid values: ${VALID_LOG_LEVELS.join(', ')}`);
26
27
  }
27
28
  }
29
+ // Check for pending migrations before starting
30
+ const { hasPending } = await (0, migration_state_1.checkPendingMigrations)({
31
+ assumePendingOnError: true,
32
+ });
33
+ if (hasPending) {
34
+ console.error('Pending database migrations detected.\n' +
35
+ 'Run the following command before starting the server:\n\n' +
36
+ ' agent-orchestrator migrate --yes\n\n' +
37
+ 'Then run:\n\n' +
38
+ ' agent-orchestrator run');
39
+ process.exit(1);
40
+ }
28
41
  const existingProcess = (0, process_manager_1.findManagedProcess)();
29
42
  if (existingProcess) {
30
43
  console.log(`Orchestrator is already running.\n${(0, process_manager_1.formatProcessSummary)(existingProcess)}`);
@@ -32,6 +32,27 @@ class ProductionHardening1776400000000 {
32
32
  await queryRunner.query(`CREATE INDEX "IDX_a03520bcf60ada1a46bf548e22" ON "recurrent_tasks" ("status")`);
33
33
  await queryRunner.query(`CREATE INDEX "IDX_0f9f543bd40419122e69aeff00" ON "recurrent_tasks" ("projectId")`);
34
34
  await queryRunner.query(`CREATE INDEX "IDX_6755ac981ae6d51a135f2ead5d" ON "recurrent_tasks" ("projectId", "updatedAt")`);
35
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f5bb7be33ddb87ac0f04807b4a"`);
36
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f076c050d237e20ec9335cdeff"`);
37
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_96d8e0300e884502e0382f67bb"`);
38
+ await queryRunner.query(`ALTER TABLE "recurrent_task_execs" RENAME TO "recurrent_task_execs_old"`);
39
+ await queryRunner.query(`CREATE TABLE "recurrent_task_execs" (` +
40
+ `"id" varchar PRIMARY KEY NOT NULL, ` +
41
+ `"status" varchar CHECK("status" IN ('running','success','failure','canceled')) NOT NULL DEFAULT ('running'), ` +
42
+ `"result" text, ` +
43
+ `"latencyMs" integer, ` +
44
+ `"artifacts" text, ` +
45
+ `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
46
+ `"updatedAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
47
+ `"recurrentTaskId" varchar NOT NULL, ` +
48
+ `CONSTRAINT "FK_f5bb7be33ddb87ac0f04807b4ab" FOREIGN KEY ("recurrentTaskId") REFERENCES "recurrent_tasks" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
49
+ `)`);
50
+ await queryRunner.query(`INSERT INTO "recurrent_task_execs" ("id", "status", "result", "latencyMs", "artifacts", "createdAt", "updatedAt", "recurrentTaskId") ` +
51
+ `SELECT "id", "status", "result", "latencyMs", "artifacts", "createdAt", "updatedAt", "recurrentTaskId" FROM "recurrent_task_execs_old"`);
52
+ await queryRunner.query(`DROP TABLE "recurrent_task_execs_old"`);
53
+ await queryRunner.query(`CREATE INDEX "IDX_f5bb7be33ddb87ac0f04807b4a" ON "recurrent_task_execs" ("recurrentTaskId")`);
54
+ await queryRunner.query(`CREATE INDEX "IDX_f076c050d237e20ec9335cdeff" ON "recurrent_task_execs" ("recurrentTaskId", "createdAt")`);
55
+ await queryRunner.query(`CREATE INDEX "IDX_96d8e0300e884502e0382f67bb" ON "recurrent_task_execs" ("recurrentTaskId", "updatedAt")`);
35
56
  await queryRunner.query(`DROP INDEX IF EXISTS "IDX_d19892d8f03928e5bfc7313780"`);
36
57
  await queryRunner.query(`ALTER TABLE "project_members" RENAME TO "project_members_old"`);
37
58
  await queryRunner.query(`CREATE TABLE "project_members" (` +
@@ -63,48 +84,79 @@ class ProductionHardening1776400000000 {
63
84
  const isSqlite = queryRunner.connection.options.type === 'sqlite' ||
64
85
  queryRunner.connection.options.type === 'better-sqlite3';
65
86
  if (isSqlite) {
66
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_refresh_tokens_absoluteExpiry"`);
67
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_project_members_userId"`);
68
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_d19892d8f03928e5bfc7313780"`);
69
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_9ec101b70a5f9612b0757d87c8"`);
70
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_a03520bcf60ada1a46bf548e22"`);
71
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_0f9f543bd40419122e69aeff00"`);
72
- await queryRunner.query(`DROP INDEX IF EXISTS "IDX_6755ac981ae6d51a135f2ead5d"`);
73
- await queryRunner.query(`ALTER TABLE "recurrent_tasks" RENAME TO "recurrent_tasks_new"`);
74
- await queryRunner.query(`CREATE TABLE "recurrent_tasks" (` +
75
- `"id" varchar PRIMARY KEY NOT NULL, ` +
76
- `"title" varchar NOT NULL, ` +
77
- `"description" text NOT NULL, ` +
78
- `"status" varchar CHECK("status" IN ('active','paused','error')) NOT NULL DEFAULT ('active'), ` +
79
- `"priority" varchar CHECK("priority" IN ('0','1','2','3')) NOT NULL DEFAULT (2), ` +
80
- `"cronExpression" varchar NOT NULL, ` +
81
- `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
82
- `"updatedAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
83
- `"assigneeId" varchar NOT NULL, ` +
84
- `"projectId" varchar, ` +
85
- `CONSTRAINT "FK_0f9f543bd40419122e69aeff006" FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, ` +
86
- `CONSTRAINT "FK_9ec101b70a5f9612b0757d87c83" FOREIGN KEY ("assigneeId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
87
- `)`);
88
- await queryRunner.query(`INSERT INTO "recurrent_tasks" SELECT * FROM "recurrent_tasks_new" WHERE "assigneeId" IS NOT NULL`);
89
- await queryRunner.query(`DROP TABLE "recurrent_tasks_new"`);
90
- await queryRunner.query(`CREATE INDEX "IDX_9ec101b70a5f9612b0757d87c8" ON "recurrent_tasks" ("assigneeId")`);
91
- await queryRunner.query(`CREATE INDEX "IDX_a03520bcf60ada1a46bf548e22" ON "recurrent_tasks" ("status")`);
92
- await queryRunner.query(`CREATE INDEX "IDX_0f9f543bd40419122e69aeff00" ON "recurrent_tasks" ("projectId")`);
93
- await queryRunner.query(`CREATE INDEX "IDX_6755ac981ae6d51a135f2ead5d" ON "recurrent_tasks" ("projectId", "updatedAt")`);
94
- await queryRunner.query(`ALTER TABLE "project_members" RENAME TO "project_members_new"`);
95
- await queryRunner.query(`CREATE TABLE "project_members" (` +
96
- `"id" varchar PRIMARY KEY NOT NULL, ` +
97
- `"role" varchar NOT NULL DEFAULT ('member'), ` +
98
- `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
99
- `"projectId" varchar, ` +
100
- `"userId" varchar, ` +
101
- `CONSTRAINT "UQ_project_members_project_user" UNIQUE ("projectId", "userId"), ` +
102
- `CONSTRAINT "FK_project_members_project" FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, ` +
103
- `CONSTRAINT "FK_project_members_user" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
104
- `)`);
105
- await queryRunner.query(`INSERT INTO "project_members" SELECT * FROM "project_members_new"`);
106
- await queryRunner.query(`DROP TABLE "project_members_new"`);
107
- await queryRunner.query(`CREATE INDEX "IDX_d19892d8f03928e5bfc7313780" ON "project_members" ("projectId")`);
87
+ await queryRunner.query(`PRAGMA foreign_keys = OFF`);
88
+ try {
89
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_refresh_tokens_absoluteExpiry"`);
90
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_project_members_userId"`);
91
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_d19892d8f03928e5bfc7313780"`);
92
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_9ec101b70a5f9612b0757d87c8"`);
93
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_a03520bcf60ada1a46bf548e22"`);
94
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_0f9f543bd40419122e69aeff00"`);
95
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_6755ac981ae6d51a135f2ead5d"`);
96
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_96d8e0300e884502e0382f67bb"`);
97
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f076c050d237e20ec9335cdeff"`);
98
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f5bb7be33ddb87ac0f04807b4a"`);
99
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_2b3c4d5e6f7a8b9c0d1e2f3a4b"`);
100
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_1a2b3c4d5e6f7a8b9c0d1e2f3a"`);
101
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_4a1b8f1e0c2d3e4f5a6b7c8d9e"`);
102
+ await queryRunner.query(`ALTER TABLE "recurrent_task_execs" RENAME TO "recurrent_task_execs_new"`);
103
+ await queryRunner.query(`CREATE TABLE "recurrent_task_execs" (` +
104
+ `"id" varchar PRIMARY KEY NOT NULL, ` +
105
+ `"status" varchar CHECK("status" IN ('running','success','failure','canceled')) NOT NULL DEFAULT ('running'), ` +
106
+ `"result" text, ` +
107
+ `"latencyMs" integer, ` +
108
+ `"artifacts" text, ` +
109
+ `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
110
+ `"updatedAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
111
+ `"recurrentTaskId" varchar NOT NULL, ` +
112
+ `CONSTRAINT "FK_f5bb7be33ddb87ac0f04807b4ab" FOREIGN KEY ("recurrentTaskId") REFERENCES "recurrent_tasks" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
113
+ `)`);
114
+ await queryRunner.query(`INSERT INTO "recurrent_task_execs" ("id", "status", "result", "latencyMs", "artifacts", "createdAt", "updatedAt", "recurrentTaskId") ` +
115
+ `SELECT "id", "status", "result", "latencyMs", "artifacts", "createdAt", "updatedAt", "recurrentTaskId" FROM "recurrent_task_execs_new"`);
116
+ await queryRunner.query(`DROP TABLE "recurrent_task_execs_new"`);
117
+ await queryRunner.query(`CREATE INDEX "IDX_f5bb7be33ddb87ac0f04807b4a" ON "recurrent_task_execs" ("recurrentTaskId")`);
118
+ await queryRunner.query(`CREATE INDEX "IDX_f076c050d237e20ec9335cdeff" ON "recurrent_task_execs" ("recurrentTaskId", "createdAt")`);
119
+ await queryRunner.query(`CREATE INDEX "IDX_96d8e0300e884502e0382f67bb" ON "recurrent_task_execs" ("recurrentTaskId", "updatedAt")`);
120
+ await queryRunner.query(`ALTER TABLE "recurrent_tasks" RENAME TO "recurrent_tasks_new"`);
121
+ await queryRunner.query(`CREATE TABLE "recurrent_tasks" (` +
122
+ `"id" varchar PRIMARY KEY NOT NULL, ` +
123
+ `"title" varchar NOT NULL, ` +
124
+ `"description" text NOT NULL, ` +
125
+ `"status" varchar CHECK("status" IN ('active','paused','error')) NOT NULL DEFAULT ('active'), ` +
126
+ `"priority" varchar CHECK("priority" IN ('0','1','2','3')) NOT NULL DEFAULT (2), ` +
127
+ `"cronExpression" varchar NOT NULL, ` +
128
+ `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
129
+ `"updatedAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
130
+ `"assigneeId" varchar NOT NULL, ` +
131
+ `"projectId" varchar, ` +
132
+ `CONSTRAINT "FK_0f9f543bd40419122e69aeff006" FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, ` +
133
+ `CONSTRAINT "FK_9ec101b70a5f9612b0757d87c83" FOREIGN KEY ("assigneeId") REFERENCES "agents" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
134
+ `)`);
135
+ await queryRunner.query(`INSERT INTO "recurrent_tasks" SELECT * FROM "recurrent_tasks_new" WHERE "assigneeId" IS NOT NULL`);
136
+ await queryRunner.query(`DROP TABLE "recurrent_tasks_new"`);
137
+ await queryRunner.query(`CREATE INDEX "IDX_9ec101b70a5f9612b0757d87c8" ON "recurrent_tasks" ("assigneeId")`);
138
+ await queryRunner.query(`CREATE INDEX "IDX_a03520bcf60ada1a46bf548e22" ON "recurrent_tasks" ("status")`);
139
+ await queryRunner.query(`CREATE INDEX "IDX_0f9f543bd40419122e69aeff00" ON "recurrent_tasks" ("projectId")`);
140
+ await queryRunner.query(`CREATE INDEX "IDX_6755ac981ae6d51a135f2ead5d" ON "recurrent_tasks" ("projectId", "updatedAt")`);
141
+ await queryRunner.query(`ALTER TABLE "project_members" RENAME TO "project_members_new"`);
142
+ await queryRunner.query(`CREATE TABLE "project_members" (` +
143
+ `"id" varchar PRIMARY KEY NOT NULL, ` +
144
+ `"role" varchar NOT NULL DEFAULT ('member'), ` +
145
+ `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
146
+ `"projectId" varchar, ` +
147
+ `"userId" varchar, ` +
148
+ `CONSTRAINT "UQ_project_members_project_user" UNIQUE ("projectId", "userId"), ` +
149
+ `CONSTRAINT "FK_project_members_project" FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, ` +
150
+ `CONSTRAINT "FK_project_members_user" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
151
+ `)`);
152
+ await queryRunner.query(`INSERT INTO "project_members" SELECT * FROM "project_members_new"`);
153
+ await queryRunner.query(`DROP TABLE "project_members_new"`);
154
+ await queryRunner.query(`CREATE INDEX "IDX_d19892d8f03928e5bfc7313780" ON "project_members" ("projectId")`);
155
+ await queryRunner.query(`CREATE INDEX "IDX_project_members_userId" ON "project_members" ("userId")`);
156
+ }
157
+ finally {
158
+ await queryRunner.query(`PRAGMA foreign_keys = ON`);
159
+ }
108
160
  }
109
161
  else {
110
162
  await queryRunner.query(`DROP INDEX IF EXISTS "IDX_refresh_tokens_absoluteExpiry"`);
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FixRecurrentTaskExecsForeignKey1776401000000 = void 0;
4
+ class FixRecurrentTaskExecsForeignKey1776401000000 {
5
+ name = 'FixRecurrentTaskExecsForeignKey1776401000000';
6
+ async up(queryRunner) {
7
+ const isSqlite = queryRunner.connection.options.type === 'sqlite' ||
8
+ queryRunner.connection.options.type === 'better-sqlite3';
9
+ if (!isSqlite) {
10
+ return;
11
+ }
12
+ const fks = (await queryRunner.query(`PRAGMA foreign_key_list("recurrent_task_execs")`));
13
+ const hasBrokenFk = fks.some((fk) => fk.table === 'recurrent_tasks_old');
14
+ if (!hasBrokenFk) {
15
+ return;
16
+ }
17
+ await queryRunner.query(`PRAGMA foreign_keys = OFF`);
18
+ try {
19
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f5bb7be33ddb87ac0f04807b4a"`);
20
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_f076c050d237e20ec9335cdeff"`);
21
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_96d8e0300e884502e0382f67bb"`);
22
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_4a1b8f1e0c2d3e4f5a6b7c8d9e"`);
23
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_1a2b3c4d5e6f7a8b9c0d1e2f3a"`);
24
+ await queryRunner.query(`DROP INDEX IF EXISTS "IDX_2b3c4d5e6f7a8b9c0d1e2f3a4b"`);
25
+ await queryRunner.query(`ALTER TABLE "recurrent_task_execs" RENAME TO "recurrent_task_execs_broken"`);
26
+ await queryRunner.query(`CREATE TABLE "recurrent_task_execs" (` +
27
+ `"id" varchar PRIMARY KEY NOT NULL, ` +
28
+ `"status" varchar CHECK("status" IN ('running','success','failure','canceled')) NOT NULL DEFAULT ('running'), ` +
29
+ `"result" text, ` +
30
+ `"latencyMs" integer, ` +
31
+ `"artifacts" text, ` +
32
+ `"createdAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
33
+ `"updatedAt" datetime NOT NULL DEFAULT (datetime('now')), ` +
34
+ `"recurrentTaskId" varchar NOT NULL, ` +
35
+ `CONSTRAINT "FK_f5bb7be33ddb87ac0f04807b4ab" FOREIGN KEY ("recurrentTaskId") REFERENCES "recurrent_tasks" ("id") ON DELETE CASCADE ON UPDATE NO ACTION` +
36
+ `)`);
37
+ await queryRunner.query(`INSERT INTO "recurrent_task_execs" ("id", "status", "result", "latencyMs", "artifacts", "createdAt", "updatedAt", "recurrentTaskId") ` +
38
+ `SELECT b."id", b."status", b."result", b."latencyMs", b."artifacts", b."createdAt", b."updatedAt", b."recurrentTaskId" ` +
39
+ `FROM "recurrent_task_execs_broken" b ` +
40
+ `WHERE b."recurrentTaskId" IS NOT NULL ` +
41
+ `AND EXISTS (SELECT 1 FROM "recurrent_tasks" r WHERE r."id" = b."recurrentTaskId")`);
42
+ await queryRunner.query(`DROP TABLE "recurrent_task_execs_broken"`);
43
+ await queryRunner.query(`CREATE INDEX "IDX_f5bb7be33ddb87ac0f04807b4a" ON "recurrent_task_execs" ("recurrentTaskId")`);
44
+ await queryRunner.query(`CREATE INDEX "IDX_f076c050d237e20ec9335cdeff" ON "recurrent_task_execs" ("recurrentTaskId", "createdAt")`);
45
+ await queryRunner.query(`CREATE INDEX "IDX_96d8e0300e884502e0382f67bb" ON "recurrent_task_execs" ("recurrentTaskId", "updatedAt")`);
46
+ }
47
+ finally {
48
+ await queryRunner.query(`PRAGMA foreign_keys = ON`);
49
+ }
50
+ }
51
+ async down() {
52
+ }
53
+ }
54
+ exports.FixRecurrentTaskExecsForeignKey1776401000000 = FixRecurrentTaskExecsForeignKey1776401000000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpinhosilva/agent-orchestrator",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "An open-source AI agent orchestrator platform built with NestJS.",
5
5
  "author": "bpinhosilva",
6
6
  "license": "MIT",