awb-agent-manager 1.0.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/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/lib/agent-lockfile.d.ts +37 -0
- package/dist/lib/agent-lockfile.js +205 -0
- package/dist/lib/agent-lockfile.js.map +1 -0
- package/dist/lib/agent-manager-commands.d.ts +34 -0
- package/dist/lib/agent-manager-commands.js +531 -0
- package/dist/lib/agent-manager-commands.js.map +1 -0
- package/dist/lib/base-session-manager.d.ts +127 -0
- package/dist/lib/base-session-manager.js +667 -0
- package/dist/lib/base-session-manager.js.map +1 -0
- package/dist/lib/chat-attachment-prep.d.ts +35 -0
- package/dist/lib/chat-attachment-prep.js +180 -0
- package/dist/lib/chat-attachment-prep.js.map +1 -0
- package/dist/lib/chat-session-manager.d.ts +29 -0
- package/dist/lib/chat-session-manager.js +400 -0
- package/dist/lib/chat-session-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.d.ts +34 -0
- package/dist/lib/circuit-breaker.js +146 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/cli-adapters/antigravity.d.ts +15 -0
- package/dist/lib/cli-adapters/antigravity.js +114 -0
- package/dist/lib/cli-adapters/antigravity.js.map +1 -0
- package/dist/lib/cli-adapters/base.d.ts +115 -0
- package/dist/lib/cli-adapters/base.js +111 -0
- package/dist/lib/cli-adapters/base.js.map +1 -0
- package/dist/lib/cli-adapters/claude.d.ts +19 -0
- package/dist/lib/cli-adapters/claude.js +233 -0
- package/dist/lib/cli-adapters/claude.js.map +1 -0
- package/dist/lib/cli-adapters/codex.d.ts +14 -0
- package/dist/lib/cli-adapters/codex.js +152 -0
- package/dist/lib/cli-adapters/codex.js.map +1 -0
- package/dist/lib/cli-adapters/deepseek.d.ts +15 -0
- package/dist/lib/cli-adapters/deepseek.js +70 -0
- package/dist/lib/cli-adapters/deepseek.js.map +1 -0
- package/dist/lib/cli-adapters/index.d.ts +8 -0
- package/dist/lib/cli-adapters/index.js +27 -0
- package/dist/lib/cli-adapters/index.js.map +1 -0
- package/dist/lib/cli-adapters/model-introspect.d.ts +3 -0
- package/dist/lib/cli-adapters/model-introspect.js +65 -0
- package/dist/lib/cli-adapters/model-introspect.js.map +1 -0
- package/dist/lib/cli-error-signatures.d.ts +9 -0
- package/dist/lib/cli-error-signatures.js +22 -0
- package/dist/lib/cli-error-signatures.js.map +1 -0
- package/dist/lib/cli-output-summary.d.ts +2 -0
- package/dist/lib/cli-output-summary.js +104 -0
- package/dist/lib/cli-output-summary.js.map +1 -0
- package/dist/lib/cli-resolver.d.ts +2 -0
- package/dist/lib/cli-resolver.js +177 -0
- package/dist/lib/cli-resolver.js.map +1 -0
- package/dist/lib/config.d.ts +13 -0
- package/dist/lib/config.js +124 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +36 -0
- package/dist/lib/constants.js +51 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/environment-provisioner.d.ts +31 -0
- package/dist/lib/environment-provisioner.js +182 -0
- package/dist/lib/environment-provisioner.js.map +1 -0
- package/dist/lib/error-log-uploader.d.ts +19 -0
- package/dist/lib/error-log-uploader.js +138 -0
- package/dist/lib/error-log-uploader.js.map +1 -0
- package/dist/lib/event-dispatcher.d.ts +149 -0
- package/dist/lib/event-dispatcher.js +799 -0
- package/dist/lib/event-dispatcher.js.map +1 -0
- package/dist/lib/event-log-recorder.d.ts +11 -0
- package/dist/lib/event-log-recorder.js +77 -0
- package/dist/lib/event-log-recorder.js.map +1 -0
- package/dist/lib/event-stream.d.ts +26 -0
- package/dist/lib/event-stream.js +177 -0
- package/dist/lib/event-stream.js.map +1 -0
- package/dist/lib/fs-browser.d.ts +30 -0
- package/dist/lib/fs-browser.js +287 -0
- package/dist/lib/fs-browser.js.map +1 -0
- package/dist/lib/host-mcp/logs.d.ts +19 -0
- package/dist/lib/host-mcp/logs.js +102 -0
- package/dist/lib/host-mcp/logs.js.map +1 -0
- package/dist/lib/host-mcp/platform.d.ts +24 -0
- package/dist/lib/host-mcp/platform.js +133 -0
- package/dist/lib/host-mcp/platform.js.map +1 -0
- package/dist/lib/host-mcp/screens.d.ts +22 -0
- package/dist/lib/host-mcp/screens.js +172 -0
- package/dist/lib/host-mcp/screens.js.map +1 -0
- package/dist/lib/host-mcp/server.d.ts +3 -0
- package/dist/lib/host-mcp/server.js +233 -0
- package/dist/lib/host-mcp/server.js.map +1 -0
- package/dist/lib/host-mcp/tools.d.ts +172 -0
- package/dist/lib/host-mcp/tools.js +1241 -0
- package/dist/lib/host-mcp/tools.js.map +1 -0
- package/dist/lib/instance-heartbeat.d.ts +55 -0
- package/dist/lib/instance-heartbeat.js +118 -0
- package/dist/lib/instance-heartbeat.js.map +1 -0
- package/dist/lib/legacy-import.d.ts +17 -0
- package/dist/lib/legacy-import.js +70 -0
- package/dist/lib/legacy-import.js.map +1 -0
- package/dist/lib/logging.d.ts +3 -0
- package/dist/lib/logging.js +65 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/managed-agent-context.d.ts +25 -0
- package/dist/lib/managed-agent-context.js +33 -0
- package/dist/lib/managed-agent-context.js.map +1 -0
- package/dist/lib/managed-agent-store.d.ts +34 -0
- package/dist/lib/managed-agent-store.js +160 -0
- package/dist/lib/managed-agent-store.js.map +1 -0
- package/dist/lib/managed-agents.d.ts +24 -0
- package/dist/lib/managed-agents.js +87 -0
- package/dist/lib/managed-agents.js.map +1 -0
- package/dist/lib/mcp-client.d.ts +8 -0
- package/dist/lib/mcp-client.js +122 -0
- package/dist/lib/mcp-client.js.map +1 -0
- package/dist/lib/orphan-cleanup.d.ts +6 -0
- package/dist/lib/orphan-cleanup.js +108 -0
- package/dist/lib/orphan-cleanup.js.map +1 -0
- package/dist/lib/presence-heartbeat.d.ts +8 -0
- package/dist/lib/presence-heartbeat.js +61 -0
- package/dist/lib/presence-heartbeat.js.map +1 -0
- package/dist/lib/prompts.d.ts +29 -0
- package/dist/lib/prompts.js +208 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/rest.d.ts +61 -0
- package/dist/lib/rest.js +307 -0
- package/dist/lib/rest.js.map +1 -0
- package/dist/lib/run-provisioner.d.ts +21 -0
- package/dist/lib/run-provisioner.js +115 -0
- package/dist/lib/run-provisioner.js.map +1 -0
- package/dist/lib/self-path.d.ts +7 -0
- package/dist/lib/self-path.js +29 -0
- package/dist/lib/self-path.js.map +1 -0
- package/dist/lib/self-update.d.ts +46 -0
- package/dist/lib/self-update.js +482 -0
- package/dist/lib/self-update.js.map +1 -0
- package/dist/lib/service-install.d.ts +23 -0
- package/dist/lib/service-install.js +673 -0
- package/dist/lib/service-install.js.map +1 -0
- package/dist/lib/setup.d.ts +16 -0
- package/dist/lib/setup.js +157 -0
- package/dist/lib/setup.js.map +1 -0
- package/dist/lib/subagent-manager.d.ts +84 -0
- package/dist/lib/subagent-manager.js +793 -0
- package/dist/lib/subagent-manager.js.map +1 -0
- package/dist/lib/subagent-monitor.d.ts +43 -0
- package/dist/lib/subagent-monitor.js +229 -0
- package/dist/lib/subagent-monitor.js.map +1 -0
- package/dist/lib/ticket-session-manager.d.ts +23 -0
- package/dist/lib/ticket-session-manager.js +471 -0
- package/dist/lib/ticket-session-manager.js.map +1 -0
- package/dist/lib/worktree-manager.d.ts +39 -0
- package/dist/lib/worktree-manager.js +226 -0
- package/dist/lib/worktree-manager.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +670 -0
- package/dist/main.js.map +1 -0
- package/dist/package.json +57 -0
- package/package.json +57 -0
package/dist/main.js
ADDED
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, resolve, join as pathJoin } from 'node:path';
|
|
6
|
+
import { AGENT_MANAGER_HOME, CONFIG_PATH, LEGACY_CONFIG_PATH, MANAGED_AGENTS_DIR, } from './lib/constants.js';
|
|
7
|
+
import { loadConfig, resolveAgentId } from './lib/config.js';
|
|
8
|
+
import { installCrashHandlers, log } from './lib/logging.js';
|
|
9
|
+
import { acquireAgentLock, inspectLegacyAgentLock, } from './lib/agent-lockfile.js';
|
|
10
|
+
import { importLegacyConfig } from './lib/legacy-import.js';
|
|
11
|
+
import { isSystemdReExecPending, runSelfUpdate, UpdateChecker } from './lib/self-update.js';
|
|
12
|
+
import { runSetup } from './lib/setup.js';
|
|
13
|
+
import { installService, uninstallService } from './lib/service-install.js';
|
|
14
|
+
import { PresenceHeartbeat } from './lib/presence-heartbeat.js';
|
|
15
|
+
import { InstanceHeartbeat } from './lib/instance-heartbeat.js';
|
|
16
|
+
import { EventStream } from './lib/event-stream.js';
|
|
17
|
+
import { SubagentManager } from './lib/subagent-manager.js';
|
|
18
|
+
import { ChatSessionManager } from './lib/chat-session-manager.js';
|
|
19
|
+
import { TicketSessionManager } from './lib/ticket-session-manager.js';
|
|
20
|
+
import { CircuitBreaker } from './lib/circuit-breaker.js';
|
|
21
|
+
import { uploadIfNewErrors } from './lib/error-log-uploader.js';
|
|
22
|
+
import { onFlushThreshold } from './lib/event-log-recorder.js';
|
|
23
|
+
import { cleanupOrphanSubagents } from './lib/orphan-cleanup.js';
|
|
24
|
+
import { FsBrowser } from './lib/fs-browser.js';
|
|
25
|
+
import { SubagentMonitor } from './lib/subagent-monitor.js';
|
|
26
|
+
import { KNOWN_ADAPTER_CLI_TYPES, createAdapter } from './lib/cli-adapters/index.js';
|
|
27
|
+
import { promptComposer } from './lib/prompts.js';
|
|
28
|
+
import { ManagedAgentRegistry } from './lib/managed-agents.js';
|
|
29
|
+
import { ManagedAgentContextRegistry } from './lib/managed-agent-context.js';
|
|
30
|
+
import { WorktreeManager, worktreeSlug } from './lib/worktree-manager.js';
|
|
31
|
+
import { EnvironmentProvisioner } from './lib/environment-provisioner.js';
|
|
32
|
+
import { AgentManagerCommandHandler } from './lib/agent-manager-commands.js';
|
|
33
|
+
import { listManagedAgentDirs, readManagedAgentConfig, readApiKey, readAgentCredential, mcpConfigPathFor, subagentLogPathFor, cliHomeDirFor, ensureCliHomeDir, } from './lib/managed-agent-store.js';
|
|
34
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
function readPkgVersion() {
|
|
36
|
+
try {
|
|
37
|
+
const pkgPath = resolve(__dirname, '..', 'package.json');
|
|
38
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
39
|
+
return pkg.version ?? '0.0.0';
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return '0.0.0';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function parseFlags(argv) {
|
|
46
|
+
let values;
|
|
47
|
+
try {
|
|
48
|
+
({ values } = parseArgs({
|
|
49
|
+
args: argv,
|
|
50
|
+
options: {
|
|
51
|
+
config: { type: 'string', short: 'c' },
|
|
52
|
+
workspace: { type: 'string', short: 'w' },
|
|
53
|
+
'dry-run': { type: 'boolean' },
|
|
54
|
+
help: { type: 'boolean', short: 'h' },
|
|
55
|
+
version: { type: 'boolean', short: 'v' },
|
|
56
|
+
force: { type: 'boolean', short: 'f' },
|
|
57
|
+
},
|
|
58
|
+
allowPositionals: false,
|
|
59
|
+
strict: true,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
process.stderr.write(`\n ✗ ${err?.message ?? err}\n\n`);
|
|
64
|
+
process.stderr.write(` Run \`awb-agent-manager --help\` for usage.\n\n`);
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
config: values.config,
|
|
69
|
+
workspace: values.workspace,
|
|
70
|
+
dryRun: Boolean(values['dry-run']),
|
|
71
|
+
help: Boolean(values.help),
|
|
72
|
+
version: Boolean(values.version),
|
|
73
|
+
force: Boolean(values.force),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function printHelp() {
|
|
77
|
+
process.stdout.write(`awb-agent-manager — standalone AWB subagent runner
|
|
78
|
+
|
|
79
|
+
Usage:
|
|
80
|
+
awb-agent-manager start the manager (uses saved config)
|
|
81
|
+
awb-agent-manager setup [opts] first-run pairing wizard
|
|
82
|
+
awb-agent-manager service install [..] register as a background service (auto-detects host)
|
|
83
|
+
awb-agent-manager service uninstall remove the registered service
|
|
84
|
+
awb-agent-manager mcp-host run the host-tools MCP server over stdio
|
|
85
|
+
(spawned by managed agents; not for direct use)
|
|
86
|
+
awb-agent-manager [options] start with overrides
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
-c, --config <path> Path to config.json (default: ${CONFIG_PATH})
|
|
90
|
+
-w, --workspace <id> Override workspace_id from config
|
|
91
|
+
-f, --force Take over the lockfile from a stale or running owner
|
|
92
|
+
--dry-run Load config and exit without starting runtime
|
|
93
|
+
-h, --help Show this help text
|
|
94
|
+
-v, --version Print version
|
|
95
|
+
|
|
96
|
+
Setup options (\`awb-agent-manager setup ...\`):
|
|
97
|
+
--url <url> AWB server base URL (skip prompt)
|
|
98
|
+
--token <token> Pairing token from AWB Admin → Agent Manager
|
|
99
|
+
--instance-id <id> Stable id reported on heartbeats (default <hostname>-<rand>)
|
|
100
|
+
--non-interactive Fail fast on missing fields instead of prompting
|
|
101
|
+
--force Overwrite an existing config.json
|
|
102
|
+
|
|
103
|
+
Service options (\`awb-agent-manager service install ...\`):
|
|
104
|
+
--system Install at system scope (sudo / admin, runs at boot)
|
|
105
|
+
Default: user scope (no admin, runs at logon)
|
|
106
|
+
--platform <p> Force a specific service backend instead of auto-detect:
|
|
107
|
+
auto (default) | systemd | sysvinit | synology | launchd | windows
|
|
108
|
+
--exec-path <path> Override path to dist/main.js (default: this binary's location)
|
|
109
|
+
--dry-run Print the unit/plist/script without writing or running registrar
|
|
110
|
+
--unit-only Write the unit file but skip daemon-reload / load / register
|
|
111
|
+
|
|
112
|
+
Platform mapping (auto):
|
|
113
|
+
linux + systemd → ~/.config/systemd/user/ (user) | /etc/systemd/system/ (--system)
|
|
114
|
+
linux + Synology DSM → /usr/local/etc/rc.d/awb-agent-manager.sh (always system)
|
|
115
|
+
linux without systemd → /etc/init.d/awb-agent-manager (always system, sysvinit)
|
|
116
|
+
darwin (macOS) → ~/Library/LaunchAgents/ (user) | /Library/LaunchDaemons/ (--system)
|
|
117
|
+
win32 → Task Scheduler task 'awb-agent-manager' (logon | --system boot)
|
|
118
|
+
|
|
119
|
+
Config search order:
|
|
120
|
+
1. --config flag
|
|
121
|
+
2. $AWB_AGENT_MANAGER_HOME/config.json
|
|
122
|
+
3. $XDG_CONFIG_HOME/awb-agent-manager/config.json (or %APPDATA% on Windows)
|
|
123
|
+
4. ~/.config/awb-agent-manager/config.json
|
|
124
|
+
|
|
125
|
+
Legacy import:
|
|
126
|
+
On first run, ${LEGACY_CONFIG_PATH} is copied into the new
|
|
127
|
+
config home if no config.json is present yet. A marker file is placed in
|
|
128
|
+
the legacy directory; subsequent runs skip the import. Existing legacy
|
|
129
|
+
files are NEVER deleted — the claude-plugin stdio MCP proxy may still use
|
|
130
|
+
them.
|
|
131
|
+
|
|
132
|
+
Signals:
|
|
133
|
+
SIGTERM/SIGINT graceful drain + exit
|
|
134
|
+
SIGHUP re-read config.json (delegation tunables hot-reload)
|
|
135
|
+
SIGUSR1 self-update: git pull + npm install + npm run build, then
|
|
136
|
+
re-exec with --force so the new build adopts the lockfile.
|
|
137
|
+
No-op when the manager is not running from a git checkout.
|
|
138
|
+
`);
|
|
139
|
+
}
|
|
140
|
+
function parseSetupArgs(argv) {
|
|
141
|
+
let values;
|
|
142
|
+
try {
|
|
143
|
+
({ values } = parseArgs({
|
|
144
|
+
args: argv,
|
|
145
|
+
options: {
|
|
146
|
+
config: { type: 'string', short: 'c' },
|
|
147
|
+
url: { type: 'string' },
|
|
148
|
+
token: { type: 'string' },
|
|
149
|
+
'instance-id': { type: 'string' },
|
|
150
|
+
'non-interactive': { type: 'boolean' },
|
|
151
|
+
force: { type: 'boolean', short: 'f' },
|
|
152
|
+
},
|
|
153
|
+
allowPositionals: false,
|
|
154
|
+
strict: true,
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
process.stderr.write(`\n ✗ setup: ${err?.message ?? err}\n\n`);
|
|
159
|
+
process.stderr.write(` Run \`awb-agent-manager --help\` for setup options.\n\n`);
|
|
160
|
+
process.exit(2);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
configPath: values.config || undefined,
|
|
164
|
+
url: values.url || undefined,
|
|
165
|
+
token: values.token || undefined,
|
|
166
|
+
instanceId: values['instance-id'] || undefined,
|
|
167
|
+
nonInteractive: Boolean(values['non-interactive']) || !process.stdin.isTTY,
|
|
168
|
+
force: Boolean(values.force),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function main() {
|
|
172
|
+
const argv = process.argv.slice(2);
|
|
173
|
+
if (argv[0] === 'mcp-host') {
|
|
174
|
+
try {
|
|
175
|
+
const { runHostMcpServerOverStdio } = await import('./lib/host-mcp/server.js');
|
|
176
|
+
await runHostMcpServerOverStdio();
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
process.stderr.write(`\n ✗ mcp-host failed: ${err?.stack ?? err?.message ?? err}\n\n`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (argv[0] === 'setup') {
|
|
185
|
+
try {
|
|
186
|
+
await runSetup(parseSetupArgs(argv.slice(1)));
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
process.stderr.write(`\n ✗ setup failed: ${err?.message ?? err}\n\n`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (argv[0] === 'service') {
|
|
195
|
+
const sub = argv[1] || '';
|
|
196
|
+
if (sub !== 'install' && sub !== 'uninstall') {
|
|
197
|
+
process.stderr.write(`\n ✗ service: unknown subcommand '${sub}' (expected: install | uninstall)\n\n` +
|
|
198
|
+
` Usage: awb-agent-manager service <install|uninstall> [--system] [--platform <p>] [--exec-path <p>] [--dry-run] [--unit-only]\n\n`);
|
|
199
|
+
process.exit(2);
|
|
200
|
+
}
|
|
201
|
+
let serviceValues;
|
|
202
|
+
try {
|
|
203
|
+
({ values: serviceValues } = parseArgs({
|
|
204
|
+
args: argv.slice(2),
|
|
205
|
+
options: {
|
|
206
|
+
system: { type: 'boolean' },
|
|
207
|
+
'dry-run': { type: 'boolean' },
|
|
208
|
+
'unit-only': { type: 'boolean' },
|
|
209
|
+
'exec-path': { type: 'string' },
|
|
210
|
+
platform: { type: 'string' },
|
|
211
|
+
},
|
|
212
|
+
allowPositionals: false,
|
|
213
|
+
strict: true,
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
process.stderr.write(`\n ✗ service ${sub}: ${err?.message ?? err}\n\n`);
|
|
218
|
+
process.exit(2);
|
|
219
|
+
}
|
|
220
|
+
const isSystem = Boolean(serviceValues.system);
|
|
221
|
+
const dryRun = Boolean(serviceValues['dry-run']);
|
|
222
|
+
const unitOnly = Boolean(serviceValues['unit-only']);
|
|
223
|
+
const execPath = serviceValues['exec-path'];
|
|
224
|
+
const platformRaw = serviceValues.platform;
|
|
225
|
+
const validPlatforms = ['auto', 'systemd', 'sysvinit', 'synology', 'launchd', 'windows'];
|
|
226
|
+
if (platformRaw && !validPlatforms.includes(platformRaw)) {
|
|
227
|
+
process.stderr.write(`\n ✗ service ${sub}: invalid --platform '${platformRaw}' ` +
|
|
228
|
+
`(expected: ${validPlatforms.join(' | ')})\n\n`);
|
|
229
|
+
process.exit(2);
|
|
230
|
+
}
|
|
231
|
+
const platform = (platformRaw || 'auto');
|
|
232
|
+
try {
|
|
233
|
+
if (sub === 'install') {
|
|
234
|
+
await installService({ system: isSystem, execPath, dryRun, unitOnly, platform });
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
await uninstallService({ system: isSystem, platform });
|
|
238
|
+
}
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
process.stderr.write(`\n ✗ service ${sub} failed: ${err?.message ?? err}\n\n`);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const flags = parseFlags(argv);
|
|
247
|
+
if (flags.help) {
|
|
248
|
+
printHelp();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (flags.version) {
|
|
252
|
+
process.stdout.write(`${readPkgVersion()}\n`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
installCrashHandlers();
|
|
256
|
+
const version = readPkgVersion();
|
|
257
|
+
process.stdout.write(`awb-agent-manager v${version}\n`);
|
|
258
|
+
process.stdout.write(` home: ${AGENT_MANAGER_HOME}\n`);
|
|
259
|
+
if (!flags.config) {
|
|
260
|
+
const importResult = importLegacyConfig();
|
|
261
|
+
if (importResult.imported) {
|
|
262
|
+
process.stdout.write(` imported: ${importResult.source.config} -> ${importResult.target.config}\n`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const configPath = flags.config ?? CONFIG_PATH;
|
|
266
|
+
let config = loadConfig(configPath);
|
|
267
|
+
if (!config) {
|
|
268
|
+
process.stdout.write(` config: not found at ${configPath}\n`);
|
|
269
|
+
if (existsSync(LEGACY_CONFIG_PATH)) {
|
|
270
|
+
process.stdout.write(` hint: legacy plugin config exists at ${LEGACY_CONFIG_PATH} ` +
|
|
271
|
+
`but a previous import already ran. Copy it to ${configPath} manually if you want to re-import.\n`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
process.stdout.write(`\n No config yet. Run the pairing wizard to set one up:\n\n` +
|
|
275
|
+
` awb-agent-manager setup\n\n` +
|
|
276
|
+
` Or non-interactively (CI / Ansible):\n\n` +
|
|
277
|
+
` awb-agent-manager setup --url <awb-url> --token <pairing-token>\n\n` +
|
|
278
|
+
` The token comes from AWB Admin → Agent Manager → "Pair manager…".\n`);
|
|
279
|
+
}
|
|
280
|
+
if (flags.dryRun) {
|
|
281
|
+
log('--dry-run: exiting after config load (config=missing)');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
log('No config — exiting. Run `awb-agent-manager setup` first.');
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
if (flags.workspace) {
|
|
288
|
+
config.workspace_id = flags.workspace;
|
|
289
|
+
}
|
|
290
|
+
process.stdout.write(` config: ${configPath}\n`);
|
|
291
|
+
process.stdout.write(` url: ${config.url}\n`);
|
|
292
|
+
process.stdout.write(` workspace: ${config.workspace_id ?? '(none)'}\n`);
|
|
293
|
+
if (flags.dryRun) {
|
|
294
|
+
log('--dry-run: exiting after config load (config=loaded)');
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await runRuntime(config, version, flags, argv);
|
|
298
|
+
}
|
|
299
|
+
async function runRuntime(config, version, flags, argv) {
|
|
300
|
+
void argv;
|
|
301
|
+
const legacyLock = inspectLegacyAgentLock();
|
|
302
|
+
if (legacyLock.alive && legacyLock.pid) {
|
|
303
|
+
if (flags.force) {
|
|
304
|
+
log(`[legacy-lock] --force: ignoring live plugin daemon at pid=${legacyLock.pid} ` +
|
|
305
|
+
`(role=${legacyLock.role || '?'}, version=${legacyLock.version || '?'}). ` +
|
|
306
|
+
`Stop it manually if you see double-processed events.`);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
log(`[legacy-lock] claude-plugin daemon is still running ` +
|
|
310
|
+
`(pid=${legacyLock.pid}, role=${legacyLock.role || '?'}, ` +
|
|
311
|
+
`version=${legacyLock.version || '?'}, started_at=${legacyLock.started_at || '?'}). ` +
|
|
312
|
+
`Stop it before launching agent-manager, or pass --force to start anyway.`);
|
|
313
|
+
process.exit(3);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (legacyLock.present) {
|
|
317
|
+
log(`[legacy-lock] found stale lockfile at ${legacyLock.path} — ignoring.`);
|
|
318
|
+
}
|
|
319
|
+
let lock;
|
|
320
|
+
try {
|
|
321
|
+
lock = acquireAgentLock({ role: 'manager', version, force: flags.force });
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
if (err?.code === 'EAGENTLOCKED') {
|
|
325
|
+
log(`agent-manager: ${err.message}`);
|
|
326
|
+
process.exit(2);
|
|
327
|
+
}
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
log(`agent-manager starting (server=${config.url} version=${version})`);
|
|
331
|
+
log(`Delegation: maxConcurrent=${config.delegation.maxConcurrent} ttl=${config.delegation.ttlMinutes}min idle=${config.delegation.idleMinutes}min cliBin=${config.delegation.claudeBin}`);
|
|
332
|
+
const agentIdReady = resolveAgentId(config).then((id) => {
|
|
333
|
+
if (id)
|
|
334
|
+
log(`Agent identity: ${id.slice(0, 8)}…`);
|
|
335
|
+
else
|
|
336
|
+
log('Agent identity: not resolved — presence + error-log upload disabled until pairing writes agent.json');
|
|
337
|
+
return id;
|
|
338
|
+
});
|
|
339
|
+
const presenceHeartbeat = { _real: null };
|
|
340
|
+
const kickPresencePing = () => {
|
|
341
|
+
presenceHeartbeat._real?.pingNow().catch(() => { });
|
|
342
|
+
};
|
|
343
|
+
const instanceHeartbeat = { _real: null };
|
|
344
|
+
const updateChecker = new UpdateChecker({ log });
|
|
345
|
+
updateChecker.start();
|
|
346
|
+
cleanupOrphanSubagents()
|
|
347
|
+
.then((r) => {
|
|
348
|
+
if (r.scanned > 0)
|
|
349
|
+
log(`Orphan subagent cleanup: scanned=${r.scanned} reaped=${r.reaped} skipped=${r.skipped ?? 0}`);
|
|
350
|
+
})
|
|
351
|
+
.catch((err) => log(`Orphan subagent cleanup failed: ${err?.message ?? err}`));
|
|
352
|
+
const circuitBreaker = new CircuitBreaker();
|
|
353
|
+
const subagentManager = new SubagentManager(config, circuitBreaker);
|
|
354
|
+
subagentManager.init().catch((err) => log(`SubagentManager init failed: ${err?.message ?? err}`));
|
|
355
|
+
const managedAgents = new ManagedAgentRegistry();
|
|
356
|
+
const managedAgentContexts = new ManagedAgentContextRegistry();
|
|
357
|
+
const worktreeManager = new WorktreeManager({
|
|
358
|
+
enabled: config?.delegation?.worktreeIsolation !== false,
|
|
359
|
+
});
|
|
360
|
+
const environmentProvisioner = new EnvironmentProvisioner();
|
|
361
|
+
const chatSessionManager = new ChatSessionManager(config);
|
|
362
|
+
const ticketSessionManager = new TicketSessionManager(config, circuitBreaker);
|
|
363
|
+
let eventStreamRef = null;
|
|
364
|
+
const commandHandler = new AgentManagerCommandHandler(config, {
|
|
365
|
+
registry: managedAgents,
|
|
366
|
+
contextRegistry: managedAgentContexts,
|
|
367
|
+
chatSessionManager,
|
|
368
|
+
ticketSessionManager,
|
|
369
|
+
subagentManager,
|
|
370
|
+
circuitBreaker,
|
|
371
|
+
getInstanceId: () => instanceHeartbeat._real?.instanceId ?? null,
|
|
372
|
+
requestStreamReconnect: () => eventStreamRef?.reconnect(),
|
|
373
|
+
reloadConfig: async () => {
|
|
374
|
+
const next = loadConfig();
|
|
375
|
+
if (!next?.url || !next?.apiKey)
|
|
376
|
+
return 'reload skipped: config missing';
|
|
377
|
+
const disruptive = next.url !== config.url ||
|
|
378
|
+
next.apiKey !== config.apiKey;
|
|
379
|
+
Object.assign(config, next);
|
|
380
|
+
return disruptive ? 'reloaded (disruptive — server/apiKey need restart)' : 'reloaded';
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
const fsBrowser = new FsBrowser(config, config.fs_browser || null);
|
|
384
|
+
log('fs_browser: always-on (ST-7) — construction OK, ready to handle fs_request events');
|
|
385
|
+
const subagentMonitor = new SubagentMonitor(config, null);
|
|
386
|
+
subagentManager.setMonitor(subagentMonitor);
|
|
387
|
+
chatSessionManager.setMonitor(subagentMonitor);
|
|
388
|
+
ticketSessionManager.setMonitor(subagentMonitor);
|
|
389
|
+
subagentManager.onExit = ({ record, code, signal, durationSec }) => {
|
|
390
|
+
const label = record.kind === 'chat' ? 'Chat Subagent' : 'Subagent';
|
|
391
|
+
let msg;
|
|
392
|
+
if (signal === 'SIGTERM' || signal === 'SIGKILL') {
|
|
393
|
+
msg = `[AWB ${label}] ticket=${record.ticket_id || '-'} TIMED OUT after ${durationSec}s`;
|
|
394
|
+
}
|
|
395
|
+
else if (code === 0) {
|
|
396
|
+
msg = `[AWB ${label}] ticket=${record.ticket_id || '-'} completed (duration=${durationSec}s)`;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
msg = `[AWB ${label}] ticket=${record.ticket_id || '-'} FAILED (exit=${code}, duration=${durationSec}s)`;
|
|
400
|
+
}
|
|
401
|
+
log(msg);
|
|
402
|
+
};
|
|
403
|
+
try {
|
|
404
|
+
const dirs = await listManagedAgentDirs();
|
|
405
|
+
let rehydrated = 0;
|
|
406
|
+
let skipped = 0;
|
|
407
|
+
for (const id of dirs) {
|
|
408
|
+
const cfg = await readManagedAgentConfig(id);
|
|
409
|
+
const apiKey = await readApiKey(id);
|
|
410
|
+
if (!cfg || !apiKey || !cfg.working_dir) {
|
|
411
|
+
skipped++;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
await ensureCliHomeDir(id);
|
|
415
|
+
const credential = await readAgentCredential(id);
|
|
416
|
+
let extraEnv = {};
|
|
417
|
+
try {
|
|
418
|
+
const prep = await createAdapter(cfg.cli).prepareCliHome(cliHomeDirFor(id), credential, { url: config.url, apiKey }, cfg.model || null);
|
|
419
|
+
extraEnv = prep?.extraEnv ?? {};
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
log(`rehydrate: cli-home prep failed for agent=${id.slice(0, 8)} cli=${cfg.cli}: ${err?.message ?? err}`);
|
|
423
|
+
}
|
|
424
|
+
const credentialKind = !credential
|
|
425
|
+
? 'operator_home'
|
|
426
|
+
: credential.provider.endsWith('_subscription')
|
|
427
|
+
? 'subscription'
|
|
428
|
+
: credential.provider.endsWith('_api_key')
|
|
429
|
+
? 'api_key'
|
|
430
|
+
: 'subscription';
|
|
431
|
+
managedAgentContexts.upsert({
|
|
432
|
+
agent_id: id,
|
|
433
|
+
name: cfg.name,
|
|
434
|
+
cli: cfg.cli,
|
|
435
|
+
working_dir: cfg.working_dir,
|
|
436
|
+
mcp_config_path: mcpConfigPathFor(id),
|
|
437
|
+
api_key: apiKey,
|
|
438
|
+
subagent_log_path: subagentLogPathFor(id),
|
|
439
|
+
cli_home_dir: cliHomeDirFor(id),
|
|
440
|
+
model: cfg.model || null,
|
|
441
|
+
extra_env: extraEnv,
|
|
442
|
+
credential_provider: credential?.provider ?? null,
|
|
443
|
+
credential_kind: credentialKind,
|
|
444
|
+
registered_at: new Date().toISOString(),
|
|
445
|
+
});
|
|
446
|
+
managedAgents.upsert({ agent_id: id, name: cfg.name, cli: cfg.cli, working_dir: cfg.working_dir });
|
|
447
|
+
managedAgents.markRunning(id, process.pid);
|
|
448
|
+
rehydrated++;
|
|
449
|
+
}
|
|
450
|
+
if (rehydrated || skipped) {
|
|
451
|
+
log(`Managed-agent rehydrate: rehydrated=${rehydrated} skipped=${skipped} (of ${dirs.length} on-disk dirs)`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
log(`Managed-agent rehydrate failed: ${err?.message ?? err}`);
|
|
456
|
+
}
|
|
457
|
+
const eventStream = new EventStream({
|
|
458
|
+
config,
|
|
459
|
+
deps: {
|
|
460
|
+
subagentManager,
|
|
461
|
+
chatSessionManager,
|
|
462
|
+
ticketSessionManager,
|
|
463
|
+
fsBrowser,
|
|
464
|
+
prompts: promptComposer,
|
|
465
|
+
agentManagerCommandHandler: commandHandler,
|
|
466
|
+
managedAgentContexts,
|
|
467
|
+
worktreeManager,
|
|
468
|
+
environmentProvisioner,
|
|
469
|
+
},
|
|
470
|
+
pluginVersion: version,
|
|
471
|
+
onConnect: kickPresencePing,
|
|
472
|
+
});
|
|
473
|
+
eventStreamRef = eventStream;
|
|
474
|
+
eventStream.start();
|
|
475
|
+
log('SSE event stream started');
|
|
476
|
+
let uploadTimer = null;
|
|
477
|
+
let worktreeSweepTimer = null;
|
|
478
|
+
const sweepWorktrees = async () => {
|
|
479
|
+
if (!worktreeManager.enabled)
|
|
480
|
+
return;
|
|
481
|
+
try {
|
|
482
|
+
const activeKeys = new Set();
|
|
483
|
+
for (const s of ticketSessionManager._snapshot()) {
|
|
484
|
+
if (s.ticketId && s.role)
|
|
485
|
+
activeKeys.add(worktreeSlug(s.ticketId, s.role));
|
|
486
|
+
}
|
|
487
|
+
for (const s of subagentManager._snapshot()) {
|
|
488
|
+
if (s.ticket_id && s.role)
|
|
489
|
+
activeKeys.add(worktreeSlug(s.ticket_id, s.role));
|
|
490
|
+
}
|
|
491
|
+
let total = 0;
|
|
492
|
+
const seenRoots = new Set();
|
|
493
|
+
for (const ctx of managedAgentContexts.list()) {
|
|
494
|
+
if (!ctx.working_dir)
|
|
495
|
+
continue;
|
|
496
|
+
const worktreesRoot = pathJoin(MANAGED_AGENTS_DIR, ctx.agent_id, 'worktrees');
|
|
497
|
+
const dedupeKey = `${ctx.working_dir}${worktreesRoot}`;
|
|
498
|
+
if (seenRoots.has(dedupeKey))
|
|
499
|
+
continue;
|
|
500
|
+
seenRoots.add(dedupeKey);
|
|
501
|
+
total += await worktreeManager.sweep({
|
|
502
|
+
baseWorkingDir: ctx.working_dir,
|
|
503
|
+
worktreesRoot,
|
|
504
|
+
activeKeys,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if (total > 0)
|
|
508
|
+
log(`[worktree] sweep reclaimed ${total} idle clean worktree(s)`);
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
log(`[worktree] sweep failed: ${err?.message ?? err}`);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
worktreeSweepTimer = setInterval(() => void sweepWorktrees(), 10 * 60 * 1000);
|
|
515
|
+
worktreeSweepTimer.unref?.();
|
|
516
|
+
agentIdReady.then(async (agentId) => {
|
|
517
|
+
if (!agentId)
|
|
518
|
+
return;
|
|
519
|
+
presenceHeartbeat._real = new PresenceHeartbeat(config, agentId);
|
|
520
|
+
presenceHeartbeat._real.start();
|
|
521
|
+
const availableModels = {};
|
|
522
|
+
await Promise.all(KNOWN_ADAPTER_CLI_TYPES.map(async (cli) => {
|
|
523
|
+
try {
|
|
524
|
+
const models = await createAdapter(cli).listModels();
|
|
525
|
+
if (Array.isArray(models) && models.length)
|
|
526
|
+
availableModels[cli] = models;
|
|
527
|
+
}
|
|
528
|
+
catch (err) {
|
|
529
|
+
log(`listModels failed for cli=${cli}: ${err?.message ?? err}`);
|
|
530
|
+
}
|
|
531
|
+
}));
|
|
532
|
+
instanceHeartbeat._real = new InstanceHeartbeat(config, agentId, {
|
|
533
|
+
mode: 'manager',
|
|
534
|
+
version,
|
|
535
|
+
cli: 'mixed',
|
|
536
|
+
cliAdapters: KNOWN_ADAPTER_CLI_TYPES.slice(),
|
|
537
|
+
availableModels,
|
|
538
|
+
managedAgents,
|
|
539
|
+
updateChecker,
|
|
540
|
+
agentCredentialMetaProvider: async () => {
|
|
541
|
+
const out = [];
|
|
542
|
+
for (const ctx of managedAgentContexts.list()) {
|
|
543
|
+
if (ctx.credential_kind === 'api_key') {
|
|
544
|
+
out.push({
|
|
545
|
+
agent_id: ctx.agent_id,
|
|
546
|
+
cli: ctx.cli,
|
|
547
|
+
kind: 'api_key',
|
|
548
|
+
expires_at_ms: null,
|
|
549
|
+
refresh_token_present: false,
|
|
550
|
+
});
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
let meta = null;
|
|
554
|
+
try {
|
|
555
|
+
meta = await createAdapter(ctx.cli).readCredentialMeta(ctx.cli_home_dir);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
log(`agentCredentialMetaProvider: read failed for agent=${ctx.agent_id.slice(0, 8)} cli=${ctx.cli}: ${err?.message ?? err}`);
|
|
559
|
+
meta = null;
|
|
560
|
+
}
|
|
561
|
+
if (!meta) {
|
|
562
|
+
out.push({
|
|
563
|
+
agent_id: ctx.agent_id,
|
|
564
|
+
cli: ctx.cli,
|
|
565
|
+
kind: ctx.credential_kind === 'operator_home' ? 'operator_home' : 'missing',
|
|
566
|
+
expires_at_ms: null,
|
|
567
|
+
refresh_token_present: false,
|
|
568
|
+
});
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
const kind = ctx.credential_kind === 'operator_home' && meta.kind === 'subscription'
|
|
572
|
+
? 'operator_home'
|
|
573
|
+
: meta.kind;
|
|
574
|
+
out.push({
|
|
575
|
+
agent_id: ctx.agent_id,
|
|
576
|
+
cli: ctx.cli,
|
|
577
|
+
kind,
|
|
578
|
+
expires_at_ms: meta.expires_at_ms,
|
|
579
|
+
refresh_token_present: meta.refresh_token_present,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
return out;
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
instanceHeartbeat._real.start();
|
|
586
|
+
const fireUpload = () => {
|
|
587
|
+
uploadIfNewErrors(config, agentId, version).catch(() => { });
|
|
588
|
+
};
|
|
589
|
+
fireUpload();
|
|
590
|
+
uploadTimer = setInterval(fireUpload, 30 * 1000);
|
|
591
|
+
uploadTimer.unref?.();
|
|
592
|
+
onFlushThreshold(fireUpload);
|
|
593
|
+
});
|
|
594
|
+
const shutdown = async (signal) => {
|
|
595
|
+
log(`agent-manager received ${signal} — terminating subagents`);
|
|
596
|
+
presenceHeartbeat._real?.stop();
|
|
597
|
+
instanceHeartbeat._real?.stop();
|
|
598
|
+
updateChecker.stop();
|
|
599
|
+
if (uploadTimer) {
|
|
600
|
+
clearInterval(uploadTimer);
|
|
601
|
+
uploadTimer = null;
|
|
602
|
+
}
|
|
603
|
+
if (worktreeSweepTimer) {
|
|
604
|
+
clearInterval(worktreeSweepTimer);
|
|
605
|
+
worktreeSweepTimer = null;
|
|
606
|
+
}
|
|
607
|
+
eventStream.stop();
|
|
608
|
+
try {
|
|
609
|
+
await subagentManager.stop();
|
|
610
|
+
}
|
|
611
|
+
catch (err) {
|
|
612
|
+
log(`shutdown: ${err?.message ?? err}`);
|
|
613
|
+
}
|
|
614
|
+
try {
|
|
615
|
+
await chatSessionManager.stop();
|
|
616
|
+
}
|
|
617
|
+
catch (err) {
|
|
618
|
+
log(`shutdown (chat): ${err?.message ?? err}`);
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
await ticketSessionManager.stop();
|
|
622
|
+
}
|
|
623
|
+
catch (err) {
|
|
624
|
+
log(`shutdown (ticket): ${err?.message ?? err}`);
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
subagentMonitor.stop();
|
|
628
|
+
}
|
|
629
|
+
catch (err) {
|
|
630
|
+
log(`shutdown (monitor): ${err?.message ?? err}`);
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
lock.release();
|
|
634
|
+
}
|
|
635
|
+
catch (err) {
|
|
636
|
+
log(`shutdown (lockfile): ${err?.message ?? err}`);
|
|
637
|
+
}
|
|
638
|
+
process.exit(isSystemdReExecPending() ? 1 : 0);
|
|
639
|
+
};
|
|
640
|
+
process.once('SIGTERM', () => void shutdown('SIGTERM'));
|
|
641
|
+
process.once('SIGINT', () => void shutdown('SIGINT'));
|
|
642
|
+
process.on('SIGUSR1', async () => {
|
|
643
|
+
try {
|
|
644
|
+
const result = await runSelfUpdate({ log });
|
|
645
|
+
log(`Self-update: ${result.summary}`);
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
log(`Self-update failed: ${err?.stack || err?.message || err}`);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
process.on('SIGHUP', () => {
|
|
652
|
+
const next = loadConfig();
|
|
653
|
+
if (!next?.url || !next?.apiKey) {
|
|
654
|
+
log('SIGHUP: config.json missing or unparseable — keeping previous config');
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const disruptive = next.url !== config.url ||
|
|
658
|
+
next.apiKey !== config.apiKey;
|
|
659
|
+
Object.assign(config, next);
|
|
660
|
+
log(`SIGHUP: config reloaded (delegation.maxConcurrent=${config.delegation.maxConcurrent} ` +
|
|
661
|
+
`ttl=${config.delegation.ttlMinutes}min idle=${config.delegation.idleMinutes}min)` +
|
|
662
|
+
(disruptive ? ' — server/apiKey changes need a manager restart to take effect' : ''));
|
|
663
|
+
});
|
|
664
|
+
log('agent-manager ready');
|
|
665
|
+
}
|
|
666
|
+
main().catch((err) => {
|
|
667
|
+
process.stderr.write(`agent-manager: fatal: ${err?.stack ?? err}\n`);
|
|
668
|
+
process.exit(1);
|
|
669
|
+
});
|
|
670
|
+
//# sourceMappingURL=main.js.map
|