@alfe.ai/gateway 0.10.7 → 0.10.9
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 +37 -4
- package/dist/upgrade.js +5 -5
- package/package.json +6 -6
package/dist/health.js
CHANGED
|
@@ -4718,6 +4718,14 @@ NotificationType.PaymentSucceeded, NotificationCategory.Billing, RecipientStrate
|
|
|
4718
4718
|
* Platform-agnostic: works in React Native and browser environments.
|
|
4719
4719
|
* Uses standard fetch() API — no Node.js dependencies.
|
|
4720
4720
|
*/
|
|
4721
|
+
/** A sane 429 window hint: a positive whole number of seconds (≤ 1 day). */
|
|
4722
|
+
function isRetryAfterSeconds(value) {
|
|
4723
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 86400;
|
|
4724
|
+
}
|
|
4725
|
+
/** Narrow allowlist: a non-empty list of `{ profile: string, message: string }`. */
|
|
4726
|
+
function isProfileErrorList(value) {
|
|
4727
|
+
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && typeof entry.profile === "string" && typeof entry.message === "string");
|
|
4728
|
+
}
|
|
4721
4729
|
var AlfeApiClient = class {
|
|
4722
4730
|
apiBaseUrl;
|
|
4723
4731
|
getToken;
|
|
@@ -4790,7 +4798,12 @@ var AlfeApiClient = class {
|
|
|
4790
4798
|
error: errorMessage ?? (typeof body === "string" ? body : `API error: ${String(res.status)}`),
|
|
4791
4799
|
status: res.status,
|
|
4792
4800
|
...typeof errBody?.code === "string" ? { code: errBody.code } : {},
|
|
4793
|
-
...Array.isArray(errBody?.ineligibleUserIds) && errBody.ineligibleUserIds.every((id) => typeof id === "string") ? { ineligibleUserIds: [...errBody.ineligibleUserIds] } : {}
|
|
4801
|
+
...Array.isArray(errBody?.ineligibleUserIds) && errBody.ineligibleUserIds.every((id) => typeof id === "string") ? { ineligibleUserIds: [...errBody.ineligibleUserIds] } : {},
|
|
4802
|
+
...isProfileErrorList(errBody?.errors) ? { profileErrors: errBody.errors.map(({ profile, message }) => ({
|
|
4803
|
+
profile,
|
|
4804
|
+
message
|
|
4805
|
+
})) } : {},
|
|
4806
|
+
...isRetryAfterSeconds(errBody?.retryAfterSeconds) ? { retryAfterSeconds: errBody.retryAfterSeconds } : {}
|
|
4794
4807
|
}
|
|
4795
4808
|
};
|
|
4796
4809
|
}
|
|
@@ -5931,6 +5944,13 @@ const log$6 = logger.child({ component: "RuntimeGate" });
|
|
|
5931
5944
|
var RuntimeGate = class {
|
|
5932
5945
|
depth = 0;
|
|
5933
5946
|
wasRunning = false;
|
|
5947
|
+
/**
|
|
5948
|
+
* True once the OUTERMOST `enter()` has finished stopping the runtime child
|
|
5949
|
+
* (its `stopWhenIdle()` resolved, i.e. the process exited), until the gate
|
|
5950
|
+
* fully reopens. `depth > 0` alone is not enough: depth is bumped before the
|
|
5951
|
+
* stop is awaited, so it is already set while the old child is draining.
|
|
5952
|
+
*/
|
|
5953
|
+
stopSettled = false;
|
|
5934
5954
|
exclusive = false;
|
|
5935
5955
|
/** Brackets parked behind an exclusive hold (or an exclusive request parked behind open brackets). */
|
|
5936
5956
|
waiters = [];
|
|
@@ -5945,6 +5965,14 @@ var RuntimeGate = class {
|
|
|
5945
5965
|
get isSuspended() {
|
|
5946
5966
|
return this.depth > 0;
|
|
5947
5967
|
}
|
|
5968
|
+
/**
|
|
5969
|
+
* Whether the runtime child is PROVABLY down right now: a bracket is open and
|
|
5970
|
+
* its stop has completed. The OpenClaw applier's one direct openclaw.json
|
|
5971
|
+
* write (the dangling channel-ref repair) is allow-listed on this.
|
|
5972
|
+
*/
|
|
5973
|
+
get isRuntimeStopped() {
|
|
5974
|
+
return this.depth > 0 && this.stopSettled;
|
|
5975
|
+
}
|
|
5948
5976
|
/** True while an exclusive bracket is open. */
|
|
5949
5977
|
get isExclusive() {
|
|
5950
5978
|
return this.exclusive;
|
|
@@ -5997,12 +6025,14 @@ var RuntimeGate = class {
|
|
|
5997
6025
|
}
|
|
5998
6026
|
async enter() {
|
|
5999
6027
|
if (this.depth++ > 0) return;
|
|
6028
|
+
this.stopSettled = false;
|
|
6000
6029
|
const rp = this.getRuntime();
|
|
6001
6030
|
this.wasRunning = rp?.isActive ?? false;
|
|
6002
6031
|
if (rp && this.wasRunning) {
|
|
6003
6032
|
log$6.info("Suspending runtime for mutating reconcile (avoid concurrent SQLite writers)");
|
|
6004
6033
|
await rp.stopWhenIdle();
|
|
6005
6034
|
}
|
|
6035
|
+
if (this.depth > 0) this.stopSettled = true;
|
|
6006
6036
|
}
|
|
6007
6037
|
/**
|
|
6008
6038
|
* Resume the runtime child. Only the outermost call (depth 1 → 0) actually
|
|
@@ -6019,6 +6049,7 @@ var RuntimeGate = class {
|
|
|
6019
6049
|
rp.resume();
|
|
6020
6050
|
}
|
|
6021
6051
|
this.wasRunning = false;
|
|
6052
|
+
this.stopSettled = false;
|
|
6022
6053
|
if (this.exclusive) this.exclusive = false;
|
|
6023
6054
|
this.wakeWaiters();
|
|
6024
6055
|
return Promise.resolve();
|
|
@@ -7465,7 +7496,8 @@ function generateLaunchdPlist() {
|
|
|
7465
7496
|
* ~13,570 restarts over 2 days).
|
|
7466
7497
|
*
|
|
7467
7498
|
* The guard:
|
|
7468
|
-
* - checks the CLI is intact: the symlink target (`
|
|
7499
|
+
* - checks the CLI is intact: the symlink target (the package's `bin.alfe`,
|
|
7500
|
+
* `dist/bin.js` today) exists AND
|
|
7469
7501
|
* `alfe --version` exits 0;
|
|
7470
7502
|
* - if broken, reinstalls `@alfe.ai/cli@$ALFE_CLI_VERSION` (the exact version
|
|
7471
7503
|
* baked into the unit's `Environment=`; falls back to `@latest` when
|
|
@@ -7489,7 +7521,7 @@ set -u
|
|
|
7489
7521
|
TARGET='${exactVersion ? `@alfe.ai/cli@${exactVersion}` : "@alfe.ai/cli@latest"}'
|
|
7490
7522
|
log() { echo "[alfe-cli-guard] $*" >&2; }
|
|
7491
7523
|
|
|
7492
|
-
# Resolve the alfe bin and its real target (dist/
|
|
7524
|
+
# Resolve the alfe bin and its real target (dist/bin.js). readlink -f follows
|
|
7493
7525
|
# the whole symlink chain; fall back to the bin path itself if unavailable.
|
|
7494
7526
|
BIN="$(command -v alfe 2>/dev/null || true)"
|
|
7495
7527
|
if [ -n "$BIN" ]; then
|
|
@@ -10147,7 +10179,8 @@ async function startDaemon() {
|
|
|
10147
10179
|
runtimeAppliers.set(name, new OpenClawApplier({
|
|
10148
10180
|
home: runtimeCfg.workspace,
|
|
10149
10181
|
agentWorkspace: runtimeCfg.agentWorkspace,
|
|
10150
|
-
cliLock: openclawCliLock
|
|
10182
|
+
cliLock: openclawCliLock,
|
|
10183
|
+
isRuntimeStopped: () => name === config.runtime && runtimeGateRef instanceof RuntimeGate && runtimeGateRef.isRuntimeStopped && runtimeProcess !== null && !runtimeProcess.isActive
|
|
10151
10184
|
}));
|
|
10152
10185
|
logger.info({
|
|
10153
10186
|
runtime: name,
|
package/dist/upgrade.js
CHANGED
|
@@ -21,8 +21,8 @@ import { promisify } from "node:util";
|
|
|
21
21
|
* broken) `alfe` binary. Keep the two in lockstep.
|
|
22
22
|
*
|
|
23
23
|
* The two checks:
|
|
24
|
-
* - the resolved CLI entry (`.../dist/
|
|
25
|
-
* `alfe` bin) exists on disk; and
|
|
24
|
+
* - the resolved CLI entry (the package's `bin.alfe`, `.../dist/bin.js`
|
|
25
|
+
* today, i.e. the symlink target of the `alfe` bin) exists on disk; and
|
|
26
26
|
* - running it (`node <entry> --version`, or `alfe --version`) exits 0 and
|
|
27
27
|
* prints the expected version.
|
|
28
28
|
*
|
|
@@ -61,7 +61,7 @@ function decideCliInstallOk(probe, expectedVersion) {
|
|
|
61
61
|
/**
|
|
62
62
|
* Resolve the CLI entry file this daemon is running from. When the gateway is
|
|
63
63
|
* loaded as a dependency of the globally-installed CLI the layout is
|
|
64
|
-
* `.../@alfe.ai/cli/dist/
|
|
64
|
+
* `.../@alfe.ai/cli/dist/bin.js` with `.../@alfe.ai/gateway/dist/...`
|
|
65
65
|
* alongside — walk up to the sibling `@alfe.ai/cli/package.json` and derive its
|
|
66
66
|
* `bin.alfe` / main entry. Returns undefined when it can't be resolved (e.g.
|
|
67
67
|
* `alfe gateway daemon` run from a dev checkout); the caller then skips the
|
|
@@ -106,7 +106,7 @@ async function tryVersion(cmd, args) {
|
|
|
106
106
|
* Probe + decide whether the on-disk global CLI install is intact.
|
|
107
107
|
*
|
|
108
108
|
* @param expectedVersion when set, the reported version must match exactly.
|
|
109
|
-
* @param entryPath the resolved `.../dist/
|
|
109
|
+
* @param entryPath the resolved `bin.alfe` entry, `.../dist/bin.js` (from
|
|
110
110
|
* `resolveCliEntryPath`). When provided and missing,
|
|
111
111
|
* the check fails fast. Also used to run the CLI
|
|
112
112
|
* directly via `node <entry> --version`, which is more
|
|
@@ -174,7 +174,7 @@ function findOrphanReifyTempDirs(siblings, packageBasename) {
|
|
|
174
174
|
* Detection order (robust — never hardcode `/usr/lib`):
|
|
175
175
|
* 1. `npm root -g` → the global `node_modules` root. Authoritative on the box.
|
|
176
176
|
* 2. Derive from the resolved CLI entry path (`.../node_modules/@alfe.ai/cli/
|
|
177
|
-
* dist/
|
|
177
|
+
* dist/bin.js`) by walking up to the `node_modules` segment.
|
|
178
178
|
*
|
|
179
179
|
* Returns undefined when neither resolves (dev checkout / non-global run); the
|
|
180
180
|
* caller then skips the sweep (there is no global install to clean).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/gateway",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.9",
|
|
4
4
|
"description": "Alfe local gateway daemon — persistent control plane for agent integrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
"pino-roll": "^1.2.0",
|
|
24
24
|
"smol-toml": ">=1.6.1",
|
|
25
25
|
"ws": "^8.18.0",
|
|
26
|
-
"@alfe.ai/agent-api-client": "^0.
|
|
26
|
+
"@alfe.ai/agent-api-client": "^0.20.0",
|
|
27
|
+
"@alfe.ai/ai-proxy-local": "^0.0.17",
|
|
27
28
|
"@alfe.ai/config": "^0.4.1",
|
|
28
|
-
"@alfe.ai/integration-manifest": "^0.
|
|
29
|
-
"@alfe.ai/integrations": "^0.6.
|
|
30
|
-
"@alfe.ai/mcp-bundler": "^0.4.
|
|
31
|
-
"@alfe.ai/ai-proxy-local": "^0.0.17"
|
|
29
|
+
"@alfe.ai/integration-manifest": "^0.5.0",
|
|
30
|
+
"@alfe.ai/integrations": "^0.6.16",
|
|
31
|
+
"@alfe.ai/mcp-bundler": "^0.4.3"
|
|
32
32
|
},
|
|
33
33
|
"license": "UNLICENSED",
|
|
34
34
|
"homepage": "https://alfe.ai",
|