@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.
- package/dist/health.js +255 -123
- 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
|
-
|
|
354
|
-
return this.client.request(`${this.prefix}/
|
|
354
|
+
getOnboardingStatus() {
|
|
355
|
+
return this.client.request(`${this.prefix}/onboarding/status`);
|
|
355
356
|
}
|
|
356
|
-
|
|
357
|
-
return this.client.request(`${this.prefix}/
|
|
357
|
+
completeOnboarding(input) {
|
|
358
|
+
return this.client.request(`${this.prefix}/onboarding/complete`, {
|
|
358
359
|
method: "POST",
|
|
359
|
-
body: JSON.stringify(
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
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/
|
|
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",
|