electric-ax 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { ElectricCliEnv } from "./index-B2MsxFCW.cjs";
1
+ import { ElectricCliEnv } from "./index-BQ4JyfkG.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-BDO8dHhT.js";
1
+ import { ElectricCliEnv } from "./index-D6TZ88Yg.js";
2
2
 
3
3
  //#region src/completions.d.ts
4
4
  declare function fetchEntityTypeNames(env: ElectricCliEnv): Promise<Array<string>>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
+ const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
4
+
5
+ //#region src/entity-api.ts
6
+ function withLeadingSlash(path) {
7
+ return path.startsWith(`/`) ? path : `/${path}`;
8
+ }
9
+ function entityApiPath(entityUrl, suffix = ``) {
10
+ return `/_electric/entities${withLeadingSlash(entityUrl)}${suffix}`;
11
+ }
12
+ function entityApiUrl(baseUrl, entityUrl, suffix = ``) {
13
+ return (0, __electric_ax_agents_runtime.appendPathToUrl)(baseUrl, entityApiPath(entityUrl, suffix));
14
+ }
15
+ function assertedIdentityHeaders(identity) {
16
+ const trimmed = identity?.trim();
17
+ return trimmed ? { "x-electric-asserted-email": trimmed } : {};
18
+ }
19
+
20
+ //#endregion
21
+ Object.defineProperty(exports, 'assertedIdentityHeaders', {
22
+ enumerable: true,
23
+ get: function () {
24
+ return assertedIdentityHeaders;
25
+ }
26
+ });
27
+ Object.defineProperty(exports, 'entityApiPath', {
28
+ enumerable: true,
29
+ get: function () {
30
+ return entityApiPath;
31
+ }
32
+ });
33
+ Object.defineProperty(exports, 'entityApiUrl', {
34
+ enumerable: true,
35
+ get: function () {
36
+ return entityApiUrl;
37
+ }
38
+ });
@@ -0,0 +1,19 @@
1
+ import { appendPathToUrl } from "@electric-ax/agents-runtime";
2
+
3
+ //#region src/entity-api.ts
4
+ function withLeadingSlash(path) {
5
+ return path.startsWith(`/`) ? path : `/${path}`;
6
+ }
7
+ function entityApiPath(entityUrl, suffix = ``) {
8
+ return `/_electric/entities${withLeadingSlash(entityUrl)}${suffix}`;
9
+ }
10
+ function entityApiUrl(baseUrl, entityUrl, suffix = ``) {
11
+ return appendPathToUrl(baseUrl, entityApiPath(entityUrl, suffix));
12
+ }
13
+ function assertedIdentityHeaders(identity) {
14
+ const trimmed = identity?.trim();
15
+ return trimmed ? { "x-electric-asserted-email": trimmed } : {};
16
+ }
17
+
18
+ //#endregion
19
+ export { assertedIdentityHeaders, entityApiPath, entityApiUrl };
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
+ const require_entity_api = require('./entity-api-ClpnLxWJ.cjs');
3
4
  const __durable_streams_state = require_chunk.__toESM(require("@durable-streams/state"));
4
5
  const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
5
6
 
@@ -8,21 +9,28 @@ function getMainStreamPath(entityUrl, entity) {
8
9
  return entity.streams?.main ?? `${entityUrl}/main`;
9
10
  }
10
11
  async function createEntityStreamDB(opts) {
11
- const { baseUrl, entityUrl, initialOffset } = opts;
12
+ const { baseUrl, entityUrl, initialOffset, assertedAuthEmail, assertedAuthName, headers: serverHeaders } = opts;
12
13
  console.log(`[createEntityStreamDB] Creating entity stream DB for ${baseUrl}${entityUrl}`);
14
+ const requestHeaders = {
15
+ "content-type": `application/json`,
16
+ ...require_entity_api.assertedIdentityHeaders(assertedAuthEmail),
17
+ ...assertedAuthName ? { "x-electric-asserted-name": assertedAuthName } : {},
18
+ ...serverHeaders
19
+ };
13
20
  let res;
14
21
  try {
15
- res = await fetch(`${baseUrl}${entityUrl}`, { headers: { "content-type": `application/json` } });
22
+ res = await fetch(require_entity_api.entityApiUrl(baseUrl, entityUrl), { headers: requestHeaders });
16
23
  } catch (err) {
17
24
  throw new Error(`Could not connect to the Electric Agents server at ${baseUrl} — is it running?\n ${err instanceof Error ? err.message : String(err)}`);
18
25
  }
19
26
  if (!res.ok) throw new Error(`Failed to fetch entity at ${entityUrl}: ${res.statusText}`);
20
27
  const entity = await res.json();
21
28
  const streamPath = getMainStreamPath(entityUrl, entity);
22
- const streamUrl = `${baseUrl}${streamPath}`;
29
+ const streamUrl = (0, __electric_ax_agents_runtime.appendPathToUrl)(baseUrl, streamPath);
23
30
  const db = (0, __durable_streams_state.createStreamDB)({
24
31
  streamOptions: {
25
32
  url: streamUrl,
33
+ headers: requestHeaders,
26
34
  contentType: `application/json`,
27
35
  ...initialOffset ? { offset: initialOffset } : {}
28
36
  },
@@ -6,8 +6,11 @@ declare function createEntityStreamDB(opts: {
6
6
  baseUrl: string;
7
7
  entityUrl: string;
8
8
  initialOffset?: string;
9
+ assertedAuthEmail?: string;
10
+ assertedAuthName?: string;
11
+ headers?: Record<string, string>;
9
12
  }): Promise<{
10
13
  db: EntityStreamDB;
11
14
  close: () => void;
12
15
  }>; //#endregion
13
- export { EntityStreamDB$1 as EntityStreamDB, createEntityStreamDB as createEntityStreamDB$1 };
16
+ export { EntityStreamDB$1 as EntityStreamDB, createEntityStreamDB };
@@ -1,26 +1,34 @@
1
+ import { assertedIdentityHeaders, entityApiUrl } from "./entity-api-Dh28Mz7H.js";
1
2
  import { createStreamDB } from "@durable-streams/state";
2
- import { entityStateSchema } from "@electric-ax/agents-runtime";
3
+ import { appendPathToUrl, entityStateSchema } from "@electric-ax/agents-runtime";
3
4
 
4
5
  //#region src/entity-stream-db.ts
5
6
  function getMainStreamPath(entityUrl, entity) {
6
7
  return entity.streams?.main ?? `${entityUrl}/main`;
7
8
  }
8
9
  async function createEntityStreamDB(opts) {
9
- const { baseUrl, entityUrl, initialOffset } = opts;
10
+ const { baseUrl, entityUrl, initialOffset, assertedAuthEmail, assertedAuthName, headers: serverHeaders } = opts;
10
11
  console.log(`[createEntityStreamDB] Creating entity stream DB for ${baseUrl}${entityUrl}`);
12
+ const requestHeaders = {
13
+ "content-type": `application/json`,
14
+ ...assertedIdentityHeaders(assertedAuthEmail),
15
+ ...assertedAuthName ? { "x-electric-asserted-name": assertedAuthName } : {},
16
+ ...serverHeaders
17
+ };
11
18
  let res;
12
19
  try {
13
- res = await fetch(`${baseUrl}${entityUrl}`, { headers: { "content-type": `application/json` } });
20
+ res = await fetch(entityApiUrl(baseUrl, entityUrl), { headers: requestHeaders });
14
21
  } catch (err) {
15
22
  throw new Error(`Could not connect to the Electric Agents server at ${baseUrl} — is it running?\n ${err instanceof Error ? err.message : String(err)}`);
16
23
  }
17
24
  if (!res.ok) throw new Error(`Failed to fetch entity at ${entityUrl}: ${res.statusText}`);
18
25
  const entity = await res.json();
19
26
  const streamPath = getMainStreamPath(entityUrl, entity);
20
- const streamUrl = `${baseUrl}${streamPath}`;
27
+ const streamUrl = appendPathToUrl(baseUrl, streamPath);
21
28
  const db = createStreamDB({
22
29
  streamOptions: {
23
30
  url: streamUrl,
31
+ headers: requestHeaders,
24
32
  contentType: `application/json`,
25
33
  ...initialOffset ? { offset: initialOffset } : {}
26
34
  },
@@ -6,8 +6,11 @@ declare function createEntityStreamDB(opts: {
6
6
  baseUrl: string;
7
7
  entityUrl: string;
8
8
  initialOffset?: string;
9
+ assertedAuthEmail?: string;
10
+ assertedAuthName?: string;
11
+ headers?: Record<string, string>;
9
12
  }): Promise<{
10
13
  db: EntityStreamDB;
11
14
  close: () => void;
12
15
  }>; //#endregion
13
- export { EntityStreamDB$1 as EntityStreamDB, createEntityStreamDB };
16
+ export { EntityStreamDB$1 as EntityStreamDB, createEntityStreamDB as createEntityStreamDB$1 };
@@ -1,3 +1,4 @@
1
- const require_entity_stream_db = require('./entity-stream-db-Djo-7a11.cjs');
1
+ require('./entity-api-ClpnLxWJ.cjs');
2
+ const require_entity_stream_db = require('./entity-stream-db-BLbPedyR.cjs');
2
3
 
3
4
  exports.createEntityStreamDB = require_entity_stream_db.createEntityStreamDB
@@ -1,2 +1,2 @@
1
- import { EntityStreamDB, createEntityStreamDB } from "./entity-stream-db-C2C3t_hD.cjs";
1
+ import { EntityStreamDB, createEntityStreamDB } from "./entity-stream-db-CmDwc49J.cjs";
2
2
  export { EntityStreamDB, createEntityStreamDB };
@@ -1,2 +1,2 @@
1
- import { EntityStreamDB, createEntityStreamDB$1 as createEntityStreamDB } from "./entity-stream-db-BRwzIuHl.js";
1
+ import { EntityStreamDB, createEntityStreamDB$1 as createEntityStreamDB } from "./entity-stream-db-DgzxI6C_.js";
2
2
  export { EntityStreamDB, createEntityStreamDB };
@@ -1,3 +1,4 @@
1
- import { createEntityStreamDB } from "./entity-stream-db-CGP2xVeJ.js";
1
+ import "./entity-api-Dh28Mz7H.js";
2
+ import { createEntityStreamDB } from "./entity-stream-db-Czz3S8YF.js";
2
3
 
3
4
  export { createEntityStreamDB };
@@ -19,22 +19,22 @@ interface StoppedDevEnvironment {
19
19
  removedVolumes: boolean;
20
20
  }
21
21
  interface StartedBuiltinAgentsEnvironment {
22
- port: number;
22
+ runnerId: string;
23
23
  url: string;
24
- registeredBaseUrl: string;
25
24
  agentServerUrl: string;
26
25
  }
27
26
  interface WaitForServerOptions {
28
27
  fetchImpl?: typeof globalThis.fetch;
28
+ headers?: Record<string, string>;
29
29
  timeoutMs?: number;
30
30
  intervalMs?: number;
31
31
  }
32
- declare function resolveBuiltinAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
33
- declare function resolveBuiltinAgentsHost(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
34
32
  declare function resolveElectricAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
35
33
  declare function getStartedEnvironmentMessage(started: StartedDevEnvironment): string;
36
34
  declare function getStoppedEnvironmentMessage(stopped: StoppedDevEnvironment): string;
37
35
  declare function getStartedBuiltinAgentsMessage(started: StartedBuiltinAgentsEnvironment): string;
36
+ declare function resolvePullWakeRunnerId(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
37
+ declare function resolvePullWakeOwnerId(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
38
38
  declare function resolveComposeProjectName(_cwd?: string, env?: NodeJS.ProcessEnv): string;
39
39
  declare function waitForElectricAgentsServer(baseUrl: string, options?: WaitForServerOptions): Promise<void>;
40
40
  declare function startElectricAgentsDevEnvironment(_options?: StartCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StartedDevEnvironment>;
@@ -52,6 +52,9 @@ declare const DEFAULT_ELECTRIC_AGENTS_URL = "http://localhost:4437";
52
52
  interface ElectricCliEnv {
53
53
  electricAgentsUrl: string;
54
54
  electricAgentsIdentity: string;
55
+ electricAssertedAuthEmail?: string;
56
+ electricAssertedAuthName?: string;
57
+ electricAgentsHeaders?: Record<string, string>;
55
58
  }
56
59
  interface SpawnCommandOptions {
57
60
  args?: string;
@@ -124,4 +127,4 @@ declare function createElectricProgram({
124
127
  declare function run(argv?: Array<string>): Promise<void>;
125
128
 
126
129
  //#endregion
127
- export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
130
+ export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
@@ -19,22 +19,22 @@ interface StoppedDevEnvironment {
19
19
  removedVolumes: boolean;
20
20
  }
21
21
  interface StartedBuiltinAgentsEnvironment {
22
- port: number;
22
+ runnerId: string;
23
23
  url: string;
24
- registeredBaseUrl: string;
25
24
  agentServerUrl: string;
26
25
  }
27
26
  interface WaitForServerOptions {
28
27
  fetchImpl?: typeof globalThis.fetch;
28
+ headers?: Record<string, string>;
29
29
  timeoutMs?: number;
30
30
  intervalMs?: number;
31
31
  }
32
- declare function resolveBuiltinAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
33
- declare function resolveBuiltinAgentsHost(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
34
32
  declare function resolveElectricAgentsPort(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): number;
35
33
  declare function getStartedEnvironmentMessage(started: StartedDevEnvironment): string;
36
34
  declare function getStoppedEnvironmentMessage(stopped: StoppedDevEnvironment): string;
37
35
  declare function getStartedBuiltinAgentsMessage(started: StartedBuiltinAgentsEnvironment): string;
36
+ declare function resolvePullWakeRunnerId(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
37
+ declare function resolvePullWakeOwnerId(env?: NodeJS.ProcessEnv, fileEnv?: Record<string, string>): string;
38
38
  declare function resolveComposeProjectName(_cwd?: string, env?: NodeJS.ProcessEnv): string;
39
39
  declare function waitForElectricAgentsServer(baseUrl: string, options?: WaitForServerOptions): Promise<void>;
40
40
  declare function startElectricAgentsDevEnvironment(_options?: StartCommandOptions, env?: NodeJS.ProcessEnv, cwd?: string): Promise<StartedDevEnvironment>;
@@ -52,6 +52,9 @@ declare const DEFAULT_ELECTRIC_AGENTS_URL = "http://localhost:4437";
52
52
  interface ElectricCliEnv {
53
53
  electricAgentsUrl: string;
54
54
  electricAgentsIdentity: string;
55
+ electricAssertedAuthEmail?: string;
56
+ electricAssertedAuthName?: string;
57
+ electricAgentsHeaders?: Record<string, string>;
55
58
  }
56
59
  interface SpawnCommandOptions {
57
60
  args?: string;
@@ -124,4 +127,4 @@ declare function createElectricProgram({
124
127
  declare function run(argv?: Array<string>): Promise<void>;
125
128
 
126
129
  //#endregion
127
- export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
130
+ export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveCommandPrefix, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, run, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
package/dist/index.cjs CHANGED
@@ -2,7 +2,9 @@
2
2
  "use strict";
3
3
  const require_chunk = require('./chunk-BCwAaXi7.cjs');
4
4
  const require_completions = require('./completions-Bj4D5DRa.cjs');
5
- const require_env = require('./env-BXUgom_C.cjs');
5
+ const require_entity_api = require('./entity-api-ClpnLxWJ.cjs');
6
+ const require_env = require('./env-CFKgT8o2.cjs');
7
+ const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
6
8
  const node_fs = require_chunk.__toESM(require("node:fs"));
7
9
  const node_os = require_chunk.__toESM(require("node:os"));
8
10
  const node_path = require_chunk.__toESM(require("node:path"));
@@ -140,10 +142,32 @@ var CliError = class extends Error {};
140
142
  function getDefaultElectricAgentsIdentity() {
141
143
  return `${(0, node_os.userInfo)().username}@${(0, node_os.hostname)()}`;
142
144
  }
145
+ function parseElectricAgentsHeaders(raw) {
146
+ const trimmed = raw?.trim();
147
+ if (!trimmed) return void 0;
148
+ let parsed;
149
+ try {
150
+ parsed = JSON.parse(trimmed);
151
+ } catch {
152
+ fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected JSON`);
153
+ }
154
+ if (!parsed || typeof parsed !== `object` || Array.isArray(parsed)) fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected a JSON object`);
155
+ const headers = new Headers();
156
+ for (const [name, value] of Object.entries(parsed)) {
157
+ if (typeof value !== `string`) fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: header "${name}" must be a string`);
158
+ headers.set(name, value);
159
+ }
160
+ const normalized = Object.fromEntries(headers.entries());
161
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
162
+ }
143
163
  function getElectricCliEnv(env = process.env) {
164
+ const explicitIdentity = env.ELECTRIC_AGENTS_IDENTITY?.trim();
144
165
  return {
145
166
  electricAgentsUrl: env.ELECTRIC_AGENTS_URL || DEFAULT_ELECTRIC_AGENTS_URL,
146
- electricAgentsIdentity: env.ELECTRIC_AGENTS_IDENTITY || getDefaultElectricAgentsIdentity()
167
+ electricAgentsIdentity: explicitIdentity || getDefaultElectricAgentsIdentity(),
168
+ electricAssertedAuthEmail: env.ELECTRIC_ASSERTED_AUTH_EMAIL?.trim() || explicitIdentity || void 0,
169
+ electricAssertedAuthName: env.ELECTRIC_ASSERTED_AUTH_NAME?.trim(),
170
+ electricAgentsHeaders: parseElectricAgentsHeaders(env.ELECTRIC_AGENTS_SERVER_HEADERS)
147
171
  };
148
172
  }
149
173
  function getErrorMessage(error) {
@@ -270,10 +294,13 @@ function resolveCommandPrefix(argv, env = process.env) {
270
294
  }
271
295
  async function electricAgentsFetch(env, path, opts = {}) {
272
296
  try {
273
- return await fetch(`${env.electricAgentsUrl}${path}`, {
297
+ return await fetch((0, __electric_ax_agents_runtime.appendPathToUrl)(env.electricAgentsUrl, path), {
274
298
  ...opts,
275
299
  headers: {
276
300
  "content-type": `application/json`,
301
+ ...require_entity_api.assertedIdentityHeaders(env.electricAssertedAuthEmail),
302
+ ...env.electricAssertedAuthName ? { "x-electric-asserted-name": env.electricAssertedAuthName } : {},
303
+ ...env.electricAgentsHeaders,
277
304
  ...opts.headers
278
305
  }
279
306
  });
@@ -348,7 +375,7 @@ async function spawnEntity(env, urlPath, options) {
348
375
  } catch (error) {
349
376
  fail(`--args must be valid JSON: ${getErrorMessage(error)}`);
350
377
  }
351
- const res = await electricAgentsFetch(env, urlPath, {
378
+ const res = await electricAgentsFetch(env, require_entity_api.entityApiPath(urlPath), {
352
379
  method: `PUT`,
353
380
  body: JSON.stringify({ args: spawnArgs })
354
381
  });
@@ -365,7 +392,7 @@ async function sendMessage(env, url, message, options) {
365
392
  payload
366
393
  };
367
394
  if (options.type) body.type = options.type;
368
- const res = await electricAgentsFetch(env, `${url}/send`, {
395
+ const res = await electricAgentsFetch(env, require_entity_api.entityApiPath(url, `/send`), {
369
396
  method: `POST`,
370
397
  body: JSON.stringify(body)
371
398
  });
@@ -382,11 +409,14 @@ async function observeEntity(env, url, options) {
382
409
  entityUrl: url,
383
410
  baseUrl: env.electricAgentsUrl,
384
411
  identity: env.electricAgentsIdentity,
412
+ assertedAuthEmail: env.electricAssertedAuthEmail,
413
+ assertedAuthName: env.electricAssertedAuthName,
414
+ headers: env.electricAgentsHeaders,
385
415
  initialOffset: options.from
386
416
  });
387
417
  }
388
418
  async function inspectEntity(env, url) {
389
- const res = await electricAgentsFetch(env, url);
419
+ const res = await electricAgentsFetch(env, require_entity_api.entityApiPath(url));
390
420
  const data = await parseJsonResponse(res);
391
421
  if (!res.ok) failFromResponse(data, res);
392
422
  console.log(JSON.stringify(data, null, 2));
@@ -411,7 +441,7 @@ async function listEntities(env, options) {
411
441
  }
412
442
  }
413
443
  async function killEntity(env, url) {
414
- const res = await electricAgentsFetch(env, url, { method: `DELETE` });
444
+ const res = await electricAgentsFetch(env, require_entity_api.entityApiPath(url), { method: `DELETE` });
415
445
  if (!res.ok) {
416
446
  const data = await parseJsonResponse(res);
417
447
  failFromResponse(data, res);
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, formatQuickstartBackendStartedMessage, getElectricCliEnv, resolveCommandPrefix, run } from "./index-B2MsxFCW.cjs";
2
+ import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, resolveCommandPrefix, run } from "./index-BQ4JyfkG.cjs";
3
3
  export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, 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, formatQuickstartBackendStartedMessage, getElectricCliEnv, resolveCommandPrefix, run } from "./index-BDO8dHhT.js";
2
+ import { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, resolveCommandPrefix, run } from "./index-D6TZ88Yg.js";
3
3
  export { DEFAULT_ELECTRIC_AGENTS_URL, ElectricCliEnv, ElectricCliHandlers, ObserveCommandOptions, PsCommandOptions, SendCommandOptions, SpawnCommandOptions, StartBuiltinCommandOptions, StartCommandOptions, StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StopCommandOptions, StoppedDevEnvironment, createElectricCliHandlers, createElectricProgram, formatQuickstartBackendStartedMessage, getElectricCliEnv, resolveCommandPrefix, run };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { installCompletions, setupCompletions } from "./completions-B-Lf28Wy.js";
3
- import { resolveAnthropicApiKey$1 as resolveAnthropicApiKey } from "./env-LZtIfFz1.js";
3
+ import { assertedIdentityHeaders, entityApiPath } from "./entity-api-Dh28Mz7H.js";
4
+ import { resolveAnthropicApiKey$1 as resolveAnthropicApiKey } from "./env-DIhTv987.js";
5
+ import { appendPathToUrl } from "@electric-ax/agents-runtime";
4
6
  import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
5
7
  import { hostname, userInfo } from "node:os";
6
8
  import { basename, resolve } from "node:path";
@@ -138,10 +140,32 @@ var CliError = class extends Error {};
138
140
  function getDefaultElectricAgentsIdentity() {
139
141
  return `${userInfo().username}@${hostname()}`;
140
142
  }
143
+ function parseElectricAgentsHeaders(raw) {
144
+ const trimmed = raw?.trim();
145
+ if (!trimmed) return void 0;
146
+ let parsed;
147
+ try {
148
+ parsed = JSON.parse(trimmed);
149
+ } catch {
150
+ fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected JSON`);
151
+ }
152
+ if (!parsed || typeof parsed !== `object` || Array.isArray(parsed)) fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected a JSON object`);
153
+ const headers = new Headers();
154
+ for (const [name, value] of Object.entries(parsed)) {
155
+ if (typeof value !== `string`) fail(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: header "${name}" must be a string`);
156
+ headers.set(name, value);
157
+ }
158
+ const normalized = Object.fromEntries(headers.entries());
159
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
160
+ }
141
161
  function getElectricCliEnv(env = process.env) {
162
+ const explicitIdentity = env.ELECTRIC_AGENTS_IDENTITY?.trim();
142
163
  return {
143
164
  electricAgentsUrl: env.ELECTRIC_AGENTS_URL || DEFAULT_ELECTRIC_AGENTS_URL,
144
- electricAgentsIdentity: env.ELECTRIC_AGENTS_IDENTITY || getDefaultElectricAgentsIdentity()
165
+ electricAgentsIdentity: explicitIdentity || getDefaultElectricAgentsIdentity(),
166
+ electricAssertedAuthEmail: env.ELECTRIC_ASSERTED_AUTH_EMAIL?.trim() || explicitIdentity || void 0,
167
+ electricAssertedAuthName: env.ELECTRIC_ASSERTED_AUTH_NAME?.trim(),
168
+ electricAgentsHeaders: parseElectricAgentsHeaders(env.ELECTRIC_AGENTS_SERVER_HEADERS)
145
169
  };
146
170
  }
147
171
  function getErrorMessage(error) {
@@ -268,10 +292,13 @@ function resolveCommandPrefix(argv, env = process.env) {
268
292
  }
269
293
  async function electricAgentsFetch(env, path$1, opts = {}) {
270
294
  try {
271
- return await fetch(`${env.electricAgentsUrl}${path$1}`, {
295
+ return await fetch(appendPathToUrl(env.electricAgentsUrl, path$1), {
272
296
  ...opts,
273
297
  headers: {
274
298
  "content-type": `application/json`,
299
+ ...assertedIdentityHeaders(env.electricAssertedAuthEmail),
300
+ ...env.electricAssertedAuthName ? { "x-electric-asserted-name": env.electricAssertedAuthName } : {},
301
+ ...env.electricAgentsHeaders,
275
302
  ...opts.headers
276
303
  }
277
304
  });
@@ -346,7 +373,7 @@ async function spawnEntity(env, urlPath, options) {
346
373
  } catch (error) {
347
374
  fail(`--args must be valid JSON: ${getErrorMessage(error)}`);
348
375
  }
349
- const res = await electricAgentsFetch(env, urlPath, {
376
+ const res = await electricAgentsFetch(env, entityApiPath(urlPath), {
350
377
  method: `PUT`,
351
378
  body: JSON.stringify({ args: spawnArgs })
352
379
  });
@@ -363,7 +390,7 @@ async function sendMessage(env, url, message, options) {
363
390
  payload
364
391
  };
365
392
  if (options.type) body.type = options.type;
366
- const res = await electricAgentsFetch(env, `${url}/send`, {
393
+ const res = await electricAgentsFetch(env, entityApiPath(url, `/send`), {
367
394
  method: `POST`,
368
395
  body: JSON.stringify(body)
369
396
  });
@@ -380,11 +407,14 @@ async function observeEntity(env, url, options) {
380
407
  entityUrl: url,
381
408
  baseUrl: env.electricAgentsUrl,
382
409
  identity: env.electricAgentsIdentity,
410
+ assertedAuthEmail: env.electricAssertedAuthEmail,
411
+ assertedAuthName: env.electricAssertedAuthName,
412
+ headers: env.electricAgentsHeaders,
383
413
  initialOffset: options.from
384
414
  });
385
415
  }
386
416
  async function inspectEntity(env, url) {
387
- const res = await electricAgentsFetch(env, url);
417
+ const res = await electricAgentsFetch(env, entityApiPath(url));
388
418
  const data = await parseJsonResponse(res);
389
419
  if (!res.ok) failFromResponse(data, res);
390
420
  console.log(JSON.stringify(data, null, 2));
@@ -409,7 +439,7 @@ async function listEntities(env, options) {
409
439
  }
410
440
  }
411
441
  async function killEntity(env, url) {
412
- const res = await electricAgentsFetch(env, url, { method: `DELETE` });
442
+ const res = await electricAgentsFetch(env, entityApiPath(url), { method: `DELETE` });
413
443
  if (!res.ok) {
414
444
  const data = await parseJsonResponse(res);
415
445
  failFromResponse(data, res);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
- const require_entity_stream_db = require('./entity-stream-db-Djo-7a11.cjs');
3
+ const require_entity_api = require('./entity-api-ClpnLxWJ.cjs');
4
+ const require_entity_stream_db = require('./entity-stream-db-BLbPedyR.cjs');
4
5
  const __durable_streams_state = require_chunk.__toESM(require("@durable-streams/state"));
5
6
  const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
6
7
  const react = require_chunk.__toESM(require("react"));
@@ -118,7 +119,7 @@ function ToolResultView({ result }) {
118
119
  }) : null]
119
120
  });
120
121
  }
121
- function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
122
+ function MessageInput({ db, baseUrl, entityUrl, identity, assertedAuthEmail, assertedAuthName, headers, disabled }) {
122
123
  const [value, setValue] = (0, react.useState)(``);
123
124
  const [error, setError] = (0, react.useState)(null);
124
125
  const sendAction = (0, react.useMemo)(() => (0, __durable_streams_state.createOptimisticAction)({
@@ -131,9 +132,14 @@ function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
131
132
  });
132
133
  },
133
134
  mutationFn: async ({ text }) => {
134
- const res = await fetch(`${baseUrl}${entityUrl}/send`, {
135
+ const res = await fetch(require_entity_api.entityApiUrl(baseUrl, entityUrl, `/send`), {
135
136
  method: `POST`,
136
- headers: { "content-type": `application/json` },
137
+ headers: {
138
+ "content-type": `application/json`,
139
+ ...require_entity_api.assertedIdentityHeaders(assertedAuthEmail),
140
+ ...assertedAuthName ? { "x-electric-asserted-name": assertedAuthName } : {},
141
+ ...headers
142
+ },
137
143
  body: JSON.stringify({
138
144
  from: identity,
139
145
  payload: { text }
@@ -157,7 +163,10 @@ function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
157
163
  db,
158
164
  baseUrl,
159
165
  entityUrl,
160
- identity
166
+ identity,
167
+ assertedAuthEmail,
168
+ assertedAuthName,
169
+ headers
161
170
  ]);
162
171
  (0, ink.useInput)((input, key) => {
163
172
  if (disabled) return;
@@ -256,7 +265,7 @@ function WakeView({ section }) {
256
265
  })]
257
266
  });
258
267
  }
259
- function ObserveView({ db, entityUrl, baseUrl, identity }) {
268
+ function ObserveView({ db, entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers }) {
260
269
  const timelineQuery = (0, react.useMemo)(() => (0, __electric_ax_agents_runtime.createEntityIncludesQuery)(db), [db]);
261
270
  const { data: timelineRows = [] } = (0, __tanstack_react_db.useLiveQuery)(timelineQuery, [timelineQuery]);
262
271
  const timelineData = (0, __electric_ax_agents_runtime.normalizeEntityTimelineData)(timelineRows[0] ?? {
@@ -329,12 +338,15 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
329
338
  baseUrl,
330
339
  entityUrl,
331
340
  identity,
341
+ assertedAuthEmail,
342
+ assertedAuthName,
343
+ headers,
332
344
  disabled: closed
333
345
  })
334
346
  ]
335
347
  });
336
348
  }
337
- function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
349
+ function ObserveApp({ entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset }) {
338
350
  const [db, setDb] = (0, react.useState)(null);
339
351
  const [error, setError] = (0, react.useState)(null);
340
352
  const closeRef = (0, react.useRef)(null);
@@ -343,7 +355,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
343
355
  require_entity_stream_db.createEntityStreamDB({
344
356
  baseUrl,
345
357
  entityUrl,
346
- initialOffset
358
+ initialOffset,
359
+ assertedAuthEmail,
360
+ assertedAuthName,
361
+ headers
347
362
  }).then((result) => {
348
363
  if (cancelled) {
349
364
  result.close();
@@ -361,7 +376,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
361
376
  }, [
362
377
  baseUrl,
363
378
  entityUrl,
364
- initialOffset
379
+ initialOffset,
380
+ assertedAuthEmail,
381
+ assertedAuthName,
382
+ headers
365
383
  ]);
366
384
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
367
385
  flexDirection: "column",
@@ -378,15 +396,21 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
378
396
  db,
379
397
  entityUrl,
380
398
  baseUrl,
381
- identity
399
+ identity,
400
+ assertedAuthEmail,
401
+ assertedAuthName,
402
+ headers
382
403
  });
383
404
  }
384
405
  function renderObserve(opts) {
385
- const { entityUrl, baseUrl, identity, initialOffset } = opts;
406
+ const { entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset } = opts;
386
407
  const app = (0, ink.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ObserveApp, {
387
408
  entityUrl,
388
409
  baseUrl,
389
410
  identity,
411
+ assertedAuthEmail,
412
+ assertedAuthName,
413
+ headers,
390
414
  initialOffset
391
415
  }));
392
416
  process.on(`SIGINT`, () => {
@@ -1,4 +1,4 @@
1
- import { EntityStreamDB } from "./entity-stream-db-C2C3t_hD.cjs";
1
+ import { EntityStreamDB } from "./entity-stream-db-CmDwc49J.cjs";
2
2
  import { EntityTimelineContentItem, MessageReceived } from "@electric-ax/agents-runtime";
3
3
  import React from "react";
4
4
 
@@ -40,18 +40,27 @@ declare function MessageInput({
40
40
  baseUrl,
41
41
  entityUrl,
42
42
  identity,
43
+ assertedAuthEmail,
44
+ assertedAuthName,
45
+ headers,
43
46
  disabled
44
47
  }: {
45
48
  db: EntityStreamDB;
46
49
  baseUrl: string;
47
50
  entityUrl: string;
48
51
  identity: string;
52
+ assertedAuthEmail?: string;
53
+ assertedAuthName?: string;
54
+ headers?: Record<string, string>;
49
55
  disabled: boolean;
50
56
  }): React.ReactElement;
51
57
  declare function renderObserve(opts: {
52
58
  entityUrl: string;
53
59
  baseUrl: string;
54
60
  identity: string;
61
+ assertedAuthEmail?: string;
62
+ assertedAuthName?: string;
63
+ headers?: Record<string, string>;
55
64
  initialOffset?: string;
56
65
  }): void;
57
66
 
@@ -1,4 +1,4 @@
1
- import { EntityStreamDB } from "./entity-stream-db-BRwzIuHl.js";
1
+ import { EntityStreamDB } from "./entity-stream-db-DgzxI6C_.js";
2
2
  import { EntityTimelineContentItem, MessageReceived } from "@electric-ax/agents-runtime";
3
3
  import React from "react";
4
4
 
@@ -40,18 +40,27 @@ declare function MessageInput({
40
40
  baseUrl,
41
41
  entityUrl,
42
42
  identity,
43
+ assertedAuthEmail,
44
+ assertedAuthName,
45
+ headers,
43
46
  disabled
44
47
  }: {
45
48
  db: EntityStreamDB;
46
49
  baseUrl: string;
47
50
  entityUrl: string;
48
51
  identity: string;
52
+ assertedAuthEmail?: string;
53
+ assertedAuthName?: string;
54
+ headers?: Record<string, string>;
49
55
  disabled: boolean;
50
56
  }): React.ReactElement;
51
57
  declare function renderObserve(opts: {
52
58
  entityUrl: string;
53
59
  baseUrl: string;
54
60
  identity: string;
61
+ assertedAuthEmail?: string;
62
+ assertedAuthName?: string;
63
+ headers?: Record<string, string>;
55
64
  initialOffset?: string;
56
65
  }): void;
57
66
 
@@ -1,4 +1,5 @@
1
- import { createEntityStreamDB } from "./entity-stream-db-CGP2xVeJ.js";
1
+ import { assertedIdentityHeaders, entityApiUrl } from "./entity-api-Dh28Mz7H.js";
2
+ import { createEntityStreamDB } from "./entity-stream-db-Czz3S8YF.js";
2
3
  import { createOptimisticAction } from "@durable-streams/state";
3
4
  import { buildSections, createEntityIncludesQuery, normalizeEntityTimelineData } from "@electric-ax/agents-runtime";
4
5
  import React, { useEffect, useMemo, useRef, useState } from "react";
@@ -116,7 +117,7 @@ function ToolResultView({ result }) {
116
117
  }) : null]
117
118
  });
118
119
  }
119
- function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
120
+ function MessageInput({ db, baseUrl, entityUrl, identity, assertedAuthEmail, assertedAuthName, headers, disabled }) {
120
121
  const [value, setValue] = useState(``);
121
122
  const [error, setError] = useState(null);
122
123
  const sendAction = useMemo(() => createOptimisticAction({
@@ -129,9 +130,14 @@ function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
129
130
  });
130
131
  },
131
132
  mutationFn: async ({ text }) => {
132
- const res = await fetch(`${baseUrl}${entityUrl}/send`, {
133
+ const res = await fetch(entityApiUrl(baseUrl, entityUrl, `/send`), {
133
134
  method: `POST`,
134
- headers: { "content-type": `application/json` },
135
+ headers: {
136
+ "content-type": `application/json`,
137
+ ...assertedIdentityHeaders(assertedAuthEmail),
138
+ ...assertedAuthName ? { "x-electric-asserted-name": assertedAuthName } : {},
139
+ ...headers
140
+ },
135
141
  body: JSON.stringify({
136
142
  from: identity,
137
143
  payload: { text }
@@ -155,7 +161,10 @@ function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
155
161
  db,
156
162
  baseUrl,
157
163
  entityUrl,
158
- identity
164
+ identity,
165
+ assertedAuthEmail,
166
+ assertedAuthName,
167
+ headers
159
168
  ]);
160
169
  useInput((input, key) => {
161
170
  if (disabled) return;
@@ -254,7 +263,7 @@ function WakeView({ section }) {
254
263
  })]
255
264
  });
256
265
  }
257
- function ObserveView({ db, entityUrl, baseUrl, identity }) {
266
+ function ObserveView({ db, entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers }) {
258
267
  const timelineQuery = useMemo(() => createEntityIncludesQuery(db), [db]);
259
268
  const { data: timelineRows = [] } = useLiveQuery(timelineQuery, [timelineQuery]);
260
269
  const timelineData = normalizeEntityTimelineData(timelineRows[0] ?? {
@@ -327,12 +336,15 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
327
336
  baseUrl,
328
337
  entityUrl,
329
338
  identity,
339
+ assertedAuthEmail,
340
+ assertedAuthName,
341
+ headers,
330
342
  disabled: closed
331
343
  })
332
344
  ]
333
345
  });
334
346
  }
335
- function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
347
+ function ObserveApp({ entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset }) {
336
348
  const [db, setDb] = useState(null);
337
349
  const [error, setError] = useState(null);
338
350
  const closeRef = useRef(null);
@@ -341,7 +353,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
341
353
  createEntityStreamDB({
342
354
  baseUrl,
343
355
  entityUrl,
344
- initialOffset
356
+ initialOffset,
357
+ assertedAuthEmail,
358
+ assertedAuthName,
359
+ headers
345
360
  }).then((result) => {
346
361
  if (cancelled) {
347
362
  result.close();
@@ -359,7 +374,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
359
374
  }, [
360
375
  baseUrl,
361
376
  entityUrl,
362
- initialOffset
377
+ initialOffset,
378
+ assertedAuthEmail,
379
+ assertedAuthName,
380
+ headers
363
381
  ]);
364
382
  if (error) return /* @__PURE__ */ jsx(Box, {
365
383
  flexDirection: "column",
@@ -376,15 +394,21 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
376
394
  db,
377
395
  entityUrl,
378
396
  baseUrl,
379
- identity
397
+ identity,
398
+ assertedAuthEmail,
399
+ assertedAuthName,
400
+ headers
380
401
  });
381
402
  }
382
403
  function renderObserve(opts) {
383
- const { entityUrl, baseUrl, identity, initialOffset } = opts;
404
+ const { entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset } = opts;
384
405
  const app = render(/* @__PURE__ */ jsx(ObserveApp, {
385
406
  entityUrl,
386
407
  baseUrl,
387
408
  identity,
409
+ assertedAuthEmail,
410
+ assertedAuthName,
411
+ headers,
388
412
  initialOffset
389
413
  }));
390
414
  process.on(`SIGINT`, () => {
package/dist/start.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
- const require_env = require('./env-BXUgom_C.cjs');
3
+ const require_env = require('./env-CFKgT8o2.cjs');
4
+ const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
4
5
  const node_url = require_chunk.__toESM(require("node:url"));
5
6
  const node_child_process = require_chunk.__toESM(require("node:child_process"));
6
7
  const __electric_ax_agents = require_chunk.__toESM(require("@electric-ax/agents"));
@@ -19,19 +20,11 @@ const ELECTRIC_AGENTS_SERVER_IMAGE_TAG = `latest`;
19
20
  //#endregion
20
21
  //#region src/start.ts
21
22
  const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
22
- const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
23
- const DEFAULT_BUILTIN_AGENTS_HOST = `0.0.0.0`;
24
23
  const DEFAULT_COMPOSE_PROJECT_NAME = `electric-agents`;
24
+ const DEFAULT_PULL_WAKE_RUNNER_ID = `builtin-agents`;
25
+ const DEFAULT_PULL_WAKE_OWNER_ID = `builtin-agents`;
26
+ const DEFAULT_PULL_WAKE_OWNER_NAME = `Built-in agents`;
25
27
  const DOCKER_COMPOSE_FILE = (0, node_url.fileURLToPath)(new URL(`../docker-compose.full.yml`, require("url").pathToFileURL(__filename).href));
26
- function resolveBuiltinAgentsPort(env = process.env, fileEnv = require_env.readDotEnvFile()) {
27
- const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
28
- const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
29
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
30
- return parsed;
31
- }
32
- function resolveBuiltinAgentsHost(env = process.env, fileEnv = require_env.readDotEnvFile()) {
33
- return env.ELECTRIC_AGENTS_BUILTIN_HOST?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_HOST?.trim() || DEFAULT_BUILTIN_AGENTS_HOST;
34
- }
35
28
  function resolveElectricAgentsPort(env = process.env, fileEnv = require_env.readDotEnvFile()) {
36
29
  const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
37
30
  const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
@@ -54,12 +47,73 @@ function getStoppedEnvironmentMessage(stopped) {
54
47
  }
55
48
  function getStartedBuiltinAgentsMessage(started) {
56
49
  return [
57
- `Builtin Horton server is up.`,
58
- `Webhook server: ${started.url}`,
50
+ `Builtin agents pull-wake runner is up.`,
51
+ `Runner: ${started.runnerId}`,
52
+ `Runtime: ${started.url}`,
59
53
  `Registers with: ${started.agentServerUrl}`,
60
54
  `Press Ctrl-C to stop.`
61
55
  ].join(`\n`);
62
56
  }
57
+ function readConfigValue(env, fileEnv, names) {
58
+ for (const name of names) {
59
+ const value = env[name]?.trim() || fileEnv[name]?.trim();
60
+ if (value) return value;
61
+ }
62
+ return void 0;
63
+ }
64
+ function runnerIdFromIdentity(identity) {
65
+ if (!identity) return DEFAULT_PULL_WAKE_RUNNER_ID;
66
+ const slug = identity.toLowerCase().replace(/[^a-z0-9._-]+/g, `-`).replace(/^-+|-+$/g, ``);
67
+ return slug ? `builtin-${slug}` : DEFAULT_PULL_WAKE_RUNNER_ID;
68
+ }
69
+ function resolvePullWakeRunnerId(env = process.env, fileEnv = require_env.readDotEnvFile()) {
70
+ return readConfigValue(env, fileEnv, [`ELECTRIC_AGENTS_PULL_WAKE_RUNNER_ID`, `PULL_WAKE_RUNNER_ID`]) ?? runnerIdFromIdentity(readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_EMAIL`, `ELECTRIC_AGENTS_IDENTITY`]));
71
+ }
72
+ function resolvePullWakeOwnerId(env = process.env, fileEnv = require_env.readDotEnvFile()) {
73
+ return readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_EMAIL`, `ELECTRIC_AGENTS_IDENTITY`]) ?? DEFAULT_PULL_WAKE_OWNER_ID;
74
+ }
75
+ function buildAssertedAuthHeaders(env, fileEnv) {
76
+ const email = resolvePullWakeOwnerId(env, fileEnv);
77
+ const name = readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_NAME`]) ?? DEFAULT_PULL_WAKE_OWNER_NAME;
78
+ const headers = {};
79
+ headers[`X-Electric-Asserted-Email`] = email;
80
+ headers[`X-Electric-Asserted-Name`] = name;
81
+ return {
82
+ headers,
83
+ ownerUserId: email,
84
+ name
85
+ };
86
+ }
87
+ function parseAdditionalServerHeaders(env, fileEnv) {
88
+ const raw = readConfigValue(env, fileEnv, [`ELECTRIC_AGENTS_SERVER_HEADERS`]);
89
+ if (!raw) return void 0;
90
+ let parsed;
91
+ try {
92
+ parsed = JSON.parse(raw);
93
+ } catch {
94
+ throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected JSON`);
95
+ }
96
+ if (!parsed || typeof parsed !== `object` || Array.isArray(parsed)) throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected a JSON object`);
97
+ const headers = new Headers();
98
+ for (const [name, value] of Object.entries(parsed)) {
99
+ if (typeof value !== `string`) throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: header "${name}" must be a string`);
100
+ headers.set(name, value);
101
+ }
102
+ const normalized = Object.fromEntries(headers.entries());
103
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
104
+ }
105
+ function mergeHeaders(...sources) {
106
+ const headers = new Headers();
107
+ for (const source of sources) {
108
+ if (!source) continue;
109
+ new Headers(source).forEach((value, key) => headers.set(key, value));
110
+ }
111
+ const merged = Object.fromEntries(headers.entries());
112
+ return Object.keys(merged).length > 0 ? merged : void 0;
113
+ }
114
+ function hasHeader(headers, name) {
115
+ return headers ? new Headers(headers).has(name) : false;
116
+ }
63
117
  function resolveComposeProjectName(_cwd = process.cwd(), env = process.env) {
64
118
  const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
65
119
  if (explicit) return explicit;
@@ -94,11 +148,14 @@ async function waitForElectricAgentsServer(baseUrl, options = {}) {
94
148
  const timeoutMs = options.timeoutMs ?? 6e4;
95
149
  const intervalMs = options.intervalMs ?? 1e3;
96
150
  const deadline = Date.now() + timeoutMs;
97
- const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
151
+ const healthUrl = (0, __electric_ax_agents_runtime.appendPathToUrl)(baseUrl, `/_electric/health`);
98
152
  let lastError = null;
99
153
  while (Date.now() < deadline) {
100
154
  try {
101
- const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
155
+ const response = await fetchImpl(healthUrl, {
156
+ headers: options.headers,
157
+ signal: AbortSignal.timeout(5e3)
158
+ });
102
159
  if (response.ok) return;
103
160
  lastError = `healthcheck returned ${response.status}`;
104
161
  } catch (error) {
@@ -112,6 +169,7 @@ async function startElectricAgentsDevEnvironment(_options = {}, env = process.en
112
169
  const fileEnv = require_env.readDotEnvFile(cwd);
113
170
  const port = resolveElectricAgentsPort(env, fileEnv);
114
171
  const composeProjectName = resolveComposeProjectName(cwd, env);
172
+ const assertedAuth = buildAssertedAuthHeaders(env, fileEnv);
115
173
  await runDockerCompose([
116
174
  `compose`,
117
175
  `-f`,
@@ -123,7 +181,10 @@ async function startElectricAgentsDevEnvironment(_options = {}, env = process.en
123
181
  COMPOSE_PROJECT_NAME: composeProjectName,
124
182
  ELECTRIC_AGENTS_PORT: String(port),
125
183
  ELECTRIC_IMAGE_TAG: env.ELECTRIC_IMAGE_TAG ?? ELECTRIC_IMAGE_TAG,
126
- ELECTRIC_AGENTS_SERVER_IMAGE_TAG: env.ELECTRIC_AGENTS_SERVER_IMAGE_TAG ?? ELECTRIC_AGENTS_SERVER_IMAGE_TAG
184
+ ELECTRIC_AGENTS_SERVER_IMAGE_TAG: env.ELECTRIC_AGENTS_SERVER_IMAGE_TAG ?? ELECTRIC_AGENTS_SERVER_IMAGE_TAG,
185
+ ELECTRIC_AGENTS_DEV_ASSERTED_AUTH: env.ELECTRIC_AGENTS_DEV_ASSERTED_AUTH ?? fileEnv.ELECTRIC_AGENTS_DEV_ASSERTED_AUTH ?? `1`,
186
+ ELECTRIC_ASSERTED_AUTH_EMAIL: env.ELECTRIC_ASSERTED_AUTH_EMAIL ?? fileEnv.ELECTRIC_ASSERTED_AUTH_EMAIL ?? assertedAuth.ownerUserId,
187
+ ELECTRIC_ASSERTED_AUTH_NAME: env.ELECTRIC_ASSERTED_AUTH_NAME ?? fileEnv.ELECTRIC_ASSERTED_AUTH_NAME ?? assertedAuth.name
127
188
  });
128
189
  const uiUrl = `http://localhost:${port}`;
129
190
  await waitForElectricAgentsServer(uiUrl);
@@ -181,23 +242,29 @@ async function startBuiltinAgentsServer(options, params = {}) {
181
242
  const cwd = params.cwd ?? process.cwd();
182
243
  const fileEnv = require_env.readDotEnvFile(cwd);
183
244
  const anthropicApiKey = require_env.resolveAnthropicApiKey(options, env, fileEnv);
184
- const host = resolveBuiltinAgentsHost(env, fileEnv);
185
- const port = resolveBuiltinAgentsPort(env, fileEnv);
245
+ const runnerId = resolvePullWakeRunnerId(env, fileEnv);
246
+ const assertedAuth = buildAssertedAuthHeaders(env, fileEnv);
247
+ const serverHeaders = mergeHeaders(assertedAuth.headers, parseAdditionalServerHeaders(env, fileEnv));
186
248
  const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
187
249
  process.env.ANTHROPIC_API_KEY = anthropicApiKey;
188
- await waitForElectricAgentsServer(agentServerUrl);
250
+ await waitForElectricAgentsServer(agentServerUrl, { headers: serverHeaders });
189
251
  const server = new __electric_ax_agents.BuiltinAgentsServer({
190
252
  agentServerUrl,
191
- host,
192
- port,
193
253
  workingDirectory: cwd,
194
- loadProjectMcpConfig: true
254
+ loadProjectMcpConfig: true,
255
+ pullWake: {
256
+ runnerId,
257
+ ownerUserId: assertedAuth.ownerUserId,
258
+ registerRunner: true,
259
+ headers: serverHeaders,
260
+ claimHeaders: serverHeaders,
261
+ claimTokenHeader: hasHeader(serverHeaders, `authorization`) ? `electric-claim-token` : void 0
262
+ }
195
263
  });
196
- await server.start();
264
+ const url = await server.start();
197
265
  const started = {
198
- port,
199
- url: server.url,
200
- registeredBaseUrl: server.registeredBaseUrl,
266
+ runnerId,
267
+ url,
201
268
  agentServerUrl
202
269
  };
203
270
  if (params.printStartedMessage ?? true) console.log(getStartedBuiltinAgentsMessage(started));
@@ -211,10 +278,10 @@ exports.getStartedEnvironmentMessage = getStartedEnvironmentMessage
211
278
  exports.getStoppedEnvironmentMessage = getStoppedEnvironmentMessage
212
279
  exports.readDotEnvFile = require_env.readDotEnvFile
213
280
  exports.resolveAnthropicApiKey = require_env.resolveAnthropicApiKey
214
- exports.resolveBuiltinAgentsHost = resolveBuiltinAgentsHost
215
- exports.resolveBuiltinAgentsPort = resolveBuiltinAgentsPort
216
281
  exports.resolveComposeProjectName = resolveComposeProjectName
217
282
  exports.resolveElectricAgentsPort = resolveElectricAgentsPort
283
+ exports.resolvePullWakeOwnerId = resolvePullWakeOwnerId
284
+ exports.resolvePullWakeRunnerId = resolvePullWakeRunnerId
218
285
  exports.startBuiltinAgentsServer = startBuiltinAgentsServer
219
286
  exports.startElectricAgentsDevEnvironment = startElectricAgentsDevEnvironment
220
287
  exports.stopElectricAgentsDevEnvironment = stopElectricAgentsDevEnvironment
package/dist/start.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-B2MsxFCW.cjs";
2
- export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
1
+ import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-BQ4JyfkG.cjs";
2
+ export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
package/dist/start.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-BDO8dHhT.js";
2
- export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
1
+ import { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer } from "./index-D6TZ88Yg.js";
2
+ export { StartedBuiltinAgentsEnvironment, StartedDevEnvironment, StoppedDevEnvironment, getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
package/dist/start.js CHANGED
@@ -1,4 +1,5 @@
1
- import { readDotEnvFile$1 as readDotEnvFile, resolveAnthropicApiKey$1 as resolveAnthropicApiKey } from "./env-LZtIfFz1.js";
1
+ import { readDotEnvFile$1 as readDotEnvFile, resolveAnthropicApiKey$1 as resolveAnthropicApiKey } from "./env-DIhTv987.js";
2
+ import { appendPathToUrl } from "@electric-ax/agents-runtime";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { spawn } from "node:child_process";
4
5
  import { BuiltinAgentsServer } from "@electric-ax/agents";
@@ -17,19 +18,11 @@ const ELECTRIC_AGENTS_SERVER_IMAGE_TAG = `latest`;
17
18
  //#endregion
18
19
  //#region src/start.ts
19
20
  const DEFAULT_ELECTRIC_AGENTS_PORT = 4437;
20
- const DEFAULT_BUILTIN_AGENTS_PORT = 4448;
21
- const DEFAULT_BUILTIN_AGENTS_HOST = `0.0.0.0`;
22
21
  const DEFAULT_COMPOSE_PROJECT_NAME = `electric-agents`;
22
+ const DEFAULT_PULL_WAKE_RUNNER_ID = `builtin-agents`;
23
+ const DEFAULT_PULL_WAKE_OWNER_ID = `builtin-agents`;
24
+ const DEFAULT_PULL_WAKE_OWNER_NAME = `Built-in agents`;
23
25
  const DOCKER_COMPOSE_FILE = fileURLToPath(new URL(`../docker-compose.full.yml`, import.meta.url));
24
- function resolveBuiltinAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
25
- const raw = env.ELECTRIC_AGENTS_BUILTIN_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_PORT?.trim();
26
- const parsed = raw ? Number(raw) : DEFAULT_BUILTIN_AGENTS_PORT;
27
- if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`ELECTRIC_AGENTS_BUILTIN_PORT must be a positive integer`);
28
- return parsed;
29
- }
30
- function resolveBuiltinAgentsHost(env = process.env, fileEnv = readDotEnvFile()) {
31
- return env.ELECTRIC_AGENTS_BUILTIN_HOST?.trim() || fileEnv.ELECTRIC_AGENTS_BUILTIN_HOST?.trim() || DEFAULT_BUILTIN_AGENTS_HOST;
32
- }
33
26
  function resolveElectricAgentsPort(env = process.env, fileEnv = readDotEnvFile()) {
34
27
  const raw = env.ELECTRIC_AGENTS_PORT?.trim() || fileEnv.ELECTRIC_AGENTS_PORT?.trim();
35
28
  const parsed = raw ? Number(raw) : DEFAULT_ELECTRIC_AGENTS_PORT;
@@ -52,12 +45,73 @@ function getStoppedEnvironmentMessage(stopped) {
52
45
  }
53
46
  function getStartedBuiltinAgentsMessage(started) {
54
47
  return [
55
- `Builtin Horton server is up.`,
56
- `Webhook server: ${started.url}`,
48
+ `Builtin agents pull-wake runner is up.`,
49
+ `Runner: ${started.runnerId}`,
50
+ `Runtime: ${started.url}`,
57
51
  `Registers with: ${started.agentServerUrl}`,
58
52
  `Press Ctrl-C to stop.`
59
53
  ].join(`\n`);
60
54
  }
55
+ function readConfigValue(env, fileEnv, names) {
56
+ for (const name of names) {
57
+ const value = env[name]?.trim() || fileEnv[name]?.trim();
58
+ if (value) return value;
59
+ }
60
+ return void 0;
61
+ }
62
+ function runnerIdFromIdentity(identity) {
63
+ if (!identity) return DEFAULT_PULL_WAKE_RUNNER_ID;
64
+ const slug = identity.toLowerCase().replace(/[^a-z0-9._-]+/g, `-`).replace(/^-+|-+$/g, ``);
65
+ return slug ? `builtin-${slug}` : DEFAULT_PULL_WAKE_RUNNER_ID;
66
+ }
67
+ function resolvePullWakeRunnerId(env = process.env, fileEnv = readDotEnvFile()) {
68
+ return readConfigValue(env, fileEnv, [`ELECTRIC_AGENTS_PULL_WAKE_RUNNER_ID`, `PULL_WAKE_RUNNER_ID`]) ?? runnerIdFromIdentity(readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_EMAIL`, `ELECTRIC_AGENTS_IDENTITY`]));
69
+ }
70
+ function resolvePullWakeOwnerId(env = process.env, fileEnv = readDotEnvFile()) {
71
+ return readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_EMAIL`, `ELECTRIC_AGENTS_IDENTITY`]) ?? DEFAULT_PULL_WAKE_OWNER_ID;
72
+ }
73
+ function buildAssertedAuthHeaders(env, fileEnv) {
74
+ const email = resolvePullWakeOwnerId(env, fileEnv);
75
+ const name = readConfigValue(env, fileEnv, [`ELECTRIC_ASSERTED_AUTH_NAME`]) ?? DEFAULT_PULL_WAKE_OWNER_NAME;
76
+ const headers = {};
77
+ headers[`X-Electric-Asserted-Email`] = email;
78
+ headers[`X-Electric-Asserted-Name`] = name;
79
+ return {
80
+ headers,
81
+ ownerUserId: email,
82
+ name
83
+ };
84
+ }
85
+ function parseAdditionalServerHeaders(env, fileEnv) {
86
+ const raw = readConfigValue(env, fileEnv, [`ELECTRIC_AGENTS_SERVER_HEADERS`]);
87
+ if (!raw) return void 0;
88
+ let parsed;
89
+ try {
90
+ parsed = JSON.parse(raw);
91
+ } catch {
92
+ throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected JSON`);
93
+ }
94
+ if (!parsed || typeof parsed !== `object` || Array.isArray(parsed)) throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: expected a JSON object`);
95
+ const headers = new Headers();
96
+ for (const [name, value] of Object.entries(parsed)) {
97
+ if (typeof value !== `string`) throw new Error(`Invalid ELECTRIC_AGENTS_SERVER_HEADERS: header "${name}" must be a string`);
98
+ headers.set(name, value);
99
+ }
100
+ const normalized = Object.fromEntries(headers.entries());
101
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
102
+ }
103
+ function mergeHeaders(...sources) {
104
+ const headers = new Headers();
105
+ for (const source of sources) {
106
+ if (!source) continue;
107
+ new Headers(source).forEach((value, key) => headers.set(key, value));
108
+ }
109
+ const merged = Object.fromEntries(headers.entries());
110
+ return Object.keys(merged).length > 0 ? merged : void 0;
111
+ }
112
+ function hasHeader(headers, name) {
113
+ return headers ? new Headers(headers).has(name) : false;
114
+ }
61
115
  function resolveComposeProjectName(_cwd = process.cwd(), env = process.env) {
62
116
  const explicit = env.ELECTRIC_AGENTS_COMPOSE_PROJECT?.trim();
63
117
  if (explicit) return explicit;
@@ -92,11 +146,14 @@ async function waitForElectricAgentsServer(baseUrl, options = {}) {
92
146
  const timeoutMs = options.timeoutMs ?? 6e4;
93
147
  const intervalMs = options.intervalMs ?? 1e3;
94
148
  const deadline = Date.now() + timeoutMs;
95
- const healthUrl = `${baseUrl.replace(/\/$/, ``)}/_electric/health`;
149
+ const healthUrl = appendPathToUrl(baseUrl, `/_electric/health`);
96
150
  let lastError = null;
97
151
  while (Date.now() < deadline) {
98
152
  try {
99
- const response = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5e3) });
153
+ const response = await fetchImpl(healthUrl, {
154
+ headers: options.headers,
155
+ signal: AbortSignal.timeout(5e3)
156
+ });
100
157
  if (response.ok) return;
101
158
  lastError = `healthcheck returned ${response.status}`;
102
159
  } catch (error) {
@@ -110,6 +167,7 @@ async function startElectricAgentsDevEnvironment(_options = {}, env = process.en
110
167
  const fileEnv = readDotEnvFile(cwd);
111
168
  const port = resolveElectricAgentsPort(env, fileEnv);
112
169
  const composeProjectName = resolveComposeProjectName(cwd, env);
170
+ const assertedAuth = buildAssertedAuthHeaders(env, fileEnv);
113
171
  await runDockerCompose([
114
172
  `compose`,
115
173
  `-f`,
@@ -121,7 +179,10 @@ async function startElectricAgentsDevEnvironment(_options = {}, env = process.en
121
179
  COMPOSE_PROJECT_NAME: composeProjectName,
122
180
  ELECTRIC_AGENTS_PORT: String(port),
123
181
  ELECTRIC_IMAGE_TAG: env.ELECTRIC_IMAGE_TAG ?? ELECTRIC_IMAGE_TAG,
124
- ELECTRIC_AGENTS_SERVER_IMAGE_TAG: env.ELECTRIC_AGENTS_SERVER_IMAGE_TAG ?? ELECTRIC_AGENTS_SERVER_IMAGE_TAG
182
+ ELECTRIC_AGENTS_SERVER_IMAGE_TAG: env.ELECTRIC_AGENTS_SERVER_IMAGE_TAG ?? ELECTRIC_AGENTS_SERVER_IMAGE_TAG,
183
+ ELECTRIC_AGENTS_DEV_ASSERTED_AUTH: env.ELECTRIC_AGENTS_DEV_ASSERTED_AUTH ?? fileEnv.ELECTRIC_AGENTS_DEV_ASSERTED_AUTH ?? `1`,
184
+ ELECTRIC_ASSERTED_AUTH_EMAIL: env.ELECTRIC_ASSERTED_AUTH_EMAIL ?? fileEnv.ELECTRIC_ASSERTED_AUTH_EMAIL ?? assertedAuth.ownerUserId,
185
+ ELECTRIC_ASSERTED_AUTH_NAME: env.ELECTRIC_ASSERTED_AUTH_NAME ?? fileEnv.ELECTRIC_ASSERTED_AUTH_NAME ?? assertedAuth.name
125
186
  });
126
187
  const uiUrl = `http://localhost:${port}`;
127
188
  await waitForElectricAgentsServer(uiUrl);
@@ -179,23 +240,29 @@ async function startBuiltinAgentsServer(options, params = {}) {
179
240
  const cwd = params.cwd ?? process.cwd();
180
241
  const fileEnv = readDotEnvFile(cwd);
181
242
  const anthropicApiKey = resolveAnthropicApiKey(options, env, fileEnv);
182
- const host = resolveBuiltinAgentsHost(env, fileEnv);
183
- const port = resolveBuiltinAgentsPort(env, fileEnv);
243
+ const runnerId = resolvePullWakeRunnerId(env, fileEnv);
244
+ const assertedAuth = buildAssertedAuthHeaders(env, fileEnv);
245
+ const serverHeaders = mergeHeaders(assertedAuth.headers, parseAdditionalServerHeaders(env, fileEnv));
184
246
  const agentServerUrl = params.agentServerUrl ?? env.ELECTRIC_AGENTS_URL?.trim() ?? `http://localhost:${resolveElectricAgentsPort(env, fileEnv)}`;
185
247
  process.env.ANTHROPIC_API_KEY = anthropicApiKey;
186
- await waitForElectricAgentsServer(agentServerUrl);
248
+ await waitForElectricAgentsServer(agentServerUrl, { headers: serverHeaders });
187
249
  const server = new BuiltinAgentsServer({
188
250
  agentServerUrl,
189
- host,
190
- port,
191
251
  workingDirectory: cwd,
192
- loadProjectMcpConfig: true
252
+ loadProjectMcpConfig: true,
253
+ pullWake: {
254
+ runnerId,
255
+ ownerUserId: assertedAuth.ownerUserId,
256
+ registerRunner: true,
257
+ headers: serverHeaders,
258
+ claimHeaders: serverHeaders,
259
+ claimTokenHeader: hasHeader(serverHeaders, `authorization`) ? `electric-claim-token` : void 0
260
+ }
193
261
  });
194
- await server.start();
262
+ const url = await server.start();
195
263
  const started = {
196
- port,
197
- url: server.url,
198
- registeredBaseUrl: server.registeredBaseUrl,
264
+ runnerId,
265
+ url,
199
266
  agentServerUrl
200
267
  };
201
268
  if (params.printStartedMessage ?? true) console.log(getStartedBuiltinAgentsMessage(started));
@@ -204,4 +271,4 @@ async function startBuiltinAgentsServer(options, params = {}) {
204
271
  }
205
272
 
206
273
  //#endregion
207
- export { getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveBuiltinAgentsHost, resolveBuiltinAgentsPort, resolveComposeProjectName, resolveElectricAgentsPort, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
274
+ export { getStartedBuiltinAgentsMessage, getStartedEnvironmentMessage, getStoppedEnvironmentMessage, readDotEnvFile, resolveAnthropicApiKey, resolveComposeProjectName, resolveElectricAgentsPort, resolvePullWakeOwnerId, resolvePullWakeRunnerId, startBuiltinAgentsServer, startElectricAgentsDevEnvironment, stopElectricAgentsDevEnvironment, waitForElectricAgentsServer };
@@ -38,7 +38,7 @@ services:
38
38
 
39
39
  electric-agents:
40
40
  image: electricax/agents-server:${ELECTRIC_AGENTS_SERVER_IMAGE_TAG:-latest}
41
- pull_policy: always
41
+ pull_policy: ${ELECTRIC_AGENTS_SERVER_PULL_POLICY:-missing}
42
42
  restart: unless-stopped
43
43
  extra_hosts:
44
44
  - 'host.docker.internal:host-gateway'
@@ -59,6 +59,9 @@ services:
59
59
  ELECTRIC_AGENTS_STREAMS_PORT: ${ELECTRIC_AGENTS_STREAMS_PORT:-0}
60
60
  ELECTRIC_AGENTS_ELECTRIC_URL: ${ELECTRIC_AGENTS_ELECTRIC_URL:-http://electric:3000}
61
61
  ELECTRIC_AGENTS_REWRITE_LOOPBACK_WEBHOOKS_TO: ${ELECTRIC_AGENTS_REWRITE_LOOPBACK_WEBHOOKS_TO:-host.docker.internal}
62
+ ELECTRIC_AGENTS_DEV_ASSERTED_AUTH: ${ELECTRIC_AGENTS_DEV_ASSERTED_AUTH:-1}
63
+ ELECTRIC_ASSERTED_AUTH_EMAIL: ${ELECTRIC_ASSERTED_AUTH_EMAIL:-builtin-agents}
64
+ ELECTRIC_ASSERTED_AUTH_NAME: ${ELECTRIC_ASSERTED_AUTH_NAME:-Built-in agents}
62
65
  ports:
63
66
  - '${ELECTRIC_AGENTS_PORT:-4437}:4437'
64
67
  volumes:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electric-ax",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for Electric Agents",
5
5
  "author": "ElectricSQL team and contributors",
6
6
  "license": "Apache-2.0",
@@ -37,8 +37,8 @@
37
37
  "./package.json": "./package.json"
38
38
  },
39
39
  "dependencies": {
40
- "@durable-streams/client": "npm:@electric-ax/durable-streams-client-beta@^0.3.0",
41
- "@durable-streams/state": "npm:@electric-ax/durable-streams-state-beta@^0.3.0",
40
+ "@durable-streams/client": "https://pkg.pr.new/durable-streams/durable-streams/@durable-streams/client@350",
41
+ "@durable-streams/state": "https://pkg.pr.new/durable-streams/durable-streams/@durable-streams/state@350",
42
42
  "@electric-sql/client": "^1.5.17",
43
43
  "@tanstack/db": "^0.6.4",
44
44
  "@tanstack/react-db": "^0.1.82",
@@ -46,8 +46,8 @@
46
46
  "ink": "^6.8.0",
47
47
  "omelette": "^0.4.17",
48
48
  "react": "^19.2.0",
49
- "@electric-ax/agents": "0.3.0",
50
- "@electric-ax/agents-runtime": "0.1.3"
49
+ "@electric-ax/agents": "0.4.0",
50
+ "@electric-ax/agents-runtime": "0.2.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@vitest/coverage-v8": "^4.1.0",
File without changes
File without changes