electric-ax 0.1.17 → 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,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;
@@ -231,7 +240,32 @@ function AgentResponseView({ section, label, isStreaming }) {
231
240
  ]
232
241
  });
233
242
  }
234
- function ObserveView({ db, entityUrl, baseUrl, identity }) {
243
+ function wakeReason(section) {
244
+ const { payload } = section;
245
+ if (payload.timeout) return `timeout`;
246
+ if (payload.finished_child) return `child ${payload.finished_child.run_status}`;
247
+ if (payload.changes.length > 0) return `${payload.changes.length} ${payload.changes.length === 1 ? `change` : `changes`}`;
248
+ if (payload.other_children && payload.other_children.length > 0) return `${payload.other_children.length} child ${payload.other_children.length === 1 ? `update` : `updates`}`;
249
+ return payload.source;
250
+ }
251
+ function WakeView({ section }) {
252
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
253
+ flexDirection: "column",
254
+ marginTop: 1,
255
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
256
+ bold: true,
257
+ color: "magenta",
258
+ children: `┌ wake`
259
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
260
+ dimColor: true,
261
+ children: ` ${formatTime(new Date(section.timestamp).toISOString())}`
262
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
263
+ dimColor: true,
264
+ children: `│ ${wakeReason(section)} from ${section.payload.source}`
265
+ })]
266
+ });
267
+ }
268
+ function ObserveView({ db, entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers }) {
235
269
  const timelineQuery = (0, react.useMemo)(() => (0, __electric_ax_agents_runtime.createEntityIncludesQuery)(db), [db]);
236
270
  const { data: timelineRows = [] } = (0, __tanstack_react_db.useLiveQuery)(timelineQuery, [timelineQuery]);
237
271
  const timelineData = (0, __electric_ax_agents_runtime.normalizeEntityTimelineData)(timelineRows[0] ?? {
@@ -276,6 +310,7 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
276
310
  payload: { text: section.text },
277
311
  timestamp: new Date(section.timestamp).toISOString()
278
312
  } }, `msg-${i}`);
313
+ if (section.kind === `wake`) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WakeView, { section }, `wake-${i}`);
279
314
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentResponseView, {
280
315
  section,
281
316
  label: entityUrl,
@@ -303,12 +338,15 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
303
338
  baseUrl,
304
339
  entityUrl,
305
340
  identity,
341
+ assertedAuthEmail,
342
+ assertedAuthName,
343
+ headers,
306
344
  disabled: closed
307
345
  })
308
346
  ]
309
347
  });
310
348
  }
311
- function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
349
+ function ObserveApp({ entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset }) {
312
350
  const [db, setDb] = (0, react.useState)(null);
313
351
  const [error, setError] = (0, react.useState)(null);
314
352
  const closeRef = (0, react.useRef)(null);
@@ -317,7 +355,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
317
355
  require_entity_stream_db.createEntityStreamDB({
318
356
  baseUrl,
319
357
  entityUrl,
320
- initialOffset
358
+ initialOffset,
359
+ assertedAuthEmail,
360
+ assertedAuthName,
361
+ headers
321
362
  }).then((result) => {
322
363
  if (cancelled) {
323
364
  result.close();
@@ -335,7 +376,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
335
376
  }, [
336
377
  baseUrl,
337
378
  entityUrl,
338
- initialOffset
379
+ initialOffset,
380
+ assertedAuthEmail,
381
+ assertedAuthName,
382
+ headers
339
383
  ]);
340
384
  if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
341
385
  flexDirection: "column",
@@ -352,15 +396,21 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
352
396
  db,
353
397
  entityUrl,
354
398
  baseUrl,
355
- identity
399
+ identity,
400
+ assertedAuthEmail,
401
+ assertedAuthName,
402
+ headers
356
403
  });
357
404
  }
358
405
  function renderObserve(opts) {
359
- const { entityUrl, baseUrl, identity, initialOffset } = opts;
406
+ const { entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset } = opts;
360
407
  const app = (0, ink.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ObserveApp, {
361
408
  entityUrl,
362
409
  baseUrl,
363
410
  identity,
411
+ assertedAuthEmail,
412
+ assertedAuthName,
413
+ headers,
364
414
  initialOffset
365
415
  }));
366
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;
@@ -229,7 +238,32 @@ function AgentResponseView({ section, label, isStreaming }) {
229
238
  ]
230
239
  });
231
240
  }
