@borgee/agents-host 0.1.6 → 0.1.8
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 +290 -50
- package/dist/agents-host-supervisor.d.ts +54 -0
- package/dist/agents-host-supervisor.js +313 -0
- package/dist/agents-host.d.ts +4 -4
- package/dist/agents-host.js +5 -4
- package/dist/cli-args.d.ts +35 -12
- package/dist/cli-args.js +223 -33
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +109 -26
- package/dist/config.d.ts +12 -11
- package/dist/config.js +80 -31
- package/dist/local-config.d.ts +51 -0
- package/dist/local-config.js +772 -0
- package/dist/providers/copilot/cli-client.d.ts +4 -0
- package/dist/providers/copilot/cli-client.js +56 -3
- package/dist/providers/create-provider.js +3 -1
- package/dist/run.d.ts +27 -7
- package/dist/run.js +48 -10
- package/dist/types.d.ts +61 -5
- package/package.json +4 -2
package/dist/cli-args.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maps `agents-host start` CLI flags to the env vars `config.ts`
|
|
3
|
-
* Keeping this as a plain lookup table (rather than duplicating
|
|
4
|
-
* `loadConfigFromEnv`'s parsing logic) means the CLI and the
|
|
5
|
-
*
|
|
2
|
+
* Maps single-agent `agents-host start` CLI flags to the env vars `config.ts`
|
|
3
|
+
* reads. Keeping this as a plain lookup table (rather than duplicating
|
|
4
|
+
* `loadConfigFromEnv`'s parsing logic) means the CLI and the env-var entry
|
|
5
|
+
* point (`index.ts`) always agree on defaults/validation.
|
|
6
6
|
*/
|
|
7
7
|
export const CLI_FLAG_TO_ENV = {
|
|
8
8
|
name: 'BORGEE_AGENT_NAME',
|
|
@@ -11,56 +11,246 @@ export const CLI_FLAG_TO_ENV = {
|
|
|
11
11
|
'claude-args': 'CLAUDE_ARGS',
|
|
12
12
|
'copilot-command': 'COPILOT_COMMAND',
|
|
13
13
|
'copilot-args': 'COPILOT_ARGS',
|
|
14
|
+
'copilot-session-ttl-minutes': 'COPILOT_SESSION_TTL_MINUTES',
|
|
14
15
|
};
|
|
16
|
+
const SINGLE_AGENT_FLAG_NAMES = new Set(Object.keys(CLI_FLAG_TO_ENV));
|
|
15
17
|
export class CliUsageError extends Error {
|
|
16
18
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Parses `start <serverUrl> <apiKey> [--flag value ...]` (the argv slice
|
|
19
|
-
* after the `start` command word). Throws `CliUsageError` with a
|
|
20
|
-
* human-readable message on any usage problem instead of exiting the
|
|
21
|
-
* process, so callers (and tests) can decide how to report it.
|
|
22
|
-
*/
|
|
23
19
|
export function parseStartArgs(argv) {
|
|
24
20
|
const positionals = [];
|
|
25
21
|
const env = {};
|
|
22
|
+
const seenSingleAgentFlags = new Set();
|
|
23
|
+
let configPath;
|
|
26
24
|
for (let i = 0; i < argv.length; i++) {
|
|
27
25
|
const arg = argv[i];
|
|
28
|
-
if (arg.startsWith('--')) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
if (!arg.startsWith('--')) {
|
|
27
|
+
positionals.push(arg);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const flag = arg.slice(2);
|
|
31
|
+
const value = argv[i + 1];
|
|
32
|
+
if (value === undefined || value.startsWith('--')) {
|
|
33
|
+
throw new CliUsageError(`Missing value for --${flag}`);
|
|
34
|
+
}
|
|
35
|
+
if (flag === 'config') {
|
|
36
|
+
if (configPath !== undefined) {
|
|
37
|
+
throw new CliUsageError('--config may only be specified once');
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
configPath = value;
|
|
39
40
|
i++;
|
|
41
|
+
continue;
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const envKey = CLI_FLAG_TO_ENV[flag];
|
|
44
|
+
if (!envKey) {
|
|
45
|
+
throw new CliUsageError(`Unknown option: --${flag}`);
|
|
43
46
|
}
|
|
47
|
+
env[envKey] = value;
|
|
48
|
+
seenSingleAgentFlags.add(flag);
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
if (configPath !== undefined) {
|
|
52
|
+
if (positionals.length > 0) {
|
|
53
|
+
throw new CliUsageError('Cannot combine --config with <serverUrl> <apiKey>');
|
|
54
|
+
}
|
|
55
|
+
if (seenSingleAgentFlags.size > 0) {
|
|
56
|
+
throw new CliUsageError(`Cannot combine --config with single-agent option --${[...seenSingleAgentFlags][0]}`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
mode: 'local-config',
|
|
60
|
+
configPath,
|
|
61
|
+
};
|
|
44
62
|
}
|
|
45
63
|
const [serverUrl, apiKey, ...extra] = positionals;
|
|
46
|
-
if (!serverUrl)
|
|
64
|
+
if (!serverUrl) {
|
|
47
65
|
throw new CliUsageError('Missing <serverUrl>');
|
|
48
|
-
|
|
66
|
+
}
|
|
67
|
+
if (!apiKey) {
|
|
49
68
|
throw new CliUsageError('Missing <apiKey>');
|
|
50
|
-
|
|
69
|
+
}
|
|
70
|
+
if (extra.length > 0) {
|
|
51
71
|
throw new CliUsageError(`Unexpected argument: ${extra[0]}`);
|
|
52
|
-
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
mode: 'single-agent',
|
|
75
|
+
serverUrl,
|
|
76
|
+
apiKey,
|
|
77
|
+
env,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function parseValidateArgs(argv) {
|
|
81
|
+
const positionals = [];
|
|
82
|
+
let configPath;
|
|
83
|
+
for (let i = 0; i < argv.length; i++) {
|
|
84
|
+
const arg = argv[i];
|
|
85
|
+
if (!arg.startsWith('--')) {
|
|
86
|
+
positionals.push(arg);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const flag = arg.slice(2);
|
|
90
|
+
if (flag === 'config') {
|
|
91
|
+
const value = argv[i + 1];
|
|
92
|
+
if (value === undefined || value.startsWith('--')) {
|
|
93
|
+
throw new CliUsageError('Missing value for --config');
|
|
94
|
+
}
|
|
95
|
+
if (configPath !== undefined) {
|
|
96
|
+
throw new CliUsageError('--config may only be specified once');
|
|
97
|
+
}
|
|
98
|
+
configPath = value;
|
|
99
|
+
i++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (SINGLE_AGENT_FLAG_NAMES.has(flag)) {
|
|
103
|
+
throw new CliUsageError(`Validate accepts only --config; cannot use single-agent option --${flag}`);
|
|
104
|
+
}
|
|
105
|
+
throw new CliUsageError(`Unknown option: --${flag}`);
|
|
106
|
+
}
|
|
107
|
+
if (positionals.length > 0) {
|
|
108
|
+
throw new CliUsageError(`Unexpected argument: ${positionals[0]}`);
|
|
109
|
+
}
|
|
110
|
+
if (!configPath) {
|
|
111
|
+
throw new CliUsageError('Missing required --config <path>');
|
|
112
|
+
}
|
|
113
|
+
return { configPath };
|
|
53
114
|
}
|
|
54
|
-
export
|
|
115
|
+
export function parseDescribeArgs(argv) {
|
|
116
|
+
const parsed = parseValidateArgs(argv);
|
|
117
|
+
return { configPath: parsed.configPath };
|
|
118
|
+
}
|
|
119
|
+
export function parsePrintLayoutArgs(argv) {
|
|
120
|
+
const positionals = [];
|
|
121
|
+
let rootPath;
|
|
122
|
+
for (let i = 0; i < argv.length; i++) {
|
|
123
|
+
const arg = argv[i];
|
|
124
|
+
if (!arg.startsWith('--')) {
|
|
125
|
+
positionals.push(arg);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const flag = arg.slice(2);
|
|
129
|
+
if (flag === 'root') {
|
|
130
|
+
const value = argv[i + 1];
|
|
131
|
+
if (value === undefined || value.startsWith('--')) {
|
|
132
|
+
throw new CliUsageError('Missing value for --root');
|
|
133
|
+
}
|
|
134
|
+
if (rootPath !== undefined) {
|
|
135
|
+
throw new CliUsageError('--root may only be specified once');
|
|
136
|
+
}
|
|
137
|
+
rootPath = value;
|
|
138
|
+
i++;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (flag === 'config') {
|
|
142
|
+
throw new CliUsageError('print-layout accepts only --root; cannot use --config');
|
|
143
|
+
}
|
|
144
|
+
if (SINGLE_AGENT_FLAG_NAMES.has(flag)) {
|
|
145
|
+
throw new CliUsageError(`print-layout accepts only --root; cannot use single-agent option --${flag}`);
|
|
146
|
+
}
|
|
147
|
+
throw new CliUsageError(`Unknown option: --${flag}`);
|
|
148
|
+
}
|
|
149
|
+
if (positionals.length > 0) {
|
|
150
|
+
throw new CliUsageError(`Unexpected argument: ${positionals[0]}`);
|
|
151
|
+
}
|
|
152
|
+
if (!rootPath) {
|
|
153
|
+
throw new CliUsageError('Missing required --root <dir>');
|
|
154
|
+
}
|
|
155
|
+
return { rootPath };
|
|
156
|
+
}
|
|
157
|
+
export function parseGenerateConfigArgs(argv) {
|
|
158
|
+
const positionals = [];
|
|
159
|
+
let rootPath;
|
|
160
|
+
let specJson;
|
|
161
|
+
let stdin = false;
|
|
162
|
+
for (let i = 0; i < argv.length; i++) {
|
|
163
|
+
const arg = argv[i];
|
|
164
|
+
if (!arg.startsWith('--')) {
|
|
165
|
+
positionals.push(arg);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const flag = arg.slice(2);
|
|
169
|
+
if (flag === 'stdin') {
|
|
170
|
+
if (stdin) {
|
|
171
|
+
throw new CliUsageError('--stdin may only be specified once');
|
|
172
|
+
}
|
|
173
|
+
stdin = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (flag === 'root' || flag === 'spec-json') {
|
|
177
|
+
const value = argv[i + 1];
|
|
178
|
+
if (value === undefined || value.startsWith('--')) {
|
|
179
|
+
throw new CliUsageError(`Missing value for --${flag}`);
|
|
180
|
+
}
|
|
181
|
+
if (flag === 'root') {
|
|
182
|
+
if (rootPath !== undefined) {
|
|
183
|
+
throw new CliUsageError('--root may only be specified once');
|
|
184
|
+
}
|
|
185
|
+
rootPath = value;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
if (specJson !== undefined) {
|
|
189
|
+
throw new CliUsageError('--spec-json may only be specified once');
|
|
190
|
+
}
|
|
191
|
+
specJson = value;
|
|
192
|
+
}
|
|
193
|
+
i++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (flag === 'config') {
|
|
197
|
+
throw new CliUsageError('generate-config accepts only --root and --spec-json; cannot use --config');
|
|
198
|
+
}
|
|
199
|
+
if (SINGLE_AGENT_FLAG_NAMES.has(flag)) {
|
|
200
|
+
throw new CliUsageError(`generate-config accepts only --root and --spec-json; cannot use single-agent option --${flag}`);
|
|
201
|
+
}
|
|
202
|
+
throw new CliUsageError(`Unknown option: --${flag}`);
|
|
203
|
+
}
|
|
204
|
+
if (positionals.length > 0) {
|
|
205
|
+
throw new CliUsageError(`Unexpected argument: ${positionals[0]}`);
|
|
206
|
+
}
|
|
207
|
+
if (!rootPath) {
|
|
208
|
+
throw new CliUsageError('Missing required --root <dir>');
|
|
209
|
+
}
|
|
210
|
+
if (stdin && specJson !== undefined) {
|
|
211
|
+
throw new CliUsageError('generate-config accepts exactly one input source: --stdin or --spec-json');
|
|
212
|
+
}
|
|
213
|
+
if (stdin) {
|
|
214
|
+
return { rootPath, input: 'stdin' };
|
|
215
|
+
}
|
|
216
|
+
if (!specJson) {
|
|
217
|
+
throw new CliUsageError('Missing required input: use --stdin or --spec-json <json>');
|
|
218
|
+
}
|
|
219
|
+
return { rootPath, input: 'argv', specJson };
|
|
220
|
+
}
|
|
221
|
+
export const USAGE = `Usage:
|
|
222
|
+
agents-host start <serverUrl> <apiKey> [options]
|
|
223
|
+
agents-host start --config <path-to-host-config>
|
|
224
|
+
agents-host validate --config <path-to-host-config>
|
|
225
|
+
agents-host describe --config <path-to-host-config>
|
|
226
|
+
agents-host print-layout --root <dir>
|
|
227
|
+
agents-host generate-config --root <dir> --stdin
|
|
228
|
+
agents-host generate-config --root <dir> --spec-json <json>
|
|
55
229
|
|
|
56
|
-
|
|
57
|
-
--name <name>
|
|
58
|
-
--provider <claude|copilot>
|
|
59
|
-
--claude-command <cmd>
|
|
60
|
-
--claude-args <args>
|
|
230
|
+
Single-agent options:
|
|
231
|
+
--name <name> Display name (default: Assistant)
|
|
232
|
+
--provider <claude|copilot> Runtime provider (default: claude)
|
|
233
|
+
--claude-command <cmd> Local Claude CLI command (default: claude)
|
|
234
|
+
--claude-args <args> Local Claude CLI args (default: --print)
|
|
61
235
|
--copilot-command <cmd> Local Copilot CLI command (default: copilot)
|
|
62
236
|
--copilot-args <args> Ignored by the Copilot ACP prototype
|
|
237
|
+
--copilot-session-ttl-minutes <minutes>
|
|
238
|
+
Idle session TTL for Copilot ACP sessions (default: 2880)
|
|
239
|
+
|
|
240
|
+
Local-config mode:
|
|
241
|
+
start --config <path> Start agents + supervisor + watchers from a host config file
|
|
242
|
+
validate --config <path> Validate local-config files without starting agents or watchers
|
|
243
|
+
describe --config <path> Print the current managed full-set spec as JSON
|
|
244
|
+
print-layout --root <dir> Print the canonical default local-config layout as JSON
|
|
245
|
+
generate-config --root <dir> --stdin Materialize canonical local-config files from stdin
|
|
246
|
+
generate-config --root <dir> --spec-json <json>
|
|
247
|
+
Compatibility input; JSON is exposed in process arguments
|
|
63
248
|
|
|
64
|
-
|
|
249
|
+
Examples:
|
|
65
250
|
agents-host start https://borgee.example.com bgr_xxxxxxxx --provider copilot
|
|
251
|
+
agents-host start --config ./agents-host.yaml
|
|
252
|
+
agents-host validate --config ./agents-host.yaml
|
|
253
|
+
agents-host describe --config ./agents-host.yaml
|
|
254
|
+
agents-host print-layout --root ./runtime-root
|
|
255
|
+
printf '%s' '{"host":{"borgeeBaseUrl":"https://borgee.example.com"},"agents":[{"key":"cp1","name":"Copilot","apiKey":"bgr_xxx","provider":"copilot"}]}' | agents-host generate-config --root ./runtime-root --stdin
|
|
66
256
|
`;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { describeLocalConfig, generateLocalConfig, printLocalConfigLayout, runMain, validateLocalConfig } from './run.js';
|
|
3
|
+
export interface CliDeps {
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
logger?: Pick<Console, 'error'>;
|
|
6
|
+
runMain?: typeof runMain;
|
|
7
|
+
validateLocalConfig?: typeof validateLocalConfig;
|
|
8
|
+
describeLocalConfig?: typeof describeLocalConfig;
|
|
9
|
+
printLocalConfigLayout?: typeof printLocalConfigLayout;
|
|
10
|
+
generateLocalConfig?: typeof generateLocalConfig;
|
|
11
|
+
readStdin?: () => Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
interface CliEntrypointDeps {
|
|
14
|
+
realpath?: (path: string) => string;
|
|
15
|
+
}
|
|
16
|
+
export declare function dispatchCli(argv: string[], deps?: Omit<CliDeps, 'logger'>): Promise<void>;
|
|
17
|
+
export declare function main(argv?: string[], deps?: CliDeps): Promise<number>;
|
|
18
|
+
export declare function isCliEntrypoint(importMetaUrl: string, argv1?: string | undefined, deps?: CliEntrypointDeps): boolean;
|
|
2
19
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,117 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { CliUsageError, parseDescribeArgs, parseGenerateConfigArgs, parsePrintLayoutArgs, parseStartArgs, parseValidateArgs, USAGE, } from './cli-args.js';
|
|
6
|
+
import { describeLocalConfig, generateLocalConfig, printLocalConfigLayout, runMain, validateLocalConfig } from './run.js';
|
|
7
|
+
function formatErrorMessage(error) {
|
|
8
|
+
if (error instanceof Error) {
|
|
9
|
+
return error.message;
|
|
10
|
+
}
|
|
11
|
+
return String(error);
|
|
12
|
+
}
|
|
13
|
+
export async function dispatchCli(argv, deps = {}) {
|
|
14
|
+
const [command, ...rest] = argv;
|
|
15
|
+
const env = deps.env ?? process.env;
|
|
16
|
+
const runMainImpl = deps.runMain ?? runMain;
|
|
17
|
+
const validateLocalConfigImpl = deps.validateLocalConfig ?? validateLocalConfig;
|
|
18
|
+
const describeLocalConfigImpl = deps.describeLocalConfig ?? describeLocalConfig;
|
|
19
|
+
const printLocalConfigLayoutImpl = deps.printLocalConfigLayout ?? printLocalConfigLayout;
|
|
20
|
+
const generateLocalConfigImpl = deps.generateLocalConfig ?? generateLocalConfig;
|
|
21
|
+
const readStdin = deps.readStdin ?? readAllStdin;
|
|
22
|
+
if (command === 'start') {
|
|
23
|
+
const parsed = parseStartArgs(rest);
|
|
24
|
+
if (parsed.mode === 'single-agent') {
|
|
25
|
+
env.BORGEE_BASE_URL = parsed.serverUrl;
|
|
26
|
+
env.BORGEE_AGENT_API_KEY = parsed.apiKey;
|
|
27
|
+
for (const [key, value] of Object.entries(parsed.env)) {
|
|
28
|
+
env[key] = value;
|
|
29
|
+
}
|
|
30
|
+
await runMainImpl();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
await runMainImpl({ configPath: parsed.configPath });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (command === 'validate') {
|
|
37
|
+
const parsed = parseValidateArgs(rest);
|
|
38
|
+
await validateLocalConfigImpl(parsed.configPath);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (command === 'describe') {
|
|
42
|
+
const parsed = parseDescribeArgs(rest);
|
|
43
|
+
await describeLocalConfigImpl(parsed.configPath);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (command === 'print-layout') {
|
|
47
|
+
const parsed = parsePrintLayoutArgs(rest);
|
|
48
|
+
await printLocalConfigLayoutImpl(parsed.rootPath);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (command === 'generate-config') {
|
|
52
|
+
const parsed = parseGenerateConfigArgs(rest);
|
|
53
|
+
if (parsed.input === 'stdin') {
|
|
54
|
+
await generateLocalConfigImpl(parsed.rootPath, await readStdin(), '<stdin>');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await generateLocalConfigImpl(parsed.rootPath, parsed.specJson);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
throw new CliUsageError(`Unknown command: ${command}`);
|
|
9
62
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
63
|
+
async function readAllStdin() {
|
|
64
|
+
const chunks = [];
|
|
65
|
+
for await (const chunk of process.stdin) {
|
|
66
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
67
|
+
}
|
|
68
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
13
69
|
}
|
|
14
|
-
|
|
15
|
-
|
|
70
|
+
export async function main(argv = process.argv.slice(2), deps = {}) {
|
|
71
|
+
const logger = deps.logger ?? console;
|
|
72
|
+
const [command] = argv;
|
|
73
|
+
if (command === undefined || command === '--help' || command === '-h') {
|
|
74
|
+
logger.error(USAGE);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await dispatchCli(argv, deps);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error instanceof CliUsageError) {
|
|
83
|
+
logger.error(`[agents-host] ${error.message}`);
|
|
84
|
+
logger.error(USAGE);
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
logger.error(`[agents-host] ${formatErrorMessage(error)}`, error);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
16
90
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
91
|
+
function safeRealpath(path, realpath) {
|
|
92
|
+
try {
|
|
93
|
+
return realpath(path);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
23
97
|
}
|
|
24
98
|
}
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
99
|
+
export function isCliEntrypoint(importMetaUrl, argv1 = process.argv[1], deps = {}) {
|
|
100
|
+
if (!argv1) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const importMetaPath = fileURLToPath(importMetaUrl);
|
|
104
|
+
const argvPath = resolve(argv1);
|
|
105
|
+
if (importMetaPath === argvPath) {
|
|
106
|
+
return true;
|
|
28
107
|
}
|
|
29
|
-
|
|
108
|
+
const realpath = deps.realpath ?? realpathSync.native;
|
|
109
|
+
const importMetaRealpath = safeRealpath(importMetaPath, realpath);
|
|
110
|
+
const argvRealpath = safeRealpath(argvPath, realpath);
|
|
111
|
+
return importMetaRealpath !== null && importMetaRealpath === argvRealpath;
|
|
112
|
+
}
|
|
113
|
+
if (isCliEntrypoint(import.meta.url)) {
|
|
114
|
+
void main().then((exitCode) => {
|
|
115
|
+
process.exitCode = exitCode;
|
|
116
|
+
});
|
|
30
117
|
}
|
|
31
|
-
runMain().catch((error) => {
|
|
32
|
-
console.error('[agents-host] fatal error:', error);
|
|
33
|
-
process.exitCode = 1;
|
|
34
|
-
});
|
package/dist/config.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { AgentsHostConfig } from './types.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export declare function
|
|
1
|
+
import type { AgentsHostConfig, ProviderCommandConfig, ProviderKind } from './types.js';
|
|
2
|
+
export declare const MAX_COPILOT_SESSION_TTL_MINUTES: number;
|
|
3
|
+
export declare const DEFAULT_COPILOT_SESSION_TTL_MINUTES: number;
|
|
4
|
+
export declare const DEFAULT_PROVIDER_COMMAND_CONFIG: ProviderCommandConfig;
|
|
5
|
+
export declare function requireNonEmptyString(value: unknown, message: string): string;
|
|
6
|
+
export declare function optionalNonEmptyString(value: unknown, fieldName: string, sourceLabel: string): string | undefined;
|
|
7
|
+
export declare function parseArgs(value: string): string[];
|
|
8
|
+
export declare function optionalStringArray(value: unknown, fieldName: string, sourceLabel: string): string[] | undefined;
|
|
9
|
+
export declare function parseCopilotSessionTtlMinutesValue(value: unknown, sourceLabel: string): number;
|
|
10
|
+
export declare function resolveProvider(rawValue: string, sourceLabel: string): ProviderKind;
|
|
11
|
+
export declare function resolveProviderCommandConfig(overrides?: Partial<ProviderCommandConfig>): ProviderCommandConfig;
|
|
12
|
+
export declare function loadConfigFromEnv(env?: NodeJS.ProcessEnv): AgentsHostConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,43 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
2
|
+
export const MAX_COPILOT_SESSION_TTL_MINUTES = MAX_TIMER_DELAY_MS / 60_000;
|
|
3
|
+
export const DEFAULT_COPILOT_SESSION_TTL_MINUTES = 2 * 24 * 60;
|
|
4
|
+
export const DEFAULT_PROVIDER_COMMAND_CONFIG = {
|
|
5
|
+
claudeCommand: 'claude',
|
|
6
|
+
claudeArgs: ['--print'],
|
|
7
|
+
copilotCommand: 'copilot',
|
|
8
|
+
copilotArgs: ['-s', '--no-color', '--allow-all-tools', '--output-format', 'text'],
|
|
9
|
+
copilotSessionTtlMinutes: DEFAULT_COPILOT_SESSION_TTL_MINUTES,
|
|
10
|
+
};
|
|
11
|
+
function requireEnv(name, env) {
|
|
12
|
+
return requireNonEmptyString(env[name], `Missing required environment variable: ${name}`);
|
|
13
|
+
}
|
|
14
|
+
function envOr(name, fallback, env) {
|
|
15
|
+
const value = env[name];
|
|
16
|
+
return value && value.trim().length > 0 ? value.trim() : fallback;
|
|
17
|
+
}
|
|
18
|
+
export function requireNonEmptyString(value, message) {
|
|
19
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
20
|
+
throw new Error(message);
|
|
5
21
|
}
|
|
6
22
|
return value.trim();
|
|
7
23
|
}
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
export function optionalNonEmptyString(value, fieldName, sourceLabel) {
|
|
25
|
+
if (value === undefined || value === null) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return requireNonEmptyString(value, `Invalid ${fieldName} in ${sourceLabel}: expected a non-empty string`);
|
|
11
29
|
}
|
|
12
|
-
function parseArgs(value) {
|
|
30
|
+
export function parseArgs(value) {
|
|
13
31
|
return value.trim().length > 0 ? value.trim().split(/\s+/) : [];
|
|
14
32
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
33
|
+
export function optionalStringArray(value, fieldName, sourceLabel) {
|
|
34
|
+
if (value === undefined || value === null) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
if (!Array.isArray(value)) {
|
|
38
|
+
throw new Error(`Invalid ${fieldName} in ${sourceLabel}: expected an array of strings`);
|
|
39
|
+
}
|
|
40
|
+
return value.map((entry, index) => {
|
|
41
|
+
if (typeof entry !== 'string' || entry.trim().length === 0) {
|
|
42
|
+
throw new Error(`Invalid ${fieldName}[${index}] in ${sourceLabel}: expected a non-empty string`);
|
|
43
|
+
}
|
|
44
|
+
return entry.trim();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function parseCopilotSessionTtlMinutesValue(value, sourceLabel) {
|
|
48
|
+
const parsed = typeof value === 'number' ? value : Number(String(value).trim());
|
|
49
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
50
|
+
throw new Error(`Invalid COPILOT_SESSION_TTL_MINUTES in ${sourceLabel}: expected a positive number`);
|
|
51
|
+
}
|
|
52
|
+
if (parsed > MAX_COPILOT_SESSION_TTL_MINUTES) {
|
|
53
|
+
throw new Error(`Invalid COPILOT_SESSION_TTL_MINUTES in ${sourceLabel}: must be <= ${MAX_COPILOT_SESSION_TTL_MINUTES.toFixed(2)} minutes to fit within the Node.js timer limit`);
|
|
54
|
+
}
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
export function resolveProvider(rawValue, sourceLabel) {
|
|
58
|
+
const raw = rawValue.trim().toLowerCase();
|
|
59
|
+
if (raw === 'claude' || raw === 'copilot') {
|
|
18
60
|
return raw;
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`${sourceLabel}: expected "claude" or "copilot", got "${rawValue}"`);
|
|
63
|
+
}
|
|
64
|
+
export function resolveProviderCommandConfig(overrides = {}) {
|
|
65
|
+
return {
|
|
66
|
+
claudeCommand: overrides.claudeCommand ?? DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand,
|
|
67
|
+
claudeArgs: [...(overrides.claudeArgs ?? DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs)],
|
|
68
|
+
copilotCommand: overrides.copilotCommand ?? DEFAULT_PROVIDER_COMMAND_CONFIG.copilotCommand,
|
|
69
|
+
copilotArgs: [...(overrides.copilotArgs ?? DEFAULT_PROVIDER_COMMAND_CONFIG.copilotArgs)],
|
|
70
|
+
copilotSessionTtlMinutes: overrides.copilotSessionTtlMinutes ?? DEFAULT_PROVIDER_COMMAND_CONFIG.copilotSessionTtlMinutes,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function loadConfigFromEnv(env = process.env) {
|
|
74
|
+
const provider = resolveProvider(envOr('RUNTIME_PROVIDER', 'claude', env), 'Unsupported RUNTIME_PROVIDER');
|
|
31
75
|
return {
|
|
32
|
-
borgeeBaseUrl: requireEnv('BORGEE_BASE_URL'),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
borgeeBaseUrl: requireEnv('BORGEE_BASE_URL', env),
|
|
77
|
+
...resolveProviderCommandConfig({
|
|
78
|
+
claudeCommand: envOr('CLAUDE_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand, env),
|
|
79
|
+
claudeArgs: parseArgs(envOr('CLAUDE_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs.join(' '), env)),
|
|
80
|
+
copilotCommand: envOr('COPILOT_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotCommand, env),
|
|
81
|
+
copilotArgs: parseArgs(envOr('COPILOT_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotArgs.join(' '), env)),
|
|
82
|
+
copilotSessionTtlMinutes: env.COPILOT_SESSION_TTL_MINUTES && env.COPILOT_SESSION_TTL_MINUTES.trim().length > 0
|
|
83
|
+
? parseCopilotSessionTtlMinutesValue(env.COPILOT_SESSION_TTL_MINUTES, 'environment variable COPILOT_SESSION_TTL_MINUTES')
|
|
84
|
+
: DEFAULT_PROVIDER_COMMAND_CONFIG.copilotSessionTtlMinutes,
|
|
85
|
+
}),
|
|
37
86
|
agent: {
|
|
38
|
-
agentApiKey: requireEnv('BORGEE_AGENT_API_KEY'),
|
|
39
|
-
agentName: envOr('BORGEE_AGENT_NAME', 'Assistant'),
|
|
40
|
-
provider
|
|
87
|
+
agentApiKey: requireEnv('BORGEE_AGENT_API_KEY', env),
|
|
88
|
+
agentName: envOr('BORGEE_AGENT_NAME', 'Assistant', env),
|
|
89
|
+
provider,
|
|
41
90
|
},
|
|
42
91
|
};
|
|
43
92
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { LocalConfigGenerateResult, LocalConfigGenerateSpec, LocalConfigSnapshot } from './types.js';
|
|
2
|
+
export declare const DEFAULT_LOCAL_HOST_CONFIG_FILENAME = "agents-host.yaml";
|
|
3
|
+
export declare const DEFAULT_LOCAL_AGENTS_DIRNAME = "agents";
|
|
4
|
+
export declare const MANAGED_WRITE_LOCK_DIRNAME = ".generate-config.lock";
|
|
5
|
+
export interface LocalConfigLayout {
|
|
6
|
+
root: string;
|
|
7
|
+
hostConfigPath: string;
|
|
8
|
+
agentsDir: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LocalConfigDirEntry {
|
|
11
|
+
name: string;
|
|
12
|
+
isFile: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface LocalConfigFileSystem {
|
|
15
|
+
readFile(path: string): Promise<string>;
|
|
16
|
+
readDir(path: string): Promise<LocalConfigDirEntry[]>;
|
|
17
|
+
realPath?(path: string): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
interface ManagedPathStatus {
|
|
20
|
+
isDirectory: boolean;
|
|
21
|
+
isSymbolicLink: boolean;
|
|
22
|
+
mtimeMs: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ManagedLocalConfigFileSystem extends LocalConfigFileSystem {
|
|
25
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
26
|
+
mkdir(path: string, options?: {
|
|
27
|
+
recursive?: boolean;
|
|
28
|
+
mode?: number;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
removeFile(path: string): Promise<void>;
|
|
31
|
+
removeTree(path: string): Promise<void>;
|
|
32
|
+
rename(from: string, to: string): Promise<void>;
|
|
33
|
+
symlink(target: string, path: string): Promise<void>;
|
|
34
|
+
readLink(path: string): Promise<string>;
|
|
35
|
+
lstat(path: string): Promise<ManagedPathStatus>;
|
|
36
|
+
chmod(path: string, mode: number): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export declare function resolveLocalConfigLayout(rootPath: string): LocalConfigLayout;
|
|
39
|
+
export declare function parseGenerateConfigSpec(value: unknown, sourceLabel: string): LocalConfigGenerateSpec;
|
|
40
|
+
export declare function loadLocalConfigSnapshot(hostConfigPath: string, deps?: {
|
|
41
|
+
fileSystem?: LocalConfigFileSystem;
|
|
42
|
+
acquireManagedGenerationLease?: boolean;
|
|
43
|
+
}): Promise<LocalConfigSnapshot>;
|
|
44
|
+
export declare function loadLocalConfigGenerateSpec(hostConfigPath: string, deps?: {
|
|
45
|
+
fileSystem?: LocalConfigFileSystem;
|
|
46
|
+
acquireManagedGenerationLease?: boolean;
|
|
47
|
+
}): Promise<LocalConfigGenerateSpec>;
|
|
48
|
+
export declare function materializeLocalConfig(rootPath: string, spec: LocalConfigGenerateSpec, deps?: {
|
|
49
|
+
fileSystem?: ManagedLocalConfigFileSystem;
|
|
50
|
+
}): Promise<LocalConfigGenerateResult>;
|
|
51
|
+
export {};
|