@alfe.ai/gateway 0.10.6 → 0.10.8
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 +437 -242
- 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 +3 -3
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
|
|
@@ -5868,6 +5931,13 @@ const log$6 = logger.child({ component: "RuntimeGate" });
|
|
|
5868
5931
|
var RuntimeGate = class {
|
|
5869
5932
|
depth = 0;
|
|
5870
5933
|
wasRunning = false;
|
|
5934
|
+
/**
|
|
5935
|
+
* True once the OUTERMOST `enter()` has finished stopping the runtime child
|
|
5936
|
+
* (its `stopWhenIdle()` resolved, i.e. the process exited), until the gate
|
|
5937
|
+
* fully reopens. `depth > 0` alone is not enough: depth is bumped before the
|
|
5938
|
+
* stop is awaited, so it is already set while the old child is draining.
|
|
5939
|
+
*/
|
|
5940
|
+
stopSettled = false;
|
|
5871
5941
|
exclusive = false;
|
|
5872
5942
|
/** Brackets parked behind an exclusive hold (or an exclusive request parked behind open brackets). */
|
|
5873
5943
|
waiters = [];
|
|
@@ -5882,6 +5952,14 @@ var RuntimeGate = class {
|
|
|
5882
5952
|
get isSuspended() {
|
|
5883
5953
|
return this.depth > 0;
|
|
5884
5954
|
}
|
|
5955
|
+
/**
|
|
5956
|
+
* Whether the runtime child is PROVABLY down right now: a bracket is open and
|
|
5957
|
+
* its stop has completed. The OpenClaw applier's one direct openclaw.json
|
|
5958
|
+
* write (the dangling channel-ref repair) is allow-listed on this.
|
|
5959
|
+
*/
|
|
5960
|
+
get isRuntimeStopped() {
|
|
5961
|
+
return this.depth > 0 && this.stopSettled;
|
|
5962
|
+
}
|
|
5885
5963
|
/** True while an exclusive bracket is open. */
|
|
5886
5964
|
get isExclusive() {
|
|
5887
5965
|
return this.exclusive;
|
|
@@ -5934,12 +6012,14 @@ var RuntimeGate = class {
|
|
|
5934
6012
|
}
|
|
5935
6013
|
async enter() {
|
|
5936
6014
|
if (this.depth++ > 0) return;
|
|
6015
|
+
this.stopSettled = false;
|
|
5937
6016
|
const rp = this.getRuntime();
|
|
5938
6017
|
this.wasRunning = rp?.isActive ?? false;
|
|
5939
6018
|
if (rp && this.wasRunning) {
|
|
5940
6019
|
log$6.info("Suspending runtime for mutating reconcile (avoid concurrent SQLite writers)");
|
|
5941
6020
|
await rp.stopWhenIdle();
|
|
5942
6021
|
}
|
|
6022
|
+
if (this.depth > 0) this.stopSettled = true;
|
|
5943
6023
|
}
|
|
5944
6024
|
/**
|
|
5945
6025
|
* Resume the runtime child. Only the outermost call (depth 1 → 0) actually
|
|
@@ -5956,6 +6036,7 @@ var RuntimeGate = class {
|
|
|
5956
6036
|
rp.resume();
|
|
5957
6037
|
}
|
|
5958
6038
|
this.wasRunning = false;
|
|
6039
|
+
this.stopSettled = false;
|
|
5959
6040
|
if (this.exclusive) this.exclusive = false;
|
|
5960
6041
|
this.wakeWaiters();
|
|
5961
6042
|
return Promise.resolve();
|
|
@@ -6027,6 +6108,22 @@ var NoopRuntimeGate = class {
|
|
|
6027
6108
|
};
|
|
6028
6109
|
//#endregion
|
|
6029
6110
|
//#region src/reconciliation.ts
|
|
6111
|
+
/**
|
|
6112
|
+
* ReconciliationEngine — converges local integration state toward cloud desired state.
|
|
6113
|
+
*
|
|
6114
|
+
* Takes: desired integrations from cloud + reference to the local integration manager.
|
|
6115
|
+
* Produces: a ReconciliationReport of actions taken and resulting actual statuses.
|
|
6116
|
+
*
|
|
6117
|
+
* For each desired integration with desiredStatus="active":
|
|
6118
|
+
* 1. Check if installed at correct version (from ~/.alfe/integrations.json)
|
|
6119
|
+
* 2. If not installed or wrong version: call integrationManager.install()
|
|
6120
|
+
* 3. If installed but not active: call integrationManager.activate()
|
|
6121
|
+
*
|
|
6122
|
+
* For each locally installed integration NOT in desired list (or desiredStatus="removed"):
|
|
6123
|
+
* 1. Call integrationManager.deactivate() then integrationManager.uninstall()
|
|
6124
|
+
*
|
|
6125
|
+
* Returns ReconciliationReport with results for each integration processed.
|
|
6126
|
+
*/
|
|
6030
6127
|
const log$5 = logger.child({ component: "Reconciliation" });
|
|
6031
6128
|
/**
|
|
6032
6129
|
* How many times reconcile will re-attempt activation of an intact integration
|
|
@@ -6055,6 +6152,25 @@ var ReconciliationEngine = class {
|
|
|
6055
6152
|
*/
|
|
6056
6153
|
heldSuspension = false;
|
|
6057
6154
|
/**
|
|
6155
|
+
* Most recent per-attempt re-activation error per integration. Becomes the
|
|
6156
|
+
* error signature of the "re-activation attempts exhausted" report, so the
|
|
6157
|
+
* terminal event says WHY the integration is stuck.
|
|
6158
|
+
*/
|
|
6159
|
+
lastReactivateError = /* @__PURE__ */ new Map();
|
|
6160
|
+
/**
|
|
6161
|
+
* Sentry dedupe for reconcile outcomes that RECUR on every DESIRED_STATE
|
|
6162
|
+
* push once a budget is spent (re-activation exhausted, auto-reinstall
|
|
6163
|
+
* blocked) or that repeat identically across attempts. Keyed on
|
|
6164
|
+
* integration + phase + error signature, for one stuck EPISODE: the keys
|
|
6165
|
+
* are cleared by {@link resetSelfHealBudget} whenever the retry budget
|
|
6166
|
+
* resets (reinstall, upgrade, successful activation, healthy pass), so a
|
|
6167
|
+
* manual reinstall that fails with the same error reports again. The engine
|
|
6168
|
+
* lives as long as the daemon process (owned by `CloudClient`). Without it
|
|
6169
|
+
* a single stuck integration produced one event per push (51 for one agent).
|
|
6170
|
+
* Logs and the cloud-facing report are NOT deduped, only the Sentry capture.
|
|
6171
|
+
*/
|
|
6172
|
+
reportedFailures = /* @__PURE__ */ new Set();
|
|
6173
|
+
/**
|
|
6058
6174
|
* @param manager The integration manager to converge.
|
|
6059
6175
|
* @param gate Runtime gate used to suspend the runtime child around
|
|
6060
6176
|
* mutating branches so `openclaw` CLI subprocesses don't write
|
|
@@ -6067,6 +6183,29 @@ var ReconciliationEngine = class {
|
|
|
6067
6183
|
this.gate = gate;
|
|
6068
6184
|
}
|
|
6069
6185
|
/**
|
|
6186
|
+
* Capture an integration failure to Sentry at most once per
|
|
6187
|
+
* `(integration, phase, signature)` within the current stuck episode. A new
|
|
6188
|
+
* signature (a different underlying error) reports again.
|
|
6189
|
+
*/
|
|
6190
|
+
captureFailureOnce(integrationId, phase, signature, cause, fingerprint) {
|
|
6191
|
+
const key = `${integrationId}\u0000${phase}\u0000${createHash("sha256").update(signature).digest("hex")}`;
|
|
6192
|
+
if (this.reportedFailures.has(key)) return;
|
|
6193
|
+
this.reportedFailures.add(key);
|
|
6194
|
+
if (fingerprint) captureIntegrationFailure(integrationId, phase, cause, { fingerprint });
|
|
6195
|
+
else captureIntegrationFailure(integrationId, phase, cause);
|
|
6196
|
+
}
|
|
6197
|
+
/**
|
|
6198
|
+
* Start a fresh self-heal episode for an integration: a new re-activation
|
|
6199
|
+
* budget, and a cleared Sentry dedupe so a repeat of the same failure in the
|
|
6200
|
+
* new episode is reported again.
|
|
6201
|
+
*/
|
|
6202
|
+
resetSelfHealBudget(integrationId) {
|
|
6203
|
+
this.activateAttempts.delete(integrationId);
|
|
6204
|
+
this.lastReactivateError.delete(integrationId);
|
|
6205
|
+
const prefix = `${integrationId}\u0000`;
|
|
6206
|
+
for (const key of this.reportedFailures) if (key.startsWith(prefix)) this.reportedFailures.delete(key);
|
|
6207
|
+
}
|
|
6208
|
+
/**
|
|
6070
6209
|
* Suspend the runtime on the first mutating branch of a reconcile pass.
|
|
6071
6210
|
* Idempotent within a pass — subsequent mutating branches are covered by the
|
|
6072
6211
|
* single suspend held until the `finally` in {@link reconcile}.
|
|
@@ -6182,6 +6321,7 @@ var ReconciliationEngine = class {
|
|
|
6182
6321
|
try {
|
|
6183
6322
|
if (!local) {
|
|
6184
6323
|
await this.ensureSuspended();
|
|
6324
|
+
this.resetSelfHealBudget(id);
|
|
6185
6325
|
log$5.info(`Installing ${id}@${desired.version}`);
|
|
6186
6326
|
await this.manager.install(id, desired.version, desired.config, desired.customSource);
|
|
6187
6327
|
report.installed.push(id);
|
|
@@ -6200,7 +6340,7 @@ var ReconciliationEngine = class {
|
|
|
6200
6340
|
await this.ensureSuspended();
|
|
6201
6341
|
log$5.info(`Reinstalling ${id} on version change (reinstall requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt}): ${local.version} → ${desired.version}`);
|
|
6202
6342
|
this.manager.resetReinstallAttempts(id);
|
|
6203
|
-
this.
|
|
6343
|
+
this.resetSelfHealBudget(id);
|
|
6204
6344
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6205
6345
|
report.installed.push(id);
|
|
6206
6346
|
report.activated.push(id);
|
|
@@ -6215,7 +6355,7 @@ var ReconciliationEngine = class {
|
|
|
6215
6355
|
try {
|
|
6216
6356
|
if ((await this.manager.upgrade(id, desired.version, desired.config, desired.customSource, { onBeforeRuntimeMutation: () => this.ensureSuspended() })).configApplied) report.configApplied = true;
|
|
6217
6357
|
this.manager.resetReinstallAttempts(id);
|
|
6218
|
-
this.
|
|
6358
|
+
this.resetSelfHealBudget(id);
|
|
6219
6359
|
report.installed.push(id);
|
|
6220
6360
|
report.activated.push(id);
|
|
6221
6361
|
report.results.push({
|
|
@@ -6245,7 +6385,7 @@ var ReconciliationEngine = class {
|
|
|
6245
6385
|
await this.ensureSuspended();
|
|
6246
6386
|
log$5.info(`Reinstalling ${id} from error state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6247
6387
|
this.manager.resetReinstallAttempts(id);
|
|
6248
|
-
this.
|
|
6388
|
+
this.resetSelfHealBudget(id);
|
|
6249
6389
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6250
6390
|
report.installed.push(id);
|
|
6251
6391
|
report.activated.push(id);
|
|
@@ -6260,7 +6400,8 @@ var ReconciliationEngine = class {
|
|
|
6260
6400
|
const attempts = this.manager.getReinstallAttempts(id);
|
|
6261
6401
|
if (attempts >= 3) {
|
|
6262
6402
|
log$5.error(`Auto-reinstall blocked for ${id} — ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6263
|
-
|
|
6403
|
+
const blockedMessage = `Max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6404
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${blockedMessage}`, blockedMessage);
|
|
6264
6405
|
report.errors.push({
|
|
6265
6406
|
integrationId: id,
|
|
6266
6407
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6279,7 +6420,7 @@ var ReconciliationEngine = class {
|
|
|
6279
6420
|
try {
|
|
6280
6421
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6281
6422
|
this.manager.resetReinstallAttempts(id);
|
|
6282
|
-
this.
|
|
6423
|
+
this.resetSelfHealBudget(id);
|
|
6283
6424
|
report.installed.push(id);
|
|
6284
6425
|
report.activated.push(id);
|
|
6285
6426
|
report.results.push({
|
|
@@ -6311,7 +6452,7 @@ var ReconciliationEngine = class {
|
|
|
6311
6452
|
log$5.info(`Re-activating ${id} from error state (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)})`);
|
|
6312
6453
|
try {
|
|
6313
6454
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6314
|
-
this.
|
|
6455
|
+
this.resetSelfHealBudget(id);
|
|
6315
6456
|
report.activated.push(id);
|
|
6316
6457
|
report.results.push({
|
|
6317
6458
|
integrationId: id,
|
|
@@ -6322,7 +6463,8 @@ var ReconciliationEngine = class {
|
|
|
6322
6463
|
} catch (reactivateErr) {
|
|
6323
6464
|
const reactivateMsg = reactivateErr instanceof Error ? reactivateErr.message : String(reactivateErr);
|
|
6324
6465
|
log$5.warn(`Re-activation of ${id} from error state failed (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)}): ${reactivateMsg}`);
|
|
6325
|
-
|
|
6466
|
+
this.lastReactivateError.set(id, reactivateMsg);
|
|
6467
|
+
this.captureFailureOnce(id, "reactivate", `attempt:${reactivateMsg}`, reactivateErr);
|
|
6326
6468
|
report.errors.push({
|
|
6327
6469
|
integrationId: id,
|
|
6328
6470
|
error: reactivateMsg
|
|
@@ -6337,7 +6479,8 @@ var ReconciliationEngine = class {
|
|
|
6337
6479
|
}
|
|
6338
6480
|
}
|
|
6339
6481
|
log$5.warn(`Integration ${id} is in error state — re-activation exhausted, waiting for reinstall request`);
|
|
6340
|
-
|
|
6482
|
+
const lastReactivateError = this.lastReactivateError.get(id);
|
|
6483
|
+
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
6484
|
report.errors.push({
|
|
6342
6485
|
integrationId: id,
|
|
6343
6486
|
error: "Integration is in error state"
|
|
@@ -6355,7 +6498,7 @@ var ReconciliationEngine = class {
|
|
|
6355
6498
|
await this.ensureSuspended();
|
|
6356
6499
|
log$5.info(`Reinstalling ${id} from stale installing state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6357
6500
|
this.manager.resetReinstallAttempts(id);
|
|
6358
|
-
this.
|
|
6501
|
+
this.resetSelfHealBudget(id);
|
|
6359
6502
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6360
6503
|
report.installed.push(id);
|
|
6361
6504
|
report.activated.push(id);
|
|
@@ -6370,7 +6513,7 @@ var ReconciliationEngine = class {
|
|
|
6370
6513
|
if (attempts >= 3) {
|
|
6371
6514
|
const errorMessage = `Stale installing state — max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6372
6515
|
log$5.error(`Auto-reinstall blocked for ${id} — stuck in "installing", ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6373
|
-
|
|
6516
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${errorMessage}`, errorMessage);
|
|
6374
6517
|
report.errors.push({
|
|
6375
6518
|
integrationId: id,
|
|
6376
6519
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6389,7 +6532,7 @@ var ReconciliationEngine = class {
|
|
|
6389
6532
|
try {
|
|
6390
6533
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6391
6534
|
this.manager.resetReinstallAttempts(id);
|
|
6392
|
-
this.
|
|
6535
|
+
this.resetSelfHealBudget(id);
|
|
6393
6536
|
report.installed.push(id);
|
|
6394
6537
|
report.activated.push(id);
|
|
6395
6538
|
report.results.push({
|
|
@@ -6418,6 +6561,7 @@ var ReconciliationEngine = class {
|
|
|
6418
6561
|
await this.ensureSuspended();
|
|
6419
6562
|
log$5.info(`Activating ${id}`);
|
|
6420
6563
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6564
|
+
this.resetSelfHealBudget(id);
|
|
6421
6565
|
report.activated.push(id);
|
|
6422
6566
|
report.results.push({
|
|
6423
6567
|
integrationId: id,
|
|
@@ -6430,7 +6574,7 @@ var ReconciliationEngine = class {
|
|
|
6430
6574
|
await this.ensureSuspended();
|
|
6431
6575
|
log$5.info(`Reinstall requested for ${id} (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6432
6576
|
this.manager.resetReinstallAttempts(id);
|
|
6433
|
-
this.
|
|
6577
|
+
this.resetSelfHealBudget(id);
|
|
6434
6578
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6435
6579
|
report.installed.push(id);
|
|
6436
6580
|
report.activated.push(id);
|
|
@@ -6441,7 +6585,7 @@ var ReconciliationEngine = class {
|
|
|
6441
6585
|
});
|
|
6442
6586
|
return;
|
|
6443
6587
|
}
|
|
6444
|
-
this.
|
|
6588
|
+
this.resetSelfHealBudget(id);
|
|
6445
6589
|
report.results.push({
|
|
6446
6590
|
integrationId: id,
|
|
6447
6591
|
action: "up_to_date",
|
|
@@ -6480,6 +6624,7 @@ var ReconciliationEngine = class {
|
|
|
6480
6624
|
report.deactivated.push(id);
|
|
6481
6625
|
}
|
|
6482
6626
|
await this.manager.uninstall(id);
|
|
6627
|
+
this.resetSelfHealBudget(id);
|
|
6483
6628
|
report.uninstalled.push(id);
|
|
6484
6629
|
report.results.push({
|
|
6485
6630
|
integrationId: id,
|
|
@@ -9885,6 +10030,45 @@ let runtimeGateRef = null;
|
|
|
9885
10030
|
let firstRuntimeStart = null;
|
|
9886
10031
|
const LEGACY_CONFIG_SET_KEYS = new Set(["agents.defaults.model"]);
|
|
9887
10032
|
const MAX_LEGACY_CONFIG_VALUE_LENGTH = 16 * 1024;
|
|
10033
|
+
/** Daemon-only `upgradeRuntime` options for OpenClaw (see runtime-upgrade.ts). */
|
|
10034
|
+
function openClawUpgradeExtras() {
|
|
10035
|
+
const applier = runtimeAppliersRef?.get("openclaw");
|
|
10036
|
+
const isolation = detectUpgradeIsolation();
|
|
10037
|
+
return {
|
|
10038
|
+
markerPath: defaultRuntimeUpgradeMarkerPath(),
|
|
10039
|
+
...applier ? { pluginOwnership: buildOpenClawPluginOwnership(applier) } : {},
|
|
10040
|
+
...isolation ? { isolation } : {}
|
|
10041
|
+
};
|
|
10042
|
+
}
|
|
10043
|
+
/**
|
|
10044
|
+
* Boot-time repair of an interrupted OpenClaw upgrade (see
|
|
10045
|
+
* `startBootUpgradeRecovery`). Must be called BEFORE the runtime's first
|
|
10046
|
+
* `requestStart()`.
|
|
10047
|
+
*/
|
|
10048
|
+
function recoverInterruptedOpenClawUpgrade(gate, cliLock) {
|
|
10049
|
+
const recovery = startBootUpgradeRecovery({
|
|
10050
|
+
gate,
|
|
10051
|
+
cliLock,
|
|
10052
|
+
markerPath: defaultRuntimeUpgradeMarkerPath(),
|
|
10053
|
+
recover: (markerPath) => {
|
|
10054
|
+
const isolation = detectUpgradeIsolation();
|
|
10055
|
+
return recoverInterruptedRuntimeUpgrade({
|
|
10056
|
+
markerPath,
|
|
10057
|
+
...isolation ? { isolation } : {}
|
|
10058
|
+
});
|
|
10059
|
+
},
|
|
10060
|
+
onRecovered: (version) => {
|
|
10061
|
+
if (version === resolvedRuntimeVersion) return;
|
|
10062
|
+
resolvedRuntimeVersion = version;
|
|
10063
|
+
cloudClient.updateRuntimeVersionAndReRegister(version);
|
|
10064
|
+
}
|
|
10065
|
+
});
|
|
10066
|
+
if (!recovery) return;
|
|
10067
|
+
upgradingRuntime = true;
|
|
10068
|
+
recovery.finally(() => {
|
|
10069
|
+
upgradingRuntime = false;
|
|
10070
|
+
});
|
|
10071
|
+
}
|
|
9888
10072
|
async function startDaemon() {
|
|
9889
10073
|
startedAt = Date.now();
|
|
9890
10074
|
const managed = isManagedMode();
|
|
@@ -9981,7 +10165,8 @@ async function startDaemon() {
|
|
|
9981
10165
|
runtimeAppliers.set(name, new OpenClawApplier({
|
|
9982
10166
|
home: runtimeCfg.workspace,
|
|
9983
10167
|
agentWorkspace: runtimeCfg.agentWorkspace,
|
|
9984
|
-
cliLock: openclawCliLock
|
|
10168
|
+
cliLock: openclawCliLock,
|
|
10169
|
+
isRuntimeStopped: () => name === config.runtime && runtimeGateRef instanceof RuntimeGate && runtimeGateRef.isRuntimeStopped && runtimeProcess !== null && !runtimeProcess.isActive
|
|
9985
10170
|
}));
|
|
9986
10171
|
logger.info({
|
|
9987
10172
|
runtime: name,
|
|
@@ -10123,6 +10308,7 @@ async function startDaemon() {
|
|
|
10123
10308
|
});
|
|
10124
10309
|
}
|
|
10125
10310
|
}
|
|
10311
|
+
if (config.autoStartRuntime && config.runtime === "openclaw") recoverInterruptedOpenClawUpgrade(runtimeGate, openclawCliLock);
|
|
10126
10312
|
cloudClient.start();
|
|
10127
10313
|
logger.debug("Cloud client started");
|
|
10128
10314
|
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 +10534,15 @@ async function executeCloudCommand(command) {
|
|
|
10348
10534
|
};
|
|
10349
10535
|
const payload = command.payload;
|
|
10350
10536
|
const runtime = config.runtime;
|
|
10351
|
-
|
|
10537
|
+
let version;
|
|
10538
|
+
if (runtime === "openclaw") {
|
|
10539
|
+
const target = resolveOpenClawUpgradeTarget(payload?.version);
|
|
10540
|
+
if (target.clamped) logger.warn({
|
|
10541
|
+
requestedVersion: payload?.version,
|
|
10542
|
+
pinnedVersion: target.version
|
|
10543
|
+
}, "runtime.update requested an OpenClaw version above the pin — upgrading to the pin instead");
|
|
10544
|
+
version = target.version;
|
|
10545
|
+
} else version = payload?.version ?? (runtime === "claude-code" ? "0.1.6" : void 0);
|
|
10352
10546
|
upgradingRuntime = true;
|
|
10353
10547
|
setTimeout(() => {
|
|
10354
10548
|
(async () => {
|
|
@@ -10357,7 +10551,8 @@ async function executeCloudCommand(command) {
|
|
|
10357
10551
|
const result = await upgradeRuntime(runtime, version, runtimeProcess, {
|
|
10358
10552
|
...runtime === "openclaw" && openclawCliLockRef ? { cliLock: openclawCliLockRef } : {},
|
|
10359
10553
|
...runtime === "openclaw" && isManagedMode() && process.env.ALFE_PRIVATE_DATA_PLANE !== "true" ? { nodeRemediation: {} } : {},
|
|
10360
|
-
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {}
|
|
10554
|
+
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {},
|
|
10555
|
+
...runtime === "openclaw" ? openClawUpgradeExtras() : {}
|
|
10361
10556
|
});
|
|
10362
10557
|
if (result.success || result.runtimeChanged) {
|
|
10363
10558
|
resolvedRuntimeVersion = await getRuntimeVersion(runtime);
|