@alfe.ai/gateway 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/health.js +255 -123
  2. package/package.json +3 -3
package/dist/health.js CHANGED
@@ -20,6 +20,7 @@ import url from "url";
20
20
  import http2 from "http2";
21
21
  import zlib from "zlib";
22
22
  import { EventEmitter } from "events";
23
+ import { pathToFileURL } from "node:url";
23
24
  import { randomUUID } from "node:crypto";
24
25
  //#region \0rolldown/runtime.js
25
26
  var __create = Object.create;
@@ -350,21 +351,15 @@ var AuthService = class {
350
351
  deleteToken(tokenId) {
351
352
  return this.client.request(`${this.prefix}/tokens/${tokenId}`, { method: "DELETE" });
352
353
  }
353
- getReferralCode() {
354
- return this.client.request(`${this.prefix}/referral/code`);
354
+ getOnboardingStatus() {
355
+ return this.client.request(`${this.prefix}/onboarding/status`);
355
356
  }
356
- redeemCode(code) {
357
- return this.client.request(`${this.prefix}/referral/redeem`, {
357
+ completeOnboarding(input) {
358
+ return this.client.request(`${this.prefix}/onboarding/complete`, {
358
359
  method: "POST",
359
- body: JSON.stringify({ code })
360
+ body: JSON.stringify(input)
360
361
  });
361
362
  }
362
- getReferralStatus() {
363
- return this.client.request(`${this.prefix}/referral/status`);
364
- }
365
- getReferralHistory() {
366
- return this.client.request(`${this.prefix}/referral/history`);
367
- }
368
363
  };
369
364
  //#endregion
370
365
  //#region ../../packages-internal/api-client/dist/services/integrations.js
@@ -3216,7 +3211,8 @@ enumValues({
3216
3211
  Revoked: "revoked"
3217
3212
  });
3218
3213
  enumValues({
3219
- Pending: "pending",
3214
+ PendingCard: "pending_card",
3215
+ CardFulfilled: "card_fulfilled",
3220
3216
  Completed: "completed",
3221
3217
  Failed: "failed"
3222
3218
  });
@@ -3705,7 +3701,7 @@ function isIPCResponse(msg) {
3705
3701
  const PROTOCOL_VERSION = 1;
3706
3702
  //#endregion
3707
3703
  //#region src/reconciliation.ts
3708
- const log$1 = logger$1.child({ component: "Reconciliation" });
3704
+ const log$2 = logger$1.child({ component: "Reconciliation" });
3709
3705
  var ReconciliationEngine = class {
3710
3706
  constructor(manager) {
3711
3707
  this.manager = manager;
@@ -3726,7 +3722,7 @@ var ReconciliationEngine = class {
3726
3722
  try {
3727
3723
  localIntegrations = await this.manager.getInstalledIntegrations();
3728
3724
  } catch (err) {
3729
- log$1.error({ err }, "Failed to get local integrations");
3725
+ log$2.error({ err }, "Failed to get local integrations");
3730
3726
  localIntegrations = [];
3731
3727
  }
3732
3728
  const localMap = new Map(localIntegrations.map((i) => [i.id, i]));
@@ -3740,10 +3736,10 @@ var ReconciliationEngine = class {
3740
3736
  const id = desired.integrationId;
3741
3737
  try {
3742
3738
  if (!local) {
3743
- log$1.info(`Installing ${id}@${desired.version}`);
3739
+ log$2.info(`Installing ${id}@${desired.version}`);
3744
3740
  await this.manager.install(id, desired.version, desired.config);
3745
3741
  report.installed.push(id);
3746
- log$1.info(`Activating ${id}`);
3742
+ log$2.info(`Activating ${id}`);
3747
3743
  await this.manager.activate(id);
3748
3744
  report.activated.push(id);
3749
3745
  report.results.push({
@@ -3754,14 +3750,14 @@ var ReconciliationEngine = class {
3754
3750
  return;
3755
3751
  }
3756
3752
  if (local.version !== desired.version && desired.version !== "" && local.version !== "unknown") {
3757
- log$1.info(`Upgrading ${id}: ${local.version} → ${desired.version}`);
3753
+ log$2.info(`Upgrading ${id}: ${local.version} → ${desired.version}`);
3758
3754
  try {
3759
3755
  await this.manager.deactivate(id);
3760
3756
  await this.manager.uninstall(id);
3761
3757
  } catch {}
3762
3758
  await this.manager.install(id, desired.version, desired.config);
3763
3759
  report.installed.push(id);
3764
- log$1.info(`Activating ${id}`);
3760
+ log$2.info(`Activating ${id}`);
3765
3761
  await this.manager.activate(id);
3766
3762
  report.activated.push(id);
3767
3763
  report.results.push({
@@ -3772,7 +3768,7 @@ var ReconciliationEngine = class {
3772
3768
  return;
3773
3769
  }
3774
3770
  if (local.status !== "active") {
3775
- log$1.info(`Activating ${id}`);
3771
+ log$2.info(`Activating ${id}`);
3776
3772
  await this.manager.activate(id);
3777
3773
  report.activated.push(id);
3778
3774
  report.results.push({
@@ -3789,7 +3785,7 @@ var ReconciliationEngine = class {
3789
3785
  });
3790
3786
  } catch (err) {
3791
3787
  const message = err instanceof Error ? err.message : String(err);
3792
- log$1.error({ err }, `Error reconciling ${id}`);
3788
+ log$2.error({ err }, `Error reconciling ${id}`);
3793
3789
  report.errors.push({
3794
3790
  integrationId: id,
3795
3791
  error: message
@@ -3812,7 +3808,7 @@ var ReconciliationEngine = class {
3812
3808
  return;
3813
3809
  }
3814
3810
  try {
3815
- log$1.info(`Removing ${id}`);
3811
+ log$2.info(`Removing ${id}`);
3816
3812
  if (local.status === "active") {
3817
3813
  await this.manager.deactivate(id);
3818
3814
  report.deactivated.push(id);
@@ -3826,7 +3822,7 @@ var ReconciliationEngine = class {
3826
3822
  });
3827
3823
  } catch (err) {
3828
3824
  const message = err instanceof Error ? err.message : String(err);
3829
- log$1.error({ err }, `Error removing ${id}`);
3825
+ log$2.error({ err }, `Error removing ${id}`);
3830
3826
  report.errors.push({
3831
3827
  integrationId: id,
3832
3828
  error: message
@@ -3861,6 +3857,7 @@ var CloudClient = class {
3861
3857
  onCommand = null;
3862
3858
  onConnectionChange = null;
3863
3859
  onPluginsChanged = null;
3860
+ onReconciliationComplete = null;
3864
3861
  reconciliationEngine = null;
3865
3862
  constructor(config) {
3866
3863
  this.config = config;
@@ -3890,6 +3887,12 @@ var CloudClient = class {
3890
3887
  setPluginsChangedHandler(handler) {
3891
3888
  this.onPluginsChanged = handler;
3892
3889
  }
3890
+ /**
3891
+ * Set a callback for when reconciliation completes — used to rebuild command registry.
3892
+ */
3893
+ setOnReconciliationComplete(handler) {
3894
+ this.onReconciliationComplete = handler;
3895
+ }
3893
3896
  setIntegrationManager(manager) {
3894
3897
  this.reconciliationEngine = new ReconciliationEngine(manager);
3895
3898
  }
@@ -3922,6 +3925,13 @@ var CloudClient = class {
3922
3925
  logger$1.debug("Cloud client stopped");
3923
3926
  }
3924
3927
  /**
3928
+ * Send a typed message to the cloud gateway.
3929
+ * Silently drops if WebSocket is not open.
3930
+ */
3931
+ sendMessage(msg) {
3932
+ this.send(msg);
3933
+ }
3934
+ /**
3925
3935
  * Get connection latency (time since last pong).
3926
3936
  * Returns -1 if no pong received yet.
3927
3937
  */
@@ -4083,6 +4093,7 @@ var CloudClient = class {
4083
4093
  const reportMsg = createReconciliationReport(report.results);
4084
4094
  this.send(reportMsg);
4085
4095
  if ((report.installed.length > 0 || report.uninstalled.length > 0) && this.onPluginsChanged) this.onPluginsChanged();
4096
+ this.onReconciliationComplete?.();
4086
4097
  } catch (err) {
4087
4098
  const message = err instanceof Error ? err.message : String(err);
4088
4099
  logger$1.error({ err: message }, "Cloud: reconciliation failed");
@@ -19743,7 +19754,7 @@ function createLogger(component, additionalData) {
19743
19754
  * Spawns the runtime binary, pipes stdout/stderr to the daemon logger,
19744
19755
  * and restarts on crash with exponential backoff.
19745
19756
  */
19746
- const log = createLogger("RuntimeProcess");
19757
+ const log$1 = createLogger("RuntimeProcess");
19747
19758
  const BACKOFF_INITIAL_MS = 1e3;
19748
19759
  const BACKOFF_MAX_MS = 3e4;
19749
19760
  const STABLE_UPTIME_MS = 6e4;
@@ -19779,13 +19790,14 @@ var RuntimeProcess = class {
19779
19790
  if (this.stopped) return;
19780
19791
  const { command, args } = this.resolveCommand();
19781
19792
  this.lastStartTime = Date.now();
19782
- log.info({
19793
+ log$1.info({
19783
19794
  runtime: this.options.runtime,
19784
19795
  command,
19785
19796
  args,
19786
19797
  workspace: this.options.workspace
19787
19798
  }, "Starting runtime process");
19788
19799
  this.child = spawn(command, args, {
19800
+ cwd: this.options.workspace,
19789
19801
  env: {
19790
19802
  ...process.env,
19791
19803
  ...this.options.env
@@ -19798,14 +19810,14 @@ var RuntimeProcess = class {
19798
19810
  });
19799
19811
  this.child.stdout?.on("data", (data) => {
19800
19812
  const lines = data.toString().trim().split("\n");
19801
- for (const line of lines) log.info({
19813
+ for (const line of lines) log$1.info({
19802
19814
  runtime: this.options.runtime,
19803
19815
  stream: "stdout"
19804
19816
  }, line);
19805
19817
  });
19806
19818
  this.child.stderr?.on("data", (data) => {
19807
19819
  const lines = data.toString().trim().split("\n");
19808
- for (const line of lines) log.warn({
19820
+ for (const line of lines) log$1.warn({
19809
19821
  runtime: this.options.runtime,
19810
19822
  stream: "stderr"
19811
19823
  }, line);
@@ -19813,14 +19825,14 @@ var RuntimeProcess = class {
19813
19825
  this.child.on("exit", (code, signal) => {
19814
19826
  this.child = null;
19815
19827
  if (this.stopped) {
19816
- log.info({
19828
+ log$1.info({
19817
19829
  runtime: this.options.runtime,
19818
19830
  code,
19819
19831
  signal
19820
19832
  }, "Runtime stopped (expected)");
19821
19833
  return;
19822
19834
  }
19823
- log.warn({
19835
+ log$1.warn({
19824
19836
  runtime: this.options.runtime,
19825
19837
  code,
19826
19838
  signal,
@@ -19834,7 +19846,7 @@ var RuntimeProcess = class {
19834
19846
  this.backoffMs = Math.min(this.backoffMs * 2, BACKOFF_MAX_MS);
19835
19847
  });
19836
19848
  this.child.on("error", (err) => {
19837
- log.error({
19849
+ log$1.error({
19838
19850
  runtime: this.options.runtime,
19839
19851
  err: err.message
19840
19852
  }, "Runtime process error");
@@ -19853,7 +19865,7 @@ var RuntimeProcess = class {
19853
19865
  if (!child) return;
19854
19866
  return new Promise((resolve) => {
19855
19867
  const killTimer = setTimeout(() => {
19856
- log.warn({ runtime: this.options.runtime }, "Runtime did not exit in time — sending SIGKILL");
19868
+ log$1.warn({ runtime: this.options.runtime }, "Runtime did not exit in time — sending SIGKILL");
19857
19869
  child.kill("SIGKILL");
19858
19870
  }, 5e3);
19859
19871
  child.on("exit", () => {
@@ -19867,7 +19879,7 @@ var RuntimeProcess = class {
19867
19879
  * Restart the runtime process (stop then start with fresh backoff).
19868
19880
  */
19869
19881
  async restart() {
19870
- log.info({ runtime: this.options.runtime }, "Restarting runtime...");
19882
+ log$1.info({ runtime: this.options.runtime }, "Restarting runtime...");
19871
19883
  await this.stop();
19872
19884
  this.stopped = false;
19873
19885
  this.backoffMs = BACKOFF_INITIAL_MS;
@@ -19881,6 +19893,154 @@ var RuntimeProcess = class {
19881
19893
  }
19882
19894
  };
19883
19895
  //#endregion
19896
+ //#region src/command-registry.ts
19897
+ /**
19898
+ * Command Registry — maps integration command names to local handler files.
19899
+ *
19900
+ * After reconciliation installs/activates integrations, the daemon rebuilds
19901
+ * this registry from active integration manifests. Handler files are loaded
19902
+ * via ESM dynamic import on first use and cached until the registry is cleared.
19903
+ */
19904
+ const log = createLogger("CommandRegistry");
19905
+ var CommandRegistry = class {
19906
+ commands = /* @__PURE__ */ new Map();
19907
+ version = 0;
19908
+ /**
19909
+ * Register a command from an integration.
19910
+ * Rejects duplicate command names — first registration wins.
19911
+ */
19912
+ register(integrationId, name, handlerPath, method = "handle", timeoutMs = 3e4) {
19913
+ const existing = this.commands.get(name);
19914
+ if (existing) {
19915
+ log.warn({
19916
+ command: name,
19917
+ existing: existing.integrationId,
19918
+ attempted: integrationId
19919
+ }, "Command name collision — ignoring duplicate registration");
19920
+ return;
19921
+ }
19922
+ if (!existsSync(handlerPath)) {
19923
+ log.warn({
19924
+ command: name,
19925
+ handlerPath,
19926
+ integrationId
19927
+ }, "Handler file does not exist — skipping registration");
19928
+ return;
19929
+ }
19930
+ this.commands.set(name, {
19931
+ commandName: name,
19932
+ integrationId,
19933
+ handlerPath,
19934
+ handlerMethod: method,
19935
+ timeoutMs,
19936
+ handler: null
19937
+ });
19938
+ log.info({
19939
+ command: name,
19940
+ integrationId,
19941
+ handlerPath,
19942
+ method,
19943
+ timeoutMs
19944
+ }, "Registered command");
19945
+ }
19946
+ /**
19947
+ * Unregister all commands owned by an integration.
19948
+ */
19949
+ unregisterAll(integrationId) {
19950
+ for (const [name, entry] of this.commands) if (entry.integrationId === integrationId) {
19951
+ this.commands.delete(name);
19952
+ log.info({
19953
+ command: name,
19954
+ integrationId
19955
+ }, "Unregistered command");
19956
+ }
19957
+ }
19958
+ /**
19959
+ * Check if a command is registered.
19960
+ */
19961
+ has(name) {
19962
+ return this.commands.has(name);
19963
+ }
19964
+ /**
19965
+ * Execute a registered command handler.
19966
+ * Lazily imports the handler module on first call, caches it.
19967
+ * Wraps execution in Promise.race with the command's timeout.
19968
+ */
19969
+ async execute(name, payload, context) {
19970
+ const entry = this.commands.get(name);
19971
+ if (!entry) return {
19972
+ status: "error",
19973
+ result: {
19974
+ code: "UNKNOWN_COMMAND",
19975
+ message: `Command "${name}" not registered`
19976
+ }
19977
+ };
19978
+ if (!entry.handler) try {
19979
+ const fn = (await import(pathToFileURL(entry.handlerPath).href + "?v=" + String(this.version)))[entry.handlerMethod];
19980
+ if (typeof fn !== "function") return {
19981
+ status: "error",
19982
+ result: {
19983
+ code: "HANDLER_NOT_FOUND",
19984
+ message: `Handler "${entry.handlerMethod}" not found in ${entry.handlerPath}`
19985
+ }
19986
+ };
19987
+ entry.handler = fn;
19988
+ } catch (err) {
19989
+ const message = err instanceof Error ? err.message : String(err);
19990
+ log.error({
19991
+ err: message,
19992
+ command: name,
19993
+ handlerPath: entry.handlerPath
19994
+ }, "Failed to import handler");
19995
+ return {
19996
+ status: "error",
19997
+ result: {
19998
+ code: "IMPORT_FAILED",
19999
+ message
20000
+ }
20001
+ };
20002
+ }
20003
+ try {
20004
+ return await Promise.race([entry.handler(payload, context), new Promise((_, reject) => {
20005
+ setTimeout(() => {
20006
+ reject(/* @__PURE__ */ new Error(`Command "${name}" timed out after ${String(entry.timeoutMs)}ms`));
20007
+ }, entry.timeoutMs);
20008
+ })]);
20009
+ } catch (err) {
20010
+ const message = err instanceof Error ? err.message : String(err);
20011
+ log.error({
20012
+ err: message,
20013
+ command: name
20014
+ }, "Command execution failed");
20015
+ return {
20016
+ status: "error",
20017
+ result: {
20018
+ code: "EXECUTION_FAILED",
20019
+ message
20020
+ }
20021
+ };
20022
+ }
20023
+ }
20024
+ /**
20025
+ * List all registered commands (for COMMANDS_AVAILABLE message).
20026
+ */
20027
+ listCommands() {
20028
+ return Array.from(this.commands.values()).map((e) => ({
20029
+ name: e.commandName,
20030
+ integrationId: e.integrationId,
20031
+ timeoutMs: e.timeoutMs
20032
+ }));
20033
+ }
20034
+ /**
20035
+ * Clear all entries and increment version (busts ESM import cache).
20036
+ */
20037
+ clear() {
20038
+ this.commands.clear();
20039
+ this.version++;
20040
+ log.info({ version: this.version }, "Command registry cleared");
20041
+ }
20042
+ };
20043
+ //#endregion
19884
20044
  //#region src/daemon.ts
19885
20045
  /**
19886
20046
  * Alfe Gateway Daemon — main entry point.
@@ -19908,6 +20068,7 @@ let aiProxyUrl = null;
19908
20068
  let aiProxyRunning = false;
19909
20069
  let cloudConnected = false;
19910
20070
  let shuttingDown = false;
20071
+ let commandRegistry;
19911
20072
  let resolvedCliVersion;
19912
20073
  /**
19913
20074
  * Resolve the installed @alfe.ai/cli version.
@@ -19984,6 +20145,7 @@ async function startDaemon() {
19984
20145
  logger$1.debug("Initializing command queue...");
19985
20146
  commandQueue = new CommandQueue();
19986
20147
  commandQueue.startGC();
20148
+ commandRegistry = new CommandRegistry();
19987
20149
  logger$1.debug("Starting AI proxy...");
19988
20150
  try {
19989
20151
  const { createProxyServer, DEFAULT_AI_PROXY_PORT } = await import("@alfe.ai/ai-proxy-local");
@@ -20079,6 +20241,21 @@ async function startDaemon() {
20079
20241
  });
20080
20242
  }
20081
20243
  });
20244
+ cloudClient.setOnReconciliationComplete(() => {
20245
+ try {
20246
+ const activeCommands = integrationManager.getActiveCommands();
20247
+ commandRegistry.clear();
20248
+ for (const { integrationId, commands } of activeCommands) for (const cmd of commands) commandRegistry.register(integrationId, cmd.name, cmd.resolvedPath, cmd.method, cmd.timeoutMs);
20249
+ const commandsMsg = {
20250
+ type: "COMMANDS_AVAILABLE",
20251
+ commands: commandRegistry.listCommands()
20252
+ };
20253
+ cloudClient.sendMessage(commandsMsg);
20254
+ logger$1.info({ commandCount: commandsMsg.commands.length }, "Reported available commands to cloud");
20255
+ } catch (err) {
20256
+ logger$1.error({ err: err instanceof Error ? err.message : String(err) }, "Failed to rebuild command registry");
20257
+ }
20258
+ });
20082
20259
  cloudClient.start();
20083
20260
  logger$1.debug("Cloud client started");
20084
20261
  if (config.autoStartRuntime && config.runtime) {
@@ -20146,98 +20323,6 @@ async function startDaemon() {
20146
20323
  }, "Alfe Gateway Daemon started ✅");
20147
20324
  }
20148
20325
  async function handleCloudCommand(command) {
20149
- if (command.command === "support.diagnostic") {
20150
- const payload = command.payload;
20151
- if (!payload.apiKey && !aiProxyRunning) return {
20152
- type: "COMMAND_ACK",
20153
- commandId: command.commandId,
20154
- status: "error",
20155
- result: {
20156
- code: "PROXY_NOT_RUNNING",
20157
- message: "AI proxy is not running — diagnostic requires LLM access"
20158
- }
20159
- };
20160
- let proxyUrl;
20161
- if (aiProxyRunning) proxyUrl = aiProxyUrl ?? void 0;
20162
- else {
20163
- const { getAiServiceUrlFromToken } = await import("@alfe.ai/config");
20164
- proxyUrl = getAiServiceUrlFromToken(config.apiKey);
20165
- }
20166
- try {
20167
- const { runDiagnostic } = await import("@alfe.ai/doctor");
20168
- const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
20169
- const report = await runDiagnostic({
20170
- task: payload.task,
20171
- workspacePath,
20172
- timeoutSeconds: 300,
20173
- proxyUrl,
20174
- apiKey: payload.apiKey
20175
- });
20176
- return {
20177
- type: "COMMAND_ACK",
20178
- commandId: command.commandId,
20179
- status: report.success ? "ok" : "error",
20180
- result: report
20181
- };
20182
- } catch (err) {
20183
- const message = err instanceof Error ? err.message : String(err);
20184
- logger$1.error({ err: message }, "Diagnostic failed");
20185
- return {
20186
- type: "COMMAND_ACK",
20187
- commandId: command.commandId,
20188
- status: "error",
20189
- result: {
20190
- code: "DIAGNOSTIC_FAILED",
20191
- message
20192
- }
20193
- };
20194
- }
20195
- }
20196
- if (command.command === "support.bash") {
20197
- const payload = command.payload;
20198
- if (!payload.cmd) return {
20199
- type: "COMMAND_ACK",
20200
- commandId: command.commandId,
20201
- status: "error",
20202
- result: {
20203
- code: "MISSING_CMD",
20204
- message: "No command provided"
20205
- }
20206
- };
20207
- const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
20208
- try {
20209
- const { exec } = await import("child_process");
20210
- const { promisify } = await import("util");
20211
- const { stdout, stderr } = await promisify(exec)(payload.cmd, {
20212
- cwd: workspacePath,
20213
- timeout: 25e3,
20214
- maxBuffer: 512 * 1024
20215
- });
20216
- return {
20217
- type: "COMMAND_ACK",
20218
- commandId: command.commandId,
20219
- status: "ok",
20220
- result: {
20221
- stdout: stdout.trim(),
20222
- stderr: stderr.trim()
20223
- }
20224
- };
20225
- } catch (err) {
20226
- const execErr = err;
20227
- return {
20228
- type: "COMMAND_ACK",
20229
- commandId: command.commandId,
20230
- status: "error",
20231
- result: {
20232
- code: "EXEC_FAILED",
20233
- stdout: execErr.stdout?.trim() ?? "",
20234
- stderr: execErr.stderr?.trim() ?? "",
20235
- message: execErr.message ?? String(err),
20236
- exitCode: execErr.code
20237
- }
20238
- };
20239
- }
20240
- }
20241
20326
  if (command.command === "daemon.update") {
20242
20327
  const version = command.payload?.version ?? "latest";
20243
20328
  setTimeout(() => {
@@ -20298,6 +20383,31 @@ async function handleCloudCommand(command) {
20298
20383
  };
20299
20384
  }
20300
20385
  }
20386
+ if (commandRegistry.has(command.command)) try {
20387
+ const ctx = buildCommandContext();
20388
+ const result = await commandRegistry.execute(command.command, typeof command.payload === "object" && command.payload !== null ? command.payload : {}, ctx);
20389
+ return {
20390
+ type: "COMMAND_ACK",
20391
+ commandId: command.commandId,
20392
+ status: result.status,
20393
+ result: result.result
20394
+ };
20395
+ } catch (err) {
20396
+ const message = err instanceof Error ? err.message : String(err);
20397
+ logger$1.error({
20398
+ err: message,
20399
+ command: command.command
20400
+ }, "Command registry execution failed");
20401
+ return {
20402
+ type: "COMMAND_ACK",
20403
+ commandId: command.commandId,
20404
+ status: "error",
20405
+ result: {
20406
+ code: "REGISTRY_ERROR",
20407
+ message
20408
+ }
20409
+ };
20410
+ }
20301
20411
  const ipcRequest = cloudCommandToIPCRequest(command);
20302
20412
  if (!ipcRequest) {
20303
20413
  logger$1.warn({ command: command.command }, "Unrecognized cloud command");
@@ -20354,6 +20464,28 @@ async function handleCloudCommand(command) {
20354
20464
  };
20355
20465
  }
20356
20466
  }
20467
+ function buildCommandContext() {
20468
+ const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
20469
+ return {
20470
+ workspacePath,
20471
+ aiProxyUrl: aiProxyUrl ?? void 0,
20472
+ aiProxyRunning,
20473
+ apiKey: config.apiKey,
20474
+ async exec(cmd, opts) {
20475
+ const { exec: execCb } = await import("child_process");
20476
+ const { promisify } = await import("util");
20477
+ const { stdout, stderr } = await promisify(execCb)(cmd, {
20478
+ cwd: workspacePath,
20479
+ timeout: opts?.timeoutMs ?? 25e3,
20480
+ maxBuffer: opts?.maxBuffer ?? 512 * 1024
20481
+ });
20482
+ return {
20483
+ stdout: stdout.trim(),
20484
+ stderr: stderr.trim()
20485
+ };
20486
+ }
20487
+ };
20488
+ }
20357
20489
  function handlePluginRequest(method, params, pluginId) {
20358
20490
  switch (method) {
20359
20491
  case "status": return Promise.resolve(handleStatus());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/gateway",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Alfe local gateway daemon — persistent control plane for agent integrations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,8 +24,8 @@
24
24
  "ws": "^8.18.0",
25
25
  "@alfe.ai/ai-proxy-local": "^0.0.5",
26
26
  "@alfe.ai/config": "^0.0.5",
27
- "@alfe.ai/doctor": "^0.0.6",
28
- "@alfe.ai/integrations": "^0.0.14"
27
+ "@alfe.ai/integration-manifest": "^0.0.6",
28
+ "@alfe.ai/integrations": "^0.0.15"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.5.13",