@orchagent/cli 0.3.86 → 0.3.88

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.
Files changed (34) hide show
  1. package/dist/commands/agent-keys.js +21 -7
  2. package/dist/commands/agents.js +60 -5
  3. package/dist/commands/config.js +4 -0
  4. package/dist/commands/delete.js +2 -2
  5. package/dist/commands/dev.js +226 -0
  6. package/dist/commands/diff.js +418 -0
  7. package/dist/commands/estimate.js +105 -0
  8. package/dist/commands/fork.js +11 -1
  9. package/dist/commands/health.js +226 -0
  10. package/dist/commands/index.js +14 -0
  11. package/dist/commands/info.js +75 -0
  12. package/dist/commands/init.js +729 -38
  13. package/dist/commands/metrics.js +137 -0
  14. package/dist/commands/publish.js +237 -21
  15. package/dist/commands/replay.js +198 -0
  16. package/dist/commands/run.js +272 -28
  17. package/dist/commands/schedule.js +11 -6
  18. package/dist/commands/test.js +68 -1
  19. package/dist/commands/trace.js +311 -0
  20. package/dist/lib/api.js +29 -4
  21. package/dist/lib/batch-publish.js +223 -0
  22. package/dist/lib/dev-server.js +425 -0
  23. package/dist/lib/doctor/checks/environment.js +1 -1
  24. package/dist/lib/key-store.js +121 -0
  25. package/dist/lib/spinner.js +50 -0
  26. package/dist/lib/test-mock-runner.js +334 -0
  27. package/dist/lib/update-notifier.js +1 -1
  28. package/package.json +1 -1
  29. package/src/resources/__pycache__/agent_runner.cpython-311.pyc +0 -0
  30. package/src/resources/__pycache__/agent_runner.cpython-312.pyc +0 -0
  31. package/src/resources/__pycache__/test_agent_runner_mocks.cpython-311-pytest-9.0.2.pyc +0 -0
  32. package/src/resources/__pycache__/test_agent_runner_mocks.cpython-312-pytest-8.4.2.pyc +0 -0
  33. package/src/resources/agent_runner.py +29 -2
  34. package/src/resources/test_agent_runner_mocks.py +290 -0
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const config_1 = require("../lib/config");
9
9
  const api_1 = require("../lib/api");
10
10
  const errors_1 = require("../lib/errors");
11
+ const key_store_1 = require("../lib/key-store");
11
12
  /**
12
13
  * Resolve an agent reference ("org/agent" or just "agent") to an agent ID.
13
14
  * Uses the authenticated list-agents endpoint and finds the latest version.
@@ -27,7 +28,7 @@ async function resolveAgentId(config, ref) {
27
28
  }
28
29
  // Use the latest version
29
30
  const latest = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
30
- return { agent: latest, agentId: latest.id };
31
+ return { agent: latest, agentId: latest.id, orgSlug: latest.org_slug ?? resolvedOrg ?? '' };
31
32
  }
32
33
  function registerAgentKeysCommand(program) {
33
34
  const agentKeys = program
@@ -41,22 +42,29 @@ function registerAgentKeysCommand(program) {
41
42
  if (!config.apiKey) {
42
43
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
43
44
  }
44
- const { agent, agentId } = await resolveAgentId(config, ref);
45
+ const { agent, agentId, orgSlug } = await resolveAgentId(config, ref);
45
46
  const result = await (0, api_1.listAgentKeys)(config, agentId);
47
+ // Load locally-saved keys for this agent
48
+ const localKeys = await (0, key_store_1.loadServiceKeys)(orgSlug, agent.name);
49
+ const localPrefixes = new Set(localKeys.map(k => k.prefix));
46
50
  if (result.keys.length === 0) {
47
51
  process.stdout.write(`No service keys for ${agent.name}.\n`);
48
52
  process.stdout.write(`\nCreate one with: orchagent agent-keys create ${ref}\n`);
49
53
  return;
50
54
  }
51
55
  process.stdout.write(`Service keys for ${agent.name}:\n\n`);
52
- process.stdout.write(` ${'ID'.padEnd(38)} ${'PREFIX'.padEnd(14)} ${'CREATED'.padEnd(22)} LAST USED\n`);
53
- process.stdout.write(` ${'─'.repeat(38)} ${'─'.repeat(14)} ${'─'.repeat(22)} ${'─'.repeat(22)}\n`);
56
+ process.stdout.write(` ${'ID'.padEnd(38)} ${'PREFIX'.padEnd(14)} ${'CREATED'.padEnd(22)} ${'LAST USED'.padEnd(22)} SAVED\n`);
57
+ process.stdout.write(` ${'─'.repeat(38)} ${'─'.repeat(14)} ${'─'.repeat(22)} ${'─'.repeat(22)} ${'─'.repeat(5)}\n`);
54
58
  for (const key of result.keys) {
55
59
  const created = new Date(key.created_at).toLocaleDateString();
56
60
  const lastUsed = key.last_used_at
57
61
  ? new Date(key.last_used_at).toLocaleDateString()
58
62
  : chalk_1.default.gray('never');
59
- process.stdout.write(` ${key.id.padEnd(38)} ${key.prefix.padEnd(14)} ${created.padEnd(22)} ${lastUsed}\n`);
63
+ const saved = localPrefixes.has(key.prefix) ? chalk_1.default.green('yes') : chalk_1.default.gray('no');
64
+ process.stdout.write(` ${key.id.padEnd(38)} ${key.prefix.padEnd(14)} ${created.padEnd(22)} ${String(lastUsed).padEnd(22)} ${saved}\n`);
65
+ }
66
+ if (localKeys.length > 0) {
67
+ process.stdout.write(`\n ${chalk_1.default.gray('Keys marked "yes" can be retrieved from ~/.orchagent/keys/')}\n`);
60
68
  }
61
69
  process.stdout.write('\n');
62
70
  });
@@ -68,11 +76,17 @@ function registerAgentKeysCommand(program) {
68
76
  if (!config.apiKey) {
69
77
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
70
78
  }
71
- const { agent } = await resolveAgentId(config, ref);
79
+ const { agent, orgSlug } = await resolveAgentId(config, ref);
72
80
  const result = await (0, api_1.createAgentKey)(config, agent.id);
73
81
  process.stdout.write(`\nNew service key for ${agent.name}:\n\n`);
74
82
  process.stdout.write(` ${result.key}\n\n`);
75
- process.stderr.write(chalk_1.default.yellow('Save this key now — it cannot be retrieved again.\n'));
83
+ try {
84
+ const savedPath = await (0, key_store_1.saveServiceKey)(orgSlug, agent.name, agent.version, result.key, result.prefix);
85
+ process.stdout.write(` ${chalk_1.default.gray(`Saved to ${savedPath}`)}\n`);
86
+ }
87
+ catch {
88
+ process.stderr.write(chalk_1.default.yellow('Could not save key locally. Copy it now — it cannot be retrieved from the server.\n'));
89
+ }
76
90
  });
77
91
  agentKeys
78
92
  .command('delete <agent> <key-id>')
@@ -36,16 +36,42 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.latestOnly = latestOnly;
39
40
  exports.registerAgentsCommand = registerAgentsCommand;
40
41
  const chalk_1 = __importDefault(require("chalk"));
41
42
  const config_1 = require("../lib/config");
42
43
  const api_1 = require("../lib/api");
43
44
  const output_1 = require("../lib/output");
45
+ /**
46
+ * Given a list of agents, return only the latest version of each agent name.
47
+ * "Latest" = highest created_at timestamp (most recently published).
48
+ * Also returns the total version count per agent name for display.
49
+ */
50
+ function latestOnly(agents) {
51
+ const byName = new Map();
52
+ for (const agent of agents) {
53
+ const existing = byName.get(agent.name) ?? [];
54
+ existing.push(agent);
55
+ byName.set(agent.name, existing);
56
+ }
57
+ const result = [];
58
+ const versionCounts = new Map();
59
+ for (const [name, versions] of byName) {
60
+ versionCounts.set(name, versions.length);
61
+ // Sort by created_at descending, take the first (latest)
62
+ versions.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
63
+ result.push(versions[0]);
64
+ }
65
+ // Sort final list alphabetically by name for stable output
66
+ result.sort((a, b) => a.name.localeCompare(b.name));
67
+ return { agents: result, versionCounts };
68
+ }
44
69
  function registerAgentsCommand(program) {
45
70
  program
46
71
  .command('agents')
47
72
  .description('List your published agents')
48
73
  .option('--filter <text>', 'Filter by name')
74
+ .option('--all-versions', 'Show all versions (default: latest only)')
49
75
  .option('--json', 'Output raw JSON')
50
76
  .action(async (options) => {
51
77
  const config = await (0, config_1.getResolvedConfig)();
@@ -58,11 +84,22 @@ function registerAgentsCommand(program) {
58
84
  const filteredAgents = options.filter
59
85
  ? agents.filter(a => a.name.toLowerCase().includes(options.filter.toLowerCase()))
60
86
  : agents;
87
+ // Determine display set: latest-only (default) or all versions
88
+ let displayAgents;
89
+ let versionCounts;
90
+ if (options.allVersions) {
91
+ displayAgents = filteredAgents;
92
+ }
93
+ else {
94
+ const grouped = latestOnly(filteredAgents);
95
+ displayAgents = grouped.agents;
96
+ versionCounts = grouped.versionCounts;
97
+ }
61
98
  if (options.json) {
62
- (0, output_1.printJson)(filteredAgents);
99
+ (0, output_1.printJson)(displayAgents);
63
100
  return;
64
101
  }
65
- if (filteredAgents.length === 0) {
102
+ if (displayAgents.length === 0) {
66
103
  process.stdout.write(options.filter
67
104
  ? `No agents found matching "${options.filter}"\n`
68
105
  : 'No agents published yet.\n\nPublish an agent: orch publish\n');
@@ -77,18 +114,36 @@ function registerAgentsCommand(program) {
77
114
  chalk_1.default.bold('Description'),
78
115
  ],
79
116
  });
80
- filteredAgents.forEach((agent) => {
117
+ displayAgents.forEach((agent) => {
81
118
  const name = agent.name;
82
- const version = agent.version;
83
119
  const type = agent.type || 'tool';
84
120
  const desc = agent.description
85
121
  ? agent.description.length > 60
86
122
  ? agent.description.slice(0, 57) + '...'
87
123
  : agent.description
88
124
  : '-';
125
+ // In latest-only mode, show version count if > 1
126
+ let version = agent.version;
127
+ if (!options.allVersions && versionCounts) {
128
+ const count = versionCounts.get(agent.name) ?? 1;
129
+ if (count > 1) {
130
+ version = `${agent.version} (${count} total)`;
131
+ }
132
+ }
89
133
  table.push([name, version, type, desc]);
90
134
  });
91
135
  process.stdout.write(`${table.toString()}\n`);
92
- process.stdout.write(`\nTotal: ${filteredAgents.length} agent${filteredAgents.length === 1 ? '' : 's'}\n`);
136
+ if (options.allVersions) {
137
+ process.stdout.write(`\nTotal: ${displayAgents.length} version${displayAgents.length === 1 ? '' : 's'}\n`);
138
+ }
139
+ else {
140
+ const totalVersions = filteredAgents.length;
141
+ const agentCount = displayAgents.length;
142
+ process.stdout.write(`\n${agentCount} agent${agentCount === 1 ? '' : 's'}`);
143
+ if (totalVersions > agentCount) {
144
+ process.stdout.write(` (${totalVersions} versions total, use --all-versions to show all)`);
145
+ }
146
+ process.stdout.write('\n');
147
+ }
93
148
  });
94
149
  }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setConfigValue = setConfigValue;
3
4
  exports.registerConfigCommand = registerConfigCommand;
4
5
  const config_1 = require("../lib/config");
5
6
  const errors_1 = require("../lib/errors");
@@ -18,6 +19,9 @@ async function setConfigValue(key, value) {
18
19
  if (!isValidKey(key)) {
19
20
  throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
20
21
  }
22
+ if (!value || !value.trim()) {
23
+ throw new errors_1.CliError(`Cannot set ${key} to an empty value. To clear this setting, use: orch config unset ${key}`);
24
+ }
21
25
  if (key === 'default-format') {
22
26
  const formats = value.split(',').map((f) => f.trim()).filter(Boolean);
23
27
  // Validate format IDs against union of skill formats and agent adapters
@@ -64,7 +64,7 @@ Examples:
64
64
  selectedAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
65
65
  }
66
66
  // Check if confirmation is required
67
- const deleteCheck = await (0, api_1.checkAgentDelete)(config, selectedAgent.id);
67
+ const deleteCheck = await (0, api_1.checkAgentDelete)(config, selectedAgent.id, workspaceId);
68
68
  // Show agent info
69
69
  process.stdout.write(`\n${chalk_1.default.bold('Agent:')} ${selectedAgent.name}@${selectedAgent.version}\n`);
70
70
  process.stdout.write('\n');
@@ -97,7 +97,7 @@ Examples:
97
97
  // Perform deletion
98
98
  process.stdout.write('Deleting agent...\n');
99
99
  const confirmationName = deleteCheck.requires_confirmation ? selectedAgent.name : undefined;
100
- await (0, api_1.deleteAgent)(config, selectedAgent.id, confirmationName);
100
+ await (0, api_1.deleteAgent)(config, selectedAgent.id, confirmationName, workspaceId);
101
101
  await (0, analytics_1.track)('cli_delete', { agent_name: selectedAgent.name, version: selectedAgent.version });
102
102
  process.stdout.write(`✓ Deleted ${selectedAgent.name}@${selectedAgent.version}\n`);
103
103
  process.stdout.write(chalk_1.default.gray('\nData will be retained for 30 days before permanent deletion.\n'));
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ /**
3
+ * `orch dev` — local development server with hot-reload.
4
+ *
5
+ * Starts an HTTP server that accepts JSON input and runs the agent locally.
6
+ * Watches for file changes and reloads agent configuration automatically.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.registerDevCommand = registerDevCommand;
13
+ const path_1 = __importDefault(require("path"));
14
+ const promises_1 = __importDefault(require("fs/promises"));
15
+ const chalk_1 = __importDefault(require("chalk"));
16
+ const chokidar_1 = __importDefault(require("chokidar"));
17
+ const errors_1 = require("../lib/errors");
18
+ const dotenv_1 = require("../lib/dotenv");
19
+ const dev_server_1 = require("../lib/dev-server");
20
+ // ─── Console UI ─────────────────────────────────────────────────────────────
21
+ function printBanner(config, port) {
22
+ const name = config.manifest.name || 'unknown';
23
+ const version = config.manifest.version || 'local';
24
+ const engine = (0, dev_server_1.engineLabel)(config.engine);
25
+ process.stderr.write('\n');
26
+ process.stderr.write(chalk_1.default.cyan.bold(` orch dev`) + chalk_1.default.gray(` — local development server\n`));
27
+ process.stderr.write('\n');
28
+ process.stderr.write(` ${chalk_1.default.bold('Agent:')} ${name}@${version}\n`);
29
+ process.stderr.write(` ${chalk_1.default.bold('Engine:')} ${engine}`);
30
+ if (config.entrypoint) {
31
+ process.stderr.write(chalk_1.default.gray(` (${config.entrypoint})`));
32
+ }
33
+ process.stderr.write('\n');
34
+ process.stderr.write(` ${chalk_1.default.bold('Server:')} ${chalk_1.default.green(`http://localhost:${port}`)}\n`);
35
+ process.stderr.write('\n');
36
+ process.stderr.write(chalk_1.default.gray(` POST http://localhost:${port}/run Execute agent\n`));
37
+ process.stderr.write(chalk_1.default.gray(` GET http://localhost:${port}/health Agent info\n`));
38
+ process.stderr.write('\n');
39
+ process.stderr.write(chalk_1.default.gray(' Watching for file changes... (Ctrl+C to stop)\n'));
40
+ process.stderr.write(chalk_1.default.gray(' ─'.repeat(32)) + '\n');
41
+ }
42
+ function printRequestLog(log) {
43
+ const status = log.statusCode < 400
44
+ ? chalk_1.default.green(`${log.statusCode}`)
45
+ : chalk_1.default.red(`${log.statusCode}`);
46
+ const duration = chalk_1.default.gray(`${log.durationMs}ms`);
47
+ const id = chalk_1.default.gray(`#${log.id}`);
48
+ if (log.error) {
49
+ process.stderr.write(` ${id} ${log.method} ${log.path} ${status} ${duration}\n` +
50
+ chalk_1.default.red(` ${log.error.split('\n')[0].slice(0, 120)}\n`));
51
+ }
52
+ else {
53
+ process.stderr.write(` ${id} ${log.method} ${log.path} ${status} ${duration}\n`);
54
+ }
55
+ }
56
+ function printReload(reason) {
57
+ const time = new Date().toLocaleTimeString();
58
+ process.stderr.write(chalk_1.default.cyan(`\n [${time}] ${reason}\n`));
59
+ }
60
+ function printReloadError(error) {
61
+ process.stderr.write(chalk_1.default.red(` Reload failed: ${error}\n`));
62
+ process.stderr.write(chalk_1.default.yellow(` Server still running with previous configuration.\n`));
63
+ }
64
+ function setupWatcher(agentDir, state, verbose) {
65
+ const watcher = chokidar_1.default.watch(agentDir, {
66
+ ignored: /(node_modules|__pycache__|\.git|dist|build|\.venv|venv|\.next|target)/,
67
+ persistent: true,
68
+ ignoreInitial: true,
69
+ });
70
+ const reloadConfig = async (filePath) => {
71
+ const relPath = path_1.default.relative(agentDir, filePath);
72
+ printReload(`Changed: ${relPath}`);
73
+ try {
74
+ const newConfig = await (0, dev_server_1.loadAgentConfig)(agentDir);
75
+ state.config = newConfig;
76
+ const engine = (0, dev_server_1.engineLabel)(newConfig.engine);
77
+ const ep = newConfig.entrypoint ? `, ${newConfig.entrypoint}` : '';
78
+ process.stderr.write(chalk_1.default.green(` Reloaded`) + chalk_1.default.gray(` (${engine}${ep})\n`));
79
+ }
80
+ catch (err) {
81
+ const message = err instanceof Error ? err.message : String(err);
82
+ printReloadError(message);
83
+ }
84
+ };
85
+ const onChange = (filePath) => {
86
+ if (state.debounceTimer)
87
+ clearTimeout(state.debounceTimer);
88
+ state.debounceTimer = setTimeout(() => reloadConfig(filePath), 300);
89
+ };
90
+ watcher
91
+ .on('change', onChange)
92
+ .on('add', onChange)
93
+ .on('unlink', onChange)
94
+ .on('error', (error) => {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ process.stderr.write(chalk_1.default.red(` Watcher error: ${message}\n`));
97
+ });
98
+ return watcher;
99
+ }
100
+ // ─── Command registration ───────────────────────────────────────────────────
101
+ function registerDevCommand(program) {
102
+ program
103
+ .command('dev [path]')
104
+ .description('Start a local development server with hot-reload')
105
+ .option('-p, --port <port>', 'Server port', '4900')
106
+ .option('-v, --verbose', 'Show detailed execution output')
107
+ .option('--no-watch', 'Disable file watching')
108
+ .addHelpText('after', `
109
+ Examples:
110
+ orch dev Start dev server in current directory
111
+ orch dev ./my-agent Start dev server for agent in specified directory
112
+ orch dev --port 3001 Use custom port
113
+ orch dev --verbose Show detailed execution output
114
+ orch dev --no-watch Disable file watching (no hot-reload)
115
+
116
+ Then send requests:
117
+ curl -X POST http://localhost:4900/run \\
118
+ -H "Content-Type: application/json" \\
119
+ -d '{"task": "hello world"}'
120
+ `)
121
+ .action(async (dirPath, options) => {
122
+ const agentDir = path_1.default.resolve(dirPath || '.');
123
+ const port = parseInt(options.port, 10);
124
+ const verbose = options.verbose ?? false;
125
+ const watchEnabled = options.watch !== false;
126
+ if (isNaN(port) || port < 1 || port > 65535) {
127
+ throw new errors_1.CliError('Port must be a number between 1 and 65535');
128
+ }
129
+ // Verify directory exists
130
+ try {
131
+ const stat = await promises_1.default.stat(agentDir);
132
+ if (!stat.isDirectory()) {
133
+ throw new errors_1.CliError(`Not a directory: ${agentDir}`);
134
+ }
135
+ }
136
+ catch (err) {
137
+ if (err instanceof errors_1.CliError)
138
+ throw err;
139
+ throw new errors_1.CliError(`Directory not found: ${agentDir}`);
140
+ }
141
+ // Verify orchagent.json exists
142
+ try {
143
+ await promises_1.default.access(path_1.default.join(agentDir, 'orchagent.json'));
144
+ }
145
+ catch {
146
+ throw new errors_1.CliError(`No orchagent.json found in ${agentDir}\n\n` +
147
+ `To start a dev server, the directory must contain orchagent.json.\n` +
148
+ `Create one with: orch init`);
149
+ }
150
+ // Load .env
151
+ const dotEnvVars = await (0, dotenv_1.loadDotEnv)(agentDir);
152
+ const dotEnvCount = Object.keys(dotEnvVars).length;
153
+ if (dotEnvCount > 0) {
154
+ for (const [key, value] of Object.entries(dotEnvVars)) {
155
+ if (!(key in process.env) || process.env[key] === undefined) {
156
+ process.env[key] = value;
157
+ }
158
+ }
159
+ }
160
+ // Initial config load
161
+ let initialConfig;
162
+ try {
163
+ initialConfig = await (0, dev_server_1.loadAgentConfig)(agentDir);
164
+ }
165
+ catch (err) {
166
+ const message = err instanceof Error ? err.message : String(err);
167
+ throw new errors_1.CliError(`Failed to load agent configuration: ${message}`);
168
+ }
169
+ const type = (initialConfig.manifest.type || 'agent').toLowerCase();
170
+ if (type === 'skill') {
171
+ throw new errors_1.CliError('Skills cannot be served as a dev server.\n' +
172
+ 'Skills are instructions meant to be injected into AI agent contexts.');
173
+ }
174
+ // Set up state
175
+ const state = {
176
+ config: initialConfig,
177
+ debounceTimer: null,
178
+ };
179
+ // Create server
180
+ const { server, close } = (0, dev_server_1.createDevServer)(port, verbose, () => state.config, {
181
+ onRequest: printRequestLog,
182
+ onError: verbose ? (err) => {
183
+ process.stderr.write(chalk_1.default.red(` Detail: ${err.message}\n`));
184
+ } : undefined,
185
+ });
186
+ // Start server
187
+ await new Promise((resolve, reject) => {
188
+ server.on('error', (err) => {
189
+ if (err.code === 'EADDRINUSE') {
190
+ reject(new errors_1.CliError(`Port ${port} is already in use.\n\n` +
191
+ `Try a different port: orch dev --port ${port + 1}`));
192
+ }
193
+ else {
194
+ reject(new errors_1.CliError(`Server error: ${err.message}`));
195
+ }
196
+ });
197
+ server.listen(port, () => resolve());
198
+ });
199
+ // Print banner
200
+ printBanner(initialConfig, port);
201
+ if (dotEnvCount > 0) {
202
+ process.stderr.write(chalk_1.default.gray(` Loaded ${dotEnvCount} variable${dotEnvCount === 1 ? '' : 's'} from .env\n`));
203
+ }
204
+ // Set up file watcher
205
+ let watcher = null;
206
+ if (watchEnabled) {
207
+ watcher = setupWatcher(agentDir, state, verbose);
208
+ }
209
+ else {
210
+ process.stderr.write(chalk_1.default.gray(` File watching disabled\n`));
211
+ }
212
+ // Handle shutdown
213
+ const shutdown = async () => {
214
+ process.stderr.write(chalk_1.default.gray('\n Shutting down...\n'));
215
+ if (watcher) {
216
+ await watcher.close();
217
+ }
218
+ await close();
219
+ process.exit(0);
220
+ };
221
+ process.on('SIGINT', shutdown);
222
+ process.on('SIGTERM', shutdown);
223
+ // Keep process alive
224
+ await new Promise(() => { });
225
+ });
226
+ }