@alfe.ai/gateway 0.0.4 → 0.0.5

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.
@@ -118,7 +118,9 @@ Examples:
118
118
  }
119
119
  main().catch((err) => {
120
120
  const message = err instanceof Error ? err.message : String(err);
121
+ const stack = err instanceof Error ? err.stack : void 0;
121
122
  console.error("Fatal error:", message);
123
+ if (stack) console.error(stack);
122
124
  process.exit(1);
123
125
  });
124
126
  //#endregion
package/dist/health.js CHANGED
@@ -11,6 +11,7 @@ import WebSocket from "ws";
11
11
  import { createConnection, createServer } from "node:net";
12
12
  import { execSync, spawn } from "node:child_process";
13
13
  import { IntegrationManager, IntegrationManagerAdapter, OpenClawApplier } from "@alfe.ai/integrations";
14
+ import { randomUUID } from "node:crypto";
14
15
  import stream, { Readable } from "stream";
15
16
  import util, { format } from "util";
16
17
  import http from "http";
@@ -19,7 +20,6 @@ import url from "url";
19
20
  import http2 from "http2";
20
21
  import zlib from "zlib";
21
22
  import { EventEmitter } from "events";
22
- import { randomUUID } from "node:crypto";
23
23
  //#region \0rolldown/runtime.js
24
24
  var __create = Object.create;
25
25
  var __defProp = Object.defineProperty;
@@ -58,22 +58,25 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
58
58
  * Daemon logger — pino with rolling file output.
59
59
  *
60
60
  * Writes to ~/.alfe/logs/gateway.log with 10MB rotation, keep 5 files.
61
- * Also outputs to stdout in development mode.
61
+ * In managed mode (ECS Fargate): always stdout for CloudWatch capture.
62
+ * In development: stdout.
62
63
  */
63
64
  const LOG_DIR = join(homedir(), ".alfe", "logs");
64
65
  const LOG_FILE = join(LOG_DIR, "gateway.log");
