@alfe.ai/gateway 0.0.21 → 0.0.23
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.
- package/dist/health.js +256 -111
- 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;
|
|
@@ -365,6 +366,15 @@ var AuthService = class {
|
|
|
365
366
|
getReferralHistory() {
|
|
366
367
|
return this.client.request(`${this.prefix}/referral/history`);
|
|
367
368
|
}
|
|
369
|
+
getOnboardingStatus() {
|
|
370
|
+
return this.client.request(`${this.prefix}/onboarding/status`);
|
|
371
|
+
}
|
|
372
|
+
completeOnboarding(input) {
|
|
373
|
+
return this.client.request(`${this.prefix}/onboarding/complete`, {
|
|
374
|
+
method: "POST",
|
|
375
|
+
body: JSON.stringify(input)
|
|
376
|
+
});
|
|
377
|
+
}
|
|
368
378
|
};
|
|
369
379
|
//#endregion
|
|
370
380
|
//#region ../../packages-internal/api-client/dist/services/integrations.js
|
|
@@ -3705,7 +3715,7 @@ function isIPCResponse(msg) {
|
|
|
3705
3715
|
const PROTOCOL_VERSION = 1;
|
|
3706
3716
|
//#endregion
|
|
3707
3717
|
//#region src/reconciliation.ts
|
|
3708
|
-
const log$
|
|
3718
|
+
const log$2 = logger$1.child({ component: "Reconciliation" });
|
|
3709
3719
|
var ReconciliationEngine = class {
|
|
3710
3720
|
constructor(manager) {
|
|
3711
3721
|
this.manager = manager;
|
|
@@ -3726,7 +3736,7 @@ var ReconciliationEngine = class {
|
|
|
3726
3736
|
try {
|
|
3727
3737
|
localIntegrations = await this.manager.getInstalledIntegrations();
|
|
3728
3738
|
} catch (err) {
|
|
3729
|
-
log$
|
|
3739
|
+
log$2.error({ err }, "Failed to get local integrations");
|
|
3730
3740
|
localIntegrations = [];
|
|
3731
3741
|
}
|
|
3732
3742
|
const localMap = new Map(localIntegrations.map((i) => [i.id, i]));
|
|
@@ -3740,10 +3750,10 @@ var ReconciliationEngine = class {
|
|
|
3740
3750
|
const id = desired.integrationId;
|
|
3741
3751
|
try {
|
|
3742
3752
|
if (!local) {
|
|
3743
|
-
log$
|
|
3753
|
+
log$2.info(`Installing ${id}@${desired.version}`);
|
|
3744
3754
|
await this.manager.install(id, desired.version, desired.config);
|
|
3745
3755
|
report.installed.push(id);
|
|
3746
|
-
log$
|
|
3756
|
+
log$2.info(`Activating ${id}`);
|
|
3747
3757
|
await this.manager.activate(id);
|
|
3748
3758
|
report.activated.push(id);
|
|
3749
3759
|
report.results.push({
|
|
@@ -3754,14 +3764,14 @@ var ReconciliationEngine = class {
|
|
|
3754
3764
|
return;
|
|
3755
3765
|
}
|
|
3756
3766
|
if (local.version !== desired.version && desired.version !== "" && local.version !== "unknown") {
|
|
3757
|
-
log$
|
|
3767
|
+
log$2.info(`Upgrading ${id}: ${local.version} → ${desired.version}`);
|
|
3758
3768
|
try {
|
|
3759
3769
|
await this.manager.deactivate(id);
|
|
3760
3770
|
await this.manager.uninstall(id);
|
|
3761
3771
|
} catch {}
|
|
3762
3772
|
await this.manager.install(id, desired.version, desired.config);
|
|
3763
3773
|
report.installed.push(id);
|
|
3764
|
-
log$
|
|
3774
|
+
log$2.info(`Activating ${id}`);
|
|
3765
3775
|
await this.manager.activate(id);
|
|
3766
3776
|
report.activated.push(id);
|
|
3767
3777
|
report.results.push({
|
|
@@ -3772,7 +3782,7 @@ var ReconciliationEngine = class {
|
|
|
3772
3782
|
return;
|
|
3773
3783
|
}
|
|
3774
3784
|
if (local.status !== "active") {
|
|
3775
|
-
log$
|
|
3785
|
+
log$2.info(`Activating ${id}`);
|
|
3776
3786
|
await this.manager.activate(id);
|
|
3777
3787
|
report.activated.push(id);
|
|
3778
3788
|
report.results.push({
|
|
@@ -3789,7 +3799,7 @@ var ReconciliationEngine = class {
|
|
|
3789
3799
|
});
|
|
3790
3800
|
} catch (err) {
|
|
3791
3801
|
const message = err instanceof Error ? err.message : String(err);
|
|
3792
|
-
log$
|
|
3802
|
+
log$2.error({ err }, `Error reconciling ${id}`);
|
|
3793
3803
|
report.errors.push({
|
|
3794
3804
|
integrationId: id,
|
|
3795
3805
|
error: message
|
|
@@ -3812,7 +3822,7 @@ var ReconciliationEngine = class {
|
|
|
3812
3822
|
return;
|
|
3813
3823
|
}
|
|
3814
3824
|
try {
|
|
3815
|
-
log$
|
|
3825
|
+
log$2.info(`Removing ${id}`);
|
|
3816
3826
|
if (local.status === "active") {
|
|
3817
3827
|
await this.manager.deactivate(id);
|
|
3818
3828
|
report.deactivated.push(id);
|
|
@@ -3826,7 +3836,7 @@ var ReconciliationEngine = class {
|
|
|
3826
3836
|
});
|
|
3827
3837
|
} catch (err) {
|
|
3828
3838
|
const message = err instanceof Error ? err.message : String(err);
|
|
3829
|
-
log$
|
|
3839
|
+
log$2.error({ err }, `Error removing ${id}`);
|
|
3830
3840
|
report.errors.push({
|
|
3831
3841
|
integrationId: id,
|
|
3832
3842
|
error: message
|
|
@@ -3861,6 +3871,7 @@ var CloudClient = class {
|
|
|
3861
3871
|
onCommand = null;
|
|
3862
3872
|
onConnectionChange = null;
|
|
3863
3873
|
onPluginsChanged = null;
|
|
3874
|
+
onReconciliationComplete = null;
|
|
3864
3875
|
reconciliationEngine = null;
|
|
3865
3876
|
constructor(config) {
|
|
3866
3877
|
this.config = config;
|
|
@@ -3890,6 +3901,12 @@ var CloudClient = class {
|
|
|
3890
3901
|
setPluginsChangedHandler(handler) {
|
|
3891
3902
|
this.onPluginsChanged = handler;
|
|
3892
3903
|
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Set a callback for when reconciliation completes — used to rebuild command registry.
|
|
3906
|
+
*/
|
|
3907
|
+
setOnReconciliationComplete(handler) {
|
|
3908
|
+
this.onReconciliationComplete = handler;
|
|
3909
|
+
}
|
|
3893
3910
|
setIntegrationManager(manager) {
|
|
3894
3911
|
this.reconciliationEngine = new ReconciliationEngine(manager);
|
|
3895
3912
|
}
|
|
@@ -3922,6 +3939,13 @@ var CloudClient = class {
|
|
|
3922
3939
|
logger$1.debug("Cloud client stopped");
|
|
3923
3940
|
}
|
|
3924
3941
|
/**
|
|
3942
|
+
* Send a typed message to the cloud gateway.
|
|
3943
|
+
* Silently drops if WebSocket is not open.
|
|
3944
|
+
*/
|
|
3945
|
+
sendMessage(msg) {
|
|
3946
|
+
this.send(msg);
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3925
3949
|
* Get connection latency (time since last pong).
|
|
3926
3950
|
* Returns -1 if no pong received yet.
|
|
3927
3951
|
*/
|
|
@@ -4083,6 +4107,7 @@ var CloudClient = class {
|
|
|
4083
4107
|
const reportMsg = createReconciliationReport(report.results);
|
|
4084
4108
|
this.send(reportMsg);
|
|
4085
4109
|
if ((report.installed.length > 0 || report.uninstalled.length > 0) && this.onPluginsChanged) this.onPluginsChanged();
|
|
4110
|
+
this.onReconciliationComplete?.();
|
|
4086
4111
|
} catch (err) {
|
|
4087
4112
|
const message = err instanceof Error ? err.message : String(err);
|
|
4088
4113
|
logger$1.error({ err: message }, "Cloud: reconciliation failed");
|
|
@@ -19743,7 +19768,7 @@ function createLogger(component, additionalData) {
|
|
|
19743
19768
|
* Spawns the runtime binary, pipes stdout/stderr to the daemon logger,
|
|
19744
19769
|
* and restarts on crash with exponential backoff.
|
|
19745
19770
|
*/
|
|
19746
|
-
const log = createLogger("RuntimeProcess");
|
|
19771
|
+
const log$1 = createLogger("RuntimeProcess");
|
|
19747
19772
|
const BACKOFF_INITIAL_MS = 1e3;
|
|
19748
19773
|
const BACKOFF_MAX_MS = 3e4;
|
|
19749
19774
|
const STABLE_UPTIME_MS = 6e4;
|
|
@@ -19779,7 +19804,7 @@ var RuntimeProcess = class {
|
|
|
19779
19804
|
if (this.stopped) return;
|
|
19780
19805
|
const { command, args } = this.resolveCommand();
|
|
19781
19806
|
this.lastStartTime = Date.now();
|
|
19782
|
-
log.info({
|
|
19807
|
+
log$1.info({
|
|
19783
19808
|
runtime: this.options.runtime,
|
|
19784
19809
|
command,
|
|
19785
19810
|
args,
|
|
@@ -19798,14 +19823,14 @@ var RuntimeProcess = class {
|
|
|
19798
19823
|
});
|
|
19799
19824
|
this.child.stdout?.on("data", (data) => {
|
|
19800
19825
|
const lines = data.toString().trim().split("\n");
|
|
19801
|
-
for (const line of lines) log.info({
|
|
19826
|
+
for (const line of lines) log$1.info({
|
|
19802
19827
|
runtime: this.options.runtime,
|
|
19803
19828
|
stream: "stdout"
|
|
19804
19829
|
}, line);
|
|
19805
19830
|
});
|
|
19806
19831
|
this.child.stderr?.on("data", (data) => {
|
|
19807
19832
|
const lines = data.toString().trim().split("\n");
|
|
19808
|
-
for (const line of lines) log.warn({
|
|
19833
|
+
for (const line of lines) log$1.warn({
|
|
19809
19834
|
runtime: this.options.runtime,
|
|
19810
19835
|
stream: "stderr"
|
|
19811
19836
|
}, line);
|
|
@@ -19813,14 +19838,14 @@ var RuntimeProcess = class {
|
|
|
19813
19838
|
this.child.on("exit", (code, signal) => {
|
|
19814
19839
|
this.child = null;
|
|
19815
19840
|
if (this.stopped) {
|
|
19816
|
-
log.info({
|
|
19841
|
+
log$1.info({
|
|
19817
19842
|
runtime: this.options.runtime,
|
|
19818
19843
|
code,
|
|
19819
19844
|
signal
|
|
19820
19845
|
}, "Runtime stopped (expected)");
|
|
19821
19846
|
return;
|
|
19822
19847
|
}
|
|
19823
|
-
log.warn({
|
|
19848
|
+
log$1.warn({
|
|
19824
19849
|
runtime: this.options.runtime,
|
|
19825
19850
|
code,
|
|
19826
19851
|
signal,
|
|
@@ -19834,7 +19859,7 @@ var RuntimeProcess = class {
|
|
|
19834
19859
|
this.backoffMs = Math.min(this.backoffMs * 2, BACKOFF_MAX_MS);
|
|
19835
19860
|
});
|
|
19836
19861
|
this.child.on("error", (err) => {
|
|
19837
|
-
log.error({
|
|
19862
|
+
log$1.error({
|
|
19838
19863
|
runtime: this.options.runtime,
|
|
19839
19864
|
err: err.message
|
|
19840
19865
|
}, "Runtime process error");
|
|
@@ -19853,7 +19878,7 @@ var RuntimeProcess = class {
|
|
|
19853
19878
|
if (!child) return;
|
|
19854
19879
|
return new Promise((resolve) => {
|
|
19855
19880
|
const killTimer = setTimeout(() => {
|
|
19856
|
-
log.warn({ runtime: this.options.runtime }, "Runtime did not exit in time — sending SIGKILL");
|
|
19881
|
+
log$1.warn({ runtime: this.options.runtime }, "Runtime did not exit in time — sending SIGKILL");
|
|
19857
19882
|
child.kill("SIGKILL");
|
|
19858
19883
|
}, 5e3);
|
|
19859
19884
|
child.on("exit", () => {
|
|
@@ -19867,7 +19892,7 @@ var RuntimeProcess = class {
|
|
|
19867
19892
|
* Restart the runtime process (stop then start with fresh backoff).
|
|
19868
19893
|
*/
|
|
19869
19894
|
async restart() {
|
|
19870
|
-
log.info({ runtime: this.options.runtime }, "Restarting runtime...");
|
|
19895
|
+
log$1.info({ runtime: this.options.runtime }, "Restarting runtime...");
|
|
19871
19896
|
await this.stop();
|
|
19872
19897
|
this.stopped = false;
|
|
19873
19898
|
this.backoffMs = BACKOFF_INITIAL_MS;
|
|
@@ -19881,6 +19906,154 @@ var RuntimeProcess = class {
|
|
|
19881
19906
|
}
|
|
19882
19907
|
};
|
|
19883
19908
|
//#endregion
|
|
19909
|
+
//#region src/command-registry.ts
|
|
19910
|
+
/**
|
|
19911
|
+
* Command Registry — maps integration command names to local handler files.
|
|
19912
|
+
*
|
|
19913
|
+
* After reconciliation installs/activates integrations, the daemon rebuilds
|
|
19914
|
+
* this registry from active integration manifests. Handler files are loaded
|
|
19915
|
+
* via ESM dynamic import on first use and cached until the registry is cleared.
|
|
19916
|
+
*/
|
|
19917
|
+
const log = createLogger("CommandRegistry");
|
|
19918
|
+
var CommandRegistry = class {
|
|
19919
|
+
commands = /* @__PURE__ */ new Map();
|
|
19920
|
+
version = 0;
|
|
19921
|
+
/**
|
|
19922
|
+
* Register a command from an integration.
|
|
19923
|
+
* Rejects duplicate command names — first registration wins.
|
|
19924
|
+
*/
|
|
19925
|
+
register(integrationId, name, handlerPath, method = "handle", timeoutMs = 3e4) {
|
|
19926
|
+
const existing = this.commands.get(name);
|
|
19927
|
+
if (existing) {
|
|
19928
|
+
log.warn({
|
|
19929
|
+
command: name,
|
|
19930
|
+
existing: existing.integrationId,
|
|
19931
|
+
attempted: integrationId
|
|
19932
|
+
}, "Command name collision — ignoring duplicate registration");
|
|
19933
|
+
return;
|
|
19934
|
+
}
|
|
19935
|
+
if (!existsSync(handlerPath)) {
|
|
19936
|
+
log.warn({
|
|
19937
|
+
command: name,
|
|
19938
|
+
handlerPath,
|
|
19939
|
+
integrationId
|
|
19940
|
+
}, "Handler file does not exist — skipping registration");
|
|
19941
|
+
return;
|
|
19942
|
+
}
|
|
19943
|
+
this.commands.set(name, {
|
|
19944
|
+
commandName: name,
|
|
19945
|
+
integrationId,
|
|
19946
|
+
handlerPath,
|
|
19947
|
+
handlerMethod: method,
|
|
19948
|
+
timeoutMs,
|
|
19949
|
+
handler: null
|
|
19950
|
+
});
|
|
19951
|
+
log.info({
|
|
19952
|
+
command: name,
|
|
19953
|
+
integrationId,
|
|
19954
|
+
handlerPath,
|
|
19955
|
+
method,
|
|
19956
|
+
timeoutMs
|
|
19957
|
+
}, "Registered command");
|
|
19958
|
+
}
|
|
19959
|
+
/**
|
|
19960
|
+
* Unregister all commands owned by an integration.
|
|
19961
|
+
*/
|
|
19962
|
+
unregisterAll(integrationId) {
|
|
19963
|
+
for (const [name, entry] of this.commands) if (entry.integrationId === integrationId) {
|
|
19964
|
+
this.commands.delete(name);
|
|
19965
|
+
log.info({
|
|
19966
|
+
command: name,
|
|
19967
|
+
integrationId
|
|
19968
|
+
}, "Unregistered command");
|
|
19969
|
+
}
|
|
19970
|
+
}
|
|
19971
|
+
/**
|
|
19972
|
+
* Check if a command is registered.
|
|
19973
|
+
*/
|
|
19974
|
+
has(name) {
|
|
19975
|
+
return this.commands.has(name);
|
|
19976
|
+
}
|
|
19977
|
+
/**
|
|
19978
|
+
* Execute a registered command handler.
|
|
19979
|
+
* Lazily imports the handler module on first call, caches it.
|
|
19980
|
+
* Wraps execution in Promise.race with the command's timeout.
|
|
19981
|
+
*/
|
|
19982
|
+
async execute(name, payload, context) {
|
|
19983
|
+
const entry = this.commands.get(name);
|
|
19984
|
+
if (!entry) return {
|
|
19985
|
+
status: "error",
|
|
19986
|
+
result: {
|
|
19987
|
+
code: "UNKNOWN_COMMAND",
|
|
19988
|
+
message: `Command "${name}" not registered`
|
|
19989
|
+
}
|
|
19990
|
+
};
|
|
19991
|
+
if (!entry.handler) try {
|
|
19992
|
+
const fn = (await import(pathToFileURL(entry.handlerPath).href + "?v=" + String(this.version)))[entry.handlerMethod];
|
|
19993
|
+
if (typeof fn !== "function") return {
|
|
19994
|
+
status: "error",
|
|
19995
|
+
result: {
|
|
19996
|
+
code: "HANDLER_NOT_FOUND",
|
|
19997
|
+
message: `Handler "${entry.handlerMethod}" not found in ${entry.handlerPath}`
|
|
19998
|
+
}
|
|
19999
|
+
};
|
|
20000
|
+
entry.handler = fn;
|
|
20001
|
+
} catch (err) {
|
|
20002
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20003
|
+
log.error({
|
|
20004
|
+
err: message,
|
|
20005
|
+
command: name,
|
|
20006
|
+
handlerPath: entry.handlerPath
|
|
20007
|
+
}, "Failed to import handler");
|
|
20008
|
+
return {
|
|
20009
|
+
status: "error",
|
|
20010
|
+
result: {
|
|
20011
|
+
code: "IMPORT_FAILED",
|
|
20012
|
+
message
|
|
20013
|
+
}
|
|
20014
|
+
};
|
|
20015
|
+
}
|
|
20016
|
+
try {
|
|
20017
|
+
return await Promise.race([entry.handler(payload, context), new Promise((_, reject) => {
|
|
20018
|
+
setTimeout(() => {
|
|
20019
|
+
reject(/* @__PURE__ */ new Error(`Command "${name}" timed out after ${String(entry.timeoutMs)}ms`));
|
|
20020
|
+
}, entry.timeoutMs);
|
|
20021
|
+
})]);
|
|
20022
|
+
} catch (err) {
|
|
20023
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20024
|
+
log.error({
|
|
20025
|
+
err: message,
|
|
20026
|
+
command: name
|
|
20027
|
+
}, "Command execution failed");
|
|
20028
|
+
return {
|
|
20029
|
+
status: "error",
|
|
20030
|
+
result: {
|
|
20031
|
+
code: "EXECUTION_FAILED",
|
|
20032
|
+
message
|
|
20033
|
+
}
|
|
20034
|
+
};
|
|
20035
|
+
}
|
|
20036
|
+
}
|
|
20037
|
+
/**
|
|
20038
|
+
* List all registered commands (for COMMANDS_AVAILABLE message).
|
|
20039
|
+
*/
|
|
20040
|
+
listCommands() {
|
|
20041
|
+
return Array.from(this.commands.values()).map((e) => ({
|
|
20042
|
+
name: e.commandName,
|
|
20043
|
+
integrationId: e.integrationId,
|
|
20044
|
+
timeoutMs: e.timeoutMs
|
|
20045
|
+
}));
|
|
20046
|
+
}
|
|
20047
|
+
/**
|
|
20048
|
+
* Clear all entries and increment version (busts ESM import cache).
|
|
20049
|
+
*/
|
|
20050
|
+
clear() {
|
|
20051
|
+
this.commands.clear();
|
|
20052
|
+
this.version++;
|
|
20053
|
+
log.info({ version: this.version }, "Command registry cleared");
|
|
20054
|
+
}
|
|
20055
|
+
};
|
|
20056
|
+
//#endregion
|
|
19884
20057
|
//#region src/daemon.ts
|
|
19885
20058
|
/**
|
|
19886
20059
|
* Alfe Gateway Daemon — main entry point.
|
|
@@ -19908,6 +20081,7 @@ let aiProxyUrl = null;
|
|
|
19908
20081
|
let aiProxyRunning = false;
|
|
19909
20082
|
let cloudConnected = false;
|
|
19910
20083
|
let shuttingDown = false;
|
|
20084
|
+
let commandRegistry;
|
|
19911
20085
|
let resolvedCliVersion;
|
|
19912
20086
|
/**
|
|
19913
20087
|
* Resolve the installed @alfe.ai/cli version.
|
|
@@ -19984,6 +20158,7 @@ async function startDaemon() {
|
|
|
19984
20158
|
logger$1.debug("Initializing command queue...");
|
|
19985
20159
|
commandQueue = new CommandQueue();
|
|
19986
20160
|
commandQueue.startGC();
|
|
20161
|
+
commandRegistry = new CommandRegistry();
|
|
19987
20162
|
logger$1.debug("Starting AI proxy...");
|
|
19988
20163
|
try {
|
|
19989
20164
|
const { createProxyServer, DEFAULT_AI_PROXY_PORT } = await import("@alfe.ai/ai-proxy-local");
|
|
@@ -20079,6 +20254,21 @@ async function startDaemon() {
|
|
|
20079
20254
|
});
|
|
20080
20255
|
}
|
|
20081
20256
|
});
|
|
20257
|
+
cloudClient.setOnReconciliationComplete(() => {
|
|
20258
|
+
try {
|
|
20259
|
+
const activeCommands = integrationManager.getActiveCommands();
|
|
20260
|
+
commandRegistry.clear();
|
|
20261
|
+
for (const { integrationId, commands } of activeCommands) for (const cmd of commands) commandRegistry.register(integrationId, cmd.name, cmd.resolvedPath, cmd.method, cmd.timeoutMs);
|
|
20262
|
+
const commandsMsg = {
|
|
20263
|
+
type: "COMMANDS_AVAILABLE",
|
|
20264
|
+
commands: commandRegistry.listCommands()
|
|
20265
|
+
};
|
|
20266
|
+
cloudClient.sendMessage(commandsMsg);
|
|
20267
|
+
logger$1.info({ commandCount: commandsMsg.commands.length }, "Reported available commands to cloud");
|
|
20268
|
+
} catch (err) {
|
|
20269
|
+
logger$1.error({ err: err instanceof Error ? err.message : String(err) }, "Failed to rebuild command registry");
|
|
20270
|
+
}
|
|
20271
|
+
});
|
|
20082
20272
|
cloudClient.start();
|
|
20083
20273
|
logger$1.debug("Cloud client started");
|
|
20084
20274
|
if (config.autoStartRuntime && config.runtime) {
|
|
@@ -20146,98 +20336,6 @@ async function startDaemon() {
|
|
|
20146
20336
|
}, "Alfe Gateway Daemon started ✅");
|
|
20147
20337
|
}
|
|
20148
20338
|
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
20339
|
if (command.command === "daemon.update") {
|
|
20242
20340
|
const version = command.payload?.version ?? "latest";
|
|
20243
20341
|
setTimeout(() => {
|
|
@@ -20298,6 +20396,31 @@ async function handleCloudCommand(command) {
|
|
|
20298
20396
|
};
|
|
20299
20397
|
}
|
|
20300
20398
|
}
|
|
20399
|
+
if (commandRegistry.has(command.command)) try {
|
|
20400
|
+
const ctx = buildCommandContext();
|
|
20401
|
+
const result = await commandRegistry.execute(command.command, typeof command.payload === "object" && command.payload !== null ? command.payload : {}, ctx);
|
|
20402
|
+
return {
|
|
20403
|
+
type: "COMMAND_ACK",
|
|
20404
|
+
commandId: command.commandId,
|
|
20405
|
+
status: result.status,
|
|
20406
|
+
result: result.result
|
|
20407
|
+
};
|
|
20408
|
+
} catch (err) {
|
|
20409
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20410
|
+
logger$1.error({
|
|
20411
|
+
err: message,
|
|
20412
|
+
command: command.command
|
|
20413
|
+
}, "Command registry execution failed");
|
|
20414
|
+
return {
|
|
20415
|
+
type: "COMMAND_ACK",
|
|
20416
|
+
commandId: command.commandId,
|
|
20417
|
+
status: "error",
|
|
20418
|
+
result: {
|
|
20419
|
+
code: "REGISTRY_ERROR",
|
|
20420
|
+
message
|
|
20421
|
+
}
|
|
20422
|
+
};
|
|
20423
|
+
}
|
|
20301
20424
|
const ipcRequest = cloudCommandToIPCRequest(command);
|
|
20302
20425
|
if (!ipcRequest) {
|
|
20303
20426
|
logger$1.warn({ command: command.command }, "Unrecognized cloud command");
|
|
@@ -20354,6 +20477,28 @@ async function handleCloudCommand(command) {
|
|
|
20354
20477
|
};
|
|
20355
20478
|
}
|
|
20356
20479
|
}
|
|
20480
|
+
function buildCommandContext() {
|
|
20481
|
+
const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
|
|
20482
|
+
return {
|
|
20483
|
+
workspacePath,
|
|
20484
|
+
aiProxyUrl: aiProxyUrl ?? void 0,
|
|
20485
|
+
aiProxyRunning,
|
|
20486
|
+
apiKey: config.apiKey,
|
|
20487
|
+
async exec(cmd, opts) {
|
|
20488
|
+
const { exec: execCb } = await import("child_process");
|
|
20489
|
+
const { promisify } = await import("util");
|
|
20490
|
+
const { stdout, stderr } = await promisify(execCb)(cmd, {
|
|
20491
|
+
cwd: workspacePath,
|
|
20492
|
+
timeout: opts?.timeoutMs ?? 25e3,
|
|
20493
|
+
maxBuffer: opts?.maxBuffer ?? 512 * 1024
|
|
20494
|
+
});
|
|
20495
|
+
return {
|
|
20496
|
+
stdout: stdout.trim(),
|
|
20497
|
+
stderr: stderr.trim()
|
|
20498
|
+
};
|
|
20499
|
+
}
|
|
20500
|
+
};
|
|
20501
|
+
}
|
|
20357
20502
|
function handlePluginRequest(method, params, pluginId) {
|
|
20358
20503
|
switch (method) {
|
|
20359
20504
|
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.
|
|
3
|
+
"version": "0.0.23",
|
|
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/
|
|
28
|
-
"@alfe.ai/integrations": "^0.0.
|
|
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",
|