232
- function ObserveView({ db, entityUrl, baseUrl, identity }) {
241
+ function wakeReason(section) {
242
+ const { payload } = section;
243
+ if (payload.timeout) return `timeout`;
244
+ if (payload.finished_child) return `child ${payload.finished_child.run_status}`;
245
+ if (payload.changes.length > 0) return `${payload.changes.length} ${payload.changes.length === 1 ? `change` : `changes`}`;
246
+ if (payload.other_children && payload.other_children.length > 0) return `${payload.other_children.length} child ${payload.other_children.length === 1 ? `update` : `updates`}`;
247
+ return payload.source;
248
+ }
249
+ function WakeView({ section }) {
250
+ return /* @__PURE__ */ jsxs(Box, {
251
+ flexDirection: "column",
252
+ marginTop: 1,
253
+ children: [/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
254
+ bold: true,
255
+ color: "magenta",
256
+ children: `┌ wake`
257
+ }), /* @__PURE__ */ jsx(Text, {
258
+ dimColor: true,
259
+ children: ` ${formatTime(new Date(section.timestamp).toISOString())}`
260
+ })] }), /* @__PURE__ */ jsx(Text, {
261
+ dimColor: true,
262
+ children: `│ ${wakeReason(section)} from ${section.payload.source}`
263
+ })]
264
+ });
265
+ }
266
+ function ObserveView({ db, entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers }) {
233
267
  const timelineQuery = useMemo(() => createEntityIncludesQuery(db), [db]);
234
268
  const { data: timelineRows = [] } = useLiveQuery(timelineQuery, [timelineQuery]);
235
269
  const timelineData = normalizeEntityTimelineData(timelineRows[0] ?? {
@@ -274,6 +308,7 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
274
308
  payload: { text: section.text },
275
309
  timestamp: new Date(section.timestamp).toISOString()
276
310
  } }, `msg-${i}`);
311
+ if (section.kind === `wake`) return /* @__PURE__ */ jsx(WakeView, { section }, `wake-${i}`);
277
312
  return /* @__PURE__ */ jsx(AgentResponseView, {
278
313
  section,
279
314
  label: entityUrl,
@@ -301,12 +336,15 @@ function ObserveView({ db, entityUrl, baseUrl, identity }) {
301
336
  baseUrl,
302
337
  entityUrl,
303
338
  identity,
339
+ assertedAuthEmail,
340
+ assertedAuthName,
341
+ headers,
304
342
  disabled: closed
305
343
  })
306
344
  ]
307
345
  });
308
346
  }
309
- function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
347
+ function ObserveApp({ entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset }) {
310
348
  const [db, setDb] = useState(null);
311
349
  const [error, setError] = useState(null);
312
350
  const closeRef = useRef(null);
@@ -315,7 +353,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
315
353
  createEntityStreamDB({
316
354
  baseUrl,
317
355
  entityUrl,
318
- initialOffset
356
+ initialOffset,
357
+ assertedAuthEmail,
358
+ assertedAuthName,
359
+ headers
319
360
  }).then((result) => {
320
361
  if (cancelled) {
321
362
  result.close();
@@ -333,7 +374,10 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
333
374
  }, [
334
375
  baseUrl,
335
376
  entityUrl,
336
- initialOffset
377
+ initialOffset,
378
+ assertedAuthEmail,
379
+ assertedAuthName,
380
+ headers
337
381
  ]);
338
382
  if (error) return /* @__PURE__ */ jsx(Box, {
339
383
  flexDirection: "column",
@@ -350,15 +394,21 @@ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
350
394
  db,
351
395
  entityUrl,
352
396
  baseUrl,
353
- identity
397
+ identity,
398
+ assertedAuthEmail,
399
+ assertedAuthName,
400
+ headers
354
401
  });
355
402
  }
356
403
  function renderObserve(opts) {
357
- const { entityUrl, baseUrl, identity, initialOffset } = opts;
404
+ const { entityUrl, baseUrl, identity, assertedAuthEmail, assertedAuthName, headers, initialOffset } = opts;
358
405
  const app = render(/* @__PURE__ */ jsx(ObserveApp, {
359
406
  entityUrl,
360
407
  baseUrl,
361
408
  identity,
409
+ assertedAuthEmail,
410
+ assertedAuthName,
411
+ headers,
362
412
  initialOffset
363
413
  }));
364
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,22 +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
- workingDirectory: cwd
253
+ workingDirectory: cwd,
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
+ }
194
263
  });
195
- await server.start();
264
+ const url = await server.start();
196
265
  const started = {
197
- port,
198
- url: server.url,
199
- registeredBaseUrl: server.registeredBaseUrl,
266
+ runnerId,
267
+ url,
200
268
  agentServerUrl
201
269
  };
202
270
  if (params.printStartedMessage ?? true) console.log(getStartedBuiltinAgentsMessage(started));
@@ -210,10 +278,10 @@ exports.getStartedEnvironmentMessage = getStartedEnvironmentMessage
210
278
  exports.getStoppedEnvironmentMessage = getStoppedEnvironmentMessage
211
279
  exports.readDotEnvFile = require_env.readDotEnvFile
212
280
  exports.resolveAnthropicApiKey = require_env.resolveAnthropicApiKey
213
- exports.resolveBuiltinAgentsHost = resolveBuiltinAgentsHost
214
- exports.resolveBuiltinAgentsPort = resolveBuiltinAgentsPort
215
281
  exports.resolveComposeProjectName = resolveComposeProjectName
216
282
  exports.resolveElectricAgentsPort = resolveElectricAgentsPort
283
+ exports.resolvePullWakeOwnerId = resolvePullWakeOwnerId
284
+ exports.resolvePullWakeRunnerId = resolvePullWakeRunnerId
217
285
  exports.startBuiltinAgentsServer = startBuiltinAgentsServer
218
286
  exports.startElectricAgentsDevEnvironment = startElectricAgentsDevEnvironment
219
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 };