65
- try {
66
+ const isDev = process.env.NODE_ENV !== "production";
67
+ const isManaged = process.env.ALFE_MANAGED === "true";
68
+ if (!isManaged) try {
66
69
  mkdirSync(LOG_DIR, { recursive: true });
67
70
  } catch {}
68
- const isDev = process.env.NODE_ENV !== "production";
69
71
  /**
70
72
  * Create the daemon logger.
71
- * In development: pretty-prints to stdout.
72
- * In production: JSON to rolling file.
73
+ * Managed mode (ECS): JSON to stdout (CloudWatch captures via awslogs driver).
74
+ * Development: JSON to stdout.
75
+ * Production (local): JSON to rolling file.
73
76
  */
74
77
  function createLogger$1() {
75
- if (isDev) return pino({
76
- level: process.env.LOG_LEVEL ?? "debug",
78
+ if (isDev || isManaged) return pino({
79
+ level: process.env.LOG_LEVEL ?? (isDev ? "debug" : "info"),
77
80
  transport: {
78
81
  target: "pino/file",
79
82
  options: { destination: 1 }
@@ -3161,6 +3164,26 @@ enumValues({
3161
3164
  Live: "live"
3162
3165
  });
3163
3166
  //#endregion
3167
+ //#region ../../packages-internal/types/dist/models.js
3168
+ const AnthropicModel = {
3169
+ Opus: "claude-opus-4-6",
3170
+ Sonnet: "claude-sonnet-4-6",
3171
+ Haiku: "claude-haiku-4-5"
3172
+ };
3173
+ const ANTHROPIC_MODELS = enumValues(AnthropicModel);
3174
+ const OpenAIModel = {
3175
+ GPT4o: "gpt-4o",
3176
+ GPT4oMini: "gpt-4o-mini",
3177
+ O3: "o3"
3178
+ };
3179
+ const OPENAI_MODELS = enumValues(OpenAIModel);
3180
+ [...ANTHROPIC_MODELS, ...OPENAI_MODELS];
3181
+ _enum(ANTHROPIC_MODELS);
3182
+ _enum(OPENAI_MODELS);
3183
+ _enum([...ANTHROPIC_MODELS, ...OPENAI_MODELS]);
3184
+ AnthropicModel.Opus, AnthropicModel.Sonnet, AnthropicModel.Haiku, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3;
3185
+ AnthropicModel.Opus, AnthropicModel.Sonnet, AnthropicModel.Haiku, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3;
3186
+ //#endregion
3164
3187
  //#region src/config.ts
3165
3188
  /**
3166
3189
  * Daemon configuration — reads ~/.alfe/config.toml and resolves agent identity.
@@ -3179,6 +3202,10 @@ const PID_PATH = join(ALFE_DIR, "gateway.pid");
3179
3202
  * Returns agentId (derived from tokenId) and orgId (tenantId).
3180
3203
  */
3181
3204
  async function resolveAgentIdentity(apiKey, apiEndpoint) {
3205
+ logger$1.debug({
3206
+ apiEndpoint,
3207
+ keyPrefix: apiKey.slice(0, 8) + "..."
3208
+ }, "Resolving agent identity...");
3182
3209
  const auth = new AuthService(new AlfeApiClient({
3183
3210
  apiBaseUrl: apiEndpoint,
3184
3211
  getToken: () => Promise.resolve(apiKey)
@@ -3186,22 +3213,47 @@ async function resolveAgentIdentity(apiKey, apiEndpoint) {
3186
3213
  const maxAttempts = 3;
3187
3214
  const delayMs = 3e3;
3188
3215
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3216
+ logger$1.debug({
3217
+ attempt,
3218
+ maxAttempts
3219
+ }, "Validating token...");
3189
3220
  const result = await auth.validate(apiKey);
3190
3221
  if (!result.ok) {
3222
+ logger$1.debug({
3223
+ status: result.status,
3224
+ error: result.error,
3225
+ attempt
3226
+ }, "Token validation failed");
3191
3227
  if (result.status === 401 || result.status === 403) throw new Error(`Token validation failed: ${result.error}. Is your API key valid? Run \`alfe login\` to reconfigure.`);
3192
3228
  if (attempt < maxAttempts) {
3229
+ logger$1.debug({
3230
+ delayMs,
3231
+ attempt
3232
+ }, "Retrying token validation after transient error...");
3193
3233
  await new Promise((r) => setTimeout(r, delayMs));
3194
3234
  continue;
3195
3235
  }
3196
3236
  throw new Error(`Token validation failed after ${String(maxAttempts)} attempts: ${result.error}. Is your API key valid? Run \`alfe login\` to reconfigure.`);
3197
3237
  }
3238
+ logger$1.debug({
3239
+ valid: result.data.valid,
3240
+ tenantId: result.data.tenantId,
3241
+ tokenId: result.data.tokenId
3242
+ }, "Token validation response");
3198
3243
  if (!result.data.valid) throw new Error("API key invalid: validation failed. Run `alfe login` to reconfigure.");
3199
3244
  const orgId = result.data.tenantId;
3200
3245
  if (!orgId) throw new Error("Token validation returned no tenantId — cannot determine org.");
3246
+ const agentId = result.data.tokenId ?? deriveAgentId(apiKey);
3247
+ const runtime = result.data.runtime ?? "openclaw";
3248
+ logger$1.debug({
3249
+ agentId,
3250
+ orgId,
3251
+ runtime
3252
+ }, "Agent identity resolved");
3201
3253
  return {
3202
- agentId: result.data.tokenId ?? deriveAgentId(apiKey),
3254
+ agentId,
3203
3255
  orgId,
3204
- runtime: result.data.runtime ?? "openclaw"
3256
+ runtime
3205
3257
  };
3206
3258
  }
3207
3259
  throw new Error("Token validation failed: exhausted retries.");
@@ -3260,15 +3312,27 @@ function isManagedMode() {
3260
3312
  * All config comes from environment variables — no local files needed.
3261
3313
  */
3262
3314
  async function loadManagedConfig() {
3315
+ logger$1.debug("Loading managed config from environment...");
3263
3316
  const apiKey = process.env.ALFE_API_KEY;
3264
3317
  const apiEndpoint = process.env.ALFE_API_ENDPOINT;
3265
- if (!apiKey || !apiEndpoint) throw new Error("ALFE_API_KEY and ALFE_API_ENDPOINT required in managed mode");
3318
+ if (!apiKey || !apiEndpoint) throw new Error(`ALFE_API_KEY and ALFE_API_ENDPOINT required in managed mode (apiKey=${apiKey ? "set" : "missing"}, apiEndpoint=${apiEndpoint ? "set" : "missing"})`);
3319
+ logger$1.debug({
3320
+ apiEndpoint,
3321
+ gatewayWsUrlEnv: process.env.ALFE_GATEWAY_WS_URL ?? "(not set)"
3322
+ }, "Environment loaded, resolving identity...");
3266
3323
  const identity = await resolveAgentIdentity(apiKey, apiEndpoint);
3324
+ const gatewayWsUrl = process.env.ALFE_GATEWAY_WS_URL ?? deriveGatewayWsUrl(apiEndpoint);
3325
+ logger$1.debug({
3326
+ agentId: identity.agentId,
3327
+ orgId: identity.orgId,
3328
+ runtime: identity.runtime,
3329
+ gatewayWsUrl
3330
+ }, "Managed config resolved");
3267
3331
  return {
3268
3332
  apiKey,
3269
3333
  apiEndpoint,
3270
- gatewayWsUrl: process.env.ALFE_GATEWAY_WS_URL ?? deriveGatewayWsUrl(apiEndpoint),
3271
- socketPath: "",
3334
+ gatewayWsUrl,
3335
+ socketPath: "/tmp/alfe-gateway.sock",
3272
3336
  pidPath: "",
3273
3337
  agentId: identity.agentId,
3274
3338
  orgId: identity.orgId,
@@ -3317,26 +3381,44 @@ async function loadDaemonConfig() {
3317
3381
  */
3318
3382
  async function fetchAgentConfig(apiKey, apiEndpoint) {
3319
3383
  try {
3384
+ logger$1.debug({ apiEndpoint }, "Fetching agent workspace config...");
3320
3385
  const wsResponse = await fetch(`${apiEndpoint}/agents/me/workspace`, {
3321
3386
  method: "GET",
3322
3387
  headers: { "Authorization": `Bearer ${apiKey}` }
3323
3388
  });
3324
- if (!wsResponse.ok) return null;
3389
+ if (!wsResponse.ok) {
3390
+ logger$1.debug({ status: wsResponse.status }, "Workspace config fetch failed");
3391
+ return null;
3392
+ }
3325
3393
  const templateKey = (await wsResponse.json()).data?.templateKey;
3326
- if (!templateKey) return null;
3394
+ if (!templateKey) {
3395
+ logger$1.debug("No templateKey in workspace response");
3396
+ return null;
3397
+ }
3398
+ logger$1.debug({ templateKey }, "Fetching template files...");
3327
3399
  const filesResponse = await fetch(`${apiEndpoint}/templates/${encodeURIComponent(templateKey)}/files`, {
3328
3400
  method: "GET",
3329
3401
  headers: { "Authorization": `Bearer ${apiKey}` }
3330
3402
  });
3331
- if (!filesResponse.ok) return {
3403
+ if (!filesResponse.ok) {
3404
+ logger$1.debug({ status: filesResponse.status }, "Template files fetch failed");
3405
+ return {
3406
+ templateKey,
3407
+ files: {}
3408
+ };
3409
+ }
3410
+ const filesResult = await filesResponse.json();
3411
+ const fileCount = Object.keys(filesResult.data?.files ?? {}).length;
3412
+ logger$1.debug({
3332
3413
  templateKey,
3333
- files: {}
3334
- };
3414
+ fileCount
3415
+ }, "Template files fetched");
3335
3416
  return {
3336
3417
  templateKey,
3337
- files: (await filesResponse.json()).data?.files ?? {}
3418
+ files: filesResult.data?.files ?? {}
3338
3419
  };
3339
- } catch {
3420
+ } catch (err) {
3421
+ logger$1.debug({ err: err instanceof Error ? err.message : String(err) }, "fetchAgentConfig failed");
3340
3422
  return null;
3341
3423
  }
3342
3424
  }
@@ -3696,6 +3778,10 @@ var CloudClient = class {
3696
3778
  * Start the cloud connection with auto-reconnect.
3697
3779
  */
3698
3780
  start() {
3781
+ logger$1.debug({
3782
+ wsUrl: this.config.wsUrl,
3783
+ agentId: this.config.agentId
3784
+ }, "Cloud client starting...");
3699
3785
  this.closed = false;
3700
3786
  this.doConnect();
3701
3787
  }
@@ -3703,15 +3789,18 @@ var CloudClient = class {
3703
3789
  * Stop the cloud connection and all timers.
3704
3790
  */
3705
3791
  stop() {
3792
+ logger$1.debug("Cloud client stopping...");
3706
3793
  this.closed = true;
3707
3794
  this.stopPingTimer();
3708
3795
  if (this.ws) {
3796
+ logger$1.debug({ readyState: this.ws.readyState }, "Cloud: closing WebSocket");
3709
3797
  try {
3710
3798
  this.ws.close(1e3, "Daemon shutting down");
3711
3799
  } catch {}
3712
3800
  this.ws = null;
3713
3801
  }
3714
3802
  this.registered = false;
3803
+ logger$1.debug("Cloud client stopped");
3715
3804
  }
3716
3805
  /**
3717
3806
  * Get connection latency (time since last pong).
@@ -3722,22 +3811,36 @@ var CloudClient = class {
3722
3811
  return Date.now() - this.lastPong;
3723
3812
  }
3724
3813
  doConnect() {
3725
- if (this.closed) return;
3726
- logger$1.info({ url: this.config.wsUrl }, "Connecting to cloud gateway...");
3814
+ if (this.closed) {
3815
+ logger$1.debug("Cloud: doConnect skipped client is closed");
3816
+ return;
3817
+ }
3818
+ logger$1.info({
3819
+ url: this.config.wsUrl,
3820
+ backoffMs: this.backoffMs
3821
+ }, "Connecting to cloud gateway...");
3822
+ logger$1.debug({
3823
+ agentId: this.config.agentId,
3824
+ keyPrefix: this.config.apiKey.slice(0, 12) + "..."
3825
+ }, "Cloud: connection details");
3727
3826
  this.ws = new WebSocket(this.config.wsUrl, {
3728
3827
  headers: { authorization: `Bearer ${this.config.apiKey}` },
3729
- maxPayload: 10 * 1024 * 1024
3828
+ maxPayload: 10 * 1024 * 1024,
3829
+ handshakeTimeout: 1e4
3730
3830
  });
3731
3831
  this.ws.on("open", () => {
3732
3832
  logger$1.info("Cloud WebSocket connected");
3833
+ logger$1.debug({ readyState: this.ws?.readyState }, "Cloud: WebSocket open, sending registration...");
3733
3834
  this.backoffMs = 1e3;
3734
3835
  this.sendRegister();
3735
3836
  });
3736
3837
  this.ws.on("message", (data) => {
3737
3838
  const text = Buffer.isBuffer(data) ? data.toString("utf-8") : Buffer.from(data).toString("utf-8");
3839
+ logger$1.debug({ size: text.length }, "Cloud: received message");
3738
3840
  this.handleMessage(text);
3739
3841
  });
3740
3842
  this.ws.on("ping", () => {
3843
+ logger$1.debug("Cloud: received ping, sending pong");
3741
3844
  this.ws?.pong();
3742
3845
  });
3743
3846
  this.ws.on("close", (code, reason) => {
@@ -3751,11 +3854,18 @@ var CloudClient = class {
3751
3854
  this.scheduleReconnect();
3752
3855
  });
3753
3856
  this.ws.on("error", (err) => {
3754
- logger$1.error({ err: err.message }, "Cloud WebSocket error");
3857
+ logger$1.error({
3858
+ err: err.message,
3859
+ url: this.config.wsUrl
3860
+ }, "Cloud WebSocket error");
3755
3861
  });
3756
3862
  }
3757
3863
  sendRegister() {
3758
3864
  const msg = createServiceRegister(this.config.agentId);
3865
+ logger$1.debug({
3866
+ serviceId: msg.serviceId,
3867
+ agentIds: msg.agentIds
3868
+ }, "Cloud: sending SERVICE_REGISTER");
3759
3869
  this.send(msg);
3760
3870
  logger$1.info({ serviceId: msg.serviceId }, "Sent SERVICE_REGISTER");
3761
3871
  }
@@ -3868,6 +3978,7 @@ var CloudClient = class {
3868
3978
  }
3869
3979
  send(msg) {
3870
3980
  if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(msg));
3981
+ else logger$1.debug({ readyState: this.ws?.readyState }, "Cloud: send skipped — WebSocket not open");
3871
3982
  }
3872
3983
  startPingTimer() {
3873
3984
  this.stopPingTimer();
@@ -3888,10 +3999,16 @@ var CloudClient = class {
3888
3999
  }
3889
4000
  }
3890
4001
  scheduleReconnect() {
3891
- if (this.closed) return;
4002
+ if (this.closed) {
4003
+ logger$1.debug("Cloud: reconnect skipped — client is closed");
4004
+ return;
4005
+ }
3892
4006
  const delay = this.backoffMs;
3893
4007
  this.backoffMs = Math.min(this.backoffMs * 2, 3e4);
3894
- logger$1.info({ delayMs: delay }, "Cloud: reconnecting...");
4008
+ logger$1.info({
4009
+ delayMs: delay,
4010
+ nextBackoffMs: this.backoffMs
4011
+ }, "Cloud: scheduling reconnect...");
3895
4012
  setTimeout(() => {
3896
4013
  this.doConnect();
3897
4014
  }, delay);
@@ -19500,7 +19617,11 @@ var RuntimeProcess = class {
19500
19617
  switch (this.options.runtime) {
19501
19618
  case "openclaw": return {
19502
19619
  command: "openclaw",
19503
- args: ["--workspace", this.options.workspace]
19620
+ args: [
19621
+ "gateway",
19622
+ "run",
19623
+ "--allow-unconfigured"
19624
+ ]
19504
19625
  };
19505
19626
  default: throw new Error(`Unsupported runtime: ${this.options.runtime}`);
19506
19627
  }
@@ -19635,6 +19756,7 @@ var LocalAgentRelay = class {
19635
19756
  * Start connecting to the local runtime WS.
19636
19757
  */
19637
19758
  start() {
19759
+ log.debug({ wsUrl: this.options.wsUrl }, "Local relay starting...");
19638
19760
  this.stopped = false;
19639
19761
  this.doConnect();
19640
19762
  }
@@ -19642,12 +19764,14 @@ var LocalAgentRelay = class {
19642
19764
  * Stop the relay and close the local WS connection.
19643
19765
  */
19644
19766
  stop() {
19767
+ log.debug("Local relay stopping...");
19645
19768
  this.stopped = true;
19646
19769
  if (this.retryTimer) {
19647
19770
  clearTimeout(this.retryTimer);
19648
19771
  this.retryTimer = null;
19649
19772
  }
19650
19773
  if (this.ws) {
19774
+ log.debug({ readyState: this.ws.readyState }, "Local relay: closing WebSocket");
19651
19775
  try {
19652
19776
  this.ws.close(1e3);
19653
19777
  } catch (err) {
@@ -19656,25 +19780,33 @@ var LocalAgentRelay = class {
19656
19780
  this.ws = null;
19657
19781
  }
19658
19782
  this.connected = false;
19783
+ log.debug("Local relay stopped");
19659
19784
  }
19660
19785
  /**
19661
19786
  * Forward a message payload from the cloud to the local runtime WS.
19662
19787
  */
19663
19788
  forward(payload) {
19664
19789
  if (this.ws?.readyState !== WebSocket.OPEN) {
19665
- log.warn("Cannot forward to local runtime — not connected");
19790
+ log.warn({ readyState: this.ws?.readyState }, "Cannot forward to local runtime — not connected");
19666
19791
  return;
19667
19792
  }
19668
19793
  const data = typeof payload === "string" ? payload : JSON.stringify(payload);
19794
+ log.debug({ size: data.length }, "Local relay: forwarding message to runtime");
19669
19795
  this.ws.send(data);
19670
19796
  }
19671
19797
  get isConnected() {
19672
19798
  return this.connected;
19673
19799
  }
19674
19800
  doConnect() {
19675
- if (this.stopped) return;
19676
- log.info({ url: this.options.wsUrl }, "Connecting to local runtime WS...");
19677
- this.ws = new WebSocket(this.options.wsUrl);
19801
+ if (this.stopped) {
19802
+ log.debug("Local relay: doConnect skipped relay is stopped");
19803
+ return;
19804
+ }
19805
+ log.info({
19806
+ url: this.options.wsUrl,
19807
+ retryCount: this.retryCount
19808
+ }, "Connecting to local runtime WS...");
19809
+ this.ws = new WebSocket(this.options.wsUrl, { handshakeTimeout: 1e4 });
19678
19810
  this.ws.on("open", () => {
19679
19811
  log.info("Local runtime WS connected — performing handshake");
19680
19812
  this.retryCount = 0;
@@ -19682,6 +19814,7 @@ var LocalAgentRelay = class {
19682
19814
  });
19683
19815
  this.ws.on("message", (data) => {
19684
19816
  const text = Buffer.isBuffer(data) ? data.toString("utf-8") : Buffer.from(data).toString("utf-8");
19817
+ log.debug({ size: text.length }, "Local relay: received message");
19685
19818
  this.handleLocalMessage(text);
19686
19819
  });
19687
19820
  this.ws.on("close", (code, reason) => {
@@ -19693,10 +19826,14 @@ var LocalAgentRelay = class {
19693
19826
  this.scheduleReconnect();
19694
19827
  });
19695
19828
  this.ws.on("error", (err) => {
19696
- log.debug({ err: err.message }, "Local runtime WS error");
19829
+ log.warn({
19830
+ err: err.message,
19831
+ url: this.options.wsUrl
19832
+ }, "Local runtime WS error");
19697
19833
  });
19698
19834
  }
19699
19835
  performHandshake() {
19836
+ log.debug("Local relay: sending connect handshake");
19700
19837
  const connectReq = {
19701
19838
  type: "req",
19702
19839
  id: randomUUID(),
@@ -19705,7 +19842,7 @@ var LocalAgentRelay = class {
19705
19842
  minProtocol: 3,
19706
19843
  maxProtocol: 3,
19707
19844
  client: {
19708
- id: "alfe-daemon-relay",
19845
+ id: "gateway-client",
19709
19846
  displayName: "Alfe Daemon Relay",
19710
19847
  version: "0.1.0",
19711
19848
  platform: process.platform,
@@ -19783,18 +19920,33 @@ let runtimeProcess = null;
19783
19920
  let localRelay = null;
19784
19921
  let cloudConnected = false;
19785
19922
  let shuttingDown = false;
19923
+ /**
19924
+ * Flush pino's async transport and exit.
19925
+ * process.exit() can drop buffered log lines — this ensures they're written first.
19926
+ */
19927
+ async function flushAndExit(code) {
19928
+ await new Promise((resolve) => {
19929
+ logger$1.flush();
19930
+ setTimeout(resolve, 500);
19931
+ });
19932
+ process.exit(code);
19933
+ }
19786
19934
  async function startDaemon() {
19787
19935
  startedAt = Date.now();
19788
19936
  const managed = isManagedMode();
19789
- logger$1.info({ managed }, "Starting Alfe Gateway Daemon...");
19937
+ logger$1.info({
19938
+ managed,
19939
+ pid: process.pid
19940
+ }, "Starting Alfe Gateway Daemon...");
19790
19941
  if (!managed) {
19791
19942
  await mkdir(join(homedir(), ".alfe"), { recursive: true });
19792
19943
  const existingPid = await checkExistingDaemon();
19793
19944
  if (existingPid) {
19794
19945
  logger$1.error({ pid: existingPid }, "Daemon already running. Use `alfe gateway stop` first.");
19795
- process.exit(1);
19946
+ await flushAndExit(1);
19796
19947
  }
19797
19948
  }
19949
+ logger$1.debug("Loading daemon config...");
19798
19950
  try {
19799
19951
  config = await loadDaemonConfig();
19800
19952
  logger$1.info({
@@ -19804,11 +19956,17 @@ async function startDaemon() {
19804
19956
  }, "Config loaded, identity resolved");
19805
19957
  } catch (err) {
19806
19958
  const message = err instanceof Error ? err.message : String(err);
19807
- logger$1.error({ err: message }, "Failed to load config");
19808
- process.exit(1);
19809
- }
19959
+ const stack = err instanceof Error ? err.stack : void 0;
19960
+ logger$1.error({
19961
+ err: message,
19962
+ stack
19963
+ }, "Failed to load config");
19964
+ await flushAndExit(1);
19965
+ }
19966
+ logger$1.debug("Initializing command queue...");
19810
19967
  commandQueue = new CommandQueue();
19811
19968
  commandQueue.startGC();
19969
+ logger$1.debug("Starting AI proxy...");
19812
19970
  try {
19813
19971
  const { createProxyServer } = await import("@alfe.ai/ai-proxy-local");
19814
19972
  const { getAiServiceUrlFromToken } = await import("@alfe.ai/config");
@@ -19832,17 +19990,31 @@ async function startDaemon() {
19832
19990
  });
19833
19991
  } catch (err) {
19834
19992
  const message = err instanceof Error ? err.message : String(err);
19835
- logger$1.error({ err: message }, "Failed to start AI proxy LLM requests will fail");
19993
+ const stack = err instanceof Error ? err.stack : void 0;
19994
+ logger$1.error({
19995
+ err: message,
19996
+ stack
19997
+ }, "Failed to start AI proxy — LLM requests will fail");
19836
19998
  }
19999
+ logger$1.debug({ socketPath: config.socketPath }, "Starting IPC server...");
19837
20000
  ipcServer = new IPCServer(config.socketPath);
19838
20001
  ipcServer.setRequestHandler(handlePluginRequest);
19839
20002
  try {
19840
20003
  await ipcServer.start();
20004
+ logger$1.debug("IPC server started");
19841
20005
  } catch (err) {
19842
20006
  const message = err instanceof Error ? err.message : String(err);
19843
- logger$1.error({ err: message }, "Failed to start IPC server");
19844
- process.exit(1);
19845
- }
20007
+ const stack = err instanceof Error ? err.stack : void 0;
20008
+ logger$1.error({
20009
+ err: message,
20010
+ stack
20011
+ }, "Failed to start IPC server");
20012
+ await flushAndExit(1);
20013
+ }
20014
+ logger$1.debug({
20015
+ wsUrl: config.gatewayWsUrl,
20016
+ agentId: config.agentId
20017
+ }, "Connecting cloud client...");
19846
20018
  cloudClient = new CloudClient({
19847
20019
  wsUrl: config.gatewayWsUrl,
19848
20020
  apiKey: config.apiKey,
@@ -19869,9 +20041,16 @@ async function startDaemon() {
19869
20041
  const integrationAdapter = new IntegrationManagerAdapter(integrationManager);
19870
20042
  cloudClient.setIntegrationManager(integrationAdapter);
19871
20043
  cloudClient.start();
20044
+ logger$1.debug("Cloud client started");
19872
20045
  if (managed && config.runtime) {
20046
+ logger$1.debug({ runtime: config.runtime }, "Starting agent runtime (managed mode)...");
19873
20047
  const runtimeCfg = config.runtimes[config.runtime];
19874
20048
  if (runtimeCfg) {
20049
+ const runtimeToken = randomUUID();
20050
+ logger$1.debug({
20051
+ runtime: config.runtime,
20052
+ workspace: runtimeCfg.workspace
20053
+ }, "Creating runtime process...");
19875
20054
  runtimeProcess = new RuntimeProcess({
19876
20055
  runtime: config.runtime,
19877
20056
  workspace: runtimeCfg.workspace,
@@ -19881,13 +20060,17 @@ async function startDaemon() {
19881
20060
  ALFE_API_URL: config.apiEndpoint,
19882
20061
  ALFE_AGENT_ID: config.agentId,
19883
20062
  ANTHROPIC_BASE_URL: "http://127.0.0.1:18193",
19884
- OPENAI_BASE_URL: "http://127.0.0.1:18193"
20063
+ OPENAI_BASE_URL: "http://127.0.0.1:18193",
20064
+ OPENCLAW_GATEWAY_TOKEN: runtimeToken
19885
20065
  }
19886
20066
  });
19887
20067
  runtimeProcess.start();
20068
+ logger$1.debug("Runtime process started");
20069
+ const RUNTIME_WS_PORT = 18789;
20070
+ logger$1.debug({ runtimeWsPort: RUNTIME_WS_PORT }, "Creating local agent relay...");
19888
20071
  const relay = new LocalAgentRelay({
19889
- wsUrl: `ws://127.0.0.1:${String(18789)}`,
19890
- apiKey: config.apiKey,
20072
+ wsUrl: `ws://127.0.0.1:${String(RUNTIME_WS_PORT)}`,
20073
+ apiKey: runtimeToken,
19891
20074
  onAgentMessage: (payload) => {
19892
20075
  cloudClient.sendAgentEvent(payload);
19893
20076
  }
@@ -19896,6 +20079,7 @@ async function startDaemon() {
19896
20079
  cloudClient.setServiceRelayHandler((msg) => {
19897
20080
  relay.forward(msg.payload);
19898
20081
  });
20082
+ logger$1.debug("Scheduling local relay start in 2s (waiting for runtime WS server)...");
19899
20083
  setTimeout(() => {
19900
20084
  relay.start();
19901
20085
  }, 2e3);
@@ -19906,22 +20090,38 @@ async function startDaemon() {
19906
20090
  if (shuttingDown) return;
19907
20091
  shuttingDown = true;
19908
20092
  logger$1.info({ signal }, "Shutting down...");
19909
- if (localRelay) localRelay.stop();
19910
- if (runtimeProcess) await runtimeProcess.stop();
19911
- if (ipcServer) await ipcServer.stop();
20093
+ if (localRelay) {
20094
+ logger$1.debug("Stopping local relay...");
20095
+ localRelay.stop();
20096
+ logger$1.debug("Local relay stopped");
20097
+ }
20098
+ if (runtimeProcess) {
20099
+ logger$1.debug("Stopping runtime process...");
20100
+ await runtimeProcess.stop();
20101
+ logger$1.debug("Runtime process stopped");
20102
+ }
20103
+ if (ipcServer) {
20104
+ logger$1.debug("Stopping IPC server...");
20105
+ await ipcServer.stop();
20106
+ logger$1.debug("IPC server stopped");
20107
+ }
19912
20108
  if (aiProxyServer) {
20109
+ logger$1.debug("Stopping AI proxy...");
19913
20110
  const proxy = aiProxyServer;
19914
20111
  await new Promise((resolve) => {
19915
20112
  proxy.close(() => {
19916
20113
  resolve();
19917
20114
  });
19918
20115
  });
20116
+ logger$1.debug("AI proxy stopped");
19919
20117
  }
20118
+ logger$1.debug("Stopping cloud client...");
19920
20119
  cloudClient.stop();
20120
+ logger$1.debug("Cloud client stopped");
19921
20121
  commandQueue.stopGC();
19922
20122
  if (!managed) await removePidFile();
19923
20123
  logger$1.info("Daemon stopped");
19924
- process.exit(0);
20124
+ await flushAndExit(0);
19925
20125
  };
19926
20126
  process.on("SIGTERM", () => {
19927
20127
  shutdown("SIGTERM");
@@ -20173,4 +20373,4 @@ function formatDuration(ms) {
20173
20373
  return `${String(Math.round(seconds / 3600))}h`;
20174
20374
  }
20175
20375
  //#endregion
20176
- export { installService as a, PROTOCOL_VERSION as c, SOCKET_PATH as d, fetchAgentConfig as f, LOG_FILE as h, checkExistingDaemon as i, ALFE_DIR as l, resolveAgentIdentity as m, queryDaemonHealth as n, stopExistingDaemon as o, loadDaemonConfig as p, startDaemon as r, uninstallService as s, formatHealthReport as t, PID_PATH as u };
20376
+ export { installService as a, PROTOCOL_VERSION as c, SOCKET_PATH as d, fetchAgentConfig as f, logger$1 as g, LOG_FILE as h, checkExistingDaemon as i, ALFE_DIR as l, resolveAgentIdentity as m, queryDaemonHealth as n, stopExistingDaemon as o, loadDaemonConfig as p, startDaemon as r, uninstallService as s, formatHealthReport as t, PID_PATH as u };
@@ -1,3 +1,5 @@
1
+ import pino from "pino";
2
+
1
3
  //#region src/protocol.d.ts
2
4
 
3
5
  interface IPCRequest {
@@ -132,6 +134,9 @@ declare function queryDaemonHealth(socketPath: string, timeoutMs?: number): Prom
132
134
  */
133
135
  declare function formatHealthReport(health: DaemonHealth): string;
134
136
  //#endregion
137
+ //#region src/logger.d.ts
138
+ declare const logger: pino.Logger<never, boolean>;
139
+ //#endregion
135
140
  //#region src/process-manager.d.ts
136
141
  /**
137
142
  * Process management — launchd/systemd service installation.
@@ -161,4 +166,4 @@ declare function checkExistingDaemon(): Promise<number | null>;
161
166
  */
162
167
  declare function stopExistingDaemon(): Promise<boolean>;
163
168
  //#endregion
164
- export { ALFE_DIR, type AgentIdentity, type AgentWorkspaceConfig, type DaemonConfig, type DaemonHealth, type IPCEvent, type IPCRequest, type IPCResponse, PID_PATH, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, queryDaemonHealth, resolveAgentIdentity, startDaemon, stopExistingDaemon, uninstallService };
169
+ export { ALFE_DIR, type AgentIdentity, type AgentWorkspaceConfig, type DaemonConfig, type DaemonHealth, type IPCEvent, type IPCRequest, type IPCResponse, PID_PATH, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, startDaemon, stopExistingDaemon, uninstallService };
package/dist/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as installService, c as PROTOCOL_VERSION, d as SOCKET_PATH, f as fetchAgentConfig, i as checkExistingDaemon, l as ALFE_DIR, m as resolveAgentIdentity, n as queryDaemonHealth, o as stopExistingDaemon, p as loadDaemonConfig, r as startDaemon, s as uninstallService, t as formatHealthReport, u as PID_PATH } from "../health.js";
2
- export { ALFE_DIR, PID_PATH, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, queryDaemonHealth, resolveAgentIdentity, startDaemon, stopExistingDaemon, uninstallService };
1
+ import { a as installService, c as PROTOCOL_VERSION, d as SOCKET_PATH, f as fetchAgentConfig, g as logger, i as checkExistingDaemon, l as ALFE_DIR, m as resolveAgentIdentity, n as queryDaemonHealth, o as stopExistingDaemon, p as loadDaemonConfig, r as startDaemon, s as uninstallService, t as formatHealthReport, u as PID_PATH } from "../health.js";
2
+ export { ALFE_DIR, PID_PATH, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, startDaemon, stopExistingDaemon, uninstallService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/gateway",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Alfe local gateway daemon — persistent control plane for agent integrations",
5
5
  "type": "module",
6
6
  "bin": {