@bpinhosilva/agent-orchestrator 1.1.3 → 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.
- package/CHANGELOG.md +25 -0
- package/README.md +8 -1
- package/dist/cli/commands/backup.command.js +143 -0
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/run.command.js +20 -1
- package/dist/cli/commands/setup.command.js +13 -0
- package/dist/cli/constants.js +2 -1
- package/dist/cli/env.js +2 -0
- package/dist/cli/preload.js +12 -0
- package/dist/cli/process-manager.js +33 -29
- package/dist/cli/setup/index.js +24 -0
- package/dist/cli/setup/validators.js +14 -0
- package/dist/config/env.validation.js +6 -0
- package/dist/config/runtime-logger.js +214 -0
- package/dist/main.js +8 -0
- package/dist/ui/assets/{AgentFleet-BarfEwcK.js → AgentFleet-B8p5vbKK.js} +1 -1
- package/dist/ui/assets/{AttachmentItem-Cerhbyya.js → AttachmentItem-C_uUACPz.js} +1 -1
- package/dist/ui/assets/{ConfirmDialog-y3vTcqFR.js → ConfirmDialog-BltdtevE.js} +1 -1
- package/dist/ui/assets/{CreateRecurrentTaskModal-BgfnJ1n8.js → CreateRecurrentTaskModal-CGSdgNAf.js} +1 -1
- package/dist/ui/assets/{CreateTaskModal-BH9lOLYT.js → CreateTaskModal-XNvIWKz3.js} +1 -1
- package/dist/ui/assets/{ExecLogModal-ShacCOOV.js → ExecLogModal-BYTGF04P.js} +1 -1
- package/dist/ui/assets/{MarkdownField--1gpyapw.js → MarkdownField-DR6tHsei.js} +1 -1
- package/dist/ui/assets/{Profile-qnuRN5Qv.js → Profile-ByioVLM8.js} +1 -1
- package/dist/ui/assets/{ProjectDetail-ZYQG9yn2.js → ProjectDetail-dwZDzb0m.js} +1 -1
- package/dist/ui/assets/{Providers-DR7srJHq.js → Providers-JAClEFt0.js} +1 -1
- package/dist/ui/assets/{Scheduler-Bs3DC0re.js → Scheduler-C1HQNxQ6.js} +2 -2
- package/dist/ui/assets/{Settings-C3ad44vP.js → Settings-Cjn2xdo1.js} +1 -1
- package/dist/ui/assets/{TaskDetail-D-f_Ao2a.js → TaskDetail-Blphf9tw.js} +1 -1
- package/dist/ui/assets/{TaskExecutions-BeodRIz1.js → TaskExecutions-ydR4KPCY.js} +3 -3
- package/dist/ui/assets/{TaskManager-BA2Y31ut.js → TaskManager-DrFbuAwN.js} +3 -3
- package/dist/ui/assets/{UserDetail-BvWm-U4y.js → UserDetail-mWjJPTzO.js} +1 -1
- package/dist/ui/assets/{Users-CQNcXLyV.js → Users-FaX0thJx.js} +1 -1
- package/dist/ui/assets/{activity-CmAajKmR.js → activity-B_2QSywM.js} +1 -1
- package/dist/ui/assets/{agents-b8btewki.js → agents-YQJ7JNfI.js} +1 -1
- package/dist/ui/assets/{auth-dROenHXk.js → auth-Lh2N3iYN.js} +1 -1
- package/dist/ui/assets/{bot-QVmnxPf4.js → bot-DadO9w3D.js} +1 -1
- package/dist/ui/assets/{box-Bw_pyYzU.js → box-CFDcDxHv.js} +1 -1
- package/dist/ui/assets/{brain-C6vlLcg3.js → brain-DWTpby6b.js} +1 -1
- package/dist/ui/assets/{briefcase-DtHlat45.js → briefcase-mFWNV6w9.js} +1 -1
- package/dist/ui/assets/check-BiORptL3.js +1 -0
- package/dist/ui/assets/chevron-down-BUOg3vvd.js +1 -0
- package/dist/ui/assets/chevron-left-B0NmQyxK.js +1 -0
- package/dist/ui/assets/chevron-right-Bm_d1KKk.js +1 -0
- package/dist/ui/assets/{circle-alert-pUx6yhcF.js → circle-alert-sCcf7ANF.js} +1 -1
- package/dist/ui/assets/circle-check-Jz8IOE4h.js +1 -0
- package/dist/ui/assets/client-B553SX3l.js +9 -0
- package/dist/ui/assets/clock-CSNFJjo4.js +1 -0
- package/dist/ui/assets/{cpu-8Sbc9Dk0.js → cpu-C2_GFeSH.js} +1 -1
- package/dist/ui/assets/{database-D6O4hvbm.js → database-zXW48-_N.js} +1 -1
- package/dist/ui/assets/{download-DHcd5oLD.js → download-DZdOdijy.js} +1 -1
- package/dist/ui/assets/{eye-Cc-vy4Zu.js → eye-CjrFwZG_.js} +1 -1
- package/dist/ui/assets/{file-text-DWBSXx5l.js → file-text-CX_Yqfjf.js} +1 -1
- package/dist/ui/assets/{index-BvUv9Lzq.js → index-IcLlFUZs.js} +3 -3
- package/dist/ui/assets/{info-1jA6X-_e.js → info-DGHChFI6.js} +1 -1
- package/dist/ui/assets/{layers-BcHXXg-4.js → layers-CVf8_Byb.js} +1 -1
- package/dist/ui/assets/loader-circle-CB6PQwdc.js +1 -0
- package/dist/ui/assets/{panels-top-left-Vk8FzAxf.js → panels-top-left-Cp169WzB.js} +1 -1
- package/dist/ui/assets/{paperclip-DWJjaP8B.js → paperclip-5Y367YTj.js} +1 -1
- package/dist/ui/assets/plus-CkteVqhM.js +1 -0
- package/dist/ui/assets/{projects-CUOthD8N.js → projects-DoalTI7c.js} +1 -1
- package/dist/ui/assets/{providers-CNrSTcdc.js → providers-AawjMy3x.js} +1 -1
- package/dist/ui/assets/{recurrent-tasks-CKT47N2l.js → recurrent-tasks-7thk45mk.js} +1 -1
- package/dist/ui/assets/{refresh-cw-DQW6jR7M.js → refresh-cw-BgDsJOC_.js} +1 -1
- package/dist/ui/assets/{rocket-CMSwBl_C.js → rocket-B9Iexkcw.js} +1 -1
- package/dist/ui/assets/{save-DycJmlX0.js → save-Cmqi1IkN.js} +1 -1
- package/dist/ui/assets/{send-BZjwSrJW.js → send-iK1X7WSe.js} +1 -1
- package/dist/ui/assets/{server-CJE2K1Vt.js → server-BBmM-vrg.js} +1 -1
- package/dist/ui/assets/{settings-at7JqFrc.js → settings-BpKLGxm8.js} +1 -1
- package/dist/ui/assets/{shield-alert-CP5GVXxa.js → shield-alert-Do_Sg9SX.js} +1 -1
- package/dist/ui/assets/{shield-check-Cr4SHjdR.js → shield-check-YKs9kUNq.js} +1 -1
- package/dist/ui/assets/{sparkles-v57Nc-z-.js → sparkles-krUpxie4.js} +1 -1
- package/dist/ui/assets/{taskFormSchemas-dl1t1OyX.js → taskFormSchemas-B6YlRxrx.js} +1 -1
- package/dist/ui/assets/{tasks-DEUQCNoH.js → tasks-DHsBE-ea.js} +1 -1
- package/dist/ui/assets/{terminal-a6AV2mPO.js → terminal-B6iyNuAm.js} +1 -1
- package/dist/ui/assets/{trash-2-DJpTXgEP.js → trash-2-CphQfk35.js} +1 -1
- package/dist/ui/assets/{trending-up-DlrXEXE6.js → trending-up-ClGbkbN9.js} +1 -1
- package/dist/ui/assets/user-Brcjlps5.js +1 -0
- package/dist/ui/assets/{user-plus-nDkwsBT1.js → user-plus-0Y0M1GKi.js} +1 -1
- package/dist/ui/assets/{users-C6WiDiuN.js → users-BcnlUG5N.js} +1 -1
- package/dist/ui/assets/{users-Ef6Sh2iR.js → users-tdyaGZbx.js} +1 -1
- package/dist/ui/assets/x-CCZGYGDC.js +1 -0
- package/dist/ui/assets/{zap-BmhZqgvm.js → zap-BDAytXMX.js} +1 -1
- package/dist/ui/index.html +17 -17
- package/package.json +2 -2
- package/dist/ui/assets/check-DTei8Qlg.js +0 -1
- package/dist/ui/assets/chevron-down-LTeQHW9O.js +0 -1
- package/dist/ui/assets/chevron-left-CJc_eQid.js +0 -1
- package/dist/ui/assets/chevron-right-G5-HRo_C.js +0 -1
- package/dist/ui/assets/circle-check-B9kyRki5.js +0 -1
- package/dist/ui/assets/client-BGOBXkO8.js +0 -9
- package/dist/ui/assets/clock-BY4rrypg.js +0 -1
- package/dist/ui/assets/loader-circle-Cl6yIkPK.js +0 -1
- package/dist/ui/assets/plus-Dx6G8SOZ.js +0 -1
- package/dist/ui/assets/user--KOoEB9m.js +0 -1
- package/dist/ui/assets/x-wDy15QMx.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
# [1.3.0](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.2.0...v1.3.0) (2026-06-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* update version and dependencies in package.json and package-lock.json ([aa1c0c1](https://github.com/bpinhosilva/agent-orchestrator/commit/aa1c0c1ac6b41d89ecaa0dccd423122287b0b988))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* force the version after creating backup feature ([790356c](https://github.com/bpinhosilva/agent-orchestrator/commit/790356c85fac8ac0d850f6ce7998904eea1c7226))
|
|
12
|
+
|
|
13
|
+
# [1.2.0](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.1.3...v1.2.0) (2026-05-17)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **tests:** move startServer tests to isolated spec using jest.mock ([8ede64a](https://github.com/bpinhosilva/agent-orchestrator/commit/8ede64a8e68175bbbe6ed3c076b682cd8412c25e))
|
|
19
|
+
* **ui:** add missing test peers ([8c3d1a8](https://github.com/bpinhosilva/agent-orchestrator/commit/8c3d1a8b6941d5eb6e0ca38e7645c7b6b56b314a))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* **cli:** add runtime log rotation ([7ccd9f2](https://github.com/bpinhosilva/agent-orchestrator/commit/7ccd9f23a19ee8de0d5c000b178f2b4d9e990f08))
|
|
25
|
+
|
|
1
26
|
## [1.1.3](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.1.2...v1.1.3) (2026-05-02)
|
|
2
27
|
|
|
3
28
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Agent Orchestrator
|
|
1
|
+
# Agent Orchestrator
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="https://raw.githubusercontent.com/bpinhosilva/agent-orchestrator/main/docs/assets/lupy-mascot.webp" alt="Lupy, the Agent Orchestrator mascot" width="500" />
|
|
@@ -132,6 +132,9 @@ DB_LOGGING=false
|
|
|
132
132
|
SERVE_STATIC_UI=true
|
|
133
133
|
CHECK_PENDING_MIGRATIONS_ON_STARTUP=false
|
|
134
134
|
LOG_LEVEL=error
|
|
135
|
+
# Optional packaged-runtime / Docker file-rotation settings
|
|
136
|
+
# LOG_ROTATION_MAX_SIZE_MB=10
|
|
137
|
+
# LOG_ROTATION_MAX_FILES=4
|
|
135
138
|
```
|
|
136
139
|
|
|
137
140
|
## Database setup
|
|
@@ -215,6 +218,8 @@ agent-orchestrator rotate-secrets
|
|
|
215
218
|
|
|
216
219
|
When running the packaged app or a production build with static UI enabled, the dashboard is served from `http://localhost:15789` by default.
|
|
217
220
|
|
|
221
|
+
By default, the packaged CLI/runtime keeps writing to `${AGENT_ORCHESTRATOR_HOME}/server.log`, rotating the active file at **10 MB** and retaining **4** timestamped archives. You can persist different defaults with `agent-orchestrator setup --log-max-size-mb <mb> --log-max-files <count>` and override them for a single launch with `agent-orchestrator run --log-max-size-mb <mb> --log-max-files <count>`.
|
|
222
|
+
|
|
218
223
|
## Docker
|
|
219
224
|
|
|
220
225
|
> For the full Docker guide, see [docs/DOCKER.md](docs/DOCKER.md).
|
|
@@ -227,6 +232,8 @@ The repository ships three Compose entrypoints:
|
|
|
227
232
|
| `docker-compose.dev.yml` | Development stack with API hot reload and Vite UI dev server |
|
|
228
233
|
| `docker-compose.test.yml` | Integration stack for migration, CLI/runtime, API, and UI checks |
|
|
229
234
|
|
|
235
|
+
Docker keeps stdout/stderr as the primary log stream, so `docker compose logs` continues to work as before. If you also want rotated in-container log files, set `LOG_ROTATION_MAX_SIZE_MB` and `LOG_ROTATION_MAX_FILES`; the API container writes to the system temp directory by default (typically `/tmp/server.log`), or `${AGENT_ORCHESTRATOR_HOME}/server.log` if you set a writable runtime home.
|
|
236
|
+
|
|
230
237
|
### Production-style stack
|
|
231
238
|
|
|
232
239
|
**Step 1: Create `.env`** — copy the example in the repo root and fill in your values:
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerBackupCommand = registerBackupCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const zlib_1 = require("zlib");
|
|
40
|
+
const typeorm_1 = require("../../config/typeorm");
|
|
41
|
+
const constants_1 = require("../constants");
|
|
42
|
+
const utils_1 = require("../utils");
|
|
43
|
+
const DEFAULT_BACKUP_DESTINATION = path.join(constants_1.PID_DIR, 'backups');
|
|
44
|
+
function quoteIdentifier(identifier) {
|
|
45
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
46
|
+
}
|
|
47
|
+
function buildTimestamp(now = new Date()) {
|
|
48
|
+
const y = now.getFullYear();
|
|
49
|
+
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
|
50
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
51
|
+
const hh = String(now.getHours()).padStart(2, '0');
|
|
52
|
+
const mi = String(now.getMinutes()).padStart(2, '0');
|
|
53
|
+
const ss = String(now.getSeconds()).padStart(2, '0');
|
|
54
|
+
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
55
|
+
return `${y}${mm}${dd}-${hh}${mi}${ss}-${ms}`;
|
|
56
|
+
}
|
|
57
|
+
function detectDbType(type) {
|
|
58
|
+
if ((0, typeorm_1.isSqliteDriver)(type)) {
|
|
59
|
+
return 'sqlite';
|
|
60
|
+
}
|
|
61
|
+
if (type === 'postgres') {
|
|
62
|
+
return 'postgres';
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Unsupported database type "${String(type)}". Backup currently supports sqlite and postgres.`);
|
|
65
|
+
}
|
|
66
|
+
async function listTables(dbType, query) {
|
|
67
|
+
if (dbType === 'sqlite') {
|
|
68
|
+
const rows = (await query("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name"));
|
|
69
|
+
return rows.map((row) => ({ name: row.name }));
|
|
70
|
+
}
|
|
71
|
+
const rows = (await query("SELECT table_schema, table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') ORDER BY table_schema, table_name"));
|
|
72
|
+
return rows.map((row) => ({
|
|
73
|
+
name: row.table_name,
|
|
74
|
+
schema: row.table_schema,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
function tableSelectSql(dbType, table) {
|
|
78
|
+
if (dbType === 'sqlite') {
|
|
79
|
+
return `SELECT * FROM ${quoteIdentifier(table.name)}`;
|
|
80
|
+
}
|
|
81
|
+
const schema = table.schema ?? 'public';
|
|
82
|
+
return `SELECT * FROM ${quoteIdentifier(schema)}.${quoteIdentifier(table.name)}`;
|
|
83
|
+
}
|
|
84
|
+
async function fetchTableRows(query, sql) {
|
|
85
|
+
const result = await query(sql);
|
|
86
|
+
if (!Array.isArray(result)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
function registerBackupCommand(program) {
|
|
92
|
+
program
|
|
93
|
+
.command('backup <target>')
|
|
94
|
+
.description('Create runtime backups (currently supports database only)')
|
|
95
|
+
.option('-d, --destination <path>', 'Destination folder for backup files', DEFAULT_BACKUP_DESTINATION)
|
|
96
|
+
.action(async (target, ...args) => {
|
|
97
|
+
const opts = (0, utils_1.resolveActionOptions)(args);
|
|
98
|
+
if (target !== 'db') {
|
|
99
|
+
console.error(`Unsupported backup target "${target}". Only "db" is currently supported.`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const destination = opts.destination ?? DEFAULT_BACKUP_DESTINATION;
|
|
104
|
+
const timestamp = buildTimestamp();
|
|
105
|
+
const filename = `agent-orchestrator-backup-${target}-${timestamp}.json.gz`;
|
|
106
|
+
const outputPath = path.join(destination, filename);
|
|
107
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
108
|
+
const dataSource = (0, typeorm_1.createDataSource)();
|
|
109
|
+
try {
|
|
110
|
+
await dataSource.initialize();
|
|
111
|
+
const dbType = detectDbType(dataSource.options.type);
|
|
112
|
+
const tables = await listTables(dbType, (sql) => dataSource.query(sql));
|
|
113
|
+
const tableData = await Promise.all(tables.map(async (table) => {
|
|
114
|
+
const rows = await fetchTableRows((sql) => dataSource.query(sql), tableSelectSql(dbType, table));
|
|
115
|
+
return {
|
|
116
|
+
name: table.name,
|
|
117
|
+
...(table.schema ? { schema: table.schema } : {}),
|
|
118
|
+
rows,
|
|
119
|
+
};
|
|
120
|
+
}));
|
|
121
|
+
const backupPayload = {
|
|
122
|
+
target: target,
|
|
123
|
+
dbType,
|
|
124
|
+
createdAt: new Date().toISOString(),
|
|
125
|
+
tableCount: tableData.length,
|
|
126
|
+
tables: tableData,
|
|
127
|
+
};
|
|
128
|
+
const compressed = (0, zlib_1.gzipSync)(Buffer.from(JSON.stringify(backupPayload)));
|
|
129
|
+
fs.writeFileSync(outputPath, compressed);
|
|
130
|
+
console.log(`Backup created: ${outputPath}`);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
134
|
+
console.error(`Backup failed: ${errorMessage}`);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (dataSource.isInitialized) {
|
|
139
|
+
await dataSource.destroy();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -13,6 +13,7 @@ const config_command_1 = require("./config.command");
|
|
|
13
13
|
const reset_password_command_1 = require("./reset-password.command");
|
|
14
14
|
const rotate_secrets_command_1 = require("./rotate-secrets.command");
|
|
15
15
|
const seed_admin_command_1 = require("./seed-admin.command");
|
|
16
|
+
const backup_command_1 = require("./backup.command");
|
|
16
17
|
function registerAllCommands(program) {
|
|
17
18
|
(0, setup_command_1.registerSetupCommand)(program);
|
|
18
19
|
(0, run_command_1.registerRunCommand)(program);
|
|
@@ -26,4 +27,5 @@ function registerAllCommands(program) {
|
|
|
26
27
|
(0, reset_password_command_1.registerResetPasswordCommand)(program);
|
|
27
28
|
(0, rotate_secrets_command_1.registerRotateSecretsCommand)(program);
|
|
28
29
|
(0, seed_admin_command_1.registerSeedAdminCommand)(program);
|
|
30
|
+
(0, backup_command_1.registerBackupCommand)(program);
|
|
29
31
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerRunCommand = registerRunCommand;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
|
+
const validators_1 = require("../setup/validators");
|
|
5
6
|
const process_manager_1 = require("../process-manager");
|
|
6
7
|
const migration_state_1 = require("../../database/migration-state");
|
|
7
8
|
const constants_1 = require("../constants");
|
|
@@ -18,6 +19,8 @@ function registerRunCommand(program) {
|
|
|
18
19
|
.command('run')
|
|
19
20
|
.description('Start the orchestrator server in detached mode')
|
|
20
21
|
.option('--log-level <level>', 'Set the log level (fatal, error, warn, log, debug, verbose)')
|
|
22
|
+
.option('--log-max-size-mb <mb>', 'One-off override: max log file size in MB before rotation', (v) => (0, validators_1.parsePositiveInt)(v) ?? Number.NaN)
|
|
23
|
+
.option('--log-max-files <count>', 'One-off override: max number of rotated log files to keep', (v) => (0, validators_1.parsePositiveInt)(v) ?? Number.NaN)
|
|
21
24
|
.action(async (...args) => {
|
|
22
25
|
try {
|
|
23
26
|
const options = (0, utils_1.resolveActionOptions)(args);
|
|
@@ -26,6 +29,18 @@ function registerRunCommand(program) {
|
|
|
26
29
|
throw new Error(`Invalid log level "${options.logLevel}". Valid values: ${VALID_LOG_LEVELS.join(', ')}`);
|
|
27
30
|
}
|
|
28
31
|
}
|
|
32
|
+
if (options.logMaxSizeMb !== undefined) {
|
|
33
|
+
if (!Number.isInteger(options.logMaxSizeMb) ||
|
|
34
|
+
options.logMaxSizeMb <= 0) {
|
|
35
|
+
throw new Error(`Invalid --log-max-size-mb "${options.logMaxSizeMb}". Must be a positive integer.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (options.logMaxFiles !== undefined) {
|
|
39
|
+
if (!Number.isInteger(options.logMaxFiles) ||
|
|
40
|
+
options.logMaxFiles <= 0) {
|
|
41
|
+
throw new Error(`Invalid --log-max-files "${options.logMaxFiles}". Must be a positive integer.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
29
44
|
// Check for pending migrations before starting
|
|
30
45
|
const { hasPending } = await (0, migration_state_1.checkPendingMigrations)({
|
|
31
46
|
assumePendingOnError: true,
|
|
@@ -44,7 +59,11 @@ function registerRunCommand(program) {
|
|
|
44
59
|
return;
|
|
45
60
|
}
|
|
46
61
|
console.log('Starting Agent Orchestrator in background...');
|
|
47
|
-
const { pid, host, port } = (0, process_manager_1.startServer)({
|
|
62
|
+
const { pid, host, port } = (0, process_manager_1.startServer)({
|
|
63
|
+
logLevel: options.logLevel,
|
|
64
|
+
logMaxSizeMb: options.logMaxSizeMb,
|
|
65
|
+
logMaxFiles: options.logMaxFiles,
|
|
66
|
+
});
|
|
48
67
|
const survived = await (0, utils_1.verifyServerStartup)(pid);
|
|
49
68
|
if (!survived) {
|
|
50
69
|
process.exit(1);
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerSetupCommand = registerSetupCommand;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const index_1 = require("../setup/index");
|
|
6
|
+
const validators_1 = require("../setup/validators");
|
|
6
7
|
const migration_state_1 = require("../../database/migration-state");
|
|
7
8
|
function registerSetupCommand(program) {
|
|
8
9
|
program
|
|
@@ -27,10 +28,22 @@ function registerSetupCommand(program) {
|
|
|
27
28
|
.option('--admin-email <email>', 'Admin user email for non-interactive setup')
|
|
28
29
|
.option('--admin-password <password>', 'Admin user password for non-interactive setup')
|
|
29
30
|
.option('--regenerate-jwt-secret', 'Generate a new JWT secret instead of preserving the existing one')
|
|
31
|
+
.option('--log-max-size-mb <mb>', 'Max log file size in MB before rotation (default: 10)', (v) => (0, validators_1.parsePositiveInt)(v) ?? Number.NaN)
|
|
32
|
+
.option('--log-max-files <count>', 'Max number of rotated log files to keep (default: 4)', (v) => (0, validators_1.parsePositiveInt)(v) ?? Number.NaN)
|
|
30
33
|
.action(async (...args) => {
|
|
31
34
|
const opts = (0, utils_1.resolveActionOptions)(args);
|
|
32
35
|
console.log('Starting setup...');
|
|
33
36
|
try {
|
|
37
|
+
if (opts.logMaxSizeMb !== undefined) {
|
|
38
|
+
if (!Number.isInteger(opts.logMaxSizeMb) || opts.logMaxSizeMb <= 0) {
|
|
39
|
+
throw new Error(`Invalid --log-max-size-mb "${opts.logMaxSizeMb}". Must be a positive integer.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (opts.logMaxFiles !== undefined) {
|
|
43
|
+
if (!Number.isInteger(opts.logMaxFiles) || opts.logMaxFiles <= 0) {
|
|
44
|
+
throw new Error(`Invalid --log-max-files "${opts.logMaxFiles}". Must be a positive integer.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
34
47
|
await (0, index_1.handleSetup)(opts);
|
|
35
48
|
if (opts.yes) {
|
|
36
49
|
const { hasPending, isEmpty } = await (0, migration_state_1.checkPendingMigrations)({
|
package/dist/cli/constants.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.PROCESS_FILE = exports.ENV_PATH = exports.LOG_FILE = exports.PID_FILE = exports.PID_DIR = exports.UI_INDEX_FILE = exports.MAIN_FILE = exports.PACKAGE_JSON_PATH = exports.PACKAGE_ROOT = exports.SUPPORTED_PROVIDERS = void 0;
|
|
36
|
+
exports.PROCESS_FILE = exports.ENV_PATH = exports.LOG_FILE = exports.PID_FILE = exports.PID_DIR = exports.PRELOAD_FILE = exports.UI_INDEX_FILE = exports.MAIN_FILE = exports.PACKAGE_JSON_PATH = exports.PACKAGE_ROOT = exports.SUPPORTED_PROVIDERS = void 0;
|
|
37
37
|
const os = __importStar(require("os"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
var types_1 = require("./types");
|
|
@@ -42,6 +42,7 @@ exports.PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
|
42
42
|
exports.PACKAGE_JSON_PATH = path.join(exports.PACKAGE_ROOT, 'package.json');
|
|
43
43
|
exports.MAIN_FILE = path.join(exports.PACKAGE_ROOT, 'dist/main.js');
|
|
44
44
|
exports.UI_INDEX_FILE = path.join(exports.PACKAGE_ROOT, 'dist/ui/index.html');
|
|
45
|
+
exports.PRELOAD_FILE = path.join(exports.PACKAGE_ROOT, 'dist/cli/preload.js');
|
|
45
46
|
// These are set at process start before any imports, so the env var is always present
|
|
46
47
|
exports.PID_DIR = process.env.AGENT_ORCHESTRATOR_HOME ??
|
|
47
48
|
path.join(os.homedir(), '.agent-orchestrator');
|
package/dist/cli/env.js
CHANGED
|
@@ -139,6 +139,8 @@ function buildEnvContent(currentEnv, basicConfig, databaseUrl, geminiKey, anthro
|
|
|
139
139
|
'ANTHROPIC_API_KEY',
|
|
140
140
|
'OLLAMA_HOST',
|
|
141
141
|
'OLLAMA_API_KEY',
|
|
142
|
+
'LOG_ROTATION_MAX_SIZE_MB',
|
|
143
|
+
'LOG_ROTATION_MAX_FILES',
|
|
142
144
|
'JWT_SECRET',
|
|
143
145
|
'JWT_REFRESH_SECRET',
|
|
144
146
|
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const runtime_paths_1 = require("../config/runtime-paths");
|
|
4
|
+
const runtime_logger_1 = require("../config/runtime-logger");
|
|
5
|
+
(0, runtime_paths_1.loadRuntimeEnv)();
|
|
6
|
+
try {
|
|
7
|
+
(0, runtime_logger_1.initRuntimeLogger)();
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
(0, runtime_logger_1.persistRuntimeLoggerInitFailure)(String(err));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
@@ -231,13 +231,16 @@ function getChildEnvironment(pidDir = constants_1.PID_DIR) {
|
|
|
231
231
|
}
|
|
232
232
|
return childEnv;
|
|
233
233
|
}
|
|
234
|
-
function assertBuildExists(mainFile = constants_1.MAIN_FILE, uiIndexFile = constants_1.UI_INDEX_FILE, fsDep = realFs) {
|
|
234
|
+
function assertBuildExists(mainFile = constants_1.MAIN_FILE, uiIndexFile = constants_1.UI_INDEX_FILE, preloadFile = constants_1.PRELOAD_FILE, fsDep = realFs) {
|
|
235
235
|
if (!fsDep.existsSync(mainFile)) {
|
|
236
236
|
throw new Error(`Missing backend build at ${mainFile}. Run "npm run build:all" before using the CLI runtime.`);
|
|
237
237
|
}
|
|
238
238
|
if (!fsDep.existsSync(uiIndexFile)) {
|
|
239
239
|
throw new Error(`Missing UI build at ${uiIndexFile}. Run "npm run build:all" so the packaged CLI starts the full application.`);
|
|
240
240
|
}
|
|
241
|
+
if (!fsDep.existsSync(preloadFile)) {
|
|
242
|
+
throw new Error(`Missing CLI preload build at ${preloadFile}. Run "npm run build:all" before using the CLI runtime.`);
|
|
243
|
+
}
|
|
241
244
|
}
|
|
242
245
|
function isManagedProcess(pid, expected, fsDep = realFs) {
|
|
243
246
|
if (os.platform() === 'win32') {
|
|
@@ -359,7 +362,8 @@ async function stopManagedProcessById(pid, cwd, mainPath, fsDep = realFs) {
|
|
|
359
362
|
* that no instance is already running before calling this.
|
|
360
363
|
*/
|
|
361
364
|
function startServer(options = {}, mainFile = constants_1.MAIN_FILE, packageRoot = constants_1.PACKAGE_ROOT, logFile = constants_1.LOG_FILE, pidDir = constants_1.PID_DIR, envPath = constants_1.ENV_PATH) {
|
|
362
|
-
|
|
365
|
+
const preloadFile = path.join(packageRoot, 'dist/cli/preload.js');
|
|
366
|
+
assertBuildExists(mainFile, constants_1.UI_INDEX_FILE, preloadFile);
|
|
363
367
|
if (!fs.existsSync(pidDir)) {
|
|
364
368
|
fs.mkdirSync(pidDir, { recursive: true, mode: 0o700 });
|
|
365
369
|
}
|
|
@@ -367,32 +371,32 @@ function startServer(options = {}, mainFile = constants_1.MAIN_FILE, packageRoot
|
|
|
367
371
|
if (options.logLevel) {
|
|
368
372
|
env.LOG_LEVEL = options.logLevel;
|
|
369
373
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
cwd: packageRoot,
|
|
376
|
-
env,
|
|
377
|
-
});
|
|
378
|
-
const pid = child.pid;
|
|
379
|
-
if (!pid)
|
|
380
|
-
throw new Error('Failed to determine spawned process PID.');
|
|
381
|
-
const port = getConfiguredPort(envPath);
|
|
382
|
-
const host = getConfiguredHost(envPath);
|
|
383
|
-
persistProcessMetadata({
|
|
384
|
-
pid,
|
|
385
|
-
cwd: packageRoot,
|
|
386
|
-
mainPath: mainFile,
|
|
387
|
-
host,
|
|
388
|
-
port,
|
|
389
|
-
logFile,
|
|
390
|
-
startedAt: new Date().toISOString(),
|
|
391
|
-
});
|
|
392
|
-
child.unref();
|
|
393
|
-
return { pid, host, port };
|
|
394
|
-
}
|
|
395
|
-
finally {
|
|
396
|
-
fs.closeSync(logFd);
|
|
374
|
+
if (options.logMaxSizeMb !== undefined) {
|
|
375
|
+
env.LOG_ROTATION_MAX_SIZE_MB = String(options.logMaxSizeMb);
|
|
376
|
+
}
|
|
377
|
+
if (options.logMaxFiles !== undefined) {
|
|
378
|
+
env.LOG_ROTATION_MAX_FILES = String(options.logMaxFiles);
|
|
397
379
|
}
|
|
380
|
+
const child = (0, child_process_1.spawn)('node', ['--require', preloadFile, mainFile], {
|
|
381
|
+
detached: true,
|
|
382
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
383
|
+
cwd: packageRoot,
|
|
384
|
+
env,
|
|
385
|
+
});
|
|
386
|
+
const pid = child.pid;
|
|
387
|
+
if (!pid)
|
|
388
|
+
throw new Error('Failed to determine spawned process PID.');
|
|
389
|
+
const port = getConfiguredPort(envPath);
|
|
390
|
+
const host = getConfiguredHost(envPath);
|
|
391
|
+
persistProcessMetadata({
|
|
392
|
+
pid,
|
|
393
|
+
cwd: packageRoot,
|
|
394
|
+
mainPath: mainFile,
|
|
395
|
+
host,
|
|
396
|
+
port,
|
|
397
|
+
logFile,
|
|
398
|
+
startedAt: new Date().toISOString(),
|
|
399
|
+
});
|
|
400
|
+
child.unref();
|
|
401
|
+
return { pid, host, port };
|
|
398
402
|
}
|
package/dist/cli/setup/index.js
CHANGED
|
@@ -40,6 +40,8 @@ const env_1 = require("../env");
|
|
|
40
40
|
const host_defaults_1 = require("../../config/host.defaults");
|
|
41
41
|
const admin_1 = require("./admin");
|
|
42
42
|
const prompts_1 = require("./prompts");
|
|
43
|
+
const validators_1 = require("./validators");
|
|
44
|
+
const LOG_ROTATION_DEFAULTS = { maxSizeMb: 10, maxFiles: 4 };
|
|
43
45
|
const SENSITIVE_FLAG_ENV_VARS = [
|
|
44
46
|
['geminiKey', 'GEMINI_API_KEY', '--gemini-key'],
|
|
45
47
|
['anthropicKey', 'ANTHROPIC_API_KEY', '--anthropic-key'],
|
|
@@ -56,8 +58,28 @@ async function handleSetup(opts, fsDep, prompter, dsFactory) {
|
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
const existingEnv = (0, env_1.readEnvFile)(constants_1.ENV_PATH, fsDep);
|
|
61
|
+
if (opts.logMaxSizeMb !== undefined &&
|
|
62
|
+
(!Number.isInteger(opts.logMaxSizeMb) || opts.logMaxSizeMb <= 0)) {
|
|
63
|
+
throw new Error('Invalid logMaxSizeMb. Expected a positive integer.');
|
|
64
|
+
}
|
|
65
|
+
if (opts.logMaxFiles !== undefined &&
|
|
66
|
+
(!Number.isInteger(opts.logMaxFiles) || opts.logMaxFiles <= 0)) {
|
|
67
|
+
throw new Error('Invalid logMaxFiles. Expected a positive integer.');
|
|
68
|
+
}
|
|
59
69
|
// Resolve a sensitive value: CLI flag > process env var > existing .env value
|
|
60
70
|
const resolveSecret = (flagValue, envVar, existingKey) => flagValue ?? process.env[envVar] ?? existingEnv[existingKey] ?? '';
|
|
71
|
+
// Resolve a positive-integer config value: flag > existing env > default
|
|
72
|
+
const resolvePositiveInt = (flagValue, existingKey, defaultValue) => {
|
|
73
|
+
if (flagValue !== undefined &&
|
|
74
|
+
Number.isInteger(flagValue) &&
|
|
75
|
+
flagValue > 0) {
|
|
76
|
+
return flagValue;
|
|
77
|
+
}
|
|
78
|
+
const fromEnv = (0, validators_1.parsePositiveInt)(existingEnv[existingKey]);
|
|
79
|
+
return fromEnv ?? defaultValue;
|
|
80
|
+
};
|
|
81
|
+
const logMaxSizeMb = resolvePositiveInt(opts.logMaxSizeMb, 'LOG_ROTATION_MAX_SIZE_MB', LOG_ROTATION_DEFAULTS.maxSizeMb);
|
|
82
|
+
const logMaxFiles = resolvePositiveInt(opts.logMaxFiles, 'LOG_ROTATION_MAX_FILES', LOG_ROTATION_DEFAULTS.maxFiles);
|
|
61
83
|
let answers;
|
|
62
84
|
if (opts.yes) {
|
|
63
85
|
const jwtSecret = opts.regenerateJwtSecret || !existingEnv.JWT_SECRET
|
|
@@ -87,6 +109,8 @@ async function handleSetup(opts, fsDep, prompter, dsFactory) {
|
|
|
87
109
|
const envContent = (0, env_1.buildEnvContent)({
|
|
88
110
|
...existingEnv,
|
|
89
111
|
SCHEDULER_ENABLED: answers.schedulerEnabled ? 'true' : 'false',
|
|
112
|
+
LOG_ROTATION_MAX_SIZE_MB: String(logMaxSizeMb),
|
|
113
|
+
LOG_ROTATION_MAX_FILES: String(logMaxFiles),
|
|
90
114
|
}, {
|
|
91
115
|
host: answers.host,
|
|
92
116
|
port: answers.port,
|
|
@@ -4,6 +4,7 @@ exports.validatePort = validatePort;
|
|
|
4
4
|
exports.isValidPostgresConnectionString = isValidPostgresConnectionString;
|
|
5
5
|
exports.collectProviders = collectProviders;
|
|
6
6
|
exports.normalizeProviders = normalizeProviders;
|
|
7
|
+
exports.parsePositiveInt = parsePositiveInt;
|
|
7
8
|
const types_1 = require("../types");
|
|
8
9
|
function validatePort(port) {
|
|
9
10
|
const parsedPort = Number(port);
|
|
@@ -48,3 +49,16 @@ function normalizeProviders(values = []) {
|
|
|
48
49
|
throw new Error(errors.join('\n'));
|
|
49
50
|
return result;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Parses a string as a positive integer.
|
|
54
|
+
* Returns the integer when the value is a strict positive integer string,
|
|
55
|
+
* or `undefined` when the input is `undefined`, a decimal, or non-numeric.
|
|
56
|
+
*/
|
|
57
|
+
function parsePositiveInt(value) {
|
|
58
|
+
if (value === undefined)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (!/^[1-9]\d*$/.test(value)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return Number(value);
|
|
64
|
+
}
|
|
@@ -53,4 +53,10 @@ exports.envValidationSchema = Joi.object({
|
|
|
53
53
|
.default('development'),
|
|
54
54
|
ALLOWED_ORIGINS: Joi.string().optional(),
|
|
55
55
|
SCHEDULER_ENABLED: Joi.boolean().default(true),
|
|
56
|
+
LOG_ROTATION_MAX_SIZE_MB: Joi.string()
|
|
57
|
+
.pattern(/^[1-9]\d*$/)
|
|
58
|
+
.optional(),
|
|
59
|
+
LOG_ROTATION_MAX_FILES: Joi.string()
|
|
60
|
+
.pattern(/^[1-9]\d*$/)
|
|
61
|
+
.optional(),
|
|
56
62
|
});
|