bunqueue-dashboard 0.0.47 → 1.0.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 +6 -6
- package/agent/managedTarget.ts +3 -1
- package/agent/manager/commandLine.ts +33 -0
- package/agent/manager/errors.ts +4 -0
- package/agent/manager/process.ts +4 -2
- package/agent/manager.ts +1 -0
- package/agent/server/controlRoutes.ts +10 -0
- package/agent/server/controlStatus.ts +26 -5
- package/agent/server/errors.ts +3 -0
- package/agent/server/router.ts +2 -1
- package/agent/shutdown.ts +3 -2
- package/dist/.vite/manifest.json +537 -575
- package/dist/assets/AddJob-CiwWycYG.js +3 -0
- package/dist/assets/Alerts-DzKKoA9U.js +1 -0
- package/dist/assets/AreaChart-Db2UVnaK.js +1 -0
- package/dist/assets/Benchmark-CHYDL31K.js +1 -0
- package/dist/assets/BulkAddJobs-GRMVnf0j.js +5 -0
- package/dist/assets/Button-_aSJvRoW.js +1 -0
- package/dist/assets/Card-egIDLwVk.js +1 -0
- package/dist/assets/Cron-XL0PdUEc.js +1 -0
- package/dist/assets/CronManager-Cqtg9Zr8.js +1 -0
- package/dist/assets/{Database-DV2pcDLL.js → Database-BoxVpjts.js} +3 -3
- package/dist/assets/Diagnostics-B3QSQuFF.js +1 -0
- package/dist/assets/Dlq-DHOfgORa.js +1 -0
- package/dist/assets/DlqControl-BY1e55-x.js +1 -0
- package/dist/assets/DlqPro-CfwKDcpc.js +1 -0
- package/dist/assets/Fleet-Bkbq0dFB.js +1 -0
- package/dist/assets/Flows-CDCQrPit.js +2 -0
- package/dist/assets/JobInspector-Bw3Gs1Iy.js +2 -0
- package/dist/assets/Jobs-IrlneOy6.js +1 -0
- package/dist/assets/JobsPro-vlPd6Ows.js +1 -0
- package/dist/assets/Logs-DG7cK-tF.js +1 -0
- package/dist/assets/LogsPro-ClSqFKys.js +1 -0
- package/dist/assets/{McpServer-fVy27Mof.js → McpServer-DoNFY0Pj.js} +1 -1
- package/dist/assets/{Metrics-Ba46pdzT.js → Metrics-BkePZiCZ.js} +1 -1
- package/dist/assets/MetricsPro-ooSvpQ3H.js +1 -0
- package/dist/assets/{Overview-DV2qo5v3.js → Overview-CuWmPRra.js} +1 -1
- package/dist/assets/{PageHeader-CvIyaFOO.js → PageHeader-CQzNsw8r.js} +1 -1
- package/dist/assets/Pagination-CSkzhNW6.js +1 -0
- package/dist/assets/QueueActions-BBxRt-t0.js +1 -0
- package/dist/assets/QueueControl-C01l6bBJ.js +1 -0
- package/dist/assets/QueueDetail-gSf9Tb7G.js +1 -0
- package/dist/assets/QueueDetailPro-BKszqI0M.js +1 -0
- package/dist/assets/Queues-BAg2nSz0.js +1 -0
- package/dist/assets/QueuesOverview-BLSjiYsA.js +1 -0
- package/dist/assets/{S3Backup-DNABHR2Q.js → S3Backup-Ci4dJPP_.js} +1 -1
- package/dist/assets/S3BackupPro-DcjlrzC5.js +3 -0
- package/dist/assets/ServerControl-SiZBNo9P.js +3 -0
- package/dist/assets/Settings-DR_iqW0T.js +1 -0
- package/dist/assets/StatusBadge-DereLzOj.js +1 -0
- package/dist/assets/{Usage-1KxG1mJ9.js → Usage-ZWfB7PcR.js} +1 -1
- package/dist/assets/UsagePro-BjdeS2zb.js +1 -0
- package/dist/assets/Webhooks-BonYUgMo.js +1 -0
- package/dist/assets/Workers-C1h6hCPp.js +1 -0
- package/dist/assets/WorkersPro-B0f_Nfdz.js +1 -0
- package/dist/assets/Workflows-CqZZ96EK.js +1 -0
- package/dist/assets/{api-BgWE-705.js → api-D9aT0ND7.js} +1 -1
- package/dist/assets/bq-DR2BTISO.js +1 -0
- package/dist/assets/classicJobsData-Csj4iihY.js +1 -0
- package/dist/assets/connectionStore-CVjQyIZv.js +1 -0
- package/dist/assets/controlConnectionEpoch-ChAeMMLk.js +1 -0
- package/dist/assets/feedback-CtGN_kqB.js +1 -0
- package/dist/assets/form-DG3X6cLL.js +1 -0
- package/dist/assets/{icons-BVzwsfbG.js → icons-DH9SS1DA.js} +1 -1
- package/dist/assets/index-Bat1DN5U.js +8 -0
- package/dist/assets/react-vendor-CFL9l8LK.js +11 -0
- package/dist/assets/{cn-zFEt5SO8.js → rolldown-runtime-QTnfLwEv.js} +1 -1
- package/dist/assets/{toastStore-IZn6OPLZ.js → toastStore-XmMX7A5W.js} +1 -1
- package/dist/assets/useControlActionGuard-1i7aFA7E.js +1 -0
- package/dist/assets/usePolledData-CEyyLRYa.js +1 -0
- package/dist/assets/useServerActionGuard-CUx8TOAC.js +1 -0
- package/dist/assets/useThroughputSeries-CeFx-Mwq.js +1 -0
- package/dist/index.html +14 -14
- package/package.json +10 -7
- package/scripts/embedded.gen.ts +152 -162
- package/scripts/serveHandler.ts +28 -1
- package/dist/assets/AddJob-FNaPbs_V.js +0 -3
- package/dist/assets/Alerts-vzIYtFpW.js +0 -1
- package/dist/assets/AreaChart-DEAalJ6P.js +0 -1
- package/dist/assets/Benchmark-C16tT4pF.js +0 -1
- package/dist/assets/BulkAddJobs-IiL1uud0.js +0 -5
- package/dist/assets/Button-Ch1W94fJ.js +0 -1
- package/dist/assets/Card-CWxrC8OY.js +0 -1
- package/dist/assets/CopilotPanel-FlAW3RlL.js +0 -44
- package/dist/assets/Cron-1DZTZWXO.js +0 -1
- package/dist/assets/CronManager-B6xLLgI5.js +0 -1
- package/dist/assets/Diagnostics-B6apZrC5.js +0 -1
- package/dist/assets/Dlq-MRmkQrNC.js +0 -1
- package/dist/assets/DlqControl-DNJPVOTF.js +0 -1
- package/dist/assets/DlqPro-CwtTDwGN.js +0 -1
- package/dist/assets/Fleet-Bdrj9bLI.js +0 -1
- package/dist/assets/Flows-1dC8F_wx.js +0 -2
- package/dist/assets/JobInspector-Ct9Ga-UT.js +0 -2
- package/dist/assets/Jobs-Chqv0RuL.js +0 -1
- package/dist/assets/JobsPro-CLzOhKAL.js +0 -1
- package/dist/assets/Logs-C5PNP9Wq.js +0 -1
- package/dist/assets/LogsPro-B3KwTSsn.js +0 -1
- package/dist/assets/MetricsPro-B9rD_KQc.js +0 -1
- package/dist/assets/Pagination-x-Cwgrul.js +0 -1
- package/dist/assets/QueueActions-BWnmNEqV.js +0 -1
- package/dist/assets/QueueControl-DqBvuDnF.js +0 -1
- package/dist/assets/QueueDetail-COSH9bJl.js +0 -1
- package/dist/assets/QueueDetailPro-ClKtyHIf.js +0 -1
- package/dist/assets/Queues-D2-R8nYh.js +0 -1
- package/dist/assets/QueuesOverview-tokquA3g.js +0 -1
- package/dist/assets/S3BackupPro-CDX5WQUY.js +0 -3
- package/dist/assets/ServerControl-BmccMNW9.js +0 -3
- package/dist/assets/Settings-UcujwvKv.js +0 -1
- package/dist/assets/StatusBadge-BoOVTnKu.js +0 -1
- package/dist/assets/UsagePro-BGLufsE7.js +0 -1
- package/dist/assets/Webhooks-DT2LK17V.js +0 -1
- package/dist/assets/Workers-BX1qspaQ.js +0 -1
- package/dist/assets/WorkersPro-BsUQVgQZ.js +0 -1
- package/dist/assets/Workflows-DZZA6aGW.js +0 -1
- package/dist/assets/bq-F5BbTG1T.js +0 -1
- package/dist/assets/connectionStore-BJLihGaC.js +0 -1
- package/dist/assets/controlConnectionEpoch-CZsS0wAb.js +0 -1
- package/dist/assets/dist-1f1fMHVO.js +0 -11
- package/dist/assets/dist-BlWBaCN6.js +0 -11
- package/dist/assets/dist-DZL8soAh.js +0 -79
- package/dist/assets/dist-DjvZiKhB.js +0 -2
- package/dist/assets/dist-DpZcbP9W.js +0 -11
- package/dist/assets/feedback-QDN1AsYt.js +0 -1
- package/dist/assets/form-C4oby9pI.js +0 -1
- package/dist/assets/index-8Ii-_Rml.js +0 -8
- package/dist/assets/react-vendor-CvwrP0tU.js +0 -11
- package/dist/assets/useControlActionGuard-B-bzKZKI.js +0 -1
- package/dist/assets/usePolledData-0CVelkl2.js +0 -1
- package/dist/assets/useServerActionGuard-uGePilQQ.js +0 -1
- package/dist/assets/useThroughputSeries-DgO8uicf.js +0 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The live demo uses sample data. The docs explain how to connect and operate a re
|
|
|
26
26
|
[](LICENSE)
|
|
27
27
|
[](https://egeominotti.github.io/bunqueue-dashboard/docs/)
|
|
28
28
|
[](https://egeominotti.github.io/bunqueue-dashboard/)
|
|
29
|
-

|
|
30
30
|
|
|
31
31
|

|
|
32
32
|

|
|
@@ -58,9 +58,9 @@ The live demo uses sample data. The docs explain how to connect and operate a re
|
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
61
|
-
>
|
|
62
|
-
>
|
|
63
|
-
>
|
|
61
|
+
> ✅ **Stable (1.x).** The dashboard follows [Semantic Versioning](https://semver.org/):
|
|
62
|
+
> breaking changes to its configuration, routes or agent API only ship in a new
|
|
63
|
+
> major version.
|
|
64
64
|
|
|
65
65
|
## 📚 Documentation
|
|
66
66
|
|
|
@@ -129,8 +129,8 @@ contracts; it never patches Bunqueue internals.
|
|
|
129
129
|
> registration or flow order. Cancel, Discard, Drain, Clean, Obliterate and DLQ Purge also fail
|
|
130
130
|
> closed. Bunqueue 2.9's `Queue.removeDlqJob()` also accepts only queue + job ID, so custom-ID reuse
|
|
131
131
|
> prevents the dashboard from proving that a later deletion still targets the observed generation.
|
|
132
|
-
> DLQ `maxAge`/`maxEntries` are read-only, auto-retry can only be disabled
|
|
133
|
-
>
|
|
132
|
+
> DLQ `maxAge`/`maxEntries` are read-only, and auto-retry can only be disabled. The in-app Copilot
|
|
133
|
+
> is switched off in official builds (`VITE_ENABLE_COPILOT=1` opts a custom build back in).
|
|
134
134
|
|
|
135
135
|
## Quick start
|
|
136
136
|
|
package/agent/managedTarget.ts
CHANGED
|
@@ -72,7 +72,9 @@ function assertLocalTarget(target: string, config: ServerConfig, label: string):
|
|
|
72
72
|
} catch {
|
|
73
73
|
throw new Error(`${label} must be an absolute local HTTP URL`);
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// URL keeps IPv6 brackets in hostname (`[::1]`), so compare without them.
|
|
76
|
+
const host = url.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
77
|
+
const local = ['localhost', '127.0.0.1', '::1'].includes(host);
|
|
76
78
|
const port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));
|
|
77
79
|
if (!local || port !== config.httpPort || (url.protocol !== 'http:' && url.protocol !== 'https:')) {
|
|
78
80
|
throw new Error(
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split the managed launch command into argv without a shell. Whitespace
|
|
3
|
+
* separates arguments; a balanced pair of double or single quotes groups an
|
|
4
|
+
* argument that contains spaces (`"C:\Program Files\bun\bun.exe" run main.ts`).
|
|
5
|
+
* Backslashes are literal so Windows paths need no escaping. A command with an
|
|
6
|
+
* unbalanced quote (an apostrophe in a path) keeps the legacy whitespace split,
|
|
7
|
+
* so a configuration that launched before still launches the same way.
|
|
8
|
+
*/
|
|
9
|
+
export function splitCommandLine(command: string): string[] {
|
|
10
|
+
const args: string[] = [];
|
|
11
|
+
let current = '';
|
|
12
|
+
let quote: '"' | "'" | null = null;
|
|
13
|
+
let inArgument = false;
|
|
14
|
+
for (const char of command.trim()) {
|
|
15
|
+
if (quote) {
|
|
16
|
+
if (char === quote) quote = null;
|
|
17
|
+
else current += char;
|
|
18
|
+
} else if (char === '"' || char === "'") {
|
|
19
|
+
quote = char;
|
|
20
|
+
inArgument = true;
|
|
21
|
+
} else if (/\s/.test(char)) {
|
|
22
|
+
if (inArgument) args.push(current);
|
|
23
|
+
current = '';
|
|
24
|
+
inArgument = false;
|
|
25
|
+
} else {
|
|
26
|
+
current += char;
|
|
27
|
+
inArgument = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (quote) return command.trim().split(/\s+/);
|
|
31
|
+
if (inArgument) args.push(current);
|
|
32
|
+
return args;
|
|
33
|
+
}
|
package/agent/manager/process.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { copyConfig, defaultConfig, validateConfigPatch, validateServerConfig } from './config';
|
|
2
2
|
import { ProcessLogs } from './logs';
|
|
3
|
+
import { splitCommandLine } from './commandLine';
|
|
4
|
+
import { ManagedProcessStuckError } from './errors';
|
|
3
5
|
import type { ConfigStore } from './configStore';
|
|
4
6
|
import { databaseStats } from './storage';
|
|
5
7
|
import { removeSqlitePaths, validateManagedStorage } from './storageMode';
|
|
@@ -118,7 +120,7 @@ export class ProcessManager {
|
|
|
118
120
|
|
|
119
121
|
const launchConfig = validateServerConfig(this.config);
|
|
120
122
|
const storageMode = validateManagedStorage(launchConfig);
|
|
121
|
-
const [command, ...args] = launchConfig.command
|
|
123
|
+
const [command, ...args] = splitCommandLine(launchConfig.command);
|
|
122
124
|
this.status = 'starting';
|
|
123
125
|
this.exitCode = null;
|
|
124
126
|
const token = ++this.procToken;
|
|
@@ -219,7 +221,7 @@ export class ProcessManager {
|
|
|
219
221
|
if (!(await exitsWithin(process, this.stopTimeoutMs))) {
|
|
220
222
|
const message = `pid ${process.pid} did not exit after SIGKILL`;
|
|
221
223
|
this.output.push('sys', `${message} — retaining failed-process tracking`);
|
|
222
|
-
throw new
|
|
224
|
+
throw new ManagedProcessStuckError(message);
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
227
|
this.output.cancel(token);
|
package/agent/manager.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Public compatibility entrypoint for managed bunqueue process supervision. */
|
|
2
2
|
export { validateConfigPatch } from './manager/config';
|
|
3
|
+
export { ManagedProcessStuckError } from './manager/errors';
|
|
3
4
|
export { ProcessManager } from './manager/process';
|
|
4
5
|
export {
|
|
5
6
|
managedPostgresNamespace,
|
|
@@ -123,6 +123,16 @@ export function assertManagedControl(controlTarget: ServerControlTarget): void {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Release the Workflow Engine's read-write SQLite connection before the data
|
|
128
|
+
* file is replaced. A broker that crashed (or was killed externally) leaves the
|
|
129
|
+
* engine open; after a restore it would keep writing to the unlinked file.
|
|
130
|
+
*/
|
|
131
|
+
export async function closeWorkflowRuntime(runtime: WorkflowRuntimePort): Promise<void> {
|
|
132
|
+
const closeResult = await settle(() => runtime.close());
|
|
133
|
+
if (!closeResult.ok) throw workflowCloseError(closeResult.error);
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
async function stopWithRuntimeCleanup(
|
|
127
137
|
manager: ProcessManager,
|
|
128
138
|
runtime: WorkflowRuntimePort
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
managedStorageMode,
|
|
5
5
|
type ProcessManager,
|
|
6
6
|
} from '../manager';
|
|
7
|
+
import { managedConnection } from '../managedConnection';
|
|
7
8
|
import { probeExternalHealth, type ServerControlTarget } from './controlTarget';
|
|
8
9
|
|
|
9
10
|
export async function statusWithHealth(
|
|
@@ -30,12 +31,10 @@ export async function statusWithHealth(
|
|
|
30
31
|
}
|
|
31
32
|
let healthy = false;
|
|
32
33
|
let version: string | undefined;
|
|
34
|
+
const effectiveConfig = snapshot.runningConfig ?? snapshot.config;
|
|
33
35
|
if (snapshot.status === 'running') {
|
|
34
|
-
const port = snapshot.runningConfig?.httpPort ?? snapshot.config.httpPort;
|
|
35
36
|
try {
|
|
36
|
-
const response = await
|
|
37
|
-
signal: AbortSignal.timeout(1500),
|
|
38
|
-
});
|
|
37
|
+
const response = await fetchManagedHealth(effectiveConfig);
|
|
39
38
|
if (response.ok) {
|
|
40
39
|
const body: unknown = await response.json();
|
|
41
40
|
if (body !== null && typeof body === 'object' && !Array.isArray(body)) {
|
|
@@ -48,7 +47,6 @@ export async function statusWithHealth(
|
|
|
48
47
|
// The managed process is not healthy yet.
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
|
-
const effectiveConfig = snapshot.runningConfig ?? snapshot.config;
|
|
52
50
|
const storageMode = statusStorageMode(effectiveConfig);
|
|
53
51
|
const database = await statusDatabase(manager, effectiveConfig);
|
|
54
52
|
const current = manager.getStatus();
|
|
@@ -85,6 +83,29 @@ export async function statusWithHealth(
|
|
|
85
83
|
};
|
|
86
84
|
}
|
|
87
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Bunqueue serves HTTPS on its HTTP port once server TLS is configured, so the
|
|
88
|
+
* probe follows the same TLS resolution (and private CA) as the agent's TCP
|
|
89
|
+
* clients; a plain-HTTP probe would report a healthy TLS server as down forever.
|
|
90
|
+
*/
|
|
91
|
+
async function fetchManagedHealth(
|
|
92
|
+
config: ReturnType<ProcessManager['getConfig']>
|
|
93
|
+
): Promise<Response> {
|
|
94
|
+
let tls: ReturnType<typeof managedConnection>['tls'];
|
|
95
|
+
try {
|
|
96
|
+
tls = managedConnection(config).tls;
|
|
97
|
+
} catch {
|
|
98
|
+
// Invalid TLS settings are reported by the routes that use them.
|
|
99
|
+
tls = undefined;
|
|
100
|
+
}
|
|
101
|
+
const caFile = typeof tls === 'object' ? tls.caFile : undefined;
|
|
102
|
+
const ca = caFile ? await Bun.file(caFile).text() : undefined;
|
|
103
|
+
return fetch(`${tls ? 'https' : 'http'}://127.0.0.1:${config.httpPort}/health`, {
|
|
104
|
+
signal: AbortSignal.timeout(1500),
|
|
105
|
+
...(ca ? { tls: { ca } } : {}),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
88
109
|
async function statusDatabase(
|
|
89
110
|
manager: ProcessManager,
|
|
90
111
|
config: ReturnType<ProcessManager['getConfig']>
|
package/agent/server/errors.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
MissingDbError,
|
|
5
5
|
} from '../db';
|
|
6
6
|
import { DbReadBusyError, DbReadUnavailableError } from '../db/types';
|
|
7
|
+
import { ManagedProcessStuckError } from '../manager';
|
|
7
8
|
import { safeErrorMessage } from '../errorMessage';
|
|
8
9
|
import { QueueOperationsUnavailableError } from '../queue/types';
|
|
9
10
|
import { WorkflowRuntimeUnavailableError } from '../workflow/runtime';
|
|
@@ -15,6 +16,7 @@ const CONFLICT_MESSAGES = [
|
|
|
15
16
|
'Another backup operation is already running',
|
|
16
17
|
'Another maintenance operation is already running',
|
|
17
18
|
'Cannot start the managed Bunqueue server',
|
|
19
|
+
'Cannot start while the previous managed process',
|
|
18
20
|
'Cannot restart the managed Bunqueue server',
|
|
19
21
|
'BUNQUEUE_WORKFLOW_QUEUE_NAME must',
|
|
20
22
|
'BUNQUEUE_WORKFLOW_CONCURRENCY must',
|
|
@@ -26,6 +28,7 @@ const CONFLICT_MESSAGES = [
|
|
|
26
28
|
export function errorStatus(error: unknown): number {
|
|
27
29
|
try {
|
|
28
30
|
if (error instanceof AgentLifecycleClosedError) return 503;
|
|
31
|
+
if (error instanceof ManagedProcessStuckError) return 500;
|
|
29
32
|
if (error instanceof MissingDbError) return 404;
|
|
30
33
|
if (error instanceof DbReadBusyError) return 429;
|
|
31
34
|
if (error instanceof DbReadUnavailableError) return 503;
|
package/agent/server/router.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { routeQueueOperationsRequest } from '../queue/routes';
|
|
|
10
10
|
import type { QueueOperationsPort } from '../queue/types';
|
|
11
11
|
import type { WorkflowRuntimePort } from '../workflow/runtime';
|
|
12
12
|
import { routeWorkflowRuntimeRequest } from '../workflow/runtimeRoutes';
|
|
13
|
-
import { assertManagedControl, routeControlRequest } from './controlRoutes';
|
|
13
|
+
import { assertManagedControl, closeWorkflowRuntime, routeControlRequest } from './controlRoutes';
|
|
14
14
|
import type { ServerControlTarget } from './controlTarget';
|
|
15
15
|
import { routeDatabaseRequest } from './databaseRoutes';
|
|
16
16
|
import type { AgentLifecyclePort } from './lifecycle';
|
|
@@ -123,6 +123,7 @@ export async function routeAgentRequest(
|
|
|
123
123
|
return sqliteOnly('S3 backup', currentStorageMode);
|
|
124
124
|
}
|
|
125
125
|
return manager.withStoppedMaintenance('restoring a backup', async () => {
|
|
126
|
+
await closeWorkflowRuntime(runtime);
|
|
126
127
|
return executePreparedBackupRequest(
|
|
127
128
|
prepared,
|
|
128
129
|
restoreConfig,
|
package/agent/shutdown.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { logger } from './logger';
|
|
|
2
2
|
import { safeErrorMessage } from './errorMessage';
|
|
3
3
|
import type { AgentFetchHandler } from './server';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** Upper bound for a graceful agent drain; supervisors must wait at least this long. */
|
|
6
|
+
export const AGENT_SHUTDOWN_GRACE_MS = 30_000;
|
|
6
7
|
|
|
7
8
|
export interface AgentShutdownOptions {
|
|
8
9
|
/** Stop listeners synchronously; returned promises drain in-flight HTTP requests. */
|
|
@@ -28,7 +29,7 @@ export function createAgentShutdown(
|
|
|
28
29
|
options: AgentShutdownOptions = {}
|
|
29
30
|
): (signal: string) => void {
|
|
30
31
|
const exit = options.exit ?? ((code: number) => process.exit(code));
|
|
31
|
-
const graceMs = options.graceMs ??
|
|
32
|
+
const graceMs = options.graceMs ?? AGENT_SHUTDOWN_GRACE_MS;
|
|
32
33
|
let shuttingDown = false;
|
|
33
34
|
let finished = false;
|
|
34
35
|
let graceTimer: ReturnType<typeof setTimeout> | null = null;
|