electric-ax 0.1.0 → 0.1.2

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.
@@ -1,4 +1,4 @@
1
- import { ElectricCliEnv } from "./index-B6gIlecl.cjs";
1
+ import { ElectricCliEnv } from "./index-CQaCUw1k.cjs";
2
2
 
3
3
  //#region src/completions.d.ts
4
4
  declare function fetchEntityTypeNames(env: ElectricCliEnv): Promise<Array<string>>;
@@ -1,4 +1,4 @@
1
- import { ElectricCliEnv } from "./index-BJvlcIKL.js";
1
+ import { ElectricCliEnv } from "./index-CrW6PmwB.js";
2
2
 
3
3
  //#region src/completions.d.ts
4
4
  declare function fetchEntityTypeNames(env: ElectricCliEnv): Promise<Array<string>>;
@@ -15,7 +15,30 @@ interface StartedBuiltinAgentsEnvironment {
15
15
  url: string;
16
16
  registeredBaseUrl: string;
17
17
  agentServerUrl: string;
18
- } //#endregion
18
+ }
19
+ interface WaitForServerOptions {
20
+ fetchImpl?: typeof globalThis.fetch;
21
+ timeoutMs?: number;
22
+ intervalMs?: number;
23
+ }
24
+ declare function readDotEnvFile(cwd?: string): Record<string, string>;
25
+ declare function resolveAnthropicApiKey(options: StartBuiltinCommandOptions, env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
26
+ declare function resolveBuiltinAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
27
+ declare function resolveElectricAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
28
+ declare function getStartedEnvironmentMessage(started: StartedDevEnvironment): string;
29
+ declare function getStoppedEnvironmentMessage(stopped: StoppedDevEnvironment): string;
30
+ declare function getStartedBuiltinAgentsMessage(started: StartedBuiltinAgentsEnvironment): string;
31
+ declare function resolveComposeProjectName(_cwd?: string, env?: NodeJS.ProcessEnv): string;
32
+ declare function waitForElectricAgentsServer(baseUrl: string, options?: WaitForServerOptions): Promise<void>;
33
+ declare function startElectricAgentsDevEnvironment(_options?: StartCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StartedDevEnvironment>;
34
+ declare function stopElectricAgentsDevEnvironment(options: StopCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StoppedDevEnvironment>;
35
+ declare function startBuiltinAgentsServer(options: StartBuiltinCommandOptions, params?: {
36
+ env?: NodeJS.ProcessEnv;
37
+ cwd?: string;
38
+ agentServerUrl?: string;
39
+ }): Promise<StartedBuiltinAgentsEnvironment>;
40
+
41
+ //#endregion
19
42
  //#region src/index.d.ts
20
43
  declare const DEFAULT_ELECTRIC_AGENTS_URL = "http://localhost:4437";
21
44
  interface ElectricCliEnv {
@@ -80,4 +103,4 @@ declare function createElectricProgram({
80
103
  declare function run(argv?: Array<string>): Promise<void>;
81
104
 
82
105
  //#endregion
83
- export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run };
106
+ export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
@@ -15,7 +15,30 @@ interface StartedBuiltinAgentsEnvironment {
15
15
  url: string;
16
16
  registeredBaseUrl: string;
17
17
  agentServerUrl: string;
18
- } //#endregion
18
+ }
19
+ interface WaitForServerOptions {
20
+ fetchImpl?: typeof globalThis.fetch;
21
+ timeoutMs?: number;
22
+ intervalMs?: number;
23
+ }
24
+ declare function readDotEnvFile(cwd?: string): Record<string, string>;
25
+ declare function resolveAnthropicApiKey(options: StartBuiltinCommandOptions, env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
26
+ declare function resolveBuiltinAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
27
+ declare function resolveElectricAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
28
+ declare function getStartedEnvironmentMessage(started: StartedDevEnvironment): string;
29
+ declare function getStoppedEnvironmentMessage(stopped: StoppedDevEnvironment): string;
30
+ declare function getStartedBuiltinAgentsMessage(started: StartedBuiltinAgentsEnvironment): string;
31
+ declare function resolveComposeProjectName(_cwd?: string, env?: NodeJS.ProcessEnv): string;
32
+ declare function waitForElectricAgentsServer(baseUrl: string, options?: WaitForServerOptions): Promise<void>;
33
+ declare function startElectricAgentsDevEnvironment(_options?: StartCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StartedDevEnvironment>;
34
+ declare function stopElectricAgentsDevEnvironment(options: StopCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StoppedDevEnvironment>;
35
+ declare function startBuiltinAgentsServer(options: StartBuiltinCommandOptions, params?: {
36
+ env?: NodeJS.ProcessEnv;
37
+ cwd?: string;
38
+ agentServerUrl?: string;
39
+ }): Promise<StartedBuiltinAgentsEnvironment>;
40
+
41
+ //#endregion
19
42
  //#region src/index.d.ts
20
43
  declare const DEFAULT_ELECTRIC_AGENTS_URL = "http://localhost:4437";
21
44
  interface ElectricCliEnv {
@@ -80,4 +103,4 @@ declare function createElectricProgram({
80
103
  declare function run(argv?: Array<string>): Promise<void>;
81
104
 
82
105
  //#endregion
83
- export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run };
106
+ export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
package/dist/index.cjs CHANGED
@@ -7,223 +7,7 @@ const node_os = require_chunk.__toESM(require("node:os"));
7
7
  const node_path = require_chunk.__toESM(require("node:path"));
8
8
  const node_url = require_chunk.__toESM(require("node:url"));
9
9
  const commander = require_chunk.__toESM(require("commander"));
10
- const node_child_process = require_chunk.__toESM(require("node:child_process"));
11
- const __electric_ax_agents = require_chunk.__toESM(require("@electric-ax/agents"));
12
10
 
13
- //#region src/start.ts
14
- const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
15
- const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
16
- const DOCKER_COMPOSE_FILE = (0, node_url.fileURLToPath)(new URL(`../docker-compose.full.yml`, require("url").pathToFileURL(__filename).href));
17
- function parseDotEnvValue(raw) {
18
- const trimmed = raw.trim();
19
- if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) return trimmed.slice(1, -1);
20
- const hashIndex = trimmed.indexOf(`#`);
21
- return hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex).trim();
22
- }
23
- function readDotEnvFile(cwd = process.cwd()) {
24
- const envPath = (0, node_path.resolve)(cwd, `.env`);
25
- try {
26
- const content = (0, node_fs.readFileSync)(envPath, `utf8`);
27
- const values = {};
28
- for (const line of content.split(/\r?\n/)) {
29
- const trimmed = line.trim();
30
- if (!trimmed || trimmed.startsWith(`#`)) continue;
31
- const equalsIndex = trimmed.indexOf(`=`);
32
- if (equalsIndex <= 0) continue;
33
- const key = trimmed.slice(0, equalsIndex).trim();
34
- const value = parseDotEnvValue(trimmed.slice(equalsIndex + 1));
35
- values[key] = value;
36
- }
37
- return values;
38
- } catch (error) {
39
- if (error.code === `ENOENT`) return {};
40
- throw error;
41
- }
42
- }
43
- function resolveAnthropicApiKey(options, env = process.env, fileEnv = readDotEnvFile()) {
44
- const candidate = options.anthropicApiKey?.trim() || env.ANTHROPIC_API_KEY?.trim() || fileEnv.ANTHROPIC_API_KEY?.trim();
45
- if (!candidate) throw new Error(`ANTHROPIC_API_KEY is required. Pass --anthropic-api-key, export it in your shell, or set it in .env.`);
46
- return candidate;
47
- }
48
- function resolveBuiltinAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
49
- const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
50
- const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
51
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
52
- return parsed;
53
- }
54
- function resolveElectricAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
55
- const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
56
- const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
57
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_PORT must be a positive integer`);
58
- return parsed;
59
- }
60
- function getStartedEnvironmentMessage(started) {
61
- return [
62
- `Electric Agents dev environment is up.`,
63
- `Server + UI: ${started.uiUrl}`,
64
- `Docker project: ${started.composeProjectName}`
65
- ].join(`\n`);
66
- }
67
- function getStoppedEnvironmentMessage(stopped) {
68
- return [
69
- `Electric Agents dev environment is down.`,
70
- `Docker project: ${stopped.composeProjectName}`,
71
- stopped.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
72
- ].join(`\n`);
73
- }
74
- function getStartedBuiltinAgentsMessage(started) {
75
- return [
76
- `Builtin Horton server is up.`,
77
- `Webhook server: ${started.url}`,
78
- `Registers with: ${started.agentServerUrl}`,
79
- `Press Ctrl-C to stop.`
80
- ].join(`\n`);
81
- }
82
- function slugify(input) {
83
- return input.toLowerCase().replace(/[^a-z0-9]+/g, `-`).replace(/^-+|-+$/g, ``);
84
- }
85
- function resolveComposeProjectName(cwd = process.cwd(), env = process.env) {
86
- const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
87
- if (explicit) return explicit;
88
- const base = slugify((0, node_path.basename)(cwd)) || `workspace`;
89
- return `electric-agents-${base}`;
90
- }
91
- async function runDockerCompose(args, env) {
92
- await new Promise((resolve, reject) => {
93
- const child = (0, node_child_process.spawn)(`docker`, args, {
94
- cwd: process.cwd(),
95
- env,
96
- stdio: `inherit`
97
- });
98
- child.on(`error`, (error) => {
99
- reject(new Error(`Failed to run docker compose: ${error instanceof Error ? error.message : String(error)}`));
100
- });
101
- child.on(`exit`, (code) => {
102
- if (code === 0) {
103
- resolve();
104
- return;
105
- }
106
- reject(new Error(`docker compose exited with code ${code ?? `unknown`}`));
107
- });
108
- });
109
- }
110
- function delay(ms) {
111
- return new Promise((resolve) => {
112
- setTimeout(resolve, ms);
113
- });
114
- }
115
- async function waitForElectricAgentsServer(baseUrl, options = {}) {
116
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
117
- const timeoutMs = options.timeoutMs ?? 6e4;
118
- const intervalMs = options.intervalMs ?? 1e3;
119
- const deadline = Date.now() + timeoutMs;
120
- const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
121
- let lastError = null;
122
- while (Date.now() < deadline) {
123
- try {
124
- const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
125
- if (response.ok) return;
126
- lastError = `healthcheck returned ${response.status}`;
127
- } catch (error) {
128
- lastError = error instanceof Error ? error.message : String(error);
129
- }
130
- await delay(intervalMs);
131
- }
132
- throw new Error(`Timed out waiting for Electric Agents server at ${healthUrl}${lastError ? `: ${lastError}` : ``}`);
133
- }
134
- async function startElectricAgentsDevEnvironment(_options = {}, env = process.env, cwd = process.cwd()) {
135
- const fileEnv = readDotEnvFile(cwd);
136
- const port = resolveElectricAgentsPort(env, fileEnv);
137
- const composeProjectName = resolveComposeProjectName(cwd, env);
138
- await runDockerCompose([
139
- `compose`,
140
- `-f`,
141
- DOCKER_COMPOSE_FILE,
142
- `up`,
143
- `-d`
144
- ], {
145
- ...env,
146
- COMPOSE_PROJECT_NAME: composeProjectName,
147
- ELECTRIC_AGENTS_PORT: String(port)
148
- });
149
- const uiUrl = `http://localhost:${port}`;
150
- await waitForElectricAgentsServer(uiUrl);
151
- return {
152
- port,
153
- uiUrl,
154
- composeProjectName
155
- };
156
- }
157
- async function stopElectricAgentsDevEnvironment(options, env = process.env, cwd = process.cwd()) {
158
- const composeProjectName = resolveComposeProjectName(cwd, env);
159
- const args = [
160
- `compose`,
161
- `-f`,
162
- DOCKER_COMPOSE_FILE,
163
- `down`
164
- ];
165
- if (options.removeVolumes) args.push(`--volumes`);
166
- await runDockerCompose(args, {
167
- ...env,
168
- COMPOSE_PROJECT_NAME: composeProjectName
169
- });
170
- return {
171
- composeProjectName,
172
- removedVolumes: options.removeVolumes ?? false
173
- };
174
- }
175
- function waitForShutdown(stop, signalSource = process) {
176
- return new Promise((resolve, reject) => {
177
- let stopping = false;
178
- const cleanup = () => {
179
- signalSource.off(`SIGINT`, onSigint);
180
- signalSource.off(`SIGTERM`, onSigterm);
181
- };
182
- const shutdown = (signal) => {
183
- if (stopping) return;
184
- stopping = true;
185
- cleanup();
186
- stop().then(resolve).catch((error) => {
187
- reject(new Error(`Failed to stop builtin agents server after ${signal}: ${error instanceof Error ? error.message : String(error)}`));
188
- });
189
- };
190
- const onSigint = () => {
191
- shutdown(`SIGINT`);
192
- };
193
- const onSigterm = () => {
194
- shutdown(`SIGTERM`);
195
- };
196
- signalSource.on(`SIGINT`, onSigint);
197
- signalSource.on(`SIGTERM`, onSigterm);
198
- });
199
- }
200
- async function startBuiltinAgentsServer(options, params = {}) {
201
- const env = params.env ?? process.env;
202
- const cwd = params.cwd ?? process.cwd();
203
- const fileEnv = readDotEnvFile(cwd);
204
- const anthropicApiKey = resolveAnthropicApiKey(options, env, fileEnv);
205
- const port = resolveBuiltinAgentsPort(env, fileEnv);
206
- const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
207
- process.env.ANTHROPIC_API_KEY = anthropicApiKey;
208
- await waitForElectricAgentsServer(agentServerUrl);
209
- const server = new __electric_ax_agents.BuiltinAgentsServer({
210
- agentServerUrl,
211
- port,
212
- workingDirectory: cwd
213
- });
214
- await server.start();
215
- const started = {
216
- port,
217
- url: server.url,
218
- registeredBaseUrl: server.registeredBaseUrl,
219
- agentServerUrl
220
- };
221
- console.log(getStartedBuiltinAgentsMessage(started));
222
- await waitForShutdown(() => server.stop());
223
- return started;
224
- }
225
-
226
- //#endregion
227
11
  //#region src/index.ts
228
12
  const DEFAULT_ELECTRIC_AGENTS_URL = `http://localhost:4437`;
229
13
  var CliError = class extends Error {};
@@ -436,10 +220,23 @@ async function killEntity(env, url) {
436
220
  console.log(`Killed ${url}`);
437
221
  }
438
222
  function printStartedEnvironment(env) {
439
- console.log(getStartedEnvironmentMessage(env));
223
+ console.log([
224
+ `Electric Agents dev environment is up.`,
225
+ `Server + UI: ${env.uiUrl}`,
226
+ `Docker project: ${env.composeProjectName}`
227
+ ].join(`\n`));
440
228
  }
441
229
  function printStoppedEnvironment(env) {
442
- console.log(getStoppedEnvironmentMessage(env));
230
+ console.log([
231
+ `Electric Agents dev environment is down.`,
232
+ `Docker project: ${env.composeProjectName}`,
233
+ env.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
234
+ ].join(`\n`));
235
+ }
236
+ let startModulePromise = null;
237
+ async function loadStartModule() {
238
+ startModulePromise ??= import(`./start.js`);
239
+ return startModulePromise;
443
240
  }
444
241
  function createElectricCliHandlers(env, commandPrefix = commandExample(`electric`)) {
445
242
  return {
@@ -453,19 +250,23 @@ function createElectricCliHandlers(env, commandPrefix = commandExample(`electric
453
250
  ps: (options) => listEntities(env, options),
454
251
  kill: (url) => killEntity(env, url),
455
252
  start: async (options) => {
253
+ const { startElectricAgentsDevEnvironment } = await loadStartModule();
456
254
  const started = await startElectricAgentsDevEnvironment(options);
457
255
  printStartedEnvironment(started);
458
256
  return started;
459
257
  },
460
258
  startBuiltin: async (options) => {
259
+ const { startBuiltinAgentsServer } = await loadStartModule();
461
260
  return startBuiltinAgentsServer(options, { agentServerUrl: env.electricAgentsUrl });
462
261
  },
463
262
  stop: async (options) => {
263
+ const { stopElectricAgentsDevEnvironment } = await loadStartModule();
464
264
  const stopped = await stopElectricAgentsDevEnvironment(options);
465
265
  printStoppedEnvironment(stopped);
466
266
  return stopped;
467
267
  },
468
268
  quickstart: async (options) => {
269
+ const { startBuiltinAgentsServer, startElectricAgentsDevEnvironment } = await loadStartModule();
469
270
  const started = await startElectricAgentsDevEnvironment();
470
271
  printStartedEnvironment(started);
471
272
  console.log(``);
package/dist/index.d.cts CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run } from "./index-B6gIlecl.cjs";
2
+ import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run } from "./index-CQaCUw1k.cjs";
3
3
  export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run } from "./index-BJvlcIKL.js";
2
+ import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run } from "./index-CrW6PmwB.js";
3
3
  export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, getElectricCliEnv, resolveCommandPrefix, run };
package/dist/index.js CHANGED
@@ -1,227 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { installCompletions, setupCompletions } from "./completions-BHILvHgZ.js";
3
- import { readFileSync, realpathSync } from "node:fs";
3
+ import { realpathSync } from "node:fs";
4
4
  import { hostname, userInfo } from "node:os";
5
5
  import { basename, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { Command } from "commander";
8
- import { spawn } from "node:child_process";
9
- import { BuiltinAgentsServer } from "@electric-ax/agents";
10
8
 
11
- //#region src/start.ts
12
- const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
13
- const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
14
- const DOCKER_COMPOSE_FILE = fileURLToPath(new URL(`../docker-compose.full.yml`, import.meta.url));
15
- function parseDotEnvValue(raw) {
16
- const trimmed = raw.trim();
17
- if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) return trimmed.slice(1, -1);
18
- const hashIndex = trimmed.indexOf(`#`);
19
- return hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex).trim();
20
- }
21
- function readDotEnvFile(cwd = process.cwd()) {
22
- const envPath = resolve(cwd, `.env`);
23
- try {
24
- const content = readFileSync(envPath, `utf8`);
25
- const values = {};
26
- for (const line of content.split(/\r?\n/)) {
27
- const trimmed = line.trim();
28
- if (!trimmed || trimmed.startsWith(`#`)) continue;
29
- const equalsIndex = trimmed.indexOf(`=`);
30
- if (equalsIndex <= 0) continue;
31
- const key = trimmed.slice(0, equalsIndex).trim();
32
- const value = parseDotEnvValue(trimmed.slice(equalsIndex + 1));
33
- values[key] = value;
34
- }
35
- return values;
36
- } catch (error) {
37
- if (error.code === `ENOENT`) return {};
38
- throw error;
39
- }
40
- }
41
- function resolveAnthropicApiKey(options, env = process.env, fileEnv = readDotEnvFile()) {
42
- const candidate = options.anthropicApiKey?.trim() || env.ANTHROPIC_API_KEY?.trim() || fileEnv.ANTHROPIC_API_KEY?.trim();
43
- if (!candidate) throw new Error(`ANTHROPIC_API_KEY is required. Pass --anthropic-api-key, export it in your shell, or set it in .env.`);
44
- return candidate;
45
- }
46
- function resolveBuiltinAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
47
- const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
48
- const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
49
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
50
- return parsed;
51
- }
52
- function resolveElectricAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
53
- const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
54
- const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
55
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_PORT must be a positive integer`);
56
- return parsed;
57
- }
58
- function getStartedEnvironmentMessage(started) {
59
- return [
60
- `Electric Agents dev environment is up.`,
61
- `Server + UI: ${started.uiUrl}`,
62
- `Docker project: ${started.composeProjectName}`
63
- ].join(`\n`);
64
- }
65
- function getStoppedEnvironmentMessage(stopped) {
66
- return [
67
- `Electric Agents dev environment is down.`,
68
- `Docker project: ${stopped.composeProjectName}`,
69
- stopped.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
70
- ].join(`\n`);
71
- }
72
- function getStartedBuiltinAgentsMessage(started) {
73
- return [
74
- `Builtin Horton server is up.`,
75
- `Webhook server: ${started.url}`,
76
- `Registers with: ${started.agentServerUrl}`,
77
- `Press Ctrl-C to stop.`
78
- ].join(`\n`);
79
- }
80
- function slugify(input) {
81
- return input.toLowerCase().replace(/[^a-z0-9]+/g, `-`).replace(/^-+|-+$/g, ``);
82
- }
83
- function resolveComposeProjectName(cwd = process.cwd(), env = process.env) {
84
- const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
85
- if (explicit) return explicit;
86
- const base = slugify(basename(cwd)) || `workspace`;
87
- return `electric-agents-${base}`;
88
- }
89
- async function runDockerCompose(args, env) {
90
- await new Promise((resolve$1, reject) => {
91
- const child = spawn(`docker`, args, {
92
- cwd: process.cwd(),
93
- env,
94
- stdio: `inherit`
95
- });
96
- child.on(`error`, (error) => {
97
- reject(new Error(`Failed to run docker compose: ${error instanceof Error ? error.message : String(error)}`));
98
- });
99
- child.on(`exit`, (code) => {
100
- if (code === 0) {
101
- resolve$1();
102
- return;
103
- }
104
- reject(new Error(`docker compose exited with code ${code ?? `unknown`}`));
105
- });
106
- });
107
- }
108
- function delay(ms) {
109
- return new Promise((resolve$1) => {
110
- setTimeout(resolve$1, ms);
111
- });
112
- }
113
- async function waitForElectricAgentsServer(baseUrl, options = {}) {
114
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
115
- const timeoutMs = options.timeoutMs ?? 6e4;
116
- const intervalMs = options.intervalMs ?? 1e3;
117
- const deadline = Date.now() + timeoutMs;
118
- const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
119
- let lastError = null;
120
- while (Date.now() < deadline) {
121
- try {
122
- const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
123
- if (response.ok) return;
124
- lastError = `healthcheck returned ${response.status}`;
125
- } catch (error) {
126
- lastError = error instanceof Error ? error.message : String(error);
127
- }
128
- await delay(intervalMs);
129
- }
130
- throw new Error(`Timed out waiting for Electric Agents server at ${healthUrl}${lastError ? `: ${lastError}` : ``}`);
131
- }
132
- async function startElectricAgentsDevEnvironment(_options = {}, env = process.env, cwd = process.cwd()) {
133
- const fileEnv = readDotEnvFile(cwd);
134
- const port = resolveElectricAgentsPort(env, fileEnv);
135
- const composeProjectName = resolveComposeProjectName(cwd, env);
136
- await runDockerCompose([
137
- `compose`,
138
- `-f`,
139
- DOCKER_COMPOSE_FILE,
140
- `up`,
141
- `-d`
142
- ], {
143
- ...env,
144
- COMPOSE_PROJECT_NAME: composeProjectName,
145
- ELECTRIC_AGENTS_PORT: String(port)
146
- });
147
- const uiUrl = `http://localhost:${port}`;
148
- await waitForElectricAgentsServer(uiUrl);
149
- return {
150
- port,
151
- uiUrl,
152
- composeProjectName
153
- };
154
- }
155
- async function stopElectricAgentsDevEnvironment(options, env = process.env, cwd = process.cwd()) {
156
- const composeProjectName = resolveComposeProjectName(cwd, env);
157
- const args = [
158
- `compose`,
159
- `-f`,
160
- DOCKER_COMPOSE_FILE,
161
- `down`
162
- ];
163
- if (options.removeVolumes) args.push(`--volumes`);
164
- await runDockerCompose(args, {
165
- ...env,
166
- COMPOSE_PROJECT_NAME: composeProjectName
167
- });
168
- return {
169
- composeProjectName,
170
- removedVolumes: options.removeVolumes ?? false
171
- };
172
- }
173
- function waitForShutdown(stop, signalSource = process) {
174
- return new Promise((resolve$1, reject) => {
175
- let stopping = false;
176
- const cleanup = () => {
177
- signalSource.off(`SIGINT`, onSigint);
178
- signalSource.off(`SIGTERM`, onSigterm);
179
- };
180
- const shutdown = (signal) => {
181
- if (stopping) return;
182
- stopping = true;
183
- cleanup();
184
- stop().then(resolve$1).catch((error) => {
185
- reject(new Error(`Failed to stop builtin agents server after ${signal}: ${error instanceof Error ? error.message : String(error)}`));
186
- });
187
- };
188
- const onSigint = () => {
189
- shutdown(`SIGINT`);
190
- };
191
- const onSigterm = () => {
192
- shutdown(`SIGTERM`);
193
- };
194
- signalSource.on(`SIGINT`, onSigint);
195
- signalSource.on(`SIGTERM`, onSigterm);
196
- });
197
- }
198
- async function startBuiltinAgentsServer(options, params = {}) {
199
- const env = params.env ?? process.env;
200
- const cwd = params.cwd ?? process.cwd();
201
- const fileEnv = readDotEnvFile(cwd);
202
- const anthropicApiKey = resolveAnthropicApiKey(options, env, fileEnv);
203
- const port = resolveBuiltinAgentsPort(env, fileEnv);
204
- const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
205
- process.env.ANTHROPIC_API_KEY = anthropicApiKey;
206
- await waitForElectricAgentsServer(agentServerUrl);
207
- const server = new BuiltinAgentsServer({
208
- agentServerUrl,
209
- port,
210
- workingDirectory: cwd
211
- });
212
- await server.start();
213
- const started = {
214
- port,
215
- url: server.url,
216
- registeredBaseUrl: server.registeredBaseUrl,
217
- agentServerUrl
218
- };
219
- console.log(getStartedBuiltinAgentsMessage(started));
220
- await waitForShutdown(() => server.stop());
221
- return started;
222
- }
223
-
224
- //#endregion
225
9
  //#region src/index.ts
226
10
  const DEFAULT_ELECTRIC_AGENTS_URL = `http://localhost:4437`;
227
11
  var CliError = class extends Error {};
@@ -434,10 +218,23 @@ async function killEntity(env, url) {
434
218
  console.log(`Killed ${url}`);
435
219
  }
436
220
  function printStartedEnvironment(env) {
437
- console.log(getStartedEnvironmentMessage(env));
221
+ console.log([
222
+ `Electric Agents dev environment is up.`,
223
+ `Server + UI: ${env.uiUrl}`,
224
+ `Docker project: ${env.composeProjectName}`
225
+ ].join(`\n`));
438
226
  }
439
227
  function printStoppedEnvironment(env) {
440
- console.log(getStoppedEnvironmentMessage(env));
228
+ console.log([
229
+ `Electric Agents dev environment is down.`,
230
+ `Docker project: ${env.composeProjectName}`,
231
+ env.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
232
+ ].join(`\n`));
233
+ }
234
+ let startModulePromise = null;
235
+ async function loadStartModule() {
236
+ startModulePromise ??= import(`./start.js`);
237
+ return startModulePromise;
441
238
  }
442
239
  function createElectricCliHandlers(env, commandPrefix = commandExample(`electric`)) {
443
240
  return {
@@ -451,19 +248,23 @@ function createElectricCliHandlers(env, commandPrefix = commandExample(`electric
451
248
  ps: (options) => listEntities(env, options),
452
249
  kill: (url) => killEntity(env, url),
453
250
  start: async (options) => {
251
+ const { startElectricAgentsDevEnvironment } = await loadStartModule();
454
252
  const started = await startElectricAgentsDevEnvironment(options);
455
253
  printStartedEnvironment(started);
456
254
  return started;
457
255
  },
458
256
  startBuiltin: async (options) => {
257
+ const { startBuiltinAgentsServer } = await loadStartModule();
459
258
  return startBuiltinAgentsServer(options, { agentServerUrl: env.electricAgentsUrl });
460
259
  },
461
260
  stop: async (options) => {
261
+ const { stopElectricAgentsDevEnvironment } = await loadStartModule();
462
262
  const stopped = await stopElectricAgentsDevEnvironment(options);
463
263
  printStoppedEnvironment(stopped);
464
264
  return stopped;
465
265
  },
466
266
  quickstart: async (options) => {
267
+ const { startBuiltinAgentsServer, startElectricAgentsDevEnvironment } = await loadStartModule();
467
268
  const started = await startElectricAgentsDevEnvironment();
468
269
  printStartedEnvironment(started);
469
270
  console.log(``);
package/dist/start.cjs ADDED
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
+ const node_fs = require_chunk.__toESM(require("node:fs"));
4
+ const node_path = require_chunk.__toESM(require("node:path"));
5
+ const node_url = require_chunk.__toESM(require("node:url"));
6
+ const node_child_process = require_chunk.__toESM(require("node:child_process"));
7
+ const __electric_ax_agents = require_chunk.__toESM(require("@electric-ax/agents"));
8
+
9
+ //#region src/start.ts
10
+ const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
11
+ const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
12
+ const DEFAULT_COMPOSE_PROJECT_NAME = `electric-agents`;
13
+ const DOCKER_COMPOSE_FILE = (0, node_url.fileURLToPath)(new URL(`../docker-compose.full.yml`, require("url").pathToFileURL(__filename).href));
14
+ function parseDotEnvValue(raw) {
15
+ const trimmed = raw.trim();
16
+ if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) return trimmed.slice(1, -1);
17
+ const hashIndex = trimmed.indexOf(`#`);
18
+ return hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex).trim();
19
+ }
20
+ function readDotEnvFile(cwd = process.cwd()) {
21
+ const envPath = (0, node_path.resolve)(cwd, `.env`);
22
+ try {
23
+ const content = (0, node_fs.readFileSync)(envPath, `utf8`);
24
+ const values = {};
25
+ for (const line of content.split(/\r?\n/)) {
26
+ const trimmed = line.trim();
27
+ if (!trimmed || trimmed.startsWith(`#`)) continue;
28
+ const equalsIndex = trimmed.indexOf(`=`);
29
+ if (equalsIndex <= 0) continue;
30
+ const key = trimmed.slice(0, equalsIndex).trim();
31
+ const value = parseDotEnvValue(trimmed.slice(equalsIndex + 1));
32
+ values[key] = value;
33
+ }
34
+ return values;
35
+ } catch (error) {
36
+ if (error.code === `ENOENT`) return {};
37
+ throw error;
38
+ }
39
+ }
40
+ function resolveAnthropicApiKey(options, env = process.env, fileEnv = readDotEnvFile()) {
41
+ const candidate = options.anthropicApiKey?.trim() || env.ANTHROPIC_API_KEY?.trim() || fileEnv.ANTHROPIC_API_KEY?.trim();
42
+ if (!candidate) throw new Error(`ANTHROPIC_API_KEY is required. Pass --anthropic-api-key, export it in your shell, or set it in .env.`);
43
+ return candidate;
44
+ }
45
+ function resolveBuiltinAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
46
+ const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
47
+ const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
48
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
49
+ return parsed;
50
+ }
51
+ function resolveElectricAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
52
+ const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
53
+ const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
54
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_PORT must be a positive integer`);
55
+ return parsed;
56
+ }
57
+ function getStartedEnvironmentMessage(started) {
58
+ return [
59
+ `Electric Agents dev environment is up.`,
60
+ `Server + UI: ${started.uiUrl}`,
61
+ `Docker project: ${started.composeProjectName}`
62
+ ].join(`\n`);
63
+ }
64
+ function getStoppedEnvironmentMessage(stopped) {
65
+ return [
66
+ `Electric Agents dev environment is down.`,
67
+ `Docker project: ${stopped.composeProjectName}`,
68
+ stopped.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
69
+ ].join(`\n`);
70
+ }
71
+ function getStartedBuiltinAgentsMessage(started) {
72
+ return [
73
+ `Builtin Horton server is up.`,
74
+ `Webhook server: ${started.url}`,
75
+ `Registers with: ${started.agentServerUrl}`,
76
+ `Press Ctrl-C to stop.`
77
+ ].join(`\n`);
78
+ }
79
+ function resolveComposeProjectName(_cwd = process.cwd(), env = process.env) {
80
+ const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
81
+ if (explicit) return explicit;
82
+ return DEFAULT_COMPOSE_PROJECT_NAME;
83
+ }
84
+ async function runDockerCompose(args, env) {
85
+ await new Promise((resolve, reject) => {
86
+ const child = (0, node_child_process.spawn)(`docker`, args, {
87
+ cwd: process.cwd(),
88
+ env,
89
+ stdio: `inherit`
90
+ });
91
+ child.on(`error`, (error) => {
92
+ reject(new Error(`Failed to run docker compose: ${error instanceof Error ? error.message : String(error)}`));
93
+ });
94
+ child.on(`exit`, (code) => {
95
+ if (code === 0) {
96
+ resolve();
97
+ return;
98
+ }
99
+ reject(new Error(`docker compose exited with code ${code ?? `unknown`}`));
100
+ });
101
+ });
102
+ }
103
+ function delay(ms) {
104
+ return new Promise((resolve) => {
105
+ setTimeout(resolve, ms);
106
+ });
107
+ }
108
+ async function waitForElectricAgentsServer(baseUrl, options = {}) {
109
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
110
+ const timeoutMs = options.timeoutMs ?? 6e4;
111
+ const intervalMs = options.intervalMs ?? 1e3;
112
+ const deadline = Date.now() + timeoutMs;
113
+ const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
114
+ let lastError = null;
115
+ while (Date.now() < deadline) {
116
+ try {
117
+ const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
118
+ if (response.ok) return;
119
+ lastError = `healthcheck returned ${response.status}`;
120
+ } catch (error) {
121
+ lastError = error instanceof Error ? error.message : String(error);
122
+ }
123
+ await delay(intervalMs);
124
+ }
125
+ throw new Error(`Timed out waiting for Electric Agents server at ${healthUrl}${lastError ? `: ${lastError}` : ``}`);
126
+ }
127
+ async function startElectricAgentsDevEnvironment(_options = {}, env = process.env, cwd = process.cwd()) {
128
+ const fileEnv = readDotEnvFile(cwd);
129
+ const port = resolveElectricAgentsPort(env, fileEnv);
130
+ const composeProjectName = resolveComposeProjectName(cwd, env);
131
+ await runDockerCompose([
132
+ `compose`,
133
+ `-f`,
134
+ DOCKER_COMPOSE_FILE,
135
+ `up`,
136
+ `-d`
137
+ ], {
138
+ ...env,
139
+ COMPOSE_PROJECT_NAME: composeProjectName,
140
+ ELECTRIC_AGENTS_PORT: String(port)
141
+ });
142
+ const uiUrl = `http://localhost:${port}`;
143
+ await waitForElectricAgentsServer(uiUrl);
144
+ return {
145
+ port,
146
+ uiUrl,
147
+ composeProjectName
148
+ };
149
+ }
150
+ async function stopElectricAgentsDevEnvironment(options, env = process.env, cwd = process.cwd()) {
151
+ const composeProjectName = resolveComposeProjectName(cwd, env);
152
+ const args = [
153
+ `compose`,
154
+ `-f`,
155
+ DOCKER_COMPOSE_FILE,
156
+ `down`
157
+ ];
158
+ if (options.removeVolumes) args.push(`--volumes`);
159
+ await runDockerCompose(args, {
160
+ ...env,
161
+ COMPOSE_PROJECT_NAME: composeProjectName
162
+ });
163
+ return {
164
+ composeProjectName,
165
+ removedVolumes: options.removeVolumes ?? false
166
+ };
167
+ }
168
+ function waitForShutdown(stop, signalSource = process) {
169
+ return new Promise((resolve, reject) => {
170
+ let stopping = false;
171
+ const cleanup = () => {
172
+ signalSource.off(`SIGINT`, onSigint);
173
+ signalSource.off(`SIGTERM`, onSigterm);
174
+ };
175
+ const shutdown = (signal) => {
176
+ if (stopping) return;
177
+ stopping = true;
178
+ cleanup();
179
+ stop().then(resolve).catch((error) => {
180
+ reject(new Error(`Failed to stop builtin agents server after ${signal}: ${error instanceof Error ? error.message : String(error)}`));
181
+ });
182
+ };
183
+ const onSigint = () => {
184
+ shutdown(`SIGINT`);
185
+ };
186
+ const onSigterm = () => {
187
+ shutdown(`SIGTERM`);
188
+ };
189
+ signalSource.on(`SIGINT`, onSigint);
190
+ signalSource.on(`SIGTERM`, onSigterm);
191
+ });
192
+ }
193
+ async function startBuiltinAgentsServer(options, params = {}) {
194
+ const env = params.env ?? process.env;
195
+ const cwd = params.cwd ?? process.cwd();
196
+ const fileEnv = readDotEnvFile(cwd);
197
+ const anthropicApiKey = resolveAnthropicApiKey(options, env, fileEnv);
198
+ const port = resolveBuiltinAgentsPort(env, fileEnv);
199
+ const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
200
+ process.env.ANTHROPIC_API_KEY = anthropicApiKey;
201
+ await waitForElectricAgentsServer(agentServerUrl);
202
+ const server = new __electric_ax_agents.BuiltinAgentsServer({
203
+ agentServerUrl,
204
+ port,
205
+ workingDirectory: cwd
206
+ });
207
+ await server.start();
208
+ const started = {
209
+ port,
210
+ url: server.url,
211
+ registeredBaseUrl: server.registeredBaseUrl,
212
+ agentServerUrl
213
+ };
214
+ console.log(getStartedBuiltinAgentsMessage(started));
215
+ await waitForShutdown(() => server.stop());
216
+ return started;
217
+ }
218
+
219
+ //#endregion
220
+ exports.getStartedBuiltinAgentsMessage = getStartedBuiltinAgentsMessage
221
+ exports.getStartedEnvironmentMessage = getStartedEnvironmentMessage
222
+ exports.getStoppedEnvironmentMessage = getStoppedEnvironmentMessage
223
+ exports.readDotEnvFile = readDotEnvFile
224
+ exports.resolveAnthropicApiKey = resolveAnthropicApiKey
225
+ exports.resolveBuiltinAgentsPort = resolveBuiltinAgentsPort
226
+ exports.resolveComposeProjectName = resolveComposeProjectName
227
+ exports.resolveElectricAgentsPort = resolveElectricAgentsPort
228
+ exports.startBuiltinAgentsServer = startBuiltinAgentsServer
229
+ exports.startElectricAgentsDevEnvironment = startElectricAgentsDevEnvironment
230
+ exports.stopElectricAgentsDevEnvironment = stopElectricAgentsDevEnvironment
231
+ exports.waitForElectricAgentsServer = waitForElectricAgentsServer
@@ -0,0 +1,2 @@
1
+ import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-CQaCUw1k.cjs";
2
+ export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
@@ -0,0 +1,2 @@
1
+ import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-CrW6PmwB.js";
2
+ export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
package/dist/start.js ADDED
@@ -0,0 +1,218 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { spawn } from "node:child_process";
5
+ import { BuiltinAgentsServer } from "@electric-ax/agents";
6
+
7
+ //#region src/start.ts
8
+ const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
9
+ const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
10
+ const DEFAULT_COMPOSE_PROJECT_NAME = `electric-agents`;
11
+ const DOCKER_COMPOSE_FILE = fileURLToPath(new URL(`../docker-compose.full.yml`, import.meta.url));
12
+ function parseDotEnvValue(raw) {
13
+ const trimmed = raw.trim();
14
+ if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) return trimmed.slice(1, -1);
15
+ const hashIndex = trimmed.indexOf(`#`);
16
+ return hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex).trim();
17
+ }
18
+ function readDotEnvFile(cwd = process.cwd()) {
19
+ const envPath = resolve(cwd, `.env`);
20
+ try {
21
+ const content = readFileSync(envPath, `utf8`);
22
+ const values = {};
23
+ for (const line of content.split(/\r?\n/)) {
24
+ const trimmed = line.trim();
25
+ if (!trimmed || trimmed.startsWith(`#`)) continue;
26
+ const equalsIndex = trimmed.indexOf(`=`);
27
+ if (equalsIndex <= 0) continue;
28
+ const key = trimmed.slice(0, equalsIndex).trim();
29
+ const value = parseDotEnvValue(trimmed.slice(equalsIndex + 1));
30
+ values[key] = value;
31
+ }
32
+ return values;
33
+ } catch (error) {
34
+ if (error.code === `ENOENT`) return {};
35
+ throw error;
36
+ }
37
+ }
38
+ function resolveAnthropicApiKey(options, env = process.env, fileEnv = readDotEnvFile()) {
39
+ const candidate = options.anthropicApiKey?.trim() || env.ANTHROPIC_API_KEY?.trim() || fileEnv.ANTHROPIC_API_KEY?.trim();
40
+ if (!candidate) throw new Error(`ANTHROPIC_API_KEY is required. Pass --anthropic-api-key, export it in your shell, or set it in .env.`);
41
+ return candidate;
42
+ }
43
+ function resolveBuiltinAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
44
+ const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
45
+ const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
46
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
47
+ return parsed;
48
+ }
49
+ function resolveElectricAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
50
+ const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
51
+ const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
52
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_PORT must be a positive integer`);
53
+ return parsed;
54
+ }
55
+ function getStartedEnvironmentMessage(started) {
56
+ return [
57
+ `Electric Agents dev environment is up.`,
58
+ `Server + UI: ${started.uiUrl}`,
59
+ `Docker project: ${started.composeProjectName}`
60
+ ].join(`\n`);
61
+ }
62
+ function getStoppedEnvironmentMessage(stopped) {
63
+ return [
64
+ `Electric Agents dev environment is down.`,
65
+ `Docker project: ${stopped.composeProjectName}`,
66
+ stopped.removedVolumes ? `Volumes removed: yes` : `Volumes removed: no`
67
+ ].join(`\n`);
68
+ }
69
+ function getStartedBuiltinAgentsMessage(started) {
70
+ return [
71
+ `Builtin Horton server is up.`,
72
+ `Webhook server: ${started.url}`,
73
+ `Registers with: ${started.agentServerUrl}`,
74
+ `Press Ctrl-C to stop.`
75
+ ].join(`\n`);
76
+ }
77
+ function resolveComposeProjectName(_cwd = process.cwd(), env = process.env) {
78
+ const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
79
+ if (explicit) return explicit;
80
+ return DEFAULT_COMPOSE_PROJECT_NAME;
81
+ }
82
+ async function runDockerCompose(args, env) {
83
+ await new Promise((resolve$1, reject) => {
84
+ const child = spawn(`docker`, args, {
85
+ cwd: process.cwd(),
86
+ env,
87
+ stdio: `inherit`
88
+ });
89
+ child.on(`error`, (error) => {
90
+ reject(new Error(`Failed to run docker compose: ${error instanceof Error ? error.message : String(error)}`));
91
+ });
92
+ child.on(`exit`, (code) => {
93
+ if (code === 0) {
94
+ resolve$1();
95
+ return;
96
+ }
97
+ reject(new Error(`docker compose exited with code ${code ?? `unknown`}`));
98
+ });
99
+ });
100
+ }
101
+ function delay(ms) {
102
+ return new Promise((resolve$1) => {
103
+ setTimeout(resolve$1, ms);
104
+ });
105
+ }
106
+ async function waitForElectricAgentsServer(baseUrl, options = {}) {
107
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
108
+ const timeoutMs = options.timeoutMs ?? 6e4;
109
+ const intervalMs = options.intervalMs ?? 1e3;
110
+ const deadline = Date.now() + timeoutMs;
111
+ const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
112
+ let lastError = null;
113
+ while (Date.now() < deadline) {
114
+ try {
115
+ const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
116
+ if (response.ok) return;
117
+ lastError = `healthcheck returned ${response.status}`;
118
+ } catch (error) {
119
+ lastError = error instanceof Error ? error.message : String(error);
120
+ }
121
+ await delay(intervalMs);
122
+ }
123
+ throw new Error(`Timed out waiting for Electric Agents server at ${healthUrl}${lastError ? `: ${lastError}` : ``}`);
124
+ }
125
+ async function startElectricAgentsDevEnvironment(_options = {}, env = process.env, cwd = process.cwd()) {
126
+ const fileEnv = readDotEnvFile(cwd);
127
+ const port = resolveElectricAgentsPort(env, fileEnv);
128
+ const composeProjectName = resolveComposeProjectName(cwd, env);
129
+ await runDockerCompose([
130
+ `compose`,
131
+ `-f`,
132
+ DOCKER_COMPOSE_FILE,
133
+ `up`,
134
+ `-d`
135
+ ], {
136
+ ...env,
137
+ COMPOSE_PROJECT_NAME: composeProjectName,
138
+ ELECTRIC_AGENTS_PORT: String(port)
139
+ });
140
+ const uiUrl = `http://localhost:${port}`;
141
+ await waitForElectricAgentsServer(uiUrl);
142
+ return {
143
+ port,
144
+ uiUrl,
145
+ composeProjectName
146
+ };
147
+ }
148
+ async function stopElectricAgentsDevEnvironment(options, env = process.env, cwd = process.cwd()) {
149
+ const composeProjectName = resolveComposeProjectName(cwd, env);
150
+ const args = [
151
+ `compose`,
152
+ `-f`,
153
+ DOCKER_COMPOSE_FILE,
154
+ `down`
155
+ ];
156
+ if (options.removeVolumes) args.push(`--volumes`);
157
+ await runDockerCompose(args, {
158
+ ...env,
159
+ COMPOSE_PROJECT_NAME: composeProjectName
160
+ });
161
+ return {
162
+ composeProjectName,
163
+ removedVolumes: options.removeVolumes ?? false
164
+ };
165
+ }
166
+ function waitForShutdown(stop, signalSource = process) {
167
+ return new Promise((resolve$1, reject) => {
168
+ let stopping = false;
169
+ const cleanup = () => {
170
+ signalSource.off(`SIGINT`, onSigint);
171
+ signalSource.off(`SIGTERM`, onSigterm);
172
+ };
173
+ const shutdown = (signal) => {
174
+ if (stopping) return;
175
+ stopping = true;
176
+ cleanup();
177
+ stop().then(resolve$1).catch((error) => {
178
+ reject(new Error(`Failed to stop builtin agents server after ${signal}: ${error instanceof Error ? error.message : String(error)}`));
179
+ });
180
+ };
181
+ const onSigint = () => {
182
+ shutdown(`SIGINT`);
183
+ };
184
+ const onSigterm = () => {
185
+ shutdown(`SIGTERM`);
186
+ };
187
+ signalSource.on(`SIGINT`, onSigint);
188
+ signalSource.on(`SIGTERM`, onSigterm);
189
+ });
190
+ }
191
+ async function startBuiltinAgentsServer(options, params = {}) {
192
+ const env = params.env ?? process.env;
193
+ const cwd = params.cwd ?? process.cwd();
194
+ const fileEnv = readDotEnvFile(cwd);
195
+ const anthropicApiKey = resolveAnthropicApiKey(options, env, fileEnv);
196
+ const port = resolveBuiltinAgentsPort(env, fileEnv);
197
+ const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
198
+ process.env.ANTHROPIC_API_KEY = anthropicApiKey;
199
+ await waitForElectricAgentsServer(agentServerUrl);
200
+ const server = new BuiltinAgentsServer({
201
+ agentServerUrl,
202
+ port,
203
+ workingDirectory: cwd
204
+ });
205
+ await server.start();
206
+ const started = {
207
+ port,
208
+ url: server.url,
209
+ registeredBaseUrl: server.registeredBaseUrl,
210
+ agentServerUrl
211
+ };
212
+ console.log(getStartedBuiltinAgentsMessage(started));
213
+ await waitForShutdown(() => server.stop());
214
+ return started;
215
+ }
216
+
217
+ //#endregion
218
+ export { getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
+ const react = require_chunk.__toESM(require("react"));
4
+ const ink = require_chunk.__toESM(require("ink"));
5
+ const react_jsx_runtime = require_chunk.__toESM(require("react/jsx-runtime"));
6
+
7
+ //#region src/types-table.tsx
8
+ function TypesTable({ types }) {
9
+ const groups = new Map();
10
+ for (const t of types) {
11
+ const server = t.serve_endpoint ?? `built-in`;
12
+ if (!groups.has(server)) groups.set(server, []);
13
+ groups.get(server).push(t);
14
+ }
15
+ const entries = Array.from(groups.entries());
16
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
17
+ flexDirection: "column",
18
+ children: entries.map(([server, serverTypes], i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
19
+ flexDirection: "column",
20
+ marginTop: i > 0 ? 1 : 0,
21
+ children: [
22
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
23
+ bold: true,
24
+ dimColor: true,
25
+ children: server === `built-in` ? `Built-in agents` : server
26
+ }),
27
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
28
+ width: 25,
29
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
30
+ bold: true,
31
+ children: "NAME"
32
+ })
33
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
34
+ bold: true,
35
+ children: "DESCRIPTION"
36
+ })] }),
37
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
38
+ width: 25,
39
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
40
+ dimColor: true,
41
+ children: `─`.repeat(23)
42
+ })
43
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
44
+ dimColor: true,
45
+ children: `─`.repeat(40)
46
+ })] }),
47
+ serverTypes.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
48
+ width: 25,
49
+ flexShrink: 0,
50
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, { children: t.name })
51
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
52
+ flexGrow: 1,
53
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
54
+ wrap: "wrap",
55
+ children: t.description
56
+ })
57
+ })] }, t.name))
58
+ ]
59
+ }, server))
60
+ });
61
+ }
62
+ function renderTypesTable(types) {
63
+ console.log((0, ink.renderToString)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TypesTable, { types })));
64
+ }
65
+
66
+ //#endregion
67
+ exports.renderTypesTable = renderTypesTable
@@ -0,0 +1,10 @@
1
+ //#region src/types-table.d.ts
2
+ interface EntityType {
3
+ name: string;
4
+ description: string;
5
+ serve_endpoint?: string;
6
+ }
7
+ declare function renderTypesTable(types: Array<EntityType>): void;
8
+
9
+ //#endregion
10
+ export { renderTypesTable };
@@ -0,0 +1,10 @@
1
+ //#region src/types-table.d.ts
2
+ interface EntityType {
3
+ name: string;
4
+ description: string;
5
+ serve_endpoint?: string;
6
+ }
7
+ declare function renderTypesTable(types: Array<EntityType>): void;
8
+
9
+ //#endregion
10
+ export { renderTypesTable };
@@ -0,0 +1,65 @@
1
+ import React from "react";
2
+ import { Box, Text, renderToString } from "ink";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+
5
+ //#region src/types-table.tsx
6
+ function TypesTable({ types }) {
7
+ const groups = new Map();
8
+ for (const t of types) {
9
+ const server = t.serve_endpoint ?? `built-in`;
10
+ if (!groups.has(server)) groups.set(server, []);
11
+ groups.get(server).push(t);
12
+ }
13
+ const entries = Array.from(groups.entries());
14
+ return /* @__PURE__ */ jsx(Box, {
15
+ flexDirection: "column",
16
+ children: entries.map(([server, serverTypes], i) => /* @__PURE__ */ jsxs(Box, {
17
+ flexDirection: "column",
18
+ marginTop: i > 0 ? 1 : 0,
19
+ children: [
20
+ /* @__PURE__ */ jsx(Text, {
21
+ bold: true,
22
+ dimColor: true,
23
+ children: server === `built-in` ? `Built-in agents` : server
24
+ }),
25
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Box, {
26
+ width: 25,
27
+ children: /* @__PURE__ */ jsx(Text, {
28
+ bold: true,
29
+ children: "NAME"
30
+ })
31
+ }), /* @__PURE__ */ jsx(Text, {
32
+ bold: true,
33
+ children: "DESCRIPTION"
34
+ })] }),
35
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Box, {
36
+ width: 25,
37
+ children: /* @__PURE__ */ jsx(Text, {
38
+ dimColor: true,
39
+ children: `─`.repeat(23)
40
+ })
41
+ }), /* @__PURE__ */ jsx(Text, {
42
+ dimColor: true,
43
+ children: `─`.repeat(40)
44
+ })] }),
45
+ serverTypes.map((t) => /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Box, {
46
+ width: 25,
47
+ flexShrink: 0,
48
+ children: /* @__PURE__ */ jsx(Text, { children: t.name })
49
+ }), /* @__PURE__ */ jsx(Box, {
50
+ flexGrow: 1,
51
+ children: /* @__PURE__ */ jsx(Text, {
52
+ wrap: "wrap",
53
+ children: t.description
54
+ })
55
+ })] }, t.name))
56
+ ]
57
+ }, server))
58
+ });
59
+ }
60
+ function renderTypesTable(types) {
61
+ console.log(renderToString(/* @__PURE__ */ jsx(TypesTable, { types })));
62
+ }
63
+
64
+ //#endregion
65
+ export { renderTypesTable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electric-ax",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "CLI for Electric Agents",
5
5
  "author": "ElectricSQL team and contributors",
6
6
  "license": "Apache-2.0",