@alfe.ai/gateway 0.10.5 → 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 +848 -415
- package/dist/node-remediation.js +1 -1
- package/dist/{runtime-process.js → runtime-upgrade-recovery.js} +524 -7
- 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)
|
|
20
|
+
* Runtime-lock plugin ownership for the OpenClaw upgrade preflight.
|
|
24
21
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* `
|
|
34
|
-
* keep in sync.
|
|
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)()}`;
|
|
221
|
-
}
|
|
222
|
-
function correlationId() {
|
|
223
|
-
return createId(ID_PREFIXES.correlation);
|
|
224
|
-
}
|
|
225
|
-
function pluginConnectionId() {
|
|
226
|
-
return createId(ID_PREFIXES.pluginConnection);
|
|
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);
|
|
227
36
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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) => {
|
|
@@ -3838,86 +3648,310 @@ function _default(innerType, defaultValue) {
|
|
|
3838
3648
|
get defaultValue() {
|
|
3839
3649
|
return typeof defaultValue === "function" ? defaultValue() : shallowClone(defaultValue);
|
|
3840
3650
|
}
|
|
3841
|
-
});
|
|
3842
|
-
}
|
|
3843
|
-
const ZodPrefault = /* @__PURE__ */ $constructor("ZodPrefault", (inst, def) => {
|
|
3844
|
-
$ZodPrefault.init(inst, def);
|
|
3845
|
-
ZodType.init(inst, def);
|
|
3846
|
-
inst._zod.processJSONSchema = (ctx, json, params) => prefaultProcessor(inst, ctx, json, params);
|
|
3847
|
-
inst.unwrap = () => inst._zod.def.innerType;
|
|
3848
|
-
});
|
|
3849
|
-
function prefault(innerType, defaultValue) {
|
|
3850
|
-
return new ZodPrefault({
|
|
3851
|
-
type: "prefault",
|
|
3852
|
-
innerType,
|
|
3853
|
-
get defaultValue() {
|
|
3854
|
-
return typeof defaultValue === "function" ? defaultValue() : shallowClone(defaultValue);
|
|
3651
|
+
});
|
|
3652
|
+
}
|
|
3653
|
+
const ZodPrefault = /* @__PURE__ */ $constructor("ZodPrefault", (inst, def) => {
|
|
3654
|
+
$ZodPrefault.init(inst, def);
|
|
3655
|
+
ZodType.init(inst, def);
|
|
3656
|
+
inst._zod.processJSONSchema = (ctx, json, params) => prefaultProcessor(inst, ctx, json, params);
|
|
3657
|
+
inst.unwrap = () => inst._zod.def.innerType;
|
|
3658
|
+
});
|
|
3659
|
+
function prefault(innerType, defaultValue) {
|
|
3660
|
+
return new ZodPrefault({
|
|
3661
|
+
type: "prefault",
|
|
3662
|
+
innerType,
|
|
3663
|
+
get defaultValue() {
|
|
3664
|
+
return typeof defaultValue === "function" ? defaultValue() : shallowClone(defaultValue);
|
|
3665
|
+
}
|
|
3666
|
+
});
|
|
3667
|
+
}
|
|
3668
|
+
const ZodNonOptional = /* @__PURE__ */ $constructor("ZodNonOptional", (inst, def) => {
|
|
3669
|
+
$ZodNonOptional.init(inst, def);
|
|
3670
|
+
ZodType.init(inst, def);
|
|
3671
|
+
inst._zod.processJSONSchema = (ctx, json, params) => nonoptionalProcessor(inst, ctx, json, params);
|
|
3672
|
+
inst.unwrap = () => inst._zod.def.innerType;
|
|
3673
|
+
});
|
|
3674
|
+
function nonoptional(innerType, params) {
|
|
3675
|
+
return new ZodNonOptional({
|
|
3676
|
+
type: "nonoptional",
|
|
3677
|
+
innerType,
|
|
3678
|
+
...normalizeParams(params)
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
const ZodCatch = /* @__PURE__ */ $constructor("ZodCatch", (inst, def) => {
|
|
3682
|
+
$ZodCatch.init(inst, def);
|
|
3683
|
+
ZodType.init(inst, def);
|
|
3684
|
+
inst._zod.processJSONSchema = (ctx, json, params) => catchProcessor(inst, ctx, json, params);
|
|
3685
|
+
inst.unwrap = () => inst._zod.def.innerType;
|
|
3686
|
+
inst.removeCatch = inst.unwrap;
|
|
3687
|
+
});
|
|
3688
|
+
function _catch(innerType, catchValue) {
|
|
3689
|
+
return new ZodCatch({
|
|
3690
|
+
type: "catch",
|
|
3691
|
+
innerType,
|
|
3692
|
+
catchValue: typeof catchValue === "function" ? catchValue : () => catchValue
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
const ZodPipe = /* @__PURE__ */ $constructor("ZodPipe", (inst, def) => {
|
|
3696
|
+
$ZodPipe.init(inst, def);
|
|
3697
|
+
ZodType.init(inst, def);
|
|
3698
|
+
inst._zod.processJSONSchema = (ctx, json, params) => pipeProcessor(inst, ctx, json, params);
|
|
3699
|
+
inst.in = def.in;
|
|
3700
|
+
inst.out = def.out;
|
|
3701
|
+
});
|
|
3702
|
+
function pipe(in_, out) {
|
|
3703
|
+
return new ZodPipe({
|
|
3704
|
+
type: "pipe",
|
|
3705
|
+
in: in_,
|
|
3706
|
+
out
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3709
|
+
const ZodReadonly = /* @__PURE__ */ $constructor("ZodReadonly", (inst, def) => {
|
|
3710
|
+
$ZodReadonly.init(inst, def);
|
|
3711
|
+
ZodType.init(inst, def);
|
|
3712
|
+
inst._zod.processJSONSchema = (ctx, json, params) => readonlyProcessor(inst, ctx, json, params);
|
|
3713
|
+
inst.unwrap = () => inst._zod.def.innerType;
|
|
3714
|
+
});
|
|
3715
|
+
function readonly(innerType) {
|
|
3716
|
+
return new ZodReadonly({
|
|
3717
|
+
type: "readonly",
|
|
3718
|
+
innerType
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
const ZodCustom = /* @__PURE__ */ $constructor("ZodCustom", (inst, def) => {
|
|
3722
|
+
$ZodCustom.init(inst, def);
|
|
3723
|
+
ZodType.init(inst, def);
|
|
3724
|
+
inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
|
|
3725
|
+
});
|
|
3726
|
+
function refine(fn, _params = {}) {
|
|
3727
|
+
return /* @__PURE__ */ _refine(ZodCustom, fn, _params);
|
|
3728
|
+
}
|
|
3729
|
+
function superRefine(fn) {
|
|
3730
|
+
return /* @__PURE__ */ _superRefine(fn);
|
|
3731
|
+
}
|
|
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
|
+
};
|
|
3855
3905
|
}
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
}
|
|
3871
|
-
const
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
$ZodPipe.init(inst, def);
|
|
3887
|
-
ZodType.init(inst, def);
|
|
3888
|
-
inst._zod.processJSONSchema = (ctx, json, params) => pipeProcessor(inst, ctx, json, params);
|
|
3889
|
-
inst.in = def.in;
|
|
3890
|
-
inst.out = def.out;
|
|
3891
|
-
});
|
|
3892
|
-
function pipe(in_, out) {
|
|
3893
|
-
return new ZodPipe({
|
|
3894
|
-
type: "pipe",
|
|
3895
|
-
in: in_,
|
|
3896
|
-
out
|
|
3897
|
-
});
|
|
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)()}`;
|
|
3898
3936
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
ZodType.init(inst, def);
|
|
3902
|
-
inst._zod.processJSONSchema = (ctx, json, params) => readonlyProcessor(inst, ctx, json, params);
|
|
3903
|
-
inst.unwrap = () => inst._zod.def.innerType;
|
|
3904
|
-
});
|
|
3905
|
-
function readonly(innerType) {
|
|
3906
|
-
return new ZodReadonly({
|
|
3907
|
-
type: "readonly",
|
|
3908
|
-
innerType
|
|
3909
|
-
});
|
|
3937
|
+
function correlationId() {
|
|
3938
|
+
return createId(ID_PREFIXES.correlation);
|
|
3910
3939
|
}
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
ZodType.init(inst, def);
|
|
3914
|
-
inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
|
|
3915
|
-
});
|
|
3916
|
-
function refine(fn, _params = {}) {
|
|
3917
|
-
return /* @__PURE__ */ _refine(ZodCustom, fn, _params);
|
|
3940
|
+
function pluginConnectionId() {
|
|
3941
|
+
return createId(ID_PREFIXES.pluginConnection);
|
|
3918
3942
|
}
|
|
3919
|
-
|
|
3920
|
-
|
|
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;
|
|
3921
3955
|
}
|
|
3922
3956
|
//#endregion
|
|
3923
3957
|
//#region ../../packages-internal/types/dist/lib/usd.js
|
|
@@ -4349,6 +4383,7 @@ function isClerkReverificationResponse(value) {
|
|
|
4349
4383
|
//#endregion
|
|
4350
4384
|
//#region ../../packages-internal/types/dist/models.js
|
|
4351
4385
|
const AnthropicModel = {
|
|
4386
|
+
Opus55: "claude-opus-5-5",
|
|
4352
4387
|
Opus5: "claude-opus-5",
|
|
4353
4388
|
Opus48: "claude-opus-4-8",
|
|
4354
4389
|
Opus47: "claude-opus-4-7",
|
|
@@ -4359,6 +4394,7 @@ const AnthropicModel = {
|
|
|
4359
4394
|
};
|
|
4360
4395
|
const ANTHROPIC_MODELS = enumValues(AnthropicModel);
|
|
4361
4396
|
const OpenAIModel = {
|
|
4397
|
+
GPT6Astra: "gpt-6-astra",
|
|
4362
4398
|
GPT4o: "gpt-4o",
|
|
4363
4399
|
GPT4oMini: "gpt-4o-mini",
|
|
4364
4400
|
O3: "o3",
|
|
@@ -4381,20 +4417,22 @@ const OpenAIModel = {
|
|
|
4381
4417
|
};
|
|
4382
4418
|
const OPENAI_MODELS = enumValues(OpenAIModel);
|
|
4383
4419
|
const DeepSeekModel = {
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
V4Flash: "deepseek-v4-flash"
|
|
4387
|
-
V4Pro: "deepseek-v4-pro"
|
|
4420
|
+
Flash: "deepseek-flash",
|
|
4421
|
+
V4Pro: "deepseek-v4-pro",
|
|
4422
|
+
V4Flash: "deepseek-v4-flash"
|
|
4388
4423
|
};
|
|
4389
4424
|
const DEEPSEEK_MODELS = enumValues(DeepSeekModel);
|
|
4390
4425
|
const GoogleModel = {
|
|
4426
|
+
Gemini38Flash: "gemini-3.8-flash",
|
|
4427
|
+
Gemini37Flash: "gemini-3.7-flash",
|
|
4428
|
+
Gemini36Flash: "gemini-3.6-flash",
|
|
4391
4429
|
Gemini35Flash: "gemini-3.5-flash",
|
|
4430
|
+
Gemini35FlashLite: "gemini-3.5-flash-lite",
|
|
4392
4431
|
Gemini31Pro: "gemini-3.1-pro-preview",
|
|
4393
4432
|
Gemini31FlashLite: "gemini-3.1-flash-lite",
|
|
4394
4433
|
Gemini25Pro: "gemini-2.5-pro",
|
|
4395
4434
|
Gemini25Flash: "gemini-2.5-flash",
|
|
4396
|
-
Gemini25FlashLite: "gemini-2.5-flash-lite"
|
|
4397
|
-
Gemini20Flash: "gemini-2.0-flash"
|
|
4435
|
+
Gemini25FlashLite: "gemini-2.5-flash-lite"
|
|
4398
4436
|
};
|
|
4399
4437
|
const GOOGLE_MODELS = enumValues(GoogleModel);
|
|
4400
4438
|
const MiniMaxModel = {
|
|
@@ -4412,20 +4450,23 @@ const MistralModel = {
|
|
|
4412
4450
|
Small: "mistral-small-latest",
|
|
4413
4451
|
Codestral: "codestral-latest",
|
|
4414
4452
|
Ministral8b: "ministral-8b-latest",
|
|
4415
|
-
Ministral3b: "ministral-3b-latest"
|
|
4416
|
-
MagistralMedium: "magistral-medium-latest",
|
|
4417
|
-
MagistralSmall: "magistral-small-latest",
|
|
4418
|
-
DevstralMedium: "devstral-medium-latest"
|
|
4453
|
+
Ministral3b: "ministral-3b-latest"
|
|
4419
4454
|
};
|
|
4420
4455
|
const MISTRAL_MODELS = enumValues(MistralModel);
|
|
4421
4456
|
const XAIModel = {
|
|
4457
|
+
Grok47: "grok-4.7",
|
|
4458
|
+
Grok46: "grok-4.6",
|
|
4422
4459
|
Grok45: "grok-4.5",
|
|
4423
4460
|
Grok43: "grok-4.3",
|
|
4461
|
+
GrokBuild01: "grok-build-0.1",
|
|
4424
4462
|
Grok4: "grok-4",
|
|
4425
4463
|
Grok41Fast: "grok-4.1-fast"
|
|
4426
4464
|
};
|
|
4427
4465
|
const XAI_MODELS = enumValues(XAIModel);
|
|
4428
4466
|
const ZhipuModel = {
|
|
4467
|
+
GLM53: "glm-5.3",
|
|
4468
|
+
GLM53Flash: "glm-5.3-flash",
|
|
4469
|
+
GLM53FlashX: "glm-5.3-flashx",
|
|
4429
4470
|
GLM52: "glm-5.2",
|
|
4430
4471
|
GLM51: "glm-5.1",
|
|
4431
4472
|
GLM46: "glm-4.6",
|
|
@@ -4441,6 +4482,8 @@ const MoonshotModel = {
|
|
|
4441
4482
|
};
|
|
4442
4483
|
const MOONSHOT_MODELS = enumValues(MoonshotModel);
|
|
4443
4484
|
const QwenModel = {
|
|
4485
|
+
Qwen38Max: "qwen3.8-max",
|
|
4486
|
+
Qwen38Flash: "qwen3.8-flash",
|
|
4444
4487
|
Qwen37Max: "qwen3.7-max",
|
|
4445
4488
|
Qwen37Plus: "qwen3.7-plus",
|
|
4446
4489
|
Qwen36Flash: "qwen3.6-flash",
|
|
@@ -4471,7 +4514,7 @@ _enum(ZHIPU_MODELS);
|
|
|
4471
4514
|
_enum(MOONSHOT_MODELS);
|
|
4472
4515
|
_enum(QWEN_MODELS);
|
|
4473
4516
|
string().trim().min(1);
|
|
4474
|
-
AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.
|
|
4517
|
+
AnthropicModel.Opus55, AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT6Astra, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.Flash, DeepSeekModel.V4Pro, DeepSeekModel.V4Flash, GoogleModel.Gemini38Flash, GoogleModel.Gemini37Flash, GoogleModel.Gemini36Flash, GoogleModel.Gemini35Flash, GoogleModel.Gemini35FlashLite, GoogleModel.Gemini31Pro, GoogleModel.Gemini31FlashLite, GoogleModel.Gemini25Pro, GoogleModel.Gemini25Flash, GoogleModel.Gemini25FlashLite, MiniMaxModel.M3, MiniMaxModel.M27, MiniMaxModel.M27HighSpeed, MiniMaxModel.M25, MiniMaxModel.M21, MiniMaxModel.M2, MistralModel.Large, MistralModel.Medium, MistralModel.Small, MistralModel.Codestral, MistralModel.Ministral8b, MistralModel.Ministral3b, XAIModel.Grok47, XAIModel.Grok46, XAIModel.Grok45, XAIModel.Grok43, XAIModel.GrokBuild01, XAIModel.Grok4, XAIModel.Grok41Fast, ZhipuModel.GLM53, ZhipuModel.GLM53Flash, ZhipuModel.GLM53FlashX, ZhipuModel.GLM52, ZhipuModel.GLM51, ZhipuModel.GLM46, ZhipuModel.GLM45, ZhipuModel.GLM45Air, MoonshotModel.K3, MoonshotModel.K26, MoonshotModel.K27Code, MoonshotModel.K27CodeHighSpeed, QwenModel.Qwen38Max, QwenModel.Qwen38Flash, QwenModel.Qwen37Max, QwenModel.Qwen37Plus, QwenModel.Qwen36Flash, QwenModel.Qwen35Flash;
|
|
4475
4518
|
/**
|
|
4476
4519
|
* Alfe-hosted open-weight models are promoted dynamically only in regions
|
|
4477
4520
|
* whose private data plane exists. Keep them out of ALL_MODELS so the standard
|
|
@@ -4531,7 +4574,7 @@ const AWS_PRIVATE_MODEL_PRICING = {
|
|
|
4531
4574
|
[BedrockModel.TitanEmbedTextV2]: { input: .026004 }
|
|
4532
4575
|
};
|
|
4533
4576
|
Object.fromEntries(AWS_PRIVATE_BEDROCK_CHAT_MODELS.map((modelId) => [`alfe-bedrock-${modelId}`, AWS_PRIVATE_MODEL_PRICING[modelId]]));
|
|
4534
|
-
AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.
|
|
4577
|
+
AnthropicModel.Opus55, AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT6Astra, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.Flash, DeepSeekModel.V4Pro, DeepSeekModel.V4Flash, GoogleModel.Gemini38Flash, GoogleModel.Gemini37Flash, GoogleModel.Gemini36Flash, GoogleModel.Gemini35Flash, GoogleModel.Gemini35FlashLite, GoogleModel.Gemini31Pro, GoogleModel.Gemini31FlashLite, GoogleModel.Gemini25Pro, GoogleModel.Gemini25Flash, GoogleModel.Gemini25FlashLite, MiniMaxModel.M3, MiniMaxModel.M27, MiniMaxModel.M27HighSpeed, MiniMaxModel.M25, MiniMaxModel.M21, MiniMaxModel.M2, MistralModel.Large, MistralModel.Medium, MistralModel.Small, MistralModel.Codestral, MistralModel.Ministral8b, MistralModel.Ministral3b, XAIModel.Grok47, XAIModel.Grok46, XAIModel.Grok45, XAIModel.Grok43, XAIModel.GrokBuild01, XAIModel.Grok4, XAIModel.Grok41Fast, ZhipuModel.GLM53, ZhipuModel.GLM53Flash, ZhipuModel.GLM53FlashX, ZhipuModel.GLM52, ZhipuModel.GLM51, ZhipuModel.GLM46, ZhipuModel.GLM45, ZhipuModel.GLM45Air, MoonshotModel.K3, MoonshotModel.K26, MoonshotModel.K27Code, MoonshotModel.K27CodeHighSpeed, QwenModel.Qwen38Max, QwenModel.Qwen38Flash, QwenModel.Qwen37Max, QwenModel.Qwen37Plus, QwenModel.Qwen36Flash, QwenModel.Qwen35Flash;
|
|
4535
4578
|
enumValues({
|
|
4536
4579
|
Standard: "standard",
|
|
4537
4580
|
AwsPrivate: "aws_private"
|
|
@@ -4746,7 +4789,8 @@ var AlfeApiClient = class {
|
|
|
4746
4789
|
ok: false,
|
|
4747
4790
|
error: errorMessage ?? (typeof body === "string" ? body : `API error: ${String(res.status)}`),
|
|
4748
4791
|
status: res.status,
|
|
4749
|
-
...typeof errBody?.code === "string" ? { code: errBody.code } : {}
|
|
4792
|
+
...typeof errBody?.code === "string" ? { code: errBody.code } : {},
|
|
4793
|
+
...Array.isArray(errBody?.ineligibleUserIds) && errBody.ineligibleUserIds.every((id) => typeof id === "string") ? { ineligibleUserIds: [...errBody.ineligibleUserIds] } : {}
|
|
4750
4794
|
}
|
|
4751
4795
|
};
|
|
4752
4796
|
}
|
|
@@ -4875,7 +4919,33 @@ var AlfeApiClient = class {
|
|
|
4875
4919
|
}
|
|
4876
4920
|
};
|
|
4877
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
|
|
4878
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
|
+
}
|
|
4879
4949
|
var AuthService = class {
|
|
4880
4950
|
client;
|
|
4881
4951
|
constructor(client) {
|
|
@@ -5030,7 +5100,10 @@ var AuthService = class {
|
|
|
5030
5100
|
});
|
|
5031
5101
|
}
|
|
5032
5102
|
getSubscriptionStatus() {
|
|
5033
|
-
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);
|
|
5034
5107
|
}
|
|
5035
5108
|
/**
|
|
5036
5109
|
* Mint a short-lived, single-use Clerk sign-in token for the calling user so
|
|
@@ -5854,13 +5927,17 @@ function isIPCResponse(msg) {
|
|
|
5854
5927
|
const PROTOCOL_VERSION = 1;
|
|
5855
5928
|
//#endregion
|
|
5856
5929
|
//#region src/runtime-gate.ts
|
|
5857
|
-
const log$
|
|
5930
|
+
const log$6 = logger.child({ component: "RuntimeGate" });
|
|
5858
5931
|
var RuntimeGate = class {
|
|
5859
5932
|
depth = 0;
|
|
5860
5933
|
wasRunning = false;
|
|
5861
5934
|
exclusive = false;
|
|
5862
5935
|
/** Brackets parked behind an exclusive hold (or an exclusive request parked behind open brackets). */
|
|
5863
5936
|
waiters = [];
|
|
5937
|
+
/** `suspendExclusive()` callers currently parked behind open brackets. */
|
|
5938
|
+
parkedExclusive = 0;
|
|
5939
|
+
/** The daemon is holding the runtime's FIRST start (fresh managed box); see {@link deferFirstStart}. */
|
|
5940
|
+
firstStartDeferred = false;
|
|
5864
5941
|
constructor(getRuntime) {
|
|
5865
5942
|
this.getRuntime = getRuntime;
|
|
5866
5943
|
}
|
|
@@ -5872,6 +5949,9 @@ var RuntimeGate = class {
|
|
|
5872
5949
|
get isExclusive() {
|
|
5873
5950
|
return this.exclusive;
|
|
5874
5951
|
}
|
|
5952
|
+
get hasParkedExclusive() {
|
|
5953
|
+
return this.parkedExclusive > 0;
|
|
5954
|
+
}
|
|
5875
5955
|
waitFor(ready) {
|
|
5876
5956
|
if (ready()) return Promise.resolve();
|
|
5877
5957
|
return new Promise((resolve) => {
|
|
@@ -5894,26 +5974,33 @@ var RuntimeGate = class {
|
|
|
5894
5974
|
* up (e.g. autoStartRuntime=false). Waits while an exclusive bracket is open.
|
|
5895
5975
|
*/
|
|
5896
5976
|
async suspend() {
|
|
5897
|
-
if (this.exclusive) log$
|
|
5977
|
+
if (this.exclusive) log$6.info("Mutating reconcile parked behind an exclusive runtime hold (upgrade in flight)");
|
|
5898
5978
|
while (this.exclusive) await this.waitFor(() => !this.exclusive);
|
|
5899
5979
|
await this.enter();
|
|
5900
5980
|
}
|
|
5901
5981
|
async suspendExclusive() {
|
|
5902
|
-
if (this.exclusive || this.depth !== 0) log$
|
|
5982
|
+
if (this.exclusive || this.depth !== 0) log$6.info({
|
|
5903
5983
|
depth: this.depth,
|
|
5904
5984
|
exclusive: this.exclusive
|
|
5905
5985
|
}, "Exclusive runtime hold parked behind open brackets (reconcile in flight)");
|
|
5906
|
-
|
|
5907
|
-
|
|
5986
|
+
if (this.exclusive || this.depth !== 0) {
|
|
5987
|
+
this.parkedExclusive += 1;
|
|
5988
|
+
try {
|
|
5989
|
+
while (this.exclusive || this.depth !== 0) await this.waitFor(() => !this.exclusive && this.depth === 0);
|
|
5990
|
+
} finally {
|
|
5991
|
+
this.parkedExclusive -= 1;
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
log$6.info("Exclusive runtime hold acquired");
|
|
5908
5995
|
this.exclusive = true;
|
|
5909
5996
|
await this.enter();
|
|
5910
5997
|
}
|
|
5911
5998
|
async enter() {
|
|
5912
5999
|
if (this.depth++ > 0) return;
|
|
5913
6000
|
const rp = this.getRuntime();
|
|
5914
|
-
this.wasRunning = rp?.
|
|
6001
|
+
this.wasRunning = rp?.isActive ?? false;
|
|
5915
6002
|
if (rp && this.wasRunning) {
|
|
5916
|
-
log$
|
|
6003
|
+
log$6.info("Suspending runtime for mutating reconcile (avoid concurrent SQLite writers)");
|
|
5917
6004
|
await rp.stopWhenIdle();
|
|
5918
6005
|
}
|
|
5919
6006
|
}
|
|
@@ -5928,7 +6015,7 @@ var RuntimeGate = class {
|
|
|
5928
6015
|
if (this.depth < 0) this.depth = 0;
|
|
5929
6016
|
const rp = this.getRuntime();
|
|
5930
6017
|
if (rp && this.wasRunning) {
|
|
5931
|
-
log$
|
|
6018
|
+
log$6.info("Resuming runtime after mutating reconcile");
|
|
5932
6019
|
rp.resume();
|
|
5933
6020
|
}
|
|
5934
6021
|
this.wasRunning = false;
|
|
@@ -5937,6 +6024,32 @@ var RuntimeGate = class {
|
|
|
5937
6024
|
return Promise.resolve();
|
|
5938
6025
|
}
|
|
5939
6026
|
/**
|
|
6027
|
+
* Start a runtime that has not been started yet (daemon boot, deferred first
|
|
6028
|
+
* start). Never spawns while a bracket is open: the start is folded into the
|
|
6029
|
+
* pending outermost `resume()` instead, so a first start can't land in the
|
|
6030
|
+
* middle of a mutating reconcile or an exclusive upgrade.
|
|
6031
|
+
*/
|
|
6032
|
+
requestStart() {
|
|
6033
|
+
this.firstStartDeferred = false;
|
|
6034
|
+
const rp = this.getRuntime();
|
|
6035
|
+
if (!rp) return;
|
|
6036
|
+
if (this.depth > 0) {
|
|
6037
|
+
log$6.info("Runtime start requested while suspended — deferring to pending resume");
|
|
6038
|
+
this.wasRunning = true;
|
|
6039
|
+
return;
|
|
6040
|
+
}
|
|
6041
|
+
rp.start();
|
|
6042
|
+
}
|
|
6043
|
+
/**
|
|
6044
|
+
* Mark the runtime's first start as deferred (fresh managed box, see
|
|
6045
|
+
* first-runtime-start.ts). Until {@link requestStart} runs, an out-of-band
|
|
6046
|
+
* {@link requestRestart} of the never-started runtime is folded into that
|
|
6047
|
+
* pending start instead of spawning it early.
|
|
6048
|
+
*/
|
|
6049
|
+
deferFirstStart() {
|
|
6050
|
+
this.firstStartDeferred = true;
|
|
6051
|
+
}
|
|
6052
|
+
/**
|
|
5940
6053
|
* Out-of-band restart request (e.g. `alfe.config_set`, Hermes MCP sync).
|
|
5941
6054
|
* Deferred while a suspend is active — the pending `resume()` will bring the
|
|
5942
6055
|
* runtime back with fresh config, so restarting mid-suspend would just fight
|
|
@@ -5945,12 +6058,16 @@ var RuntimeGate = class {
|
|
|
5945
6058
|
*/
|
|
5946
6059
|
requestRestart() {
|
|
5947
6060
|
if (this.depth > 0) {
|
|
5948
|
-
log$
|
|
6061
|
+
log$6.info("Runtime restart requested while suspended — deferring to pending resume");
|
|
5949
6062
|
return;
|
|
5950
6063
|
}
|
|
5951
6064
|
const rp = this.getRuntime();
|
|
6065
|
+
if (rp && this.firstStartDeferred && !rp.isActive) {
|
|
6066
|
+
log$6.info("Runtime restart requested before the deferred first start — folding into that start");
|
|
6067
|
+
return;
|
|
6068
|
+
}
|
|
5952
6069
|
if (rp) rp.restart().catch((err) => {
|
|
5953
|
-
log$
|
|
6070
|
+
log$6.error({ err: err instanceof Error ? err.message : String(err) }, "Failed to restart runtime");
|
|
5954
6071
|
});
|
|
5955
6072
|
}
|
|
5956
6073
|
};
|
|
@@ -5973,7 +6090,23 @@ var NoopRuntimeGate = class {
|
|
|
5973
6090
|
};
|
|
5974
6091
|
//#endregion
|
|
5975
6092
|
//#region src/reconciliation.ts
|
|
5976
|
-
|
|
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
|
+
*/
|
|
6109
|
+
const log$5 = logger.child({ component: "Reconciliation" });
|
|
5977
6110
|
/**
|
|
5978
6111
|
* How many times reconcile will re-attempt activation of an intact integration
|
|
5979
6112
|
* stuck in `error` (with no reinstall requested) before giving up and waiting
|
|
@@ -5994,6 +6127,32 @@ var ReconciliationEngine = class {
|
|
|
5994
6127
|
*/
|
|
5995
6128
|
suspended = false;
|
|
5996
6129
|
/**
|
|
6130
|
+
* A suspension carried from the previous pass because the caller already
|
|
6131
|
+
* had another DESIRED_STATE queued (see {@link ReconcileOptions.holdSuspension}).
|
|
6132
|
+
* The next pass adopts it instead of resuming → re-suspending the runtime;
|
|
6133
|
+
* {@link releaseHeldSuspension} gives it back once the queue is drained.
|
|
6134
|
+
*/
|
|
6135
|
+
heldSuspension = false;
|
|
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
|
+
/**
|
|
5997
6156
|
* @param manager The integration manager to converge.
|
|
5998
6157
|
* @param gate Runtime gate used to suspend the runtime child around
|
|
5999
6158
|
* mutating branches so `openclaw` CLI subprocesses don't write
|
|
@@ -6006,6 +6165,29 @@ var ReconciliationEngine = class {
|
|
|
6006
6165
|
this.gate = gate;
|
|
6007
6166
|
}
|
|
6008
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
|
+
/**
|
|
6009
6191
|
* Suspend the runtime on the first mutating branch of a reconcile pass.
|
|
6010
6192
|
* Idempotent within a pass — subsequent mutating branches are covered by the
|
|
6011
6193
|
* single suspend held until the `finally` in {@link reconcile}.
|
|
@@ -6022,14 +6204,43 @@ var ReconciliationEngine = class {
|
|
|
6022
6204
|
* so the mutation performed by {@link ensureSuspended} in a different method
|
|
6023
6205
|
* isn't narrowed away by control-flow analysis at the callsite.
|
|
6024
6206
|
*/
|
|
6025
|
-
async resumeIfSuspended() {
|
|
6026
|
-
if (this.suspended)
|
|
6207
|
+
async resumeIfSuspended(opts) {
|
|
6208
|
+
if (!this.suspended) return;
|
|
6209
|
+
if (opts?.holdSuspension?.()) {
|
|
6210
|
+
log$5.info("Holding runtime suspension across queued reconcile pass");
|
|
6211
|
+
if (this.gate.hasParkedExclusive) log$5.warn("Exclusive runtime hold (upgrade) is parked behind a suspension held for queued reconcile passes");
|
|
6212
|
+
this.heldSuspension = true;
|
|
6213
|
+
return;
|
|
6214
|
+
}
|
|
6215
|
+
await this.gate.resume();
|
|
6216
|
+
}
|
|
6217
|
+
/**
|
|
6218
|
+
* Resume a suspension held across passes by {@link ReconcileOptions.holdSuspension}.
|
|
6219
|
+
* The caller MUST call this once its queue of passes is drained (success or
|
|
6220
|
+
* failure), otherwise the runtime stays suspended. No-op when nothing is held.
|
|
6221
|
+
*/
|
|
6222
|
+
async releaseHeldSuspension() {
|
|
6223
|
+
if (!this.heldSuspension) return;
|
|
6224
|
+
this.heldSuspension = false;
|
|
6225
|
+
await this.gate.resume();
|
|
6226
|
+
}
|
|
6227
|
+
/** True while a suspension is carried between passes (tests/diagnostics). */
|
|
6228
|
+
get isHoldingSuspension() {
|
|
6229
|
+
return this.heldSuspension;
|
|
6027
6230
|
}
|
|
6028
6231
|
/**
|
|
6029
6232
|
* Reconcile local state against desired state from cloud.
|
|
6030
6233
|
*/
|
|
6031
|
-
async reconcile(desiredIntegrations) {
|
|
6032
|
-
this.suspended =
|
|
6234
|
+
async reconcile(desiredIntegrations, opts) {
|
|
6235
|
+
this.suspended = this.heldSuspension;
|
|
6236
|
+
this.heldSuspension = false;
|
|
6237
|
+
try {
|
|
6238
|
+
return await this.reconcilePass(desiredIntegrations);
|
|
6239
|
+
} finally {
|
|
6240
|
+
await this.resumeIfSuspended(opts);
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
async reconcilePass(desiredIntegrations) {
|
|
6033
6244
|
const report = {
|
|
6034
6245
|
results: [],
|
|
6035
6246
|
installed: [],
|
|
@@ -6043,7 +6254,7 @@ var ReconciliationEngine = class {
|
|
|
6043
6254
|
try {
|
|
6044
6255
|
localIntegrations = await this.manager.getInstalledIntegrations();
|
|
6045
6256
|
} catch (err) {
|
|
6046
|
-
log$
|
|
6257
|
+
log$5.error({ err }, "Failed to get local integrations");
|
|
6047
6258
|
captureIntegrationFailure("_reconciliation", "read_local_state", err);
|
|
6048
6259
|
const errorMessage = "Failed to read local integration state; reconciliation skipped";
|
|
6049
6260
|
const affectedIds = desiredIntegrations.length > 0 ? desiredIntegrations.map((desired) => desired.integrationId) : ["*"];
|
|
@@ -6061,34 +6272,30 @@ var ReconciliationEngine = class {
|
|
|
6061
6272
|
}
|
|
6062
6273
|
const localMap = new Map(localIntegrations.map((i) => [i.id, i]));
|
|
6063
6274
|
const desiredMap = new Map(desiredIntegrations.map((d) => [d.integrationId, d]));
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6275
|
+
for (const desired of desiredIntegrations) if (desired.desiredStatus === "active") {
|
|
6276
|
+
const local = localMap.get(desired.integrationId);
|
|
6277
|
+
if (desired.connectionAuthorityVersion !== void 0 && local?.connectionAuthorityPendingVersion !== void 0 && local.status === "error" && (local.version === desired.version || desired.version === "" || local.version === "unknown") && !(desired.reinstallRequestedAt && local.installedAt && desired.reinstallRequestedAt > local.installedAt) && await this.manager.isInstallIntact(desired.integrationId)) report.results.push({
|
|
6278
|
+
integrationId: desired.integrationId,
|
|
6279
|
+
action: "activated",
|
|
6280
|
+
actualStatus: "active"
|
|
6281
|
+
});
|
|
6282
|
+
else await this.reconcileActive(desired, local, report);
|
|
6283
|
+
const result = report.results.find((item) => item.integrationId === desired.integrationId);
|
|
6284
|
+
if (result?.actualStatus === "active" && desired.connectionAuthorityVersion !== void 0 && (local?.connectionAuthorityPendingVersion !== void 0 || desired.connectionAuthorityVersion !== local?.connectionAuthorityVersion)) try {
|
|
6285
|
+
await this.manager.refreshConnections(desired.integrationId, desired.connectionAuthorityVersion, { onBeforeRuntimeMutation: () => this.ensureSuspended() });
|
|
6286
|
+
} catch (err) {
|
|
6287
|
+
captureIntegrationFailure(desired.integrationId, "refresh_connections", err);
|
|
6288
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
6289
|
+
report.errors.push({
|
|
6068
6290
|
integrationId: desired.integrationId,
|
|
6069
|
-
|
|
6070
|
-
actualStatus: "active"
|
|
6291
|
+
error
|
|
6071
6292
|
});
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
6079
|
-
report.errors.push({
|
|
6080
|
-
integrationId: desired.integrationId,
|
|
6081
|
-
error
|
|
6082
|
-
});
|
|
6083
|
-
result.action = "error";
|
|
6084
|
-
result.actualStatus = "error";
|
|
6085
|
-
result.errorMessage = error;
|
|
6086
|
-
}
|
|
6087
|
-
} else await this.reconcileRemoved(desired.integrationId, localMap.get(desired.integrationId), report);
|
|
6088
|
-
for (const local of localIntegrations) if (!desiredMap.has(local.id)) await this.reconcileRemoved(local.id, local, report);
|
|
6089
|
-
} finally {
|
|
6090
|
-
await this.resumeIfSuspended();
|
|
6091
|
-
}
|
|
6293
|
+
result.action = "error";
|
|
6294
|
+
result.actualStatus = "error";
|
|
6295
|
+
result.errorMessage = error;
|
|
6296
|
+
}
|
|
6297
|
+
} else await this.reconcileRemoved(desired.integrationId, localMap.get(desired.integrationId), report);
|
|
6298
|
+
for (const local of localIntegrations) if (!desiredMap.has(local.id)) await this.reconcileRemoved(local.id, local, report);
|
|
6092
6299
|
return report;
|
|
6093
6300
|
}
|
|
6094
6301
|
async reconcileActive(desired, local, report) {
|
|
@@ -6096,10 +6303,11 @@ var ReconciliationEngine = class {
|
|
|
6096
6303
|
try {
|
|
6097
6304
|
if (!local) {
|
|
6098
6305
|
await this.ensureSuspended();
|
|
6099
|
-
|
|
6306
|
+
this.resetSelfHealBudget(id);
|
|
6307
|
+
log$5.info(`Installing ${id}@${desired.version}`);
|
|
6100
6308
|
await this.manager.install(id, desired.version, desired.config, desired.customSource);
|
|
6101
6309
|
report.installed.push(id);
|
|
6102
|
-
log$
|
|
6310
|
+
log$5.info(`Activating ${id}`);
|
|
6103
6311
|
if ((await this.manager.activate(id)).configApplied) report.configApplied = true;
|
|
6104
6312
|
report.activated.push(id);
|
|
6105
6313
|
report.results.push({
|
|
@@ -6112,9 +6320,9 @@ var ReconciliationEngine = class {
|
|
|
6112
6320
|
if (local.version !== desired.version && desired.version !== "" && local.version !== "unknown") {
|
|
6113
6321
|
if (desired.reinstallRequestedAt && local.installedAt && desired.reinstallRequestedAt > local.installedAt) {
|
|
6114
6322
|
await this.ensureSuspended();
|
|
6115
|
-
log$
|
|
6323
|
+
log$5.info(`Reinstalling ${id} on version change (reinstall requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt}): ${local.version} → ${desired.version}`);
|
|
6116
6324
|
this.manager.resetReinstallAttempts(id);
|
|
6117
|
-
this.
|
|
6325
|
+
this.resetSelfHealBudget(id);
|
|
6118
6326
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6119
6327
|
report.installed.push(id);
|
|
6120
6328
|
report.activated.push(id);
|
|
@@ -6125,11 +6333,11 @@ var ReconciliationEngine = class {
|
|
|
6125
6333
|
});
|
|
6126
6334
|
return;
|
|
6127
6335
|
}
|
|
6128
|
-
log$
|
|
6336
|
+
log$5.info(`Upgrading ${id} in place: ${local.version} → ${desired.version}`);
|
|
6129
6337
|
try {
|
|
6130
6338
|
if ((await this.manager.upgrade(id, desired.version, desired.config, desired.customSource, { onBeforeRuntimeMutation: () => this.ensureSuspended() })).configApplied) report.configApplied = true;
|
|
6131
6339
|
this.manager.resetReinstallAttempts(id);
|
|
6132
|
-
this.
|
|
6340
|
+
this.resetSelfHealBudget(id);
|
|
6133
6341
|
report.installed.push(id);
|
|
6134
6342
|
report.activated.push(id);
|
|
6135
6343
|
report.results.push({
|
|
@@ -6139,7 +6347,7 @@ var ReconciliationEngine = class {
|
|
|
6139
6347
|
});
|
|
6140
6348
|
} catch (upgradeErr) {
|
|
6141
6349
|
const upgradeMsg = upgradeErr instanceof Error ? upgradeErr.message : String(upgradeErr);
|
|
6142
|
-
log$
|
|
6350
|
+
log$5.error({ err: upgradeErr }, `Upgrade failed for ${id}`);
|
|
6143
6351
|
captureIntegrationFailure(id, "upgrade", upgradeErr);
|
|
6144
6352
|
report.errors.push({
|
|
6145
6353
|
integrationId: id,
|
|
@@ -6157,9 +6365,9 @@ var ReconciliationEngine = class {
|
|
|
6157
6365
|
if (local.status === "error") {
|
|
6158
6366
|
if (desired.reinstallRequestedAt && local.installedAt && desired.reinstallRequestedAt > local.installedAt) {
|
|
6159
6367
|
await this.ensureSuspended();
|
|
6160
|
-
log$
|
|
6368
|
+
log$5.info(`Reinstalling ${id} from error state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6161
6369
|
this.manager.resetReinstallAttempts(id);
|
|
6162
|
-
this.
|
|
6370
|
+
this.resetSelfHealBudget(id);
|
|
6163
6371
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6164
6372
|
report.installed.push(id);
|
|
6165
6373
|
report.activated.push(id);
|
|
@@ -6173,8 +6381,9 @@ var ReconciliationEngine = class {
|
|
|
6173
6381
|
if (!await this.manager.isInstallIntact(id)) {
|
|
6174
6382
|
const attempts = this.manager.getReinstallAttempts(id);
|
|
6175
6383
|
if (attempts >= 3) {
|
|
6176
|
-
log$
|
|
6177
|
-
|
|
6384
|
+
log$5.error(`Auto-reinstall blocked for ${id} — ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6385
|
+
const blockedMessage = `Max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6386
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${blockedMessage}`, blockedMessage);
|
|
6178
6387
|
report.errors.push({
|
|
6179
6388
|
integrationId: id,
|
|
6180
6389
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6188,12 +6397,12 @@ var ReconciliationEngine = class {
|
|
|
6188
6397
|
return;
|
|
6189
6398
|
}
|
|
6190
6399
|
await this.ensureSuspended();
|
|
6191
|
-
log$
|
|
6400
|
+
log$5.info(`Auto-reinstalling ${id} — install directory is corrupted or missing (attempt ${String(attempts + 1)}/3)`);
|
|
6192
6401
|
this.manager.incrementReinstallAttempts(id);
|
|
6193
6402
|
try {
|
|
6194
6403
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6195
6404
|
this.manager.resetReinstallAttempts(id);
|
|
6196
|
-
this.
|
|
6405
|
+
this.resetSelfHealBudget(id);
|
|
6197
6406
|
report.installed.push(id);
|
|
6198
6407
|
report.activated.push(id);
|
|
6199
6408
|
report.results.push({
|
|
@@ -6203,7 +6412,7 @@ var ReconciliationEngine = class {
|
|
|
6203
6412
|
});
|
|
6204
6413
|
} catch (reinstallErr) {
|
|
6205
6414
|
const reinstallMsg = reinstallErr instanceof Error ? reinstallErr.message : String(reinstallErr);
|
|
6206
|
-
log$
|
|
6415
|
+
log$5.error({ err: reinstallErr }, `Auto-reinstall failed for ${id}`);
|
|
6207
6416
|
captureIntegrationFailure(id, "reinstall", reinstallErr);
|
|
6208
6417
|
report.errors.push({
|
|
6209
6418
|
integrationId: id,
|
|
@@ -6222,10 +6431,10 @@ var ReconciliationEngine = class {
|
|
|
6222
6431
|
if (activateAttempts < MAX_ERROR_ACTIVATE_ATTEMPTS) {
|
|
6223
6432
|
this.activateAttempts.set(id, activateAttempts + 1);
|
|
6224
6433
|
await this.ensureSuspended();
|
|
6225
|
-
log$
|
|
6434
|
+
log$5.info(`Re-activating ${id} from error state (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)})`);
|
|
6226
6435
|
try {
|
|
6227
6436
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6228
|
-
this.
|
|
6437
|
+
this.resetSelfHealBudget(id);
|
|
6229
6438
|
report.activated.push(id);
|
|
6230
6439
|
report.results.push({
|
|
6231
6440
|
integrationId: id,
|
|
@@ -6235,8 +6444,9 @@ var ReconciliationEngine = class {
|
|
|
6235
6444
|
return;
|
|
6236
6445
|
} catch (reactivateErr) {
|
|
6237
6446
|
const reactivateMsg = reactivateErr instanceof Error ? reactivateErr.message : String(reactivateErr);
|
|
6238
|
-
log$
|
|
6239
|
-
|
|
6447
|
+
log$5.warn(`Re-activation of ${id} from error state failed (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)}): ${reactivateMsg}`);
|
|
6448
|
+
this.lastReactivateError.set(id, reactivateMsg);
|
|
6449
|
+
this.captureFailureOnce(id, "reactivate", `attempt:${reactivateMsg}`, reactivateErr);
|
|
6240
6450
|
report.errors.push({
|
|
6241
6451
|
integrationId: id,
|
|
6242
6452
|
error: reactivateMsg
|
|
@@ -6250,8 +6460,9 @@ var ReconciliationEngine = class {
|
|
|
6250
6460
|
return;
|
|
6251
6461
|
}
|
|
6252
6462
|
}
|
|
6253
|
-
log$
|
|
6254
|
-
|
|
6463
|
+
log$5.warn(`Integration ${id} is in error state — re-activation exhausted, waiting for reinstall request`);
|
|
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]);
|
|
6255
6466
|
report.errors.push({
|
|
6256
6467
|
integrationId: id,
|
|
6257
6468
|
error: "Integration is in error state"
|
|
@@ -6267,9 +6478,9 @@ var ReconciliationEngine = class {
|
|
|
6267
6478
|
if (local.status === "installing") {
|
|
6268
6479
|
if (desired.reinstallRequestedAt && local.installedAt && desired.reinstallRequestedAt > local.installedAt) {
|
|
6269
6480
|
await this.ensureSuspended();
|
|
6270
|
-
log$
|
|
6481
|
+
log$5.info(`Reinstalling ${id} from stale installing state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6271
6482
|
this.manager.resetReinstallAttempts(id);
|
|
6272
|
-
this.
|
|
6483
|
+
this.resetSelfHealBudget(id);
|
|
6273
6484
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6274
6485
|
report.installed.push(id);
|
|
6275
6486
|
report.activated.push(id);
|
|
@@ -6283,8 +6494,8 @@ var ReconciliationEngine = class {
|
|
|
6283
6494
|
const attempts = this.manager.getReinstallAttempts(id);
|
|
6284
6495
|
if (attempts >= 3) {
|
|
6285
6496
|
const errorMessage = `Stale installing state — max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`;
|
|
6286
|
-
log$
|
|
6287
|
-
|
|
6497
|
+
log$5.error(`Auto-reinstall blocked for ${id} — stuck in "installing", ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
6498
|
+
this.captureFailureOnce(id, "reinstall", `blocked:${errorMessage}`, errorMessage);
|
|
6288
6499
|
report.errors.push({
|
|
6289
6500
|
integrationId: id,
|
|
6290
6501
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -6298,12 +6509,12 @@ var ReconciliationEngine = class {
|
|
|
6298
6509
|
return;
|
|
6299
6510
|
}
|
|
6300
6511
|
await this.ensureSuspended();
|
|
6301
|
-
log$
|
|
6512
|
+
log$5.info(`Recovering ${id} from stale installing state via reinstall (attempt ${String(attempts + 1)}/3)`);
|
|
6302
6513
|
this.manager.incrementReinstallAttempts(id);
|
|
6303
6514
|
try {
|
|
6304
6515
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
|
|
6305
6516
|
this.manager.resetReinstallAttempts(id);
|
|
6306
|
-
this.
|
|
6517
|
+
this.resetSelfHealBudget(id);
|
|
6307
6518
|
report.installed.push(id);
|
|
6308
6519
|
report.activated.push(id);
|
|
6309
6520
|
report.results.push({
|
|
@@ -6313,7 +6524,7 @@ var ReconciliationEngine = class {
|
|
|
6313
6524
|
});
|
|
6314
6525
|
} catch (reinstallErr) {
|
|
6315
6526
|
const reinstallMsg = reinstallErr instanceof Error ? reinstallErr.message : String(reinstallErr);
|
|
6316
|
-
log$
|
|
6527
|
+
log$5.error({ err: reinstallErr }, `Reinstall from stale installing state failed for ${id}`);
|
|
6317
6528
|
captureIntegrationFailure(id, "reinstall", reinstallErr);
|
|
6318
6529
|
report.errors.push({
|
|
6319
6530
|
integrationId: id,
|
|
@@ -6330,8 +6541,9 @@ var ReconciliationEngine = class {
|
|
|
6330
6541
|
}
|
|
6331
6542
|
if (local.status !== "active") {
|
|
6332
6543
|
await this.ensureSuspended();
|
|
6333
|
-
log$
|
|
6544
|
+
log$5.info(`Activating ${id}`);
|
|
6334
6545
|
if ((await this.manager.activate(id, { forcePlugins: true })).configApplied) report.configApplied = true;
|
|
6546
|
+
this.resetSelfHealBudget(id);
|
|
6335
6547
|
report.activated.push(id);
|
|
6336
6548
|
report.results.push({
|
|
6337
6549
|
integrationId: id,
|
|
@@ -6342,9 +6554,9 @@ var ReconciliationEngine = class {
|
|
|
6342
6554
|
}
|
|
6343
6555
|
if (desired.reinstallRequestedAt && local.installedAt && desired.reinstallRequestedAt > local.installedAt) {
|
|
6344
6556
|
await this.ensureSuspended();
|
|
6345
|
-
log$
|
|
6557
|
+
log$5.info(`Reinstall requested for ${id} (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
|
|
6346
6558
|
this.manager.resetReinstallAttempts(id);
|
|
6347
|
-
this.
|
|
6559
|
+
this.resetSelfHealBudget(id);
|
|
6348
6560
|
if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
|
|
6349
6561
|
report.installed.push(id);
|
|
6350
6562
|
report.activated.push(id);
|
|
@@ -6355,7 +6567,7 @@ var ReconciliationEngine = class {
|
|
|
6355
6567
|
});
|
|
6356
6568
|
return;
|
|
6357
6569
|
}
|
|
6358
|
-
this.
|
|
6570
|
+
this.resetSelfHealBudget(id);
|
|
6359
6571
|
report.results.push({
|
|
6360
6572
|
integrationId: id,
|
|
6361
6573
|
action: "up_to_date",
|
|
@@ -6363,7 +6575,7 @@ var ReconciliationEngine = class {
|
|
|
6363
6575
|
});
|
|
6364
6576
|
} catch (err) {
|
|
6365
6577
|
const message = err instanceof Error ? err.message : String(err);
|
|
6366
|
-
log$
|
|
6578
|
+
log$5.error({ err }, `Error reconciling ${id}`);
|
|
6367
6579
|
captureIntegrationFailure(id, "reconcile_active", err);
|
|
6368
6580
|
report.errors.push({
|
|
6369
6581
|
integrationId: id,
|
|
@@ -6388,12 +6600,13 @@ var ReconciliationEngine = class {
|
|
|
6388
6600
|
}
|
|
6389
6601
|
try {
|
|
6390
6602
|
await this.ensureSuspended();
|
|
6391
|
-
log$
|
|
6603
|
+
log$5.info(`Removing ${id}`);
|
|
6392
6604
|
if (local.status === "active") {
|
|
6393
6605
|
await this.manager.deactivate(id);
|
|
6394
6606
|
report.deactivated.push(id);
|
|
6395
6607
|
}
|
|
6396
6608
|
await this.manager.uninstall(id);
|
|
6609
|
+
this.resetSelfHealBudget(id);
|
|
6397
6610
|
report.uninstalled.push(id);
|
|
6398
6611
|
report.results.push({
|
|
6399
6612
|
integrationId: id,
|
|
@@ -6402,7 +6615,7 @@ var ReconciliationEngine = class {
|
|
|
6402
6615
|
});
|
|
6403
6616
|
} catch (err) {
|
|
6404
6617
|
const message = err instanceof Error ? err.message : String(err);
|
|
6405
|
-
log$
|
|
6618
|
+
log$5.error({ err }, `Error removing ${id}`);
|
|
6406
6619
|
captureIntegrationFailure(id, "reconcile_removed", err);
|
|
6407
6620
|
report.errors.push({
|
|
6408
6621
|
integrationId: id,
|
|
@@ -6440,6 +6653,7 @@ var CloudClient = class {
|
|
|
6440
6653
|
onConnectionChange = null;
|
|
6441
6654
|
onRuntimeRestartNeeded = null;
|
|
6442
6655
|
onReconciliationComplete = null;
|
|
6656
|
+
onReconciliationSettled = null;
|
|
6443
6657
|
reconciliationEngine = null;
|
|
6444
6658
|
configReconciler = null;
|
|
6445
6659
|
reconciling = false;
|
|
@@ -6479,6 +6693,16 @@ var CloudClient = class {
|
|
|
6479
6693
|
this.onReconciliationComplete = handler;
|
|
6480
6694
|
}
|
|
6481
6695
|
/**
|
|
6696
|
+
* Set a callback for when a DESIRED_STATE burst has fully SETTLED: the
|
|
6697
|
+
* received snapshot and every queued one have run (each may have succeeded
|
|
6698
|
+
* or failed) and any runtime suspension held across them was released.
|
|
6699
|
+
* Fires on success AND failure — the daemon's deferred first runtime start
|
|
6700
|
+
* hangs off it, and a failed reconcile must not strand the runtime.
|
|
6701
|
+
*/
|
|
6702
|
+
setOnReconciliationSettled(handler) {
|
|
6703
|
+
this.onReconciliationSettled = handler;
|
|
6704
|
+
}
|
|
6705
|
+
/**
|
|
6482
6706
|
* Set the integration manager (and optionally the runtime gate) for
|
|
6483
6707
|
* reconciliation. When set, the cloud client handles DESIRED_STATE messages
|
|
6484
6708
|
* automatically. The gate is passed to the ReconciliationEngine so mutating
|
|
@@ -6725,20 +6949,43 @@ var CloudClient = class {
|
|
|
6725
6949
|
const message = err instanceof Error ? err.message : String(err);
|
|
6726
6950
|
logger.error({ err: message }, "Cloud: reconciliation failed");
|
|
6727
6951
|
} finally {
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6952
|
+
for (;;) {
|
|
6953
|
+
while (this.pendingDesiredState) {
|
|
6954
|
+
const next = this.pendingDesiredState;
|
|
6955
|
+
this.pendingDesiredState = null;
|
|
6956
|
+
logger.info({ count: next.integrations.length }, "Cloud: processing queued DESIRED_STATE");
|
|
6957
|
+
try {
|
|
6958
|
+
await this.runReconciliation(next);
|
|
6959
|
+
} catch (err) {
|
|
6960
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6961
|
+
logger.error({ err: message }, "Cloud: queued reconciliation failed");
|
|
6962
|
+
}
|
|
6963
|
+
}
|
|
6732
6964
|
try {
|
|
6733
|
-
await this.
|
|
6965
|
+
await this.reconciliationEngine.releaseHeldSuspension();
|
|
6734
6966
|
} catch (err) {
|
|
6735
6967
|
const message = err instanceof Error ? err.message : String(err);
|
|
6736
|
-
logger.error({ err: message }, "Cloud: queued reconciliation
|
|
6968
|
+
logger.error({ err: message }, "Cloud: failed to resume runtime after queued reconciliation");
|
|
6737
6969
|
}
|
|
6970
|
+
if (!this.hasQueuedDesiredState()) break;
|
|
6738
6971
|
}
|
|
6739
6972
|
this.reconciling = false;
|
|
6973
|
+
try {
|
|
6974
|
+
this.onReconciliationSettled?.();
|
|
6975
|
+
} catch (err) {
|
|
6976
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6977
|
+
logger.error({ err: message }, "Cloud: reconciliation-settled handler failed");
|
|
6978
|
+
}
|
|
6740
6979
|
}
|
|
6741
6980
|
}
|
|
6981
|
+
/**
|
|
6982
|
+
* Read through a method so control-flow narrowing from the drain loop above
|
|
6983
|
+
* doesn't treat `pendingDesiredState` as still null across an `await` (a
|
|
6984
|
+
* snapshot can be queued by another message while we're suspended).
|
|
6985
|
+
*/
|
|
6986
|
+
hasQueuedDesiredState() {
|
|
6987
|
+
return this.pendingDesiredState !== null;
|
|
6988
|
+
}
|
|
6742
6989
|
async runReconciliation(msg) {
|
|
6743
6990
|
if (msg.config && this.configReconciler) try {
|
|
6744
6991
|
const configReport = await this.configReconciler.reconcile(msg.config);
|
|
@@ -6754,7 +7001,7 @@ var CloudClient = class {
|
|
|
6754
7001
|
logger.error({ err: message }, "Cloud: config reconcile failed");
|
|
6755
7002
|
}
|
|
6756
7003
|
if (!this.reconciliationEngine) return;
|
|
6757
|
-
const report = await this.reconciliationEngine.reconcile(msg.integrations);
|
|
7004
|
+
const report = await this.reconciliationEngine.reconcile(msg.integrations, { holdSuspension: () => this.hasQueuedDesiredState() });
|
|
6758
7005
|
logger.info({
|
|
6759
7006
|
installed: report.installed.length,
|
|
6760
7007
|
activated: report.activated.length,
|
|
@@ -6817,7 +7064,7 @@ var CloudClient = class {
|
|
|
6817
7064
|
};
|
|
6818
7065
|
//#endregion
|
|
6819
7066
|
//#region src/config-reconciler.ts
|
|
6820
|
-
const log$
|
|
7067
|
+
const log$4 = logger.child({ component: "ConfigReconciler" });
|
|
6821
7068
|
const DEFAULT_VERIFY_RETRIES = 3;
|
|
6822
7069
|
const DEFAULT_VERIFY_RETRY_DELAY_MS = 750;
|
|
6823
7070
|
const delay = (ms) => new Promise((resolve) => {
|
|
@@ -6839,7 +7086,7 @@ var ConfigReconciler = class {
|
|
|
6839
7086
|
async reconcile(desired) {
|
|
6840
7087
|
const applier = this.getApplier();
|
|
6841
7088
|
if (!applier?.setConfigRaw) {
|
|
6842
|
-
log$
|
|
7089
|
+
log$4.debug("No applier with setConfigRaw — skipping config reconcile");
|
|
6843
7090
|
return null;
|
|
6844
7091
|
}
|
|
6845
7092
|
const setConfigRaw = applier.setConfigRaw.bind(applier);
|
|
@@ -6857,13 +7104,13 @@ var ConfigReconciler = class {
|
|
|
6857
7104
|
try {
|
|
6858
7105
|
current = await getConfigRaw(key);
|
|
6859
7106
|
} catch (err) {
|
|
6860
|
-
log$
|
|
7107
|
+
log$4.warn({
|
|
6861
7108
|
key,
|
|
6862
7109
|
err: err instanceof Error ? err.message : String(err)
|
|
6863
7110
|
}, "Config diff read failed — attempting the write");
|
|
6864
7111
|
}
|
|
6865
7112
|
if (current === want) {
|
|
6866
|
-
log$
|
|
7113
|
+
log$4.debug({ key }, "Config already at desired value — no-op");
|
|
6867
7114
|
continue;
|
|
6868
7115
|
}
|
|
6869
7116
|
}
|
|
@@ -6878,7 +7125,7 @@ var ConfigReconciler = class {
|
|
|
6878
7125
|
if (getConfigRaw) {
|
|
6879
7126
|
const outcome = await this.verify(getConfigRaw, key, want);
|
|
6880
7127
|
if (outcome === "matched") {
|
|
6881
|
-
log$
|
|
7128
|
+
log$4.warn({
|
|
6882
7129
|
key,
|
|
6883
7130
|
err: msg
|
|
6884
7131
|
}, "config set errored but value landed — treating as applied");
|
|
@@ -6894,7 +7141,7 @@ var ConfigReconciler = class {
|
|
|
6894
7141
|
if (failures.length > 0) {
|
|
6895
7142
|
const joined = failures.join("; ");
|
|
6896
7143
|
const reason = joined.length > 480 ? `${joined.slice(0, 480)}… (truncated)` : joined;
|
|
6897
|
-
log$
|
|
7144
|
+
log$4.warn({
|
|
6898
7145
|
version: desired.version,
|
|
6899
7146
|
reason
|
|
6900
7147
|
}, "Config reconcile failed");
|
|
@@ -6905,13 +7152,13 @@ var ConfigReconciler = class {
|
|
|
6905
7152
|
};
|
|
6906
7153
|
}
|
|
6907
7154
|
if (inconclusiveKeys.length > 0) {
|
|
6908
|
-
log$
|
|
7155
|
+
log$4.warn({
|
|
6909
7156
|
version: desired.version,
|
|
6910
7157
|
keys: inconclusiveKeys
|
|
6911
7158
|
}, "Config reconcile verification was inconclusive — leaving version pending");
|
|
6912
7159
|
return null;
|
|
6913
7160
|
}
|
|
6914
|
-
log$
|
|
7161
|
+
log$4.info({
|
|
6915
7162
|
version: desired.version,
|
|
6916
7163
|
keys: entries.length
|
|
6917
7164
|
}, "Config reconcile applied");
|
|
@@ -6928,7 +7175,7 @@ var ConfigReconciler = class {
|
|
|
6928
7175
|
if (current === want) return "matched";
|
|
6929
7176
|
if (current !== void 0) observedValue = true;
|
|
6930
7177
|
} catch (err) {
|
|
6931
|
-
log$
|
|
7178
|
+
log$4.debug({
|
|
6932
7179
|
key,
|
|
6933
7180
|
err: err instanceof Error ? err.message : String(err)
|
|
6934
7181
|
}, "Config verify read failed");
|
|
@@ -8945,7 +9192,7 @@ function reportAvailableCommands(deps) {
|
|
|
8945
9192
|
* arrive — the grace deadline is a backstop, but fail-open-to-idle means the
|
|
8946
9193
|
* common "nothing to drain" case restarts immediately with no added latency.
|
|
8947
9194
|
*/
|
|
8948
|
-
const log$
|
|
9195
|
+
const log$3 = logger.child({ component: "TurnActivity" });
|
|
8949
9196
|
/** The IPC method the daemon calls on plugins to ask "is a turn active?". */
|
|
8950
9197
|
const RUNTIME_ACTIVITY_METHOD = "runtime.activity";
|
|
8951
9198
|
/**
|
|
@@ -8964,17 +9211,122 @@ var IpcTurnActivityProbe = class {
|
|
|
8964
9211
|
try {
|
|
8965
9212
|
const responses = await ipc.broadcastRequest(RUNTIME_ACTIVITY_METHOD, {}, this.probeTimeoutMs);
|
|
8966
9213
|
for (const [pluginId, res] of responses) if (res.ok && res.payload != null && typeof res.payload === "object" && res.payload.active === true) {
|
|
8967
|
-
log$
|
|
9214
|
+
log$3.debug({ pluginId }, "Plugin reports an in-flight turn");
|
|
8968
9215
|
return true;
|
|
8969
9216
|
}
|
|
8970
9217
|
return false;
|
|
8971
9218
|
} catch (err) {
|
|
8972
|
-
log$
|
|
9219
|
+
log$3.debug({ err: err instanceof Error ? err.message : String(err) }, "Turn-activity probe failed — assuming idle");
|
|
8973
9220
|
return false;
|
|
8974
9221
|
}
|
|
8975
9222
|
}
|
|
8976
9223
|
};
|
|
8977
9224
|
//#endregion
|
|
9225
|
+
//#region src/first-runtime-start.ts
|
|
9226
|
+
/**
|
|
9227
|
+
* Deferred first runtime start for fresh managed boxes.
|
|
9228
|
+
*
|
|
9229
|
+
* On a brand-new managed VM the first DESIRED_STATE always installs the core
|
|
9230
|
+
* integrations, and every mutating reconcile suspends the runtime through the
|
|
9231
|
+
* RuntimeGate. Starting the runtime at daemon boot therefore buys nothing: it
|
|
9232
|
+
* boots (and runs its first-boot migrations) only to be stopped ~seconds later
|
|
9233
|
+
* by that first reconcile, then started again after it. Deferring the first
|
|
9234
|
+
* start until the first reconcile burst settles removes that whole cycle.
|
|
9235
|
+
*
|
|
9236
|
+
* The deferral is released by whichever comes first:
|
|
9237
|
+
* - the cloud client's "reconciliation settled" hook (success OR failure),
|
|
9238
|
+
* - a fallback timer armed when the cloud client first REGISTERS
|
|
9239
|
+
* ({@link DeferredRuntimeStart.armFallback}), so slow boot/connect time
|
|
9240
|
+
* doesn't eat the window the first DESIRED_STATE needs;
|
|
9241
|
+
* - a longer boot ceiling measured from daemon boot, so a daemon that never
|
|
9242
|
+
* registers (gateway down, registration failing) still brings the runtime up.
|
|
9243
|
+
*
|
|
9244
|
+
* Only fresh managed boxes defer (managed mode AND no integrations in local
|
|
9245
|
+
* state). Self-hosted daemons and restarts of an already-provisioned box start
|
|
9246
|
+
* the runtime immediately, exactly as before.
|
|
9247
|
+
*/
|
|
9248
|
+
const log$2 = logger.child({ component: "FirstRuntimeStart" });
|
|
9249
|
+
/** How long a fresh box waits, after first registering with the cloud, for its first reconcile to settle. */
|
|
9250
|
+
const FIRST_START_FALLBACK_MS = 18e4;
|
|
9251
|
+
/** Hard ceiling from daemon boot, for a daemon that never registers with the cloud. */
|
|
9252
|
+
const FIRST_START_BOOT_CEILING_MS = 6e5;
|
|
9253
|
+
/**
|
|
9254
|
+
* Fresh managed box ⇔ managed mode AND local integration state is readable and
|
|
9255
|
+
* empty. An unreadable state is NOT treated as fresh — fall back to the old
|
|
9256
|
+
* start-immediately behaviour rather than delay a box we know nothing about.
|
|
9257
|
+
*/
|
|
9258
|
+
function shouldDeferFirstRuntimeStart(input) {
|
|
9259
|
+
return input.managed && input.installedIntegrationCount === 0;
|
|
9260
|
+
}
|
|
9261
|
+
var DeferredRuntimeStart = class {
|
|
9262
|
+
released = false;
|
|
9263
|
+
cancelled = false;
|
|
9264
|
+
startFn = null;
|
|
9265
|
+
fallbackArmed = false;
|
|
9266
|
+
fallbackTimer = null;
|
|
9267
|
+
ceilingTimer;
|
|
9268
|
+
/** Arms the boot ceiling immediately; the fallback waits for {@link armFallback}. */
|
|
9269
|
+
constructor(fallbackMs = FIRST_START_FALLBACK_MS, bootCeilingMs = FIRST_START_BOOT_CEILING_MS) {
|
|
9270
|
+
this.fallbackMs = fallbackMs;
|
|
9271
|
+
this.ceilingTimer = setTimeout(() => {
|
|
9272
|
+
this.ceilingTimer = null;
|
|
9273
|
+
this.release("boot-ceiling");
|
|
9274
|
+
}, bootCeilingMs);
|
|
9275
|
+
}
|
|
9276
|
+
/**
|
|
9277
|
+
* Start the fallback clock. Called when the cloud client registers; only
|
|
9278
|
+
* the FIRST call arms it (a reconnect must not push the deadline out).
|
|
9279
|
+
*/
|
|
9280
|
+
armFallback() {
|
|
9281
|
+
if (this.released || this.cancelled || this.fallbackArmed) return;
|
|
9282
|
+
this.fallbackArmed = true;
|
|
9283
|
+
this.fallbackTimer = setTimeout(() => {
|
|
9284
|
+
this.fallbackTimer = null;
|
|
9285
|
+
this.release("fallback-timeout");
|
|
9286
|
+
}, this.fallbackMs);
|
|
9287
|
+
}
|
|
9288
|
+
/** True once released (by either path). */
|
|
9289
|
+
get isReleased() {
|
|
9290
|
+
return this.released;
|
|
9291
|
+
}
|
|
9292
|
+
/**
|
|
9293
|
+
* Provide the start action once the runtime process exists. If the deferral
|
|
9294
|
+
* was already released (a reconcile settled before the runtime object was
|
|
9295
|
+
* built), starts right away.
|
|
9296
|
+
*/
|
|
9297
|
+
attach(start) {
|
|
9298
|
+
if (this.cancelled) return;
|
|
9299
|
+
this.startFn = start;
|
|
9300
|
+
if (this.released) start();
|
|
9301
|
+
}
|
|
9302
|
+
/** Release the deferral. Idempotent — only the first call starts the runtime. */
|
|
9303
|
+
release(reason) {
|
|
9304
|
+
if (this.released || this.cancelled) return;
|
|
9305
|
+
this.released = true;
|
|
9306
|
+
this.clearTimers();
|
|
9307
|
+
log$2.info({
|
|
9308
|
+
reason,
|
|
9309
|
+
fallbackMs: this.fallbackMs
|
|
9310
|
+
}, "Releasing deferred first runtime start");
|
|
9311
|
+
this.startFn?.();
|
|
9312
|
+
}
|
|
9313
|
+
/** Daemon shutdown: never start the runtime after this. */
|
|
9314
|
+
cancel() {
|
|
9315
|
+
this.cancelled = true;
|
|
9316
|
+
this.clearTimers();
|
|
9317
|
+
}
|
|
9318
|
+
clearTimers() {
|
|
9319
|
+
if (this.fallbackTimer) {
|
|
9320
|
+
clearTimeout(this.fallbackTimer);
|
|
9321
|
+
this.fallbackTimer = null;
|
|
9322
|
+
}
|
|
9323
|
+
if (this.ceilingTimer) {
|
|
9324
|
+
clearTimeout(this.ceilingTimer);
|
|
9325
|
+
this.ceilingTimer = null;
|
|
9326
|
+
}
|
|
9327
|
+
}
|
|
9328
|
+
};
|
|
9329
|
+
//#endregion
|
|
8978
9330
|
//#region src/command-registry.ts
|
|
8979
9331
|
/**
|
|
8980
9332
|
* Command Registry — maps integration command names to local handler files.
|
|
@@ -9656,8 +10008,49 @@ let stopPairingApprovalPoller = null;
|
|
|
9656
10008
|
let runtimeAppliersRef = null;
|
|
9657
10009
|
let openclawCliLockRef = null;
|
|
9658
10010
|
let runtimeGateRef = null;
|
|
10011
|
+
/** Pending deferred first runtime start (fresh managed box only). */
|
|
10012
|
+
let firstRuntimeStart = null;
|
|
9659
10013
|
const LEGACY_CONFIG_SET_KEYS = new Set(["agents.defaults.model"]);
|
|
9660
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
|
+
}
|
|
9661
10054
|
async function startDaemon() {
|
|
9662
10055
|
startedAt = Date.now();
|
|
9663
10056
|
const managed = isManagedMode();
|
|
@@ -9744,6 +10137,7 @@ async function startDaemon() {
|
|
|
9744
10137
|
cloudClient.setConnectionChangeHandler((connected) => {
|
|
9745
10138
|
cloudConnected = connected;
|
|
9746
10139
|
logger.info({ connected }, "Cloud connection state changed");
|
|
10140
|
+
if (connected) firstRuntimeStart?.armFallback();
|
|
9747
10141
|
ipcServer?.broadcastEvent("cloud.status", { connected });
|
|
9748
10142
|
});
|
|
9749
10143
|
const openclawCliLock = new SerialOpenClawCliLock();
|
|
@@ -9874,6 +10268,28 @@ async function startDaemon() {
|
|
|
9874
10268
|
cloudClient
|
|
9875
10269
|
});
|
|
9876
10270
|
});
|
|
10271
|
+
if (config.autoStartRuntime && config.runtime) {
|
|
10272
|
+
let installedIntegrationCount;
|
|
10273
|
+
try {
|
|
10274
|
+
installedIntegrationCount = integrationManager.list().length;
|
|
10275
|
+
} catch (err) {
|
|
10276
|
+
logger.warn({ err: err instanceof Error ? err.message : String(err) }, "Could not read local integration state — starting runtime immediately");
|
|
10277
|
+
installedIntegrationCount = null;
|
|
10278
|
+
}
|
|
10279
|
+
if (shouldDeferFirstRuntimeStart({
|
|
10280
|
+
managed,
|
|
10281
|
+
installedIntegrationCount
|
|
10282
|
+
})) {
|
|
10283
|
+
logger.info("Fresh managed box — deferring first runtime start until the first reconcile settles");
|
|
10284
|
+
const deferral = new DeferredRuntimeStart();
|
|
10285
|
+
firstRuntimeStart = deferral;
|
|
10286
|
+
runtimeGate.deferFirstStart();
|
|
10287
|
+
cloudClient.setOnReconciliationSettled(() => {
|
|
10288
|
+
deferral.release("reconcile-settled");
|
|
10289
|
+
});
|
|
10290
|
+
}
|
|
10291
|
+
}
|
|
10292
|
+
if (config.autoStartRuntime && config.runtime === "openclaw") recoverInterruptedOpenClawUpgrade(runtimeGate, openclawCliLock);
|
|
9877
10293
|
cloudClient.start();
|
|
9878
10294
|
logger.debug("Cloud client started");
|
|
9879
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");
|
|
@@ -9892,8 +10308,15 @@ async function startDaemon() {
|
|
|
9892
10308
|
env: config.runtime === "claude-code" && config.defaultModel ? { ALFE_DEFAULT_MODEL: config.defaultModel } : {}
|
|
9893
10309
|
});
|
|
9894
10310
|
runtimeProcess.setTurnActivityProbe(turnActivityProbe);
|
|
9895
|
-
|
|
9896
|
-
|
|
10311
|
+
if (firstRuntimeStart) {
|
|
10312
|
+
firstRuntimeStart.attach(() => {
|
|
10313
|
+
runtimeGate.requestStart();
|
|
10314
|
+
});
|
|
10315
|
+
logger.debug("Runtime process created — first start deferred");
|
|
10316
|
+
} else {
|
|
10317
|
+
runtimeGate.requestStart();
|
|
10318
|
+
logger.debug("Runtime process started");
|
|
10319
|
+
}
|
|
9897
10320
|
if (config.runtime === "openclaw") {
|
|
9898
10321
|
stopPairingApprovalPoller = startPairingApprovalPoller({
|
|
9899
10322
|
log: logger,
|
|
@@ -9913,9 +10336,10 @@ async function startDaemon() {
|
|
|
9913
10336
|
stopPairingApprovalPoller();
|
|
9914
10337
|
stopPairingApprovalPoller = null;
|
|
9915
10338
|
}
|
|
10339
|
+
firstRuntimeStart?.cancel();
|
|
9916
10340
|
if (runtimeProcess) {
|
|
9917
10341
|
logger.debug("Stopping runtime process...");
|
|
9918
|
-
await runtimeProcess.
|
|
10342
|
+
await runtimeProcess.shutdown();
|
|
9919
10343
|
logger.debug("Runtime process stopped");
|
|
9920
10344
|
}
|
|
9921
10345
|
if (hermesMcpSync) {
|
|
@@ -10091,7 +10515,15 @@ async function executeCloudCommand(command) {
|
|
|
10091
10515
|
};
|
|
10092
10516
|
const payload = command.payload;
|
|
10093
10517
|
const runtime = config.runtime;
|
|
10094
|
-
|
|
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);
|
|
10095
10527
|
upgradingRuntime = true;
|
|
10096
10528
|
setTimeout(() => {
|
|
10097
10529
|
(async () => {
|
|
@@ -10100,7 +10532,8 @@ async function executeCloudCommand(command) {
|
|
|
10100
10532
|
const result = await upgradeRuntime(runtime, version, runtimeProcess, {
|
|
10101
10533
|
...runtime === "openclaw" && openclawCliLockRef ? { cliLock: openclawCliLockRef } : {},
|
|
10102
10534
|
...runtime === "openclaw" && isManagedMode() && process.env.ALFE_PRIVATE_DATA_PLANE !== "true" ? { nodeRemediation: {} } : {},
|
|
10103
|
-
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {}
|
|
10535
|
+
...runtime === "openclaw" && runtimeGateRef ? { gate: runtimeGateRef } : {},
|
|
10536
|
+
...runtime === "openclaw" ? openClawUpgradeExtras() : {}
|
|
10104
10537
|
});
|
|
10105
10538
|
if (result.success || result.runtimeChanged) {
|
|
10106
10539
|
resolvedRuntimeVersion = await getRuntimeVersion(runtime);
|