@alfe.ai/gateway 0.10.6 → 0.10.7
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 +417 -241
- package/dist/node-remediation.js +1 -1
- package/dist/{runtime-process.js → runtime-upgrade-recovery.js} +464 -3
- package/dist/runtime-upgrade.js +327 -18
- package/dist/sentry.js +16 -9
- package/dist/src/index.d.ts +10 -3
- package/dist/src/index.js +1 -1
- package/package.json +4 -4
package/dist/health.js
CHANGED
|
@@ -1,242 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as resolveOpenClawUpgradeTarget, E as __require, S as parseOpenClawVersion, T as __commonJSMin, _ as createLogger, a as recoverInterruptedRuntimeUpgrade, d as OPENCLAW_EXTERNAL_SUPERVISOR_ENV, f as RuntimeProcess, g as toFingerprintKey, h as STRICT_ERROR_CAPTURE_PROFILE, i as detectUpgradeIsolation, m as ErrorLineDetector, o as startBootUpgradeRecovery, p as CaptureThrottle, r as defaultRuntimeUpgradeMarkerPath, u as DRAIN_GRACE_MS } from "./runtime-upgrade-recovery.js";
|
|
2
2
|
import { a as captureFatal, d as flushSentry, f as initAgentSentry, h as logger, o as captureIntegrationFailure, p as setAgentContext, s as captureMcpFailure } from "./sentry.js";
|
|
3
3
|
import { chmod, mkdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { ClaudeCodeApplier, ClaudeCodeMcpSync, HermesApplier, HermesMcpSync, IntegrationManager, IntegrationManagerAdapter, LockManager, McpApplier, NoopOpenClawCliLock, OpenClawApplier, SerialOpenClawCliLock } from "@alfe.ai/integrations";
|
|
8
|
+
import { execFile, execSync } from "node:child_process";
|
|
9
|
+
import { promisify } from "node:util";
|
|
7
10
|
import { getEndpointFromToken, readConfig } from "@alfe.ai/config";
|
|
8
11
|
import { AgentApiClient } from "@alfe.ai/agent-api-client";
|
|
9
12
|
import { parse } from "smol-toml";
|
|
10
13
|
import WebSocket from "ws";
|
|
11
|
-
import {
|
|
12
|
-
import { promisify } from "node:util";
|
|
13
|
-
import { ClaudeCodeApplier, ClaudeCodeMcpSync, HermesApplier, HermesMcpSync, IntegrationManager, IntegrationManagerAdapter, McpApplier, NoopOpenClawCliLock, OpenClawApplier, SerialOpenClawCliLock } from "@alfe.ai/integrations";
|
|
14
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
15
|
import { Manager, McpBundler, defaultConnect, serverLaunchFingerprint } from "@alfe.ai/mcp-bundler";
|
|
15
16
|
import { createConnection, createServer } from "node:net";
|
|
16
17
|
import { pathToFileURL } from "node:url";
|
|
17
|
-
|
|
18
|
-
//#region src/claude-code-host-version.ts
|
|
18
|
+
//#region src/openclaw-plugin-ownership.ts
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
* the single source of truth for its install paths:
|
|
22
|
-
* - `@alfe.ai/cli` `installClaudeCodeHost` (fresh `alfe setup`)
|
|
23
|
-
* - the daemon `runtime.update` default (this package)
|
|
24
|
-
*
|
|
25
|
-
* Pinning (rather than floating `npm install -g @alfe.ai/claude-code-host` →
|
|
26
|
-
* `latest`) is deliberate: the host is the claude-code runtime child on every
|
|
27
|
-
* self-hosted claude-code agent, so a bad `latest` publish would auto-reach
|
|
28
|
-
* every new `alfe setup` with no CLI gate. Bump deliberately after validating a
|
|
29
|
-
* new host, then cut a CLI release so the pin travels with the CLI version. The
|
|
30
|
-
* dashboard still surfaces npm-`latest` as an explicit opt-in upgrade.
|
|
20
|
+
* Runtime-lock plugin ownership for the OpenClaw upgrade preflight.
|
|
31
21
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
22
|
+
* `~/.alfe/runtime-lock.json` (the integrations package's `LockManager`) records
|
|
23
|
+
* which integration contributed each OpenClaw plugin. A plugin that is not in
|
|
24
|
+
* it is owned by nothing the daemon manages. The upgrade only ever acts on such
|
|
25
|
+
* a plugin when it is ALSO an `@alfe.ai/*` package and definitively
|
|
26
|
+
* activate-only (see openclaw-plugin-compat.ts). A missing lock file or one
|
|
27
|
+
* with no OpenClaw rows is "ownership unknown" (block, never sweep). The
|
|
28
|
+
* residual risk is a lock that lists SOME plugins but dropped a row for one
|
|
29
|
+
* still in use; that plugin is activate-only, so it would fail to load on the
|
|
30
|
+
* target runtime anyway (`alfe remove` scope rule: only Alfe artifacts).
|
|
35
31
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
agent: "agt",
|
|
41
|
-
organization: "org",
|
|
42
|
-
person: "per",
|
|
43
|
-
auditEvent: "evt",
|
|
44
|
-
token: "tok",
|
|
45
|
-
transaction: "txn",
|
|
46
|
-
subscription: "sub",
|
|
47
|
-
conversation: "conv",
|
|
48
|
-
referral: "ref",
|
|
49
|
-
promoCode: "prc",
|
|
50
|
-
promoRedemption: "prr",
|
|
51
|
-
pendingPromo: "pdp",
|
|
52
|
-
remoteSession: "rms",
|
|
53
|
-
webhook: "whk",
|
|
54
|
-
webhookDelivery: "wdl",
|
|
55
|
-
onboardingSession: "obs",
|
|
56
|
-
inviteToken: "inv",
|
|
57
|
-
claimToken: "clm",
|
|
58
|
-
usageLimit: "lim",
|
|
59
|
-
team: "team",
|
|
60
|
-
project: "proj",
|
|
61
|
-
goal: "goal",
|
|
62
|
-
memory: "mem",
|
|
63
|
-
knowledgeTriple: "kgt",
|
|
64
|
-
accessCode: "acc",
|
|
65
|
-
pendingInject: "pij",
|
|
66
|
-
identity: "idn",
|
|
67
|
-
identityNote: "ino",
|
|
68
|
-
identityTag: "itg",
|
|
69
|
-
identityAlias: "ial",
|
|
70
|
-
identityVerification: "ivf",
|
|
71
|
-
role: "role",
|
|
72
|
-
directGrant: "dgr",
|
|
73
|
-
revision: "rev",
|
|
74
|
-
fact: "kfc",
|
|
75
|
-
changeRequest: "chr",
|
|
76
|
-
oauthConnection: "con",
|
|
77
|
-
channel: "chn",
|
|
78
|
-
oauthConnectionRequest: "crq",
|
|
79
|
-
deviceCode: "dvc",
|
|
80
|
-
attachment: "att",
|
|
81
|
-
run: "run",
|
|
82
|
-
request: "req",
|
|
83
|
-
connection: "conn",
|
|
84
|
-
correlation: "cor",
|
|
85
|
-
command: "cmd",
|
|
86
|
-
message: "msg",
|
|
87
|
-
ipcRequest: "ipc",
|
|
88
|
-
pluginConnection: "plg"
|
|
89
|
-
};
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region ../../packages-internal/ids/dist/create.js
|
|
92
|
-
var import_index_umd = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
93
|
-
(function(global, factory) {
|
|
94
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.ULID = {}));
|
|
95
|
-
})(exports, (function(exports$1) {
|
|
96
|
-
"use strict";
|
|
97
|
-
function createError(message) {
|
|
98
|
-
const err = new Error(message);
|
|
99
|
-
err.source = "ulid";
|
|
100
|
-
return err;
|
|
101
|
-
}
|
|
102
|
-
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
103
|
-
const ENCODING_LEN = 32;
|
|
104
|
-
const TIME_MAX = Math.pow(2, 48) - 1;
|
|
105
|
-
const TIME_LEN = 10;
|
|
106
|
-
const RANDOM_LEN = 16;
|
|
107
|
-
function replaceCharAt(str, index, char) {
|
|
108
|
-
if (index > str.length - 1) return str;
|
|
109
|
-
return str.substr(0, index) + char + str.substr(index + 1);
|
|
110
|
-
}
|
|
111
|
-
function incrementBase32(str) {
|
|
112
|
-
let done = void 0;
|
|
113
|
-
let index = str.length;
|
|
114
|
-
let char;
|
|
115
|
-
let charIndex;
|
|
116
|
-
const maxCharIndex = ENCODING_LEN - 1;
|
|
117
|
-
while (!done && index-- >= 0) {
|
|
118
|
-
char = str[index];
|
|
119
|
-
charIndex = ENCODING.indexOf(char);
|
|
120
|
-
if (charIndex === -1) throw createError("incorrectly encoded string");
|
|
121
|
-
if (charIndex === maxCharIndex) {
|
|
122
|
-
str = replaceCharAt(str, index, ENCODING[0]);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
done = replaceCharAt(str, index, ENCODING[charIndex + 1]);
|
|
126
|
-
}
|
|
127
|
-
if (typeof done === "string") return done;
|
|
128
|
-
throw createError("cannot increment this string");
|
|
129
|
-
}
|
|
130
|
-
function randomChar(prng) {
|
|
131
|
-
let rand = Math.floor(prng() * ENCODING_LEN);
|
|
132
|
-
if (rand === ENCODING_LEN) rand = ENCODING_LEN - 1;
|
|
133
|
-
return ENCODING.charAt(rand);
|
|
134
|
-
}
|
|
135
|
-
function encodeTime(now, len) {
|
|
136
|
-
if (isNaN(now)) throw new Error(now + " must be a number");
|
|
137
|
-
if (now > TIME_MAX) throw createError("cannot encode time greater than " + TIME_MAX);
|
|
138
|
-
if (now < 0) throw createError("time must be positive");
|
|
139
|
-
if (Number.isInteger(Number(now)) === false) throw createError("time must be an integer");
|
|
140
|
-
let mod;
|
|
141
|
-
let str = "";
|
|
142
|
-
for (; len > 0; len--) {
|
|
143
|
-
mod = now % ENCODING_LEN;
|
|
144
|
-
str = ENCODING.charAt(mod) + str;
|
|
145
|
-
now = (now - mod) / ENCODING_LEN;
|
|
146
|
-
}
|
|
147
|
-
return str;
|
|
148
|
-
}
|
|
149
|
-
function encodeRandom(len, prng) {
|
|
150
|
-
let str = "";
|
|
151
|
-
for (; len > 0; len--) str = randomChar(prng) + str;
|
|
152
|
-
return str;
|
|
153
|
-
}
|
|
154
|
-
function decodeTime(id) {
|
|
155
|
-
if (id.length !== TIME_LEN + RANDOM_LEN) throw createError("malformed ulid");
|
|
156
|
-
var time = id.substr(0, TIME_LEN).split("").reverse().reduce((carry, char, index) => {
|
|
157
|
-
const encodingIndex = ENCODING.indexOf(char);
|
|
158
|
-
if (encodingIndex === -1) throw createError("invalid character found: " + char);
|
|
159
|
-
return carry += encodingIndex * Math.pow(ENCODING_LEN, index);
|
|
160
|
-
}, 0);
|
|
161
|
-
if (time > TIME_MAX) throw createError("malformed ulid, timestamp too large");
|
|
162
|
-
return time;
|
|
163
|
-
}
|
|
164
|
-
function detectPrng(allowInsecure = false, root) {
|
|
165
|
-
if (!root) root = typeof window !== "undefined" ? window : null;
|
|
166
|
-
const browserCrypto = root && (root.crypto || root.msCrypto);
|
|
167
|
-
if (browserCrypto) return () => {
|
|
168
|
-
const buffer = new Uint8Array(1);
|
|
169
|
-
browserCrypto.getRandomValues(buffer);
|
|
170
|
-
return buffer[0] / 255;
|
|
171
|
-
};
|
|
172
|
-
else try {
|
|
173
|
-
const nodeCrypto = __require("crypto");
|
|
174
|
-
return () => nodeCrypto.randomBytes(1).readUInt8() / 255;
|
|
175
|
-
} catch (e) {}
|
|
176
|
-
if (allowInsecure) {
|
|
177
|
-
try {
|
|
178
|
-
console.error("secure crypto unusable, falling back to insecure Math.random()!");
|
|
179
|
-
} catch (e) {}
|
|
180
|
-
return () => Math.random();
|
|
181
|
-
}
|
|
182
|
-
throw createError("secure crypto unusable, insecure Math.random not allowed");
|
|
183
|
-
}
|
|
184
|
-
function factory(currPrng) {
|
|
185
|
-
if (!currPrng) currPrng = detectPrng();
|
|
186
|
-
return function ulid(seedTime) {
|
|
187
|
-
if (isNaN(seedTime)) seedTime = Date.now();
|
|
188
|
-
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng);
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function monotonicFactory(currPrng) {
|
|
192
|
-
if (!currPrng) currPrng = detectPrng();
|
|
193
|
-
let lastTime = 0;
|
|
194
|
-
let lastRandom;
|
|
195
|
-
return function ulid(seedTime) {
|
|
196
|
-
if (isNaN(seedTime)) seedTime = Date.now();
|
|
197
|
-
if (seedTime <= lastTime) {
|
|
198
|
-
const incrementedRandom = lastRandom = incrementBase32(lastRandom);
|
|
199
|
-
return encodeTime(lastTime, TIME_LEN) + incrementedRandom;
|
|
200
|
-
}
|
|
201
|
-
lastTime = seedTime;
|
|
202
|
-
const newRandom = lastRandom = encodeRandom(RANDOM_LEN, currPrng);
|
|
203
|
-
return encodeTime(seedTime, TIME_LEN) + newRandom;
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
const ulid = factory();
|
|
207
|
-
exports$1.decodeTime = decodeTime;
|
|
208
|
-
exports$1.detectPrng = detectPrng;
|
|
209
|
-
exports$1.encodeRandom = encodeRandom;
|
|
210
|
-
exports$1.encodeTime = encodeTime;
|
|
211
|
-
exports$1.factory = factory;
|
|
212
|
-
exports$1.incrementBase32 = incrementBase32;
|
|
213
|
-
exports$1.monotonicFactory = monotonicFactory;
|
|
214
|
-
exports$1.randomChar = randomChar;
|
|
215
|
-
exports$1.replaceCharAt = replaceCharAt;
|
|
216
|
-
exports$1.ulid = ulid;
|
|
217
|
-
}));
|
|
218
|
-
})))();
|
|
219
|
-
function createId(prefix) {
|
|
220
|
-
return `${prefix}_${(0, import_index_umd.ulid)()}`;
|
|
32
|
+
/** Bare npm package name from a manifest plugin spec (`@scope/name@1.2.3` → `@scope/name`). */
|
|
33
|
+
function barePluginPackage(spec) {
|
|
34
|
+
const at = spec.lastIndexOf("@");
|
|
35
|
+
return at <= 0 ? spec : spec.slice(0, at);
|
|
221
36
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*/
|
|
236
|
-
function enumValues(obj) {
|
|
237
|
-
const values = Object.values(obj);
|
|
238
|
-
if (values.length === 0) throw new TypeError("enumValues requires at least one value");
|
|
239
|
-
return values;
|
|
37
|
+
const DEFAULT_LOCK_PATH = join(homedir(), ".alfe", "runtime-lock.json");
|
|
38
|
+
function buildOpenClawPluginOwnership(applier, lockPath = DEFAULT_LOCK_PATH, lock = new LockManager(lockPath)) {
|
|
39
|
+
return {
|
|
40
|
+
ownedPlugins() {
|
|
41
|
+
if (!existsSync(lockPath)) throw new Error(`runtime lock ${lockPath} does not exist`);
|
|
42
|
+
const plugins = lock.read().runtimes.openclaw?.plugins ?? [];
|
|
43
|
+
if (plugins.length === 0) throw new Error("runtime lock records no OpenClaw plugin owners");
|
|
44
|
+
const owned = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const entry of plugins) owned.set(barePluginPackage(entry.package), entry.sourceIntegration);
|
|
46
|
+
return owned;
|
|
47
|
+
},
|
|
48
|
+
removePlugin: (pkg) => applier.removePlugin(pkg)
|
|
49
|
+
};
|
|
240
50
|
}
|
|
241
51
|
Object.freeze({ status: "aborted" });
|
|
242
52
|
function $constructor(name, initializer, params) {
|
|
@@ -758,7 +568,7 @@ const _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
|
|
|
758
568
|
//#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/regexes.js
|
|
759
569
|
const cuid = /^[cC][^\s-]{8,}$/;
|
|
760
570
|
const cuid2 = /^[0-9a-z]+$/;
|
|
761
|
-
const ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;
|
|
571
|
+
const ulid$1 = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;
|
|
762
572
|
const xid = /^[0-9a-vA-V]{20}$/;
|
|
763
573
|
const ksuid = /^[A-Za-z0-9]{27}$/;
|
|
764
574
|
const nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
@@ -1405,7 +1215,7 @@ const $ZodCUID2 = /* @__PURE__ */ $constructor("$ZodCUID2", (inst, def) => {
|
|
|
1405
1215
|
$ZodStringFormat.init(inst, def);
|
|
1406
1216
|
});
|
|
1407
1217
|
const $ZodULID = /* @__PURE__ */ $constructor("$ZodULID", (inst, def) => {
|
|
1408
|
-
def.pattern ?? (def.pattern = ulid);
|
|
1218
|
+
def.pattern ?? (def.pattern = ulid$1);
|
|
1409
1219
|
$ZodStringFormat.init(inst, def);
|
|
1410
1220
|
});
|
|
1411
1221
|
const $ZodXID = /* @__PURE__ */ $constructor("$ZodXID", (inst, def) => {
|
|
@@ -3920,6 +3730,230 @@ function superRefine(fn) {
|
|
|
3920
3730
|
return /* @__PURE__ */ _superRefine(fn);
|
|
3921
3731
|
}
|
|
3922
3732
|
//#endregion
|
|
3733
|
+
//#region src/claude-code-host-version.ts
|
|
3734
|
+
/**
|
|
3735
|
+
* The `@alfe.ai/claude-code-host` version Alfe pins the claude-code runtime to —
|
|
3736
|
+
* the single source of truth for its install paths:
|
|
3737
|
+
* - `@alfe.ai/cli` `installClaudeCodeHost` (fresh `alfe setup`)
|
|
3738
|
+
* - the daemon `runtime.update` default (this package)
|
|
3739
|
+
*
|
|
3740
|
+
* Pinning (rather than floating `npm install -g @alfe.ai/claude-code-host` →
|
|
3741
|
+
* `latest`) is deliberate: the host is the claude-code runtime child on every
|
|
3742
|
+
* self-hosted claude-code agent, so a bad `latest` publish would auto-reach
|
|
3743
|
+
* every new `alfe setup` with no CLI gate. Bump deliberately after validating a
|
|
3744
|
+
* new host, then cut a CLI release so the pin travels with the CLI version. The
|
|
3745
|
+
* dashboard still surfaces npm-`latest` as an explicit opt-in upgrade.
|
|
3746
|
+
*
|
|
3747
|
+
* NOTE: claude-code is self-hosted-only (no managed Docker image), so — unlike
|
|
3748
|
+
* `PINNED_OPENCLAW_VERSION` — there is no `services/compute/Dockerfile` mirror to
|
|
3749
|
+
* keep in sync.
|
|
3750
|
+
*/
|
|
3751
|
+
const PINNED_CLAUDE_CODE_HOST_VERSION = "0.1.6";
|
|
3752
|
+
//#endregion
|
|
3753
|
+
//#region ../../packages-internal/ids/dist/prefixes.js
|
|
3754
|
+
const ID_PREFIXES = {
|
|
3755
|
+
agent: "agt",
|
|
3756
|
+
organization: "org",
|
|
3757
|
+
person: "per",
|
|
3758
|
+
auditEvent: "evt",
|
|
3759
|
+
token: "tok",
|
|
3760
|
+
transaction: "txn",
|
|
3761
|
+
subscription: "sub",
|
|
3762
|
+
conversation: "conv",
|
|
3763
|
+
referral: "ref",
|
|
3764
|
+
promoCode: "prc",
|
|
3765
|
+
promoRedemption: "prr",
|
|
3766
|
+
pendingPromo: "pdp",
|
|
3767
|
+
remoteSession: "rms",
|
|
3768
|
+
webhook: "whk",
|
|
3769
|
+
webhookDelivery: "wdl",
|
|
3770
|
+
onboardingSession: "obs",
|
|
3771
|
+
inviteToken: "inv",
|
|
3772
|
+
claimToken: "clm",
|
|
3773
|
+
usageLimit: "lim",
|
|
3774
|
+
team: "team",
|
|
3775
|
+
project: "proj",
|
|
3776
|
+
goal: "goal",
|
|
3777
|
+
memory: "mem",
|
|
3778
|
+
knowledgeTriple: "kgt",
|
|
3779
|
+
accessCode: "acc",
|
|
3780
|
+
pendingInject: "pij",
|
|
3781
|
+
identity: "idn",
|
|
3782
|
+
identityNote: "ino",
|
|
3783
|
+
identityTag: "itg",
|
|
3784
|
+
identityAlias: "ial",
|
|
3785
|
+
identityVerification: "ivf",
|
|
3786
|
+
role: "role",
|
|
3787
|
+
directGrant: "dgr",
|
|
3788
|
+
revision: "rev",
|
|
3789
|
+
fact: "kfc",
|
|
3790
|
+
changeRequest: "chr",
|
|
3791
|
+
oauthConnection: "con",
|
|
3792
|
+
channel: "chn",
|
|
3793
|
+
oauthConnectionRequest: "crq",
|
|
3794
|
+
deviceCode: "dvc",
|
|
3795
|
+
attachment: "att",
|
|
3796
|
+
run: "run",
|
|
3797
|
+
request: "req",
|
|
3798
|
+
connection: "conn",
|
|
3799
|
+
correlation: "cor",
|
|
3800
|
+
command: "cmd",
|
|
3801
|
+
message: "msg",
|
|
3802
|
+
ipcRequest: "ipc",
|
|
3803
|
+
pluginConnection: "plg"
|
|
3804
|
+
};
|
|
3805
|
+
//#endregion
|
|
3806
|
+
//#region ../../packages-internal/ids/dist/create.js
|
|
3807
|
+
var import_index_umd = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3808
|
+
(function(global, factory) {
|
|
3809
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.ULID = {}));
|
|
3810
|
+
})(exports, (function(exports$1) {
|
|
3811
|
+
"use strict";
|
|
3812
|
+
function createError(message) {
|
|
3813
|
+
const err = new Error(message);
|
|
3814
|
+
err.source = "ulid";
|
|
3815
|
+
return err;
|
|
3816
|
+
}
|
|
3817
|
+
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
3818
|
+
const ENCODING_LEN = 32;
|
|
3819
|
+
const TIME_MAX = Math.pow(2, 48) - 1;
|
|
3820
|
+
const TIME_LEN = 10;
|
|
3821
|
+
const RANDOM_LEN = 16;
|
|
3822
|
+
function replaceCharAt(str, index, char) {
|
|
3823
|
+
if (index > str.length - 1) return str;
|
|
3824
|
+
return str.substr(0, index) + char + str.substr(index + 1);
|
|
3825
|
+
}
|
|
3826
|
+
function incrementBase32(str) {
|
|
3827
|
+
let done = void 0;
|
|
3828
|
+
let index = str.length;
|
|
3829
|
+
let char;
|
|
3830
|
+
let charIndex;
|
|
3831
|
+
const maxCharIndex = ENCODING_LEN - 1;
|
|
3832
|
+
while (!done && index-- >= 0) {
|
|
3833
|
+
char = str[index];
|
|
3834
|
+
charIndex = ENCODING.indexOf(char);
|
|
3835
|
+
if (charIndex === -1) throw createError("incorrectly encoded string");
|
|
3836
|
+
if (charIndex === maxCharIndex) {
|
|
3837
|
+
str = replaceCharAt(str, index, ENCODING[0]);
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
done = replaceCharAt(str, index, ENCODING[charIndex + 1]);
|
|
3841
|
+
}
|
|
3842
|
+
if (typeof done === "string") return done;
|
|
3843
|
+
throw createError("cannot increment this string");
|
|
3844
|
+
}
|
|
3845
|
+
function randomChar(prng) {
|
|
3846
|
+
let rand = Math.floor(prng() * ENCODING_LEN);
|
|
3847
|
+
if (rand === ENCODING_LEN) rand = ENCODING_LEN - 1;
|
|
3848
|
+
return ENCODING.charAt(rand);
|
|
3849
|
+
}
|
|
3850
|
+
function encodeTime(now, len) {
|
|
3851
|
+
if (isNaN(now)) throw new Error(now + " must be a number");
|
|
3852
|
+
if (now > TIME_MAX) throw createError("cannot encode time greater than " + TIME_MAX);
|
|
3853
|
+
if (now < 0) throw createError("time must be positive");
|
|
3854
|
+
if (Number.isInteger(Number(now)) === false) throw createError("time must be an integer");
|
|
3855
|
+
let mod;
|
|
3856
|
+
let str = "";
|
|
3857
|
+
for (; len > 0; len--) {
|
|
3858
|
+
mod = now % ENCODING_LEN;
|
|
3859
|
+
str = ENCODING.charAt(mod) + str;
|
|
3860
|
+
now = (now - mod) / ENCODING_LEN;
|
|
3861
|
+
}
|
|
3862
|
+
return str;
|
|
3863
|
+
}
|
|
3864
|
+
function encodeRandom(len, prng) {
|
|
3865
|
+
let str = "";
|
|
3866
|
+
for (; len > 0; len--) str = randomChar(prng) + str;
|
|
3867
|
+
return str;
|
|
3868
|
+
}
|
|
3869
|
+
function decodeTime(id) {
|
|
3870
|
+
if (id.length !== TIME_LEN + RANDOM_LEN) throw createError("malformed ulid");
|
|
3871
|
+
var time = id.substr(0, TIME_LEN).split("").reverse().reduce((carry, char, index) => {
|
|
3872
|
+
const encodingIndex = ENCODING.indexOf(char);
|
|
3873
|
+
if (encodingIndex === -1) throw createError("invalid character found: " + char);
|
|
3874
|
+
return carry += encodingIndex * Math.pow(ENCODING_LEN, index);
|
|
3875
|
+
}, 0);
|
|
3876
|
+
if (time > TIME_MAX) throw createError("malformed ulid, timestamp too large");
|
|
3877
|
+
return time;
|
|
3878
|
+
}
|
|
3879
|
+
function detectPrng(allowInsecure = false, root) {
|
|
3880
|
+
if (!root) root = typeof window !== "undefined" ? window : null;
|
|
3881
|
+
const browserCrypto = root && (root.crypto || root.msCrypto);
|
|
3882
|
+
if (browserCrypto) return () => {
|
|
3883
|
+
const buffer = new Uint8Array(1);
|
|
3884
|
+
browserCrypto.getRandomValues(buffer);
|
|
3885
|
+
return buffer[0] / 255;
|
|
3886
|
+
};
|
|
3887
|
+
else try {
|
|
3888
|
+
const nodeCrypto = __require("crypto");
|
|
3889
|
+
return () => nodeCrypto.randomBytes(1).readUInt8() / 255;
|
|
3890
|
+
} catch (e) {}
|
|
3891
|
+
if (allowInsecure) {
|
|
3892
|
+
try {
|
|
3893
|
+
console.error("secure crypto unusable, falling back to insecure Math.random()!");
|
|
3894
|
+
} catch (e) {}
|
|
3895
|
+
return () => Math.random();
|
|
3896
|
+
}
|
|
3897
|
+
throw createError("secure crypto unusable, insecure Math.random not allowed");
|
|
3898
|
+
}
|
|
3899
|
+
function factory(currPrng) {
|
|
3900
|
+
if (!currPrng) currPrng = detectPrng();
|
|
3901
|
+
return function ulid(seedTime) {
|
|
3902
|
+
if (isNaN(seedTime)) seedTime = Date.now();
|
|
3903
|
+
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng);
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
function monotonicFactory(currPrng) {
|
|
3907
|
+
if (!currPrng) currPrng = detectPrng();
|
|
3908
|
+
let lastTime = 0;
|
|
3909
|
+
let lastRandom;
|
|
3910
|
+
return function ulid(seedTime) {
|
|
3911
|
+
if (isNaN(seedTime)) seedTime = Date.now();
|
|
3912
|
+
if (seedTime <= lastTime) {
|
|
3913
|
+
const incrementedRandom = lastRandom = incrementBase32(lastRandom);
|
|
3914
|
+
return encodeTime(lastTime, TIME_LEN) + incrementedRandom;
|
|
3915
|
+
}
|
|
3916
|
+
lastTime = seedTime;
|
|
3917
|
+
const newRandom = lastRandom = encodeRandom(RANDOM_LEN, currPrng);
|
|
3918
|
+
return encodeTime(seedTime, TIME_LEN) + newRandom;
|
|
3919
|
+
};
|
|
3920
|
+
}
|
|
3921
|
+
const ulid = factory();
|
|
3922
|
+
exports$1.decodeTime = decodeTime;
|
|
3923
|
+
exports$1.detectPrng = detectPrng;
|
|
3924
|
+
exports$1.encodeRandom = encodeRandom;
|
|
3925
|
+
exports$1.encodeTime = encodeTime;
|
|
3926
|
+
exports$1.factory = factory;
|
|
3927
|
+
exports$1.incrementBase32 = incrementBase32;
|
|
3928
|
+
exports$1.monotonicFactory = monotonicFactory;
|
|
3929
|
+
exports$1.randomChar = randomChar;
|
|
3930
|
+
exports$1.replaceCharAt = replaceCharAt;
|
|
3931
|
+
exports$1.ulid = ulid;
|
|
3932
|
+
}));
|
|
3933
|
+
})))();
|
|
3934
|
+
function createId(prefix) {
|
|
3935
|
+
return `${prefix}_${(0, import_index_umd.ulid)()}`;
|
|
3936
|
+
}
|
|
3937
|
+
function correlationId() {
|
|
3938
|
+
return createId(ID_PREFIXES.correlation);
|
|
3939
|
+
}
|
|
3940
|
+
function pluginConnectionId() {
|
|
3941
|
+
return createId(ID_PREFIXES.pluginConnection);
|
|
3942
|
+
}
|
|
3943
|
+
//#endregion
|
|
3944
|
+
//#region ../../packages-internal/types/dist/lib/enum-values.js
|
|
3945
|
+
/**
|
|
3946
|
+
* Converts a const enum object into a non-empty readonly tuple.
|
|
3947
|
+
*
|
|
3948
|
+
* Satisfies both `z.enum()` and ElectroDB `type` field requirements
|
|
3949
|
+
* (both need `readonly [string, ...string[]]`).
|
|
3950
|
+
*/
|
|
3951
|
+
function enumValues(obj) {
|
|
3952
|
+
const values = Object.values(obj);
|
|
3953
|
+
if (values.length === 0) throw new TypeError("enumValues requires at least one value");
|
|
3954
|
+
return values;
|
|
3955
|
+
}
|
|
3956
|
+
//#endregion
|
|
3923
3957
|
//#region ../../packages-internal/types/dist/lib/usd.js
|
|
3924
3958
|
function isWholeUsdCentAmount(amountUsd) {
|
|
3925
3959
|
const roundedCents = Math.round(amountUsd * 100);
|
|
@@ -4885,7 +4919,33 @@ var AlfeApiClient = class {
|
|
|
4885
4919
|
}
|
|
4886
4920
|
};
|
|
4887
4921
|
//#endregion
|
|
4922
|
+
//#region ../../packages-internal/api-client/dist/services/billing.js
|
|
4923
|
+
/** Sizes every deployed billing API returns. */
|
|
4924
|
+
const BASE_MANAGED_AGENT_SIZES = [
|
|
4925
|
+
"small",
|
|
4926
|
+
"medium",
|
|
4927
|
+
"large"
|
|
4928
|
+
];
|
|
4929
|
+
/**
|
|
4930
|
+
* Fill `agentAddonSizesCovered` for an API that predates it. Such an API
|
|
4931
|
+
* predates xl too, so the all-sizes flag meant the sizes that existed then;
|
|
4932
|
+
* xl stays uncovered (an undiscounted price is the safe display).
|
|
4933
|
+
*/
|
|
4934
|
+
function normalizeSubscriptionDiscount(wire) {
|
|
4935
|
+
return {
|
|
4936
|
+
...wire,
|
|
4937
|
+
agentAddonSizesCovered: wire.agentAddonSizesCovered ?? (wire.appliesToAgentAddons ? [...BASE_MANAGED_AGENT_SIZES] : [])
|
|
4938
|
+
};
|
|
4939
|
+
}
|
|
4940
|
+
//#endregion
|
|
4888
4941
|
//#region ../../packages-internal/api-client/dist/services/auth.js
|
|
4942
|
+
function normalizePlatformSubscriptionStatus(wire) {
|
|
4943
|
+
const { discount, ...rest } = wire;
|
|
4944
|
+
return discount ? {
|
|
4945
|
+
...rest,
|
|
4946
|
+
discount: normalizeSubscriptionDiscount(discount)
|
|
4947
|
+
} : rest;
|
|
4948
|
+
}
|
|
4889
4949
|
var AuthService = class {
|
|
4890
4950
|
client;
|
|
4891
4951
|
constructor(client) {
|
|
@@ -5040,7 +5100,10 @@ var AuthService = class {
|
|
|
5040
5100
|
});
|
|
5041
5101
|
}
|
|
5042
5102
|
getSubscriptionStatus() {
|
|
5043
|
-
return this.client.request(`${this.prefix}/subscription`)
|
|
5103
|
+
return this.client.request(`${this.prefix}/subscription`).then((r) => r.ok ? {
|
|
5104
|
+
...r,
|
|
5105
|
+
data: normalizePlatformSubscriptionStatus(r.data)
|
|
5106
|
+
} : r);
|
|
5044
5107
|
}
|
|
5045
5108
|
/**
|
|
5046
5109
|
* Mint a short-lived, single-use Clerk sign-in token for the calling user so
|
|
@@ -6027,6 +6090,22 @@ var NoopRuntimeGate = class {
|
|
|
6027
6090
|
};
|
|
6028
6091
|
//#endregion
|
|
6029
6092
|
//#region src/reconciliation.ts
|
|
6093
|
+
/**
|
|
6094
|
+
* ReconciliationEngine — converges local integration state toward cloud desired state.
|
|
6095
|
+
*
|
|
6096
|
+
* Takes: desired integrations from cloud + reference to the local integration manager.
|
|
6097
|
+
* Produces: a ReconciliationReport of actions taken and resulting actual statuses.
|
|
6098
|
+
*
|
|
6099
|
+
* For each desired integration with desiredStatus="active":
|
|
6100
|
+
* 1. Check if installed at correct version (from ~/.alfe/integrations.json)
|
|
6101
|
+
* 2. If not installed or wrong version: call integrationManager.install()
|
|
6102
|
+
* 3. If installed but not active: call integrationManager.activate()
|
|
6103
|
+
*
|
|
6104
|
+
* For each locally installed integration NOT in desired list (or desiredStatus="removed"):
|
|
6105
|
+
* 1. Call integrationManager.deactivate() then integrationManager.uninstall()
|
|
6106
|
+
*
|
|
6107
|
+
* Returns ReconciliationReport with results for each integration processed.
|
|
6108
|
+
*/
|
|
6030
6109
|
const log$5 = logger.child({ component: "Reconciliation" });
|
|
6031
6110
|
/**
|
|
6032
6111
|
* How many times reconcile will re-attempt activation of an intact integration
|
|
@@ -6055,6 +6134,25 @@ var ReconciliationEngine = class {
|
|
|
6055
6134
|
*/
|
|
6056
6135
|
heldSuspension = false;
|
|
6057
6136
|
/**
|
|
6137
|
+
* Most recent per-attempt re-activation error per integration. Becomes the
|
|
6138
|
+
* error signature of the "re-activation attempts exhausted" report, so the
|
|
6139
|
+
* terminal event says WHY the integration is stuck.
|
|
6140
|
+
*/
|
|
6141
|
+
lastReactivateError = /* @__PURE__ */ new Map();
|
|
6142
|
+
/**
|
|
6143
|
+
* Sentry dedupe for reconcile outcomes that RECUR on every DESIRED_STATE
|
|
6144
|
+
* push once a budget is spent (re-activation exhausted, auto-reinstall
|
|
6145
|
+
* blocked) or that repeat identically across attempts. Keyed on
|
|
6146
|
+
* integration + phase + error signature, for one stuck EPISODE: the keys
|
|
6147
|
+
* are cleared by {@link resetSelfHealBudget} whenever the retry budget
|
|
6148
|
+
* resets (reinstall, upgrade, successful activation, healthy pass), so a
|
|
6149
|
+
* manual reinstall that fails with the same error reports again. The engine
|
|
6150
|
+
* lives as long as the daemon process (owned by `CloudClient`). Without it
|
|
6151
|
+
* a single stuck integration produced one event per push (51 for one agent).
|
|
6152
|
+
* Logs and the cloud-facing report are NOT deduped, only the Sentry capture.
|
|
6153
|
+
*/
|
|
6154
|
+
reportedFailures = /* @__PURE__ */ new Set();
|
|
6155
|
+
/**
|
|
6058
6156
|
* @param manager The integration manager to converge.
|
|
6059
6157
|
* @param gate Runtime gate used to suspend the runtime child around
|
|
6060
6158
|
* mutating branches so `openclaw` CLI subprocesses don't write
|
|
@@ -6067,6 +6165,29 @@ var ReconciliationEngine = class {
|
|
|
6067
6165
|
this.gate = gate;
|
|
6068
6166
|
}
|
|
6069
6167
|
/**
|
|
6168
|
+
* Capture an integration failure to Sentry at most once per
|
|
6169
|
+
* `(integration, phase, signature)` within the current stuck episode. A new
|
|
6170
|
+
* signature (a different underlying error) reports again.
|
|
6171
|
+
*/
|
|
6172
|
+
captureFailureOnce(integrationId, phase, signature, cause, fingerprint) {
|
|
6173
|
+
const key = `${integrationId}\u0000${phase}\u0000${createHash("sha256").update(signature).digest("hex")}`;
|
|
6174
|
+
if (this.reportedFailures.has(key)) return;
|
|
6175
|
+
this.reportedFailures.add(key);
|
|
6176
|
+
if (fingerprint) captureIntegrationFailure(integrationId, phase, cause, { fingerprint });
|
|
6177
|
+
else captureIntegrationFailure(integrationId, phase, cause);
|
|
6178
|
+
}
|
|
6179
|
+
/**
|
|
6180
|
+
* Start a fresh self-heal episode for an integration: a new re-activation
|
|
6181
|
+
* budget, and a cleared Sentry dedupe so a repeat of the same failure in the
|
|
6182
|
+
* new episode is reported again.
|
|
6183
|
+
*/
|
|
6184
|
+
resetSelfHealBudget(integrationId) {
|
|
6185
|
+
this.activateAttempts.delete(integrationId);
|
|
6186
|
+
this.lastReactivateError.delete(integrationId);
|
|
6187
|
+
const prefix = `${integrationId}\u0000`;
|
|
6188
|
+
for (const key of this.reportedFailures) if (key.startsWith(prefix)) this.reportedFailures.delete(key);
|
|
6189
|
+
}
|
|
6190
|
+
/**
|
|
6070
6191
|
* Suspend the runtime on the first mutating branch of a reconcile pass.
|
|
6071
6192
|
* Idempotent within a pass — subsequent mutating branches are covered by the
|
|
6072
6193
|
* single suspend held until the `finally` in {@link reconcile}.
|
|
@@ -6182,6 +6303,7 @@ var ReconciliationEngine = class {
|
|
|
6182
6303
|
try {
|
|
6183
6304
|
if (!local) {
|
|
6184
6305
|
await this.ensureSuspended();
|
|
6306
|
+
this.resetSelfHealBudget(id);
|
|
6185
6307
|
log$5.info(`Installing ${id}@${desired.version}`);
|
|
6186
6308
|
await this.manager.install(id, desired.version, desired.config, desired.customSource);
|
|
6187
6309
|
report.installed.push(id);
|
|
@@ -6200,7 +6322,7 @@ var ReconciliationEngine = class {
|
|
|
6200
6322
|
await this.ensureSuspended();
|
|
6201
6323
|
log$5.info(`Reinstalling ${id} on version change (reinstall requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt}): ${local.version} → ${desired.version}`);
|
|
6202
6324
|
this.manager.resetReinstallAttempts(id);
|
|
6203
|
-
this.
|
|
6325
|
+
this.resetSelfHealBudget(id);
|
|
6204
6326
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6205
6327
|
report.installed.push(id);
|
|
6206
6328
|
report.activated.push(id);
|
|
@@ -6215,7 +6337,7 @@ var ReconciliationEngine = class {
|
|
|
6215
6337
|
try {
|
|
6216
6338
|
if ((await this.manager.upgrade(id, desired.version, desired.config, desired.customSource, { onBeforeRuntimeMutation: () => this.ensureSuspended() })).configApplied) report.configApplied = true;
|
|
6217
6339
|
this.manager.resetReinstallAttempts(id);
|
|
6218
|
-
this.
|
|
6340
|
+
this.resetSelfHealBudget(id);
|
|
6219
6341
|
report.installed.push(id);
|
|
6220
6342
|
report.activated.push(id);
|
|
6221
6343
|
report.results.push({
|
|
@@ -6245,7 +6367,7 @@ var ReconciliationEngine = class {
|
|
|
6245
6367
|
await this.ensureSuspended();
|
|
6246
6368
|
log$5.info(`Reinstalling ${id} from error state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6247
6369
|
this.manager.resetReinstallAttempts(id);
|
|
6248
|
-
this.
|
|
6370
|
+
this.resetSelfHealBudget(id);
|
|
6249
6371
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6250
6372
|
report.installed.push(id);
|
|
6251
6373
|
report.activated.push(id);
|
|
@@ -6260,7 +6382,8 @@ var ReconciliationEngine = class {
|
|
|
6260
6382
|
const attempts = this.manager.getReinstallAttempts(id);
|
|
6261
6383
|
if (attempts >= 3) {
|
|
6262
6384
|
log$5.error(`Auto-reinstall blocked for ${id} — ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6263
|
-
|
|
6385
|
+
const blockedMessage = `Max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6386
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${blockedMessage}`, blockedMessage);
|
|
6264
6387
|
report.errors.push({
|
|
6265
6388
|
integrationId: id,
|
|
6266
6389
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6279,7 +6402,7 @@ var ReconciliationEngine = class {
|
|
|
6279
6402
|
try {
|
|
6280
6403
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6281
6404
|
this.manager.resetReinstallAttempts(id);
|
|
6282
|
-
this.
|
|
6405
|
+
this.resetSelfHealBudget(id);
|
|
6283
6406
|
report.installed.push(id);
|
|
6284
6407
|
report.activated.push(id);
|
|
6285
6408
|
report.results.push({
|
|
@@ -6311,7 +6434,7 @@ var ReconciliationEngine = class {
|
|
|
6311
6434
|
log$5.info(`Re-activating ${id} from error state (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)})`);
|
|
6312
6435
|
try {
|
|
6313
6436
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6314
|
-
this.
|
|
6437
|
+
this.resetSelfHealBudget(id);
|
|
6315
6438
|
report.activated.push(id);
|
|
6316
6439
|
report.results.push({
|
|
6317
6440
|
integrationId: id,
|
|
@@ -6322,7 +6445,8 @@ var ReconciliationEngine = class {
|
|
|
6322
6445
|
} catch (reactivateErr) {
|
|
6323
6446
|
const reactivateMsg = reactivateErr instanceof Error ? reactivateErr.message : String(reactivateErr);
|
|
6324
6447
|
log$5.warn(`Re-activation of ${id} from error state failed (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)}): ${reactivateMsg}`);
|
|
6325
|
-
|
|
6448
|
+
this.lastReactivateError.set(id, reactivateMsg);
|
|
6449
|
+
this.captureFailureOnce(id, "reactivate", `attempt:${reactivateMsg}`, reactivateErr);
|
|
6326
6450
|
report.errors.push({
|
|
6327
6451
|
integrationId: id,
|
|
6328
6452
|
error: reactivateMsg
|
|
@@ -6337,7 +6461,8 @@ var ReconciliationEngine = class {
|
|
|
6337
6461
|
}
|
|
6338
6462
|
}
|
|
6339
6463
|
log$5.warn(`Integration ${id} is in error state — re-activation exhausted, waiting for reinstall request`);
|
|
6340
|
-
|
|
6464
|
+
const lastReactivateError = this.lastReactivateError.get(id);
|
|
6465
|
+
this.captureFailureOnce(id, "reactivate", `exhausted:${lastReactivateError ?? ""}`, `Integration ${id} stuck in error state — re-activation attempts exhausted${lastReactivateError ? ` (last error: ${lastReactivateError})` : ""}`, ["integration-reactivate-exhausted", id]);
|
|
6341
6466
|
report.errors.push({
|
|
6342
6467
|
integrationId: id,
|
|
6343
6468
|
error: "Integration is in error state"
|
|
@@ -6355,7 +6480,7 @@ var ReconciliationEngine = class {
|
|
|
6355
6480
|
await this.ensureSuspended();
|
|
6356
6481
|
log$5.info(`Reinstalling ${id} from stale installing state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6357
6482
|
this.manager.resetReinstallAttempts(id);
|
|
6358
|
-
this.
|
|
6483
|
+
this.resetSelfHealBudget(id);
|
|
6359
6484
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6360
6485
|
report.installed.push(id);
|
|
6361
6486
|
report.activated.push(id);
|
|
@@ -6370,7 +6495,7 @@ var ReconciliationEngine = class {
|
|
|
6370
6495
|
if (attempts >= 3) {
|
|
6371
6496
|
const errorMessage = `Stale installing state — max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6372
6497
|
log$5.error(`Auto-reinstall blocked for ${id} — stuck in "installing", ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6373
|
-
|
|
6498
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${errorMessage}`, errorMessage);
|
|
6374
6499
|
report.errors.push({
|
|
6375
6500
|
integrationId: id,
|
|
6376
6501
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6389,7 +6514,7 @@ var ReconciliationEngine = class {
|
|
|
6389
6514
|
try {
|
|
6390
6515
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6391
6516
|
this.manager.resetReinstallAttempts(id);
|
|
6392
|
-
this.
|
|
6517
|
+
this.resetSelfHealBudget(id);
|
|
6393
6518
|
report.installed.push(id);
|
|
6394
6519
|
report.activated.push(id);
|
|
6395
6520
|
report.results.push({
|
|
@@ -6418,6 +6543,7 @@ var ReconciliationEngine = class {
|
|
|
6418
6543
|
await this.ensureSuspended();
|
|
6419
6544
|
log$5.info(`Activating ${id}`);
|
|
6420
6545
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6546
|
+
this.resetSelfHealBudget(id);
|
|
6421
6547
|
report.activated.push(id);
|
|
6422
6548
|
report.results.push({
|
|
6423
6549
|
integrationId: id,
|
|
@@ -6430,7 +6556,7 @@ var ReconciliationEngine = class {
|
|
|
6430
6556
|
await this.ensureSuspended();
|
|
6431
6557
|
log$5.info(`Reinstall requested for ${id} (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6432
6558
|
this.manager.resetReinstallAttempts(id);
|
|
6433
|
-
this.
|
|
6559
|
+
this.resetSelfHealBudget(id);
|
|
6434
6560
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6435
6561
|
report.installed.push(id);
|
|
6436
6562
|
report.activated.push(id);
|
|
@@ -6441,7 +6567,7 @@ var ReconciliationEngine = class {
|
|
|
6441
6567
|
});
|
|
6442
6568
|
return;
|
|
6443
6569
|
}
|
|
6444
|
-
this.
|
|
6570
|
+
this.resetSelfHealBudget(id);
|
|
6445
6571
|
report.results.push({
|
|
6446
6572
|
integrationId: id,
|
|
6447
6573
|
action: "up_to_date",
|
|
@@ -6480,6 +6606,7 @@ var ReconciliationEngine = class {
|
|
|
6480
6606
|
report.deactivated.push(id);
|
|
6481
6607
|
}
|
|
6482
6608
|
await this.manager.uninstall(id);
|
|
6609
|
+
this.resetSelfHealBudget(id);
|
|
6483
6610
|
report.uninstalled.push(id);
|
|
6484
6611
|
report.results.push({
|
|
6485
6612
|
integrationId: id,
|
|
@@ -9885,6 +10012,45 @@ let runtimeGateRef = null;
|
|
|
9885
10012
|
let firstRuntimeStart = null;
|
|
9886
10013
|
const LEGACY_CONFIG_SET_KEYS = new Set(["agents.defaults.model"]);
|
|
9887
10014
|
const MAX_LEGACY_CONFIG_VALUE_LENGTH = 16 * 1024;
|
|
10015
|
+
/** Daemon-only `upgradeRuntime` options for OpenClaw (see runtime-upgrade.ts). */
|
|
10016
|
+
function openClawUpgradeExtras() {
|
|
10017
|
+
const applier = runtimeAppliersRef?.get("openclaw");
|
|
10018
|
+
const isolation = detectUpgradeIsolation();
|
|
10019
|
+
return {
|
|
10020
|
+
markerPath: defaultRuntimeUpgradeMarkerPath(),
|
|
10021
|
+
...applier ? { pluginOwnership: buildOpenClawPluginOwnership(applier) } : {},
|
|
10022
|
+
...isolation ? { isolation } : {}
|
|
10023
|
+
};
|
|
10024
|
+
}
|
|
10025
|
+
/**
|
|
10026
|
+
* Boot-time repair of an interrupted OpenClaw upgrade (see
|
|
10027
|
+
* `startBootUpgradeRecovery`). Must be called BEFORE the runtime's first
|
|
10028
|
+
* `requestStart()`.
|
|
10029
|
+
*/
|
|
10030
|
+
function recoverInterruptedOpenClawUpgrade(gate, cliLock) {
|
|
10031
|
+
const recovery = startBootUpgradeRecovery({
|
|
10032
|
+
gate,
|
|
10033
|
+
cliLock,
|
|
10034
|
+
markerPath: defaultRuntimeUpgradeMarkerPath(),
|
|
10035
|
+
recover: (markerPath) => {
|
|
10036
|
+
const isolation = detectUpgradeIsolation();
|
|
10037
|
+
return recoverInterruptedRuntimeUpgrade({
|
|
10038
|
+
markerPath,
|
|
10039
|
+
...isolation ? { isolation } : {}
|
|
10040
|
+
});
|
|
10041
|
+
},
|
|
10042
|
+
onRecovered: (version) => {
|
|
10043
|
+
if (version === resolvedRuntimeVersion) return;
|
|
10044
|
+
resolvedRuntimeVersion = version;
|
|
10045
|
+
cloudClient.updateRuntimeVersionAndReRegister(version);
|
|
10046
|
+
}
|
|
10047
|
+
});
|
|
10048
|
+
if (!recovery) return;
|
|
10049
|
+
upgradingRuntime = true;
|
|
10050
|
+
recovery.finally(() => {
|
|
10051
|
+
upgradingRuntime = false;
|
|
10052
|
+
});
|
|
10053
|
+
}
|
|
9888
10054
|
async function startDaemon() {
|
|
9889
10055
|
startedAt = Date.now();
|
|
9890
10056
|
const managed = isManagedMode();
|
|
@@ -10123,6 +10289,7 @@ async function startDaemon() {
|
|
|
10123
10289
|
});
|
|
10124
10290
|
}
|
|
10125
10291
|
}
|
|
10292
|
+
if (config.autoStartRuntime && config.runtime === "openclaw") recoverInterruptedOpenClawUpgrade(runtimeGate, openclawCliLock);
|
|
10126
10293
|
cloudClient.start();
|
|
10127
10294
|
logger.debug("Cloud client started");
|
|
10128
10295
|
if (!config.autoStartRuntime && config.runtime) logger.warn({ runtime: config.runtime }, "Runtime configured but auto_start_runtime=false — runtime will not be spawned; in-runtime plugins (chat/console/sync) will be offline until you start it manually");
|
|
@@ -10348,7 +10515,15 @@ async function executeCloudCommand(command) {
|
|
|
10348
10515
|
};
|
|
10349
10516
|
const payload = command.payload;
|
|
10350
10517
|
const runtime = config.runtime;
|
|
10351
|
-
|
|
10518
|
+
let version;
|
|
10519
|
+
if (runtime === "openclaw") {
|
|
10520
|
+
const target = resolveOpenClawUpgradeTarget(payload?.version);
|
|
10521
|
+
if (target.clamped) logger.warn({
|
|
10522
|
+
requestedVersion: payload?.version,
|
|
10523
|
+
pinnedVersion: target.version
|
|
10524
|
+
}, "runtime.update requested an OpenClaw version above the pin — upgrading to the pin instead");
|
|
10525
|
+
version = target.version;
|
|
10526
|
+
} else version = payload?.version ?? (runtime === "claude-code" ? "0.1.6" : void 0);
|
|
10352
10527
|
upgradingRuntime = true;
|
|
10353
10528
|
setTimeout(() => {
|
|
10354
10529
|
(async () => {
|
|
@@ -10357,7 +10532,8 @@ async function executeCloudCommand(command) {
|
|
|
10357
10532
|
const result = await upgradeRuntime(runtime, version, runtimeProcess, {
|
|
10358
10533
|
...runtime === "openclaw" && openclawCliLockRef ? { cliLock: openclawCliLockRef } : {},
|
|
10359
10534
|
...runtime === "openclaw" && isManagedMode() && process.env.ALFE_PRIVATE_DATA_PLANE !== "true" ? { nodeRemediation: {} } : {},
|
|
10360
|
-
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {}
|
|
10535
|
+
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {},
|
|
10536
|
+
...runtime === "openclaw" ? openClawUpgradeExtras() : {}
|
|
10361
10537
|
});
|
|
10362
10538
|
if (result.success || result.runtimeChanged) {
|
|
10363
10539
|
resolvedRuntimeVersion = await getRuntimeVersion(runtime);
|