@vibe-lark/larkpal 0.1.86 → 0.1.87
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/account-id-BM1T6029-BzQ_cMoi.mjs +1 -0
- package/dist/activation-context--5yu1dPF-XV0Y0pTy.mjs +1 -0
- package/dist/anthropic-CFZUCb1w.mjs +39 -0
- package/dist/assistant-visible-text-DuFWokAt-BOJV_zwc.mjs +9 -0
- package/dist/azure-openai-responses-BEuiMaF-.mjs +1 -0
- package/dist/boundary-file-read-BSd9MXPy-gnNVhPKT.mjs +1 -0
- package/dist/browser-lifecycle-cleanup-BdNsMhMD-BM97qA_M.mjs +1 -0
- package/dist/build-Bkj0hOS2.mjs +3 -0
- package/dist/bundled-compat-BpaDaAAu-DJtrIX6i.mjs +27 -0
- package/dist/bundled-dir-U738ay4K-BxbzrJOo.mjs +1 -0
- package/dist/call-DimWbpgJ-B4pv55RH.mjs +20 -0
- package/dist/channel-bootstrap.runtime-BVclcGv1-CEZ0-JP9.mjs +1 -0
- package/dist/channel-bootstrap.runtime-Cwcr38m2-Dt6QlQkX.mjs +1 -0
- package/dist/chunk-DHbaKqs-.mjs +1 -0
- package/dist/cli.mjs +7 -92
- package/dist/command-format-LSnUCVVF-Dn4h9IOh.mjs +16 -0
- package/dist/config-Bim2pi8a-03N-28sw.mjs +1 -0
- package/dist/delivery-context.shared-j5hC5qb1-BSLQG47q.mjs +1 -0
- package/dist/diagnostic-ClMwNnUA-BMKvPQJT.mjs +1 -0
- package/dist/dist-J5UB83Ws.mjs +601 -0
- package/dist/dist-coestTs9.mjs +5 -0
- package/dist/env-api-keys-Cuz5irWW.mjs +1 -0
- package/dist/event-stream-B7Ei41Bu.mjs +1 -0
- package/dist/facade-loader-DFzIYYt_-4bzFvy-b.mjs +1 -0
- package/dist/from-Ba69_5cR.mjs +14 -0
- package/dist/gateway-method-policy-C7rf0SeV-CTJcMQCE.mjs +1 -0
- package/dist/github-copilot-headers-BfhryVuU.mjs +1 -0
- package/dist/google-ABo-1guT.mjs +1 -0
- package/dist/google-gemini-cli-CGlN1zUA.mjs +2 -0
- package/dist/google-shared-C60sNhwd.mjs +301 -0
- package/dist/google-vertex-De0nAItu.mjs +1 -0
- package/dist/hash-Dw-N8Y9l.mjs +1 -0
- package/dist/headers-Bpsp70B9.mjs +1 -0
- package/dist/home-dir-C44RaPME-B5iLWwCq.mjs +1 -0
- package/dist/ids-BPsM98Uu-CJBJAeVC.mjs +1 -0
- package/dist/image-2w3cApg7-CrpUMCvm.mjs +3 -0
- package/dist/interactive-init-C5MFL5MW.mjs +8 -0
- package/dist/io-CH2g3vsv-JJtUIa_U.mjs +10 -0
- package/dist/jiti-loader-cache-Bat69wam-DNIteTtO.mjs +9 -0
- package/dist/json-file-BQRNuxmK-hRjIgOXI.mjs +1 -0
- package/dist/json-parse-BESd8UAM.mjs +3 -0
- package/dist/jws-YaZ38F_i.mjs +12 -0
- package/dist/lark-cli-provider-BLMxH6CN.mjs +4 -0
- package/dist/lark-logger-xpMKNNtY.mjs +1 -0
- package/dist/live-smoke-DpURW676.mjs +33 -0
- package/dist/load-context-mb7uvLSn-3xYwSnOZ.mjs +1 -0
- package/dist/loader-708jrx4Y-BbJRXNlQ.mjs +4 -0
- package/dist/main.mjs +153 -74571
- package/dist/manifest-registry-_hxKCS2K-OL8KK_o4.mjs +15 -0
- package/dist/mcp-server.mjs +4 -527
- package/dist/message-channel-Bk_YHfQg-n2Hw2Y0L.mjs +1 -0
- package/dist/message-channel-core-DTA--Gjh-CIh10vfo.mjs +1 -0
- package/dist/message.config.runtime-K-k4XOyq-F_h0Cgkl.mjs +1 -0
- package/dist/message.gateway.runtime-DqHOHRTQ-ge4c5p4s.mjs +1 -0
- package/dist/mistral-DyL3Zo1b.mjs +7 -0
- package/dist/model-auth-env-oyg2Hexc-DJbt9J3o.mjs +1 -0
- package/dist/models-config-BmpSQJQF-BL6S5qAo.mjs +1 -0
- package/dist/models-config.runtime-SOhLEw-f.mjs +1 -0
- package/dist/multipart-parser-B9dwXBqu.mjs +2 -0
- package/dist/oauth-25y2wc7K.mjs +75 -0
- package/dist/openai-C-rgGb7X.mjs +16 -0
- package/dist/openai-codex-responses-BbyAPsP1.mjs +7 -0
- package/dist/openai-completions-Bu5qvQzb.mjs +5 -0
- package/dist/openai-responses-pIdwQBc4.mjs +1 -0
- package/dist/openai-responses-shared-DqW1JSux.mjs +10 -0
- package/dist/paths-D6msg0S1-BO4XgM1u.mjs +1 -0
- package/dist/paths-D92DjaZ--DdtJ-zYp.mjs +1 -0
- package/dist/pi-model-discovery-Bd0TWzo8-DvSM0eWu.mjs +1 -0
- package/dist/pi-model-discovery-runtime-PrjWOmsV.mjs +1 -0
- package/dist/pi-tools.before-tool-call.runtime-Bn7uqKie-WPxU5oNX.mjs +2 -0
- package/dist/plugin-auto-enable-DdH3n9V8-Dr_bUXkG.mjs +1 -0
- package/dist/process-scoped-map-B2cWkYET-Co-i-MXA.mjs +1 -0
- package/dist/provider-discovery.runtime-DY0VWQ81.mjs +1 -0
- package/dist/provider-model-compat-CFqcq0it-BUwZENrz.mjs +1 -0
- package/dist/provider-runtime-C8rljsNs-BDvu6FW3.mjs +1 -0
- package/dist/provider-runtime-CT50mNIX-DImw7ZZu.mjs +79 -0
- package/dist/provider-stream-COLujAAo-Dpq1DSIk.mjs +248 -0
- package/dist/providers.runtime-Ba7tOlq7-B4t6ywCr.mjs +1 -0
- package/dist/public-surface-loader-BVl0NQAE-TfJ3izj1.mjs +1 -0
- package/dist/resolve-C33B7eeu-DZM_z6Wr.mjs +1 -0
- package/dist/runtime-Cym-fqI8-1GOIuRRQ.mjs +1 -0
- package/dist/runtime-channel-state-CohMybAh-BBzhl07Q.mjs +1 -0
- package/dist/runtime-snapshot-C54O1-gI-DxusscPf.mjs +1 -0
- package/dist/runtime-state-IBOIXKOx-Cw1GA1td.mjs +1 -0
- package/dist/runtime-web-tools-fallback.runtime-Ca-qn1mj-BsYE2gOd.mjs +1 -0
- package/dist/runtime-web-tools-manifest.runtime-BgxQTjmq-P6MKP9gQ.mjs +1 -0
- package/dist/runtime-web-tools-public-artifacts.runtime-D9qcoWIX-5VoyjouO.mjs +1 -0
- package/dist/session-archive.runtime-B52_5vs4-cU2tYbBz.mjs +1 -0
- package/dist/session-key-Cn2QoOyX-QZeYmbxd.mjs +1 -0
- package/dist/session-transcript-files.fs-DEeFXruQ-CXIAFSsr.mjs +1 -0
- package/dist/src-CCDx_C-U.mjs +3 -0
- package/dist/src-DACmA9V6.mjs +5 -0
- package/dist/subagent-announce-L5WsQatW--rMn240x.mjs +2 -0
- package/dist/subagent-announce.registry.runtime-DiqZyeTs-DNfNHsls.mjs +1 -0
- package/dist/subagent-registry-steer-runtime-DFgZFC0j-XYS1_qwl.mjs +1 -0
- package/dist/subagent-system-prompt-C1qnN--K-CsU4f97T.mjs +23 -0
- package/dist/target-parsing-loaded-DKkb6jJY-B830OA1a.mjs +12 -0
- package/dist/task-registry-delivery-runtime-Dd1CUkZF-B2Hj2k8C.mjs +16 -0
- package/dist/task-registry-delivery-runtime-ragbeVx5-Bdvo2Q5u.mjs +1 -0
- package/dist/thinking-Bh-7MqXz-uApNUe6g.mjs +1 -0
- package/dist/transcript-E_-ISOkD-VTwb782D.mjs +5 -0
- package/dist/transcript.runtime-BqhvZZdl-B4E0TiX5.mjs +1 -0
- package/dist/transform-messages-BTA3xbAI.mjs +1 -0
- package/dist/web-provider-public-artifacts-BlpRwIUS-DbVhDOrY.mjs +1 -0
- package/dist/web-search-providers.runtime--0aUtC3b-Bjh4Bmrz.mjs +1 -0
- package/package.json +2 -3
- package/dist/account-id-BM1T6029-DYjVxojW.mjs +0 -46
- package/dist/activation-context--5yu1dPF-Bwsn3moT.mjs +0 -172
- package/dist/anthropic-TH8ERTXK.mjs +0 -5962
- package/dist/assistant-visible-text-DuFWokAt-CsbII5KS.mjs +0 -596
- package/dist/azure-openai-responses-Bt_Ag9Er.mjs +0 -166
- package/dist/boundary-file-read-BSd9MXPy--KThL5e6.mjs +0 -594
- package/dist/browser-lifecycle-cleanup-BdNsMhMD-O3tnAM4i.mjs +0 -60
- package/dist/build-DgTrEpge.mjs +0 -3333
- package/dist/bundled-compat-BpaDaAAu-D7oazKjD.mjs +0 -5216
- package/dist/bundled-dir-U738ay4K-DXK0q_TV.mjs +0 -258
- package/dist/call-DimWbpgJ-ColD4W-y.mjs +0 -9993
- package/dist/channel-bootstrap.runtime-BVclcGv1-DJv_Z-Kr.mjs +0 -2
- package/dist/channel-bootstrap.runtime-Cwcr38m2-VRfVCR-i.mjs +0 -33
- package/dist/chunk-D6SyT4dJ.mjs +0 -36
- package/dist/command-format-LSnUCVVF-BQ2SMKka.mjs +0 -685
- package/dist/config-Bim2pi8a-BQUdfiEi.mjs +0 -9
- package/dist/delivery-context.shared-j5hC5qb1-CI83RXXZ.mjs +0 -72
- package/dist/diagnostic-ClMwNnUA-D6nUtLnb.mjs +0 -140
- package/dist/dist-CEGw8-2W.mjs +0 -429
- package/dist/dist-DD2aJ2UL.mjs +0 -125135
- package/dist/env-api-keys-BVvSg7T2.mjs +0 -68
- package/dist/event-stream-xaQWmyas.mjs +0 -15226
- package/dist/facade-loader-DFzIYYt_-CIr4HKtE.mjs +0 -194
- package/dist/from-DF7cl2wz.mjs +0 -3842
- package/dist/gateway-method-policy-C7rf0SeV-CJQMEYIE.mjs +0 -28
- package/dist/github-copilot-headers-DjqWtUoP.mjs +0 -22
- package/dist/google-C2pVrgBZ.mjs +0 -336
- package/dist/google-gemini-cli-BBxgS5Ho.mjs +0 -623
- package/dist/google-shared-BsWgwEk1.mjs +0 -22791
- package/dist/google-vertex-BsVLJrrC.mjs +0 -358
- package/dist/hash-DAFtW_W7.mjs +0 -16
- package/dist/headers-R3HdCbAE.mjs +0 -8
- package/dist/home-dir-C44RaPME-CBRS4HLP.mjs +0 -93
- package/dist/ids-BPsM98Uu-CwuxvUai.mjs +0 -77
- package/dist/image-2w3cApg7-CxpHvoB5.mjs +0 -207
- package/dist/interactive-init-BXQBalNr.mjs +0 -450
- package/dist/io-CH2g3vsv-BOGlLlB8.mjs +0 -26218
- package/dist/jiti-loader-cache-Bat69wam-D6hCxboy.mjs +0 -5049
- package/dist/json-file-BQRNuxmK-DhJM5NsG.mjs +0 -108
- package/dist/json-parse-v_HIJjjI.mjs +0 -381
- package/dist/jws-GFcTvvVT.mjs +0 -2291
- package/dist/lark-cli-provider-BARgbbmo.mjs +0 -554
- package/dist/lark-logger-D7_pEVQc.mjs +0 -143
- package/dist/live-smoke-BhkBOcH9.mjs +0 -7135
- package/dist/load-context-mb7uvLSn-B10MAi3I.mjs +0 -29
- package/dist/loader-708jrx4Y-BOswuvpe.mjs +0 -5053
- package/dist/manifest-registry-_hxKCS2K-CZdeyN0f.mjs +0 -3512
- package/dist/message-channel-Bk_YHfQg-AeNrPPpp.mjs +0 -50
- package/dist/message-channel-core-DTA--Gjh-CY4Tkb7N.mjs +0 -29
- package/dist/message.config.runtime-K-k4XOyq-DZTmXomP.mjs +0 -2
- package/dist/message.gateway.runtime-DqHOHRTQ-DH4ml0YF.mjs +0 -2
- package/dist/mistral-CaR4DiLu.mjs +0 -28657
- package/dist/model-auth-env-oyg2Hexc-DU_rOUU5.mjs +0 -1306
- package/dist/models-config-BmpSQJQF-Gfvh1n3A.mjs +0 -1282
- package/dist/models-config.runtime-BsU_22xk.mjs +0 -2
- package/dist/multipart-parser-DedmaB5g.mjs +0 -294
- package/dist/oauth-DOJeGfcE.mjs +0 -6392
- package/dist/openai-BDtIh3Ja.mjs +0 -6693
- package/dist/openai-codex-responses-C5EORu31.mjs +0 -664
- package/dist/openai-completions-CR_cS6gX.mjs +0 -669
- package/dist/openai-responses-oGxwZyuC.mjs +0 -184
- package/dist/openai-responses-shared-BixMSDZE.mjs +0 -398
- package/dist/paths-D6msg0S1-C2lYuisN.mjs +0 -189
- package/dist/paths-D92DjaZ--CTOESW2b.mjs +0 -192
- package/dist/pi-model-discovery-Bd0TWzo8-BFsJmeMb.mjs +0 -227
- package/dist/pi-model-discovery-runtime-CCCZxkd8.mjs +0 -2
- package/dist/pi-tools.before-tool-call.runtime-Bn7uqKie-C5ewk8kK.mjs +0 -399
- package/dist/plugin-auto-enable-DdH3n9V8-DsA1isaF.mjs +0 -1456
- package/dist/process-scoped-map-B2cWkYET-CXwUZ8CZ.mjs +0 -61
- package/dist/provider-discovery.runtime-BPzz1PFj.mjs +0 -58
- package/dist/provider-model-compat-CFqcq0it-BDDuBI3A.mjs +0 -118
- package/dist/provider-runtime-C8rljsNs-CIT93zoy.mjs +0 -2
- package/dist/provider-runtime-CT50mNIX-BwEV6GyO.mjs +0 -573
- package/dist/provider-stream-COLujAAo-CbJPDngm.mjs +0 -35449
- package/dist/providers.runtime-Ba7tOlq7-CXkhkzU9.mjs +0 -219
- package/dist/public-surface-loader-BVl0NQAE-C0piXtsT.mjs +0 -117
- package/dist/resolve-C33B7eeu-BNvYTl6k.mjs +0 -1041
- package/dist/runtime-Cym-fqI8-DaSDMH-a.mjs +0 -120
- package/dist/runtime-channel-state-CohMybAh-hXR6y5Gx.mjs +0 -19
- package/dist/runtime-snapshot-C54O1-gI-40cbyi7P.mjs +0 -677
- package/dist/runtime-state-IBOIXKOx-Bf9tcLvc.mjs +0 -10
- package/dist/runtime-web-tools-fallback.runtime-Ca-qn1mj-BCVImm7a.mjs +0 -39
- package/dist/runtime-web-tools-manifest.runtime-BgxQTjmq-Cb4nJkRT.mjs +0 -2
- package/dist/runtime-web-tools-public-artifacts.runtime-D9qcoWIX-BrQdXzOQ.mjs +0 -2
- package/dist/session-archive.runtime-B52_5vs4-BG-_IwNJ.mjs +0 -2
- package/dist/session-key-Cn2QoOyX-CuHbJMgR.mjs +0 -82
- package/dist/session-transcript-files.fs-DEeFXruQ-B1PF6nLy.mjs +0 -147
- package/dist/src-BgCX0MLG.mjs +0 -813
- package/dist/src-DYbqlAqf.mjs +0 -1177
- package/dist/subagent-announce-L5WsQatW-CQWI9GMd.mjs +0 -344
- package/dist/subagent-announce.registry.runtime-DiqZyeTs-BKuniyi3.mjs +0 -28
- package/dist/subagent-registry-steer-runtime-DFgZFC0j-BhBz0roy.mjs +0 -211
- package/dist/subagent-system-prompt-C1qnN--K-DpnTFP47.mjs +0 -3061
- package/dist/target-parsing-loaded-DKkb6jJY-BMD4CkpS.mjs +0 -1002
- package/dist/task-registry-delivery-runtime-Dd1CUkZF-C8U40a3O.mjs +0 -2950
- package/dist/task-registry-delivery-runtime-ragbeVx5-CNbFAkjD.mjs +0 -2
- package/dist/thinking-Bh-7MqXz-D3woGbO7.mjs +0 -2
- package/dist/transcript-E_-ISOkD-DWCfZP2p.mjs +0 -2019
- package/dist/transcript.runtime-BqhvZZdl-BPy42vBP.mjs +0 -2
- package/dist/transform-messages-DbhIzTxq.mjs +0 -207
- package/dist/web-provider-public-artifacts-BlpRwIUS-CGPSV4W2.mjs +0 -295
- package/dist/web-search-providers.runtime--0aUtC3b-DyuhC95V.mjs +0 -200
|
@@ -1,2019 +0,0 @@
|
|
|
1
|
-
import { c as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty, s as normalizeOptionalLowercaseString, u as normalizeStringifiedOptionalString } from "./home-dir-C44RaPME-CBRS4HLP.mjs";
|
|
2
|
-
import { t as resolveBundledPluginsDir } from "./bundled-dir-U738ay4K-DXK0q_TV.mjs";
|
|
3
|
-
import { O as parseDurationMs, a as parseByteSize, n as loadConfig } from "./io-CH2g3vsv-BOGlLlB8.mjs";
|
|
4
|
-
import { Mt as formatErrorMessage, St as createSubsystemLogger, zt as normalizeChannelId } from "./bundled-compat-BpaDaAAu-D7oazKjD.mjs";
|
|
5
|
-
import "./message-channel-Bk_YHfQg-AeNrPPpp.mjs";
|
|
6
|
-
import { r as resolveLoaderPackageRoot, t as getCachedPluginJitiLoader } from "./jiti-loader-cache-Bat69wam-D6hCxboy.mjs";
|
|
7
|
-
import { C as normalizeChannelId$1, t as getRuntimeConfigSnapshot, x as getLoadedChannelPlugin } from "./runtime-snapshot-C54O1-gI-40cbyi7P.mjs";
|
|
8
|
-
import { n as getActivePluginChannelRegistryVersion } from "./runtime-Cym-fqI8-DaSDMH-a.mjs";
|
|
9
|
-
import { c as parseThreadSessionSuffix } from "./session-key-Cn2QoOyX-CuHbJMgR.mjs";
|
|
10
|
-
import "./config-Bim2pi8a-BQUdfiEi.mjs";
|
|
11
|
-
import { c as isSessionArchiveArtifactName, n as resolveSessionFilePath, s as isPrimarySessionTranscriptFileName, t as resolveDefaultSessionStorePath } from "./paths-D6msg0S1-C2lYuisN.mjs";
|
|
12
|
-
import { n as getProcessStartTime, r as isPidAlive, t as resolveProcessScopedMap } from "./process-scoped-map-B2cWkYET-CXwUZ8CZ.mjs";
|
|
13
|
-
import { i as normalizeSessionDeliveryFields } from "./delivery-context.shared-j5hC5qb1-CI83RXXZ.mjs";
|
|
14
|
-
import { a as resolveBundledFacadeModuleLocation, i as createFacadeResolutionKey$1, n as loadBundledPluginPublicSurfaceModuleSync$1, o as resolveCachedFacadeModuleLocation, r as loadFacadeModuleAtLocationSync$1 } from "./facade-loader-DFzIYYt_-CIr4HKtE.mjs";
|
|
15
|
-
import { i as SessionManager } from "./dist-DD2aJ2UL.mjs";
|
|
16
|
-
import { createRequire } from "node:module";
|
|
17
|
-
import fs from "node:fs";
|
|
18
|
-
import path from "node:path";
|
|
19
|
-
import { fileURLToPath } from "node:url";
|
|
20
|
-
import fs$1 from "node:fs/promises";
|
|
21
|
-
import { randomUUID } from "node:crypto";
|
|
22
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/types-ASqeFakT.js
|
|
23
|
-
function normalizeSessionRuntimeModelFields(entry) {
|
|
24
|
-
const normalizedModel = normalizeOptionalString(entry.model);
|
|
25
|
-
const normalizedProvider = normalizeOptionalString(entry.modelProvider);
|
|
26
|
-
let next = entry;
|
|
27
|
-
if (!normalizedModel) {
|
|
28
|
-
if (entry.model !== void 0 || entry.modelProvider !== void 0) {
|
|
29
|
-
next = { ...next };
|
|
30
|
-
delete next.model;
|
|
31
|
-
delete next.modelProvider;
|
|
32
|
-
}
|
|
33
|
-
return next;
|
|
34
|
-
}
|
|
35
|
-
if (entry.model !== normalizedModel) {
|
|
36
|
-
if (next === entry) next = { ...next };
|
|
37
|
-
next.model = normalizedModel;
|
|
38
|
-
}
|
|
39
|
-
if (!normalizedProvider) {
|
|
40
|
-
if (entry.modelProvider !== void 0) {
|
|
41
|
-
if (next === entry) next = { ...next };
|
|
42
|
-
delete next.modelProvider;
|
|
43
|
-
}
|
|
44
|
-
return next;
|
|
45
|
-
}
|
|
46
|
-
if (entry.modelProvider !== normalizedProvider) {
|
|
47
|
-
if (next === entry) next = { ...next };
|
|
48
|
-
next.modelProvider = normalizedProvider;
|
|
49
|
-
}
|
|
50
|
-
return next;
|
|
51
|
-
}
|
|
52
|
-
//#endregion
|
|
53
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/session-write-lock-3uPK7XQA.js
|
|
54
|
-
function isValidLockNumber(value) {
|
|
55
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
56
|
-
}
|
|
57
|
-
const CLEANUP_SIGNALS = [
|
|
58
|
-
"SIGINT",
|
|
59
|
-
"SIGTERM",
|
|
60
|
-
"SIGQUIT",
|
|
61
|
-
"SIGABRT"
|
|
62
|
-
];
|
|
63
|
-
const CLEANUP_STATE_KEY = Symbol.for("openclaw.sessionWriteLockCleanupState");
|
|
64
|
-
const HELD_LOCKS_KEY = Symbol.for("openclaw.sessionWriteLockHeldLocks");
|
|
65
|
-
const WATCHDOG_STATE_KEY = Symbol.for("openclaw.sessionWriteLockWatchdogState");
|
|
66
|
-
const DEFAULT_STALE_MS = 1800 * 1e3;
|
|
67
|
-
const DEFAULT_MAX_HOLD_MS = 300 * 1e3;
|
|
68
|
-
const DEFAULT_WATCHDOG_INTERVAL_MS = 6e4;
|
|
69
|
-
const DEFAULT_TIMEOUT_GRACE_MS = 120 * 1e3;
|
|
70
|
-
const MAX_LOCK_HOLD_MS = 2147e6;
|
|
71
|
-
const HELD_LOCKS = resolveProcessScopedMap(HELD_LOCKS_KEY);
|
|
72
|
-
function resolveCleanupState() {
|
|
73
|
-
const proc = process;
|
|
74
|
-
if (!proc[CLEANUP_STATE_KEY]) proc[CLEANUP_STATE_KEY] = {
|
|
75
|
-
registered: false,
|
|
76
|
-
exitHandler: void 0,
|
|
77
|
-
cleanupHandlers: /* @__PURE__ */ new Map()
|
|
78
|
-
};
|
|
79
|
-
return proc[CLEANUP_STATE_KEY];
|
|
80
|
-
}
|
|
81
|
-
function resolveWatchdogState() {
|
|
82
|
-
const proc = process;
|
|
83
|
-
if (!proc[WATCHDOG_STATE_KEY]) proc[WATCHDOG_STATE_KEY] = {
|
|
84
|
-
started: false,
|
|
85
|
-
intervalMs: DEFAULT_WATCHDOG_INTERVAL_MS
|
|
86
|
-
};
|
|
87
|
-
return proc[WATCHDOG_STATE_KEY];
|
|
88
|
-
}
|
|
89
|
-
function resolvePositiveMs(value, fallback, opts = {}) {
|
|
90
|
-
if (typeof value !== "number" || Number.isNaN(value) || value <= 0) return fallback;
|
|
91
|
-
if (value === Number.POSITIVE_INFINITY) return opts.allowInfinity ? value : fallback;
|
|
92
|
-
if (!Number.isFinite(value)) return fallback;
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
function resolveSessionLockMaxHoldFromTimeout(params) {
|
|
96
|
-
const minMs = resolvePositiveMs(params.minMs, DEFAULT_MAX_HOLD_MS);
|
|
97
|
-
const timeoutMs = resolvePositiveMs(params.timeoutMs, minMs, { allowInfinity: true });
|
|
98
|
-
if (timeoutMs === Number.POSITIVE_INFINITY) return MAX_LOCK_HOLD_MS;
|
|
99
|
-
const graceMs = resolvePositiveMs(params.graceMs, DEFAULT_TIMEOUT_GRACE_MS);
|
|
100
|
-
return Math.min(MAX_LOCK_HOLD_MS, Math.max(minMs, timeoutMs + graceMs));
|
|
101
|
-
}
|
|
102
|
-
async function releaseHeldLock(normalizedSessionFile, held, opts = {}) {
|
|
103
|
-
if (HELD_LOCKS.get(normalizedSessionFile) !== held) return false;
|
|
104
|
-
if (opts.force) held.count = 0;
|
|
105
|
-
else {
|
|
106
|
-
held.count -= 1;
|
|
107
|
-
if (held.count > 0) return false;
|
|
108
|
-
}
|
|
109
|
-
if (held.releasePromise) {
|
|
110
|
-
await held.releasePromise.catch(() => void 0);
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
HELD_LOCKS.delete(normalizedSessionFile);
|
|
114
|
-
held.releasePromise = (async () => {
|
|
115
|
-
try {
|
|
116
|
-
await held.handle.close();
|
|
117
|
-
} catch {}
|
|
118
|
-
try {
|
|
119
|
-
await fs$1.rm(held.lockPath, { force: true });
|
|
120
|
-
} catch {}
|
|
121
|
-
})();
|
|
122
|
-
try {
|
|
123
|
-
await held.releasePromise;
|
|
124
|
-
return true;
|
|
125
|
-
} finally {
|
|
126
|
-
held.releasePromise = void 0;
|
|
127
|
-
if (HELD_LOCKS.size === 0) stopWatchdogTimer();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Synchronously release all held locks.
|
|
132
|
-
* Used during process exit when async operations aren't reliable.
|
|
133
|
-
*/
|
|
134
|
-
function releaseAllLocksSync() {
|
|
135
|
-
for (const [sessionFile, held] of HELD_LOCKS) {
|
|
136
|
-
held.handle.close().catch(() => void 0);
|
|
137
|
-
try {
|
|
138
|
-
fs.rmSync(held.lockPath, { force: true });
|
|
139
|
-
} catch {}
|
|
140
|
-
HELD_LOCKS.delete(sessionFile);
|
|
141
|
-
}
|
|
142
|
-
if (HELD_LOCKS.size === 0) stopWatchdogTimer();
|
|
143
|
-
}
|
|
144
|
-
async function runLockWatchdogCheck(nowMs = Date.now()) {
|
|
145
|
-
let released = 0;
|
|
146
|
-
for (const [sessionFile, held] of HELD_LOCKS.entries()) {
|
|
147
|
-
const heldForMs = nowMs - held.acquiredAt;
|
|
148
|
-
if (heldForMs <= held.maxHoldMs) continue;
|
|
149
|
-
process.stderr.write(`[session-write-lock] releasing lock held for ${heldForMs}ms (max=${held.maxHoldMs}ms): ${held.lockPath}\n`);
|
|
150
|
-
if (await releaseHeldLock(sessionFile, held, { force: true })) released += 1;
|
|
151
|
-
}
|
|
152
|
-
return released;
|
|
153
|
-
}
|
|
154
|
-
function stopWatchdogTimer() {
|
|
155
|
-
const watchdogState = resolveWatchdogState();
|
|
156
|
-
if (watchdogState.timer) {
|
|
157
|
-
clearInterval(watchdogState.timer);
|
|
158
|
-
watchdogState.timer = void 0;
|
|
159
|
-
}
|
|
160
|
-
watchdogState.started = false;
|
|
161
|
-
}
|
|
162
|
-
function shouldStartBackgroundWatchdog() {
|
|
163
|
-
return process.env.VITEST !== "true" || process.env.OPENCLAW_TEST_SESSION_LOCK_WATCHDOG === "1";
|
|
164
|
-
}
|
|
165
|
-
function ensureWatchdogStarted(intervalMs) {
|
|
166
|
-
if (!shouldStartBackgroundWatchdog()) return;
|
|
167
|
-
const watchdogState = resolveWatchdogState();
|
|
168
|
-
if (watchdogState.started) return;
|
|
169
|
-
watchdogState.started = true;
|
|
170
|
-
watchdogState.intervalMs = intervalMs;
|
|
171
|
-
watchdogState.timer = setInterval(() => {
|
|
172
|
-
runLockWatchdogCheck().catch(() => {});
|
|
173
|
-
}, intervalMs);
|
|
174
|
-
watchdogState.timer.unref?.();
|
|
175
|
-
}
|
|
176
|
-
function handleTerminationSignal(signal) {
|
|
177
|
-
releaseAllLocksSync();
|
|
178
|
-
const cleanupState = resolveCleanupState();
|
|
179
|
-
if (process.listenerCount(signal) === 1) {
|
|
180
|
-
const handler = cleanupState.cleanupHandlers.get(signal);
|
|
181
|
-
if (handler) {
|
|
182
|
-
process.off(signal, handler);
|
|
183
|
-
cleanupState.cleanupHandlers.delete(signal);
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
process.kill(process.pid, signal);
|
|
187
|
-
} catch {}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function registerCleanupHandlers() {
|
|
191
|
-
const cleanupState = resolveCleanupState();
|
|
192
|
-
cleanupState.registered = true;
|
|
193
|
-
if (!cleanupState.exitHandler) {
|
|
194
|
-
cleanupState.exitHandler = () => {
|
|
195
|
-
releaseAllLocksSync();
|
|
196
|
-
};
|
|
197
|
-
process.on("exit", cleanupState.exitHandler);
|
|
198
|
-
}
|
|
199
|
-
ensureWatchdogStarted(DEFAULT_WATCHDOG_INTERVAL_MS);
|
|
200
|
-
for (const signal of CLEANUP_SIGNALS) {
|
|
201
|
-
if (cleanupState.cleanupHandlers.has(signal)) continue;
|
|
202
|
-
try {
|
|
203
|
-
const handler = () => handleTerminationSignal(signal);
|
|
204
|
-
cleanupState.cleanupHandlers.set(signal, handler);
|
|
205
|
-
process.on(signal, handler);
|
|
206
|
-
} catch {}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
async function readLockPayload(lockPath) {
|
|
210
|
-
try {
|
|
211
|
-
const raw = await fs$1.readFile(lockPath, "utf8");
|
|
212
|
-
const parsed = JSON.parse(raw);
|
|
213
|
-
const payload = {};
|
|
214
|
-
if (isValidLockNumber(parsed.pid) && parsed.pid > 0) payload.pid = parsed.pid;
|
|
215
|
-
if (typeof parsed.createdAt === "string") payload.createdAt = parsed.createdAt;
|
|
216
|
-
if (isValidLockNumber(parsed.starttime)) payload.starttime = parsed.starttime;
|
|
217
|
-
return payload;
|
|
218
|
-
} catch {
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function inspectLockPayload(payload, staleMs, nowMs) {
|
|
223
|
-
const pid = isValidLockNumber(payload?.pid) && payload.pid > 0 ? payload.pid : null;
|
|
224
|
-
const pidAlive = pid !== null ? isPidAlive(pid) : false;
|
|
225
|
-
const createdAt = typeof payload?.createdAt === "string" ? payload.createdAt : null;
|
|
226
|
-
const createdAtMs = createdAt ? Date.parse(createdAt) : NaN;
|
|
227
|
-
const ageMs = Number.isFinite(createdAtMs) ? Math.max(0, nowMs - createdAtMs) : null;
|
|
228
|
-
const storedStarttime = isValidLockNumber(payload?.starttime) ? payload.starttime : null;
|
|
229
|
-
const pidRecycled = pidAlive && pid !== null && storedStarttime !== null ? (() => {
|
|
230
|
-
const currentStarttime = getProcessStartTime(pid);
|
|
231
|
-
return currentStarttime !== null && currentStarttime !== storedStarttime;
|
|
232
|
-
})() : false;
|
|
233
|
-
const staleReasons = [];
|
|
234
|
-
if (pid === null) staleReasons.push("missing-pid");
|
|
235
|
-
else if (!pidAlive) staleReasons.push("dead-pid");
|
|
236
|
-
else if (pidRecycled) staleReasons.push("recycled-pid");
|
|
237
|
-
if (ageMs === null) staleReasons.push("invalid-createdAt");
|
|
238
|
-
else if (ageMs > staleMs) staleReasons.push("too-old");
|
|
239
|
-
return {
|
|
240
|
-
pid,
|
|
241
|
-
pidAlive,
|
|
242
|
-
createdAt,
|
|
243
|
-
ageMs,
|
|
244
|
-
stale: staleReasons.length > 0,
|
|
245
|
-
staleReasons
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
function lockInspectionNeedsMtimeStaleFallback(details) {
|
|
249
|
-
return details.stale && details.staleReasons.every((reason) => reason === "missing-pid" || reason === "invalid-createdAt");
|
|
250
|
-
}
|
|
251
|
-
async function shouldReclaimContendedLockFile(lockPath, details, staleMs, nowMs) {
|
|
252
|
-
if (!details.stale) return false;
|
|
253
|
-
if (!lockInspectionNeedsMtimeStaleFallback(details)) return true;
|
|
254
|
-
try {
|
|
255
|
-
const stat = await fs$1.stat(lockPath);
|
|
256
|
-
return Math.max(0, nowMs - stat.mtimeMs) > staleMs;
|
|
257
|
-
} catch (error) {
|
|
258
|
-
return error?.code !== "ENOENT";
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function shouldTreatAsOrphanSelfLock(params) {
|
|
262
|
-
if ((isValidLockNumber(params.payload?.pid) ? params.payload.pid : null) !== process.pid) return false;
|
|
263
|
-
if (isValidLockNumber(params.payload?.starttime)) return false;
|
|
264
|
-
return !HELD_LOCKS.has(params.normalizedSessionFile);
|
|
265
|
-
}
|
|
266
|
-
async function acquireSessionWriteLock(params) {
|
|
267
|
-
registerCleanupHandlers();
|
|
268
|
-
const allowReentrant = params.allowReentrant ?? false;
|
|
269
|
-
const timeoutMs = resolvePositiveMs(params.timeoutMs, 1e4, { allowInfinity: true });
|
|
270
|
-
const staleMs = resolvePositiveMs(params.staleMs, DEFAULT_STALE_MS);
|
|
271
|
-
const maxHoldMs = resolvePositiveMs(params.maxHoldMs, DEFAULT_MAX_HOLD_MS);
|
|
272
|
-
const sessionFile = path.resolve(params.sessionFile);
|
|
273
|
-
const sessionDir = path.dirname(sessionFile);
|
|
274
|
-
await fs$1.mkdir(sessionDir, { recursive: true });
|
|
275
|
-
let normalizedDir = sessionDir;
|
|
276
|
-
try {
|
|
277
|
-
normalizedDir = await fs$1.realpath(sessionDir);
|
|
278
|
-
} catch {}
|
|
279
|
-
const normalizedSessionFile = path.join(normalizedDir, path.basename(sessionFile));
|
|
280
|
-
const lockPath = `${normalizedSessionFile}.lock`;
|
|
281
|
-
const held = HELD_LOCKS.get(normalizedSessionFile);
|
|
282
|
-
if (allowReentrant && held) {
|
|
283
|
-
held.count += 1;
|
|
284
|
-
return { release: async () => {
|
|
285
|
-
await releaseHeldLock(normalizedSessionFile, held);
|
|
286
|
-
} };
|
|
287
|
-
}
|
|
288
|
-
const startedAt = Date.now();
|
|
289
|
-
let attempt = 0;
|
|
290
|
-
while (Date.now() - startedAt < timeoutMs) {
|
|
291
|
-
attempt += 1;
|
|
292
|
-
let handle = null;
|
|
293
|
-
try {
|
|
294
|
-
handle = await fs$1.open(lockPath, "wx");
|
|
295
|
-
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
296
|
-
const starttime = getProcessStartTime(process.pid);
|
|
297
|
-
const lockPayload = {
|
|
298
|
-
pid: process.pid,
|
|
299
|
-
createdAt
|
|
300
|
-
};
|
|
301
|
-
if (starttime !== null) lockPayload.starttime = starttime;
|
|
302
|
-
await handle.writeFile(JSON.stringify(lockPayload, null, 2), "utf8");
|
|
303
|
-
const createdHeld = {
|
|
304
|
-
count: 1,
|
|
305
|
-
handle,
|
|
306
|
-
lockPath,
|
|
307
|
-
acquiredAt: Date.now(),
|
|
308
|
-
maxHoldMs
|
|
309
|
-
};
|
|
310
|
-
HELD_LOCKS.set(normalizedSessionFile, createdHeld);
|
|
311
|
-
return { release: async () => {
|
|
312
|
-
await releaseHeldLock(normalizedSessionFile, createdHeld);
|
|
313
|
-
} };
|
|
314
|
-
} catch (err) {
|
|
315
|
-
if (handle) {
|
|
316
|
-
try {
|
|
317
|
-
await handle.close();
|
|
318
|
-
} catch {}
|
|
319
|
-
try {
|
|
320
|
-
await fs$1.rm(lockPath, { force: true });
|
|
321
|
-
} catch {}
|
|
322
|
-
}
|
|
323
|
-
if (err.code !== "EEXIST") throw err;
|
|
324
|
-
const payload = await readLockPayload(lockPath);
|
|
325
|
-
const nowMs = Date.now();
|
|
326
|
-
const inspected = inspectLockPayload(payload, staleMs, nowMs);
|
|
327
|
-
if (await shouldReclaimContendedLockFile(lockPath, shouldTreatAsOrphanSelfLock({
|
|
328
|
-
payload,
|
|
329
|
-
normalizedSessionFile
|
|
330
|
-
}) ? {
|
|
331
|
-
...inspected,
|
|
332
|
-
stale: true,
|
|
333
|
-
staleReasons: inspected.staleReasons.includes("orphan-self-pid") ? inspected.staleReasons : [...inspected.staleReasons, "orphan-self-pid"]
|
|
334
|
-
} : inspected, staleMs, nowMs)) {
|
|
335
|
-
await fs$1.rm(lockPath, { force: true });
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
const remainingMs = timeoutMs - (Date.now() - startedAt);
|
|
339
|
-
if (remainingMs <= 0) break;
|
|
340
|
-
const delay = Math.min(1e3, 50 * attempt, remainingMs);
|
|
341
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
const payload = await readLockPayload(lockPath);
|
|
345
|
-
const owner = typeof payload?.pid === "number" ? `pid=${payload.pid}` : "unknown";
|
|
346
|
-
throw new Error(`session file locked (timeout ${timeoutMs}ms): ${owner} ${lockPath}`);
|
|
347
|
-
}
|
|
348
|
-
[...CLEANUP_SIGNALS];
|
|
349
|
-
//#endregion
|
|
350
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/json-files-DcOQMqKi.js
|
|
351
|
-
function getErrorCode$1(err) {
|
|
352
|
-
return err instanceof Error ? err.code : void 0;
|
|
353
|
-
}
|
|
354
|
-
async function replaceFileWithWindowsFallback(tempPath, filePath, mode) {
|
|
355
|
-
try {
|
|
356
|
-
await fs$1.rename(tempPath, filePath);
|
|
357
|
-
return;
|
|
358
|
-
} catch (err) {
|
|
359
|
-
const code = getErrorCode$1(err);
|
|
360
|
-
if (process.platform !== "win32" || code !== "EPERM" && code !== "EEXIST") throw err;
|
|
361
|
-
}
|
|
362
|
-
await fs$1.copyFile(tempPath, filePath);
|
|
363
|
-
try {
|
|
364
|
-
await fs$1.chmod(filePath, mode);
|
|
365
|
-
} catch {}
|
|
366
|
-
await fs$1.rm(tempPath, { force: true }).catch(() => void 0);
|
|
367
|
-
}
|
|
368
|
-
async function writeTextAtomic(filePath, content, options) {
|
|
369
|
-
const mode = options?.mode ?? 384;
|
|
370
|
-
const payload = options?.appendTrailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
|
|
371
|
-
const mkdirOptions = { recursive: true };
|
|
372
|
-
if (typeof options?.ensureDirMode === "number") mkdirOptions.mode = options.ensureDirMode;
|
|
373
|
-
await fs$1.mkdir(path.dirname(filePath), mkdirOptions);
|
|
374
|
-
const parentDir = path.dirname(filePath);
|
|
375
|
-
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
376
|
-
try {
|
|
377
|
-
const tmpHandle = await fs$1.open(tmp, "w", mode);
|
|
378
|
-
try {
|
|
379
|
-
await tmpHandle.writeFile(payload, { encoding: "utf8" });
|
|
380
|
-
await tmpHandle.sync();
|
|
381
|
-
} finally {
|
|
382
|
-
await tmpHandle.close().catch(() => void 0);
|
|
383
|
-
}
|
|
384
|
-
try {
|
|
385
|
-
await fs$1.chmod(tmp, mode);
|
|
386
|
-
} catch {}
|
|
387
|
-
await replaceFileWithWindowsFallback(tmp, filePath, mode);
|
|
388
|
-
try {
|
|
389
|
-
const dirHandle = await fs$1.open(parentDir, "r");
|
|
390
|
-
try {
|
|
391
|
-
await dirHandle.sync();
|
|
392
|
-
} finally {
|
|
393
|
-
await dirHandle.close().catch(() => void 0);
|
|
394
|
-
}
|
|
395
|
-
} catch {}
|
|
396
|
-
try {
|
|
397
|
-
await fs$1.chmod(filePath, mode);
|
|
398
|
-
} catch {}
|
|
399
|
-
} finally {
|
|
400
|
-
await fs$1.rm(tmp, { force: true }).catch(() => void 0);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
//#endregion
|
|
404
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/parse-finite-number-DdG2XOLD.js
|
|
405
|
-
function normalizeNumericString(value) {
|
|
406
|
-
const trimmed = value.trim();
|
|
407
|
-
return trimmed ? trimmed : void 0;
|
|
408
|
-
}
|
|
409
|
-
function parseStrictInteger(value) {
|
|
410
|
-
if (typeof value === "number") return Number.isSafeInteger(value) ? value : void 0;
|
|
411
|
-
if (typeof value !== "string") return;
|
|
412
|
-
const normalized = normalizeNumericString(value);
|
|
413
|
-
if (!normalized || !/^[+-]?\d+$/.test(normalized)) return;
|
|
414
|
-
const parsed = Number(normalized);
|
|
415
|
-
return Number.isSafeInteger(parsed) ? parsed : void 0;
|
|
416
|
-
}
|
|
417
|
-
function parseStrictNonNegativeInteger(value) {
|
|
418
|
-
const parsed = parseStrictInteger(value);
|
|
419
|
-
return parsed !== void 0 && parsed >= 0 ? parsed : void 0;
|
|
420
|
-
}
|
|
421
|
-
//#endregion
|
|
422
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/store-cache-DufBlCJB.js
|
|
423
|
-
function resolveCacheTtlMs(params) {
|
|
424
|
-
const { envValue, defaultTtlMs } = params;
|
|
425
|
-
if (envValue) {
|
|
426
|
-
const parsed = parseStrictNonNegativeInteger(envValue);
|
|
427
|
-
if (parsed !== void 0) return parsed;
|
|
428
|
-
}
|
|
429
|
-
return defaultTtlMs;
|
|
430
|
-
}
|
|
431
|
-
function isCacheEnabled(ttlMs) {
|
|
432
|
-
return ttlMs > 0;
|
|
433
|
-
}
|
|
434
|
-
function resolveCacheNumeric(value) {
|
|
435
|
-
return typeof value === "function" ? value() : value;
|
|
436
|
-
}
|
|
437
|
-
function resolvePruneIntervalMs(ttlMs, pruneIntervalMs) {
|
|
438
|
-
if (typeof pruneIntervalMs === "function") return Math.max(0, Math.floor(pruneIntervalMs(ttlMs)));
|
|
439
|
-
if (typeof pruneIntervalMs === "number") return Math.max(0, Math.floor(pruneIntervalMs));
|
|
440
|
-
return ttlMs;
|
|
441
|
-
}
|
|
442
|
-
function isCacheEntryExpired(storedAt, now, ttlMs) {
|
|
443
|
-
return now - storedAt > ttlMs;
|
|
444
|
-
}
|
|
445
|
-
function createExpiringMapCache(options) {
|
|
446
|
-
const cache = /* @__PURE__ */ new Map();
|
|
447
|
-
const now = options.clock ?? Date.now;
|
|
448
|
-
let lastPruneAt = 0;
|
|
449
|
-
function getTtlMs() {
|
|
450
|
-
return Math.max(0, Math.floor(resolveCacheNumeric(options.ttlMs)));
|
|
451
|
-
}
|
|
452
|
-
function maybePruneExpiredEntries(nowMs, ttlMs) {
|
|
453
|
-
if (!isCacheEnabled(ttlMs)) return;
|
|
454
|
-
if (nowMs - lastPruneAt < resolvePruneIntervalMs(ttlMs, options.pruneIntervalMs)) return;
|
|
455
|
-
for (const [key, entry] of cache.entries()) if (isCacheEntryExpired(entry.storedAt, nowMs, ttlMs)) cache.delete(key);
|
|
456
|
-
lastPruneAt = nowMs;
|
|
457
|
-
}
|
|
458
|
-
return {
|
|
459
|
-
get: (key) => {
|
|
460
|
-
const ttlMs = getTtlMs();
|
|
461
|
-
if (!isCacheEnabled(ttlMs)) return;
|
|
462
|
-
const nowMs = now();
|
|
463
|
-
maybePruneExpiredEntries(nowMs, ttlMs);
|
|
464
|
-
const entry = cache.get(key);
|
|
465
|
-
if (!entry) return;
|
|
466
|
-
if (isCacheEntryExpired(entry.storedAt, nowMs, ttlMs)) {
|
|
467
|
-
cache.delete(key);
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
return entry.value;
|
|
471
|
-
},
|
|
472
|
-
set: (key, value) => {
|
|
473
|
-
const ttlMs = getTtlMs();
|
|
474
|
-
if (!isCacheEnabled(ttlMs)) return;
|
|
475
|
-
const nowMs = now();
|
|
476
|
-
maybePruneExpiredEntries(nowMs, ttlMs);
|
|
477
|
-
cache.set(key, {
|
|
478
|
-
storedAt: nowMs,
|
|
479
|
-
value
|
|
480
|
-
});
|
|
481
|
-
},
|
|
482
|
-
delete: (key) => {
|
|
483
|
-
cache.delete(key);
|
|
484
|
-
},
|
|
485
|
-
clear: () => {
|
|
486
|
-
cache.clear();
|
|
487
|
-
lastPruneAt = 0;
|
|
488
|
-
},
|
|
489
|
-
keys: () => [...cache.keys()],
|
|
490
|
-
size: () => cache.size,
|
|
491
|
-
pruneExpired: () => {
|
|
492
|
-
const ttlMs = getTtlMs();
|
|
493
|
-
if (!isCacheEnabled(ttlMs)) return;
|
|
494
|
-
const nowMs = now();
|
|
495
|
-
for (const [key, entry] of cache.entries()) if (isCacheEntryExpired(entry.storedAt, nowMs, ttlMs)) cache.delete(key);
|
|
496
|
-
lastPruneAt = nowMs;
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
function getFileStatSnapshot(filePath) {
|
|
501
|
-
try {
|
|
502
|
-
const stats = fs.statSync(filePath);
|
|
503
|
-
return {
|
|
504
|
-
mtimeMs: stats.mtimeMs,
|
|
505
|
-
sizeBytes: stats.size
|
|
506
|
-
};
|
|
507
|
-
} catch {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
const DEFAULT_SESSION_STORE_TTL_MS = 45e3;
|
|
512
|
-
const SESSION_STORE_CACHE = createExpiringMapCache({ ttlMs: getSessionStoreTtl });
|
|
513
|
-
const SESSION_STORE_SERIALIZED_CACHE = /* @__PURE__ */ new Map();
|
|
514
|
-
function getSessionStoreTtl() {
|
|
515
|
-
return resolveCacheTtlMs({
|
|
516
|
-
envValue: process.env.OPENCLAW_SESSION_CACHE_TTL_MS,
|
|
517
|
-
defaultTtlMs: DEFAULT_SESSION_STORE_TTL_MS
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
function isSessionStoreCacheEnabled() {
|
|
521
|
-
return isCacheEnabled(getSessionStoreTtl());
|
|
522
|
-
}
|
|
523
|
-
function invalidateSessionStoreCache(storePath) {
|
|
524
|
-
SESSION_STORE_CACHE.delete(storePath);
|
|
525
|
-
SESSION_STORE_SERIALIZED_CACHE.delete(storePath);
|
|
526
|
-
}
|
|
527
|
-
function getSerializedSessionStore(storePath) {
|
|
528
|
-
return SESSION_STORE_SERIALIZED_CACHE.get(storePath);
|
|
529
|
-
}
|
|
530
|
-
function setSerializedSessionStore(storePath, serialized) {
|
|
531
|
-
if (serialized === void 0) {
|
|
532
|
-
SESSION_STORE_SERIALIZED_CACHE.delete(storePath);
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
SESSION_STORE_SERIALIZED_CACHE.set(storePath, serialized);
|
|
536
|
-
}
|
|
537
|
-
function dropSessionStoreObjectCache(storePath) {
|
|
538
|
-
SESSION_STORE_CACHE.delete(storePath);
|
|
539
|
-
}
|
|
540
|
-
function readSessionStoreCache(params) {
|
|
541
|
-
const cached = SESSION_STORE_CACHE.get(params.storePath);
|
|
542
|
-
if (!cached) return null;
|
|
543
|
-
if (params.mtimeMs !== cached.mtimeMs || params.sizeBytes !== cached.sizeBytes) {
|
|
544
|
-
invalidateSessionStoreCache(params.storePath);
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
return structuredClone(cached.store);
|
|
548
|
-
}
|
|
549
|
-
function writeSessionStoreCache(params) {
|
|
550
|
-
SESSION_STORE_CACHE.set(params.storePath, {
|
|
551
|
-
store: structuredClone(params.store),
|
|
552
|
-
mtimeMs: params.mtimeMs,
|
|
553
|
-
sizeBytes: params.sizeBytes,
|
|
554
|
-
serialized: params.serialized
|
|
555
|
-
});
|
|
556
|
-
if (params.serialized !== void 0) SESSION_STORE_SERIALIZED_CACHE.set(params.storePath, params.serialized);
|
|
557
|
-
}
|
|
558
|
-
//#endregion
|
|
559
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/store-load-HmCzUIz7.js
|
|
560
|
-
const log$1 = createSubsystemLogger("sessions/store");
|
|
561
|
-
const DEFAULT_SESSION_PRUNE_AFTER_MS = 720 * 60 * 60 * 1e3;
|
|
562
|
-
const DEFAULT_SESSION_MAX_ENTRIES = 500;
|
|
563
|
-
const DEFAULT_SESSION_ROTATE_BYTES = 10485760;
|
|
564
|
-
const DEFAULT_SESSION_MAINTENANCE_MODE = "enforce";
|
|
565
|
-
const DEFAULT_SESSION_DISK_BUDGET_HIGH_WATER_RATIO = .8;
|
|
566
|
-
function resolvePruneAfterMs(maintenance) {
|
|
567
|
-
const normalized = normalizeStringifiedOptionalString(maintenance?.pruneAfter ?? maintenance?.pruneDays);
|
|
568
|
-
if (!normalized) return DEFAULT_SESSION_PRUNE_AFTER_MS;
|
|
569
|
-
try {
|
|
570
|
-
return parseDurationMs(normalized, { defaultUnit: "d" });
|
|
571
|
-
} catch {
|
|
572
|
-
return DEFAULT_SESSION_PRUNE_AFTER_MS;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
function resolveRotateBytes(maintenance) {
|
|
576
|
-
const raw = maintenance?.rotateBytes;
|
|
577
|
-
const normalized = normalizeStringifiedOptionalString(raw);
|
|
578
|
-
if (!normalized) return DEFAULT_SESSION_ROTATE_BYTES;
|
|
579
|
-
try {
|
|
580
|
-
return parseByteSize(normalized, { defaultUnit: "b" });
|
|
581
|
-
} catch {
|
|
582
|
-
return DEFAULT_SESSION_ROTATE_BYTES;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
function resolveResetArchiveRetentionMs(maintenance, pruneAfterMs) {
|
|
586
|
-
const raw = maintenance?.resetArchiveRetention;
|
|
587
|
-
if (raw === false) return null;
|
|
588
|
-
const normalized = normalizeStringifiedOptionalString(raw);
|
|
589
|
-
if (!normalized) return pruneAfterMs;
|
|
590
|
-
try {
|
|
591
|
-
return parseDurationMs(normalized, { defaultUnit: "d" });
|
|
592
|
-
} catch {
|
|
593
|
-
return pruneAfterMs;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
function resolveMaxDiskBytes(maintenance) {
|
|
597
|
-
const raw = maintenance?.maxDiskBytes;
|
|
598
|
-
const normalized = normalizeStringifiedOptionalString(raw);
|
|
599
|
-
if (!normalized) return null;
|
|
600
|
-
try {
|
|
601
|
-
return parseByteSize(normalized, { defaultUnit: "b" });
|
|
602
|
-
} catch {
|
|
603
|
-
return null;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
function resolveHighWaterBytes(maintenance, maxDiskBytes) {
|
|
607
|
-
const computeDefault = () => {
|
|
608
|
-
if (maxDiskBytes == null) return null;
|
|
609
|
-
if (maxDiskBytes <= 0) return 0;
|
|
610
|
-
return Math.max(1, Math.min(maxDiskBytes, Math.floor(maxDiskBytes * DEFAULT_SESSION_DISK_BUDGET_HIGH_WATER_RATIO)));
|
|
611
|
-
};
|
|
612
|
-
if (maxDiskBytes == null) return null;
|
|
613
|
-
const raw = maintenance?.highWaterBytes;
|
|
614
|
-
const normalized = normalizeStringifiedOptionalString(raw);
|
|
615
|
-
if (!normalized) return computeDefault();
|
|
616
|
-
try {
|
|
617
|
-
const parsed = parseByteSize(normalized, { defaultUnit: "b" });
|
|
618
|
-
return Math.min(parsed, maxDiskBytes);
|
|
619
|
-
} catch {
|
|
620
|
-
return computeDefault();
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Resolve maintenance settings from openclaw.json (`session.maintenance`).
|
|
625
|
-
* Falls back to built-in defaults when config is missing or unset.
|
|
626
|
-
*/
|
|
627
|
-
function resolveMaintenanceConfigFromInput(maintenance) {
|
|
628
|
-
const pruneAfterMs = resolvePruneAfterMs(maintenance);
|
|
629
|
-
const maxDiskBytes = resolveMaxDiskBytes(maintenance);
|
|
630
|
-
return {
|
|
631
|
-
mode: maintenance?.mode ?? DEFAULT_SESSION_MAINTENANCE_MODE,
|
|
632
|
-
pruneAfterMs,
|
|
633
|
-
maxEntries: maintenance?.maxEntries ?? DEFAULT_SESSION_MAX_ENTRIES,
|
|
634
|
-
rotateBytes: resolveRotateBytes(maintenance),
|
|
635
|
-
resetArchiveRetentionMs: resolveResetArchiveRetentionMs(maintenance, pruneAfterMs),
|
|
636
|
-
maxDiskBytes,
|
|
637
|
-
highWaterBytes: resolveHighWaterBytes(maintenance, maxDiskBytes)
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Remove entries whose `updatedAt` is older than the configured threshold.
|
|
642
|
-
* Entries without `updatedAt` are kept (cannot determine staleness).
|
|
643
|
-
* Mutates `store` in-place.
|
|
644
|
-
*/
|
|
645
|
-
function pruneStaleEntries(store, overrideMaxAgeMs, opts = {}) {
|
|
646
|
-
const maxAgeMs = overrideMaxAgeMs ?? resolveMaintenanceConfigFromInput().pruneAfterMs;
|
|
647
|
-
const cutoffMs = Date.now() - maxAgeMs;
|
|
648
|
-
let pruned = 0;
|
|
649
|
-
for (const [key, entry] of Object.entries(store)) {
|
|
650
|
-
if (opts.preserveKeys?.has(key)) continue;
|
|
651
|
-
if (entry?.updatedAt != null && entry.updatedAt < cutoffMs) {
|
|
652
|
-
opts.onPruned?.({
|
|
653
|
-
key,
|
|
654
|
-
entry
|
|
655
|
-
});
|
|
656
|
-
delete store[key];
|
|
657
|
-
pruned++;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (pruned > 0 && opts.log !== false) log$1.info("pruned stale session entries", {
|
|
661
|
-
pruned,
|
|
662
|
-
maxAgeMs
|
|
663
|
-
});
|
|
664
|
-
return pruned;
|
|
665
|
-
}
|
|
666
|
-
function getEntryUpdatedAt$1(entry) {
|
|
667
|
-
return entry?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
668
|
-
}
|
|
669
|
-
function getActiveSessionMaintenanceWarning(params) {
|
|
670
|
-
const activeSessionKey = params.activeSessionKey.trim();
|
|
671
|
-
if (!activeSessionKey) return null;
|
|
672
|
-
const activeEntry = params.store[activeSessionKey];
|
|
673
|
-
if (!activeEntry) return null;
|
|
674
|
-
const cutoffMs = (params.nowMs ?? Date.now()) - params.pruneAfterMs;
|
|
675
|
-
const wouldPrune = activeEntry.updatedAt != null ? activeEntry.updatedAt < cutoffMs : false;
|
|
676
|
-
const keys = Object.keys(params.store);
|
|
677
|
-
const wouldCap = wouldCapActiveSession({
|
|
678
|
-
store: params.store,
|
|
679
|
-
keys,
|
|
680
|
-
activeEntry,
|
|
681
|
-
activeSessionKey,
|
|
682
|
-
maxEntries: params.maxEntries
|
|
683
|
-
});
|
|
684
|
-
if (!wouldPrune && !wouldCap) return null;
|
|
685
|
-
return {
|
|
686
|
-
activeSessionKey,
|
|
687
|
-
activeUpdatedAt: activeEntry.updatedAt,
|
|
688
|
-
totalEntries: keys.length,
|
|
689
|
-
pruneAfterMs: params.pruneAfterMs,
|
|
690
|
-
maxEntries: params.maxEntries,
|
|
691
|
-
wouldPrune,
|
|
692
|
-
wouldCap
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
function wouldCapActiveSession(params) {
|
|
696
|
-
if (params.keys.length <= params.maxEntries) return false;
|
|
697
|
-
if (params.maxEntries <= 0) return true;
|
|
698
|
-
const activeUpdatedAt = getEntryUpdatedAt$1(params.activeEntry);
|
|
699
|
-
let newerOrTieBeforeActive = 0;
|
|
700
|
-
let seenActive = false;
|
|
701
|
-
for (const key of params.keys) {
|
|
702
|
-
if (key === params.activeSessionKey) {
|
|
703
|
-
seenActive = true;
|
|
704
|
-
continue;
|
|
705
|
-
}
|
|
706
|
-
const entryUpdatedAt = getEntryUpdatedAt$1(params.store[key]);
|
|
707
|
-
if (entryUpdatedAt > activeUpdatedAt || !seenActive && entryUpdatedAt === activeUpdatedAt) {
|
|
708
|
-
newerOrTieBeforeActive++;
|
|
709
|
-
if (newerOrTieBeforeActive >= params.maxEntries) return true;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
return false;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Cap the store to the N most recently updated entries.
|
|
716
|
-
* Entries without `updatedAt` are sorted last (removed first when over limit).
|
|
717
|
-
* Mutates `store` in-place.
|
|
718
|
-
*/
|
|
719
|
-
function capEntryCount(store, overrideMax, opts = {}) {
|
|
720
|
-
const maxEntries = overrideMax ?? resolveMaintenanceConfigFromInput().maxEntries;
|
|
721
|
-
const preservedCount = opts.preserveKeys ? Object.keys(store).filter((key) => opts.preserveKeys?.has(key)).length : 0;
|
|
722
|
-
const maxRemovableEntries = Math.max(0, maxEntries - preservedCount);
|
|
723
|
-
const keys = Object.keys(store).filter((key) => !opts.preserveKeys?.has(key));
|
|
724
|
-
if (keys.length <= maxRemovableEntries) return 0;
|
|
725
|
-
const toRemove = keys.toSorted((a, b) => {
|
|
726
|
-
const aTime = getEntryUpdatedAt$1(store[a]);
|
|
727
|
-
return getEntryUpdatedAt$1(store[b]) - aTime;
|
|
728
|
-
}).slice(maxRemovableEntries);
|
|
729
|
-
for (const key of toRemove) {
|
|
730
|
-
const entry = store[key];
|
|
731
|
-
if (entry) opts.onCapped?.({
|
|
732
|
-
key,
|
|
733
|
-
entry
|
|
734
|
-
});
|
|
735
|
-
delete store[key];
|
|
736
|
-
}
|
|
737
|
-
if (opts.log !== false) log$1.info("capped session entry count", {
|
|
738
|
-
removed: toRemove.length,
|
|
739
|
-
maxEntries
|
|
740
|
-
});
|
|
741
|
-
return toRemove.length;
|
|
742
|
-
}
|
|
743
|
-
async function getSessionFileSize(storePath) {
|
|
744
|
-
try {
|
|
745
|
-
return (await fs.promises.stat(storePath)).size;
|
|
746
|
-
} catch {
|
|
747
|
-
return null;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Rotate the sessions file if it exceeds the configured size threshold.
|
|
752
|
-
* Renames the current file to `sessions.json.bak.{timestamp}` and cleans up
|
|
753
|
-
* old rotation backups, keeping only the 3 most recent `.bak.*` files.
|
|
754
|
-
*/
|
|
755
|
-
async function rotateSessionFile(storePath, overrideBytes) {
|
|
756
|
-
const maxBytes = overrideBytes ?? resolveMaintenanceConfigFromInput().rotateBytes;
|
|
757
|
-
const fileSize = await getSessionFileSize(storePath);
|
|
758
|
-
if (fileSize == null) return false;
|
|
759
|
-
if (fileSize <= maxBytes) return false;
|
|
760
|
-
const backupPath = `${storePath}.bak.${Date.now()}`;
|
|
761
|
-
try {
|
|
762
|
-
await fs.promises.rename(storePath, backupPath);
|
|
763
|
-
log$1.info("rotated session store file", {
|
|
764
|
-
backupPath: path.basename(backupPath),
|
|
765
|
-
sizeBytes: fileSize
|
|
766
|
-
});
|
|
767
|
-
} catch {
|
|
768
|
-
return false;
|
|
769
|
-
}
|
|
770
|
-
try {
|
|
771
|
-
const dir = path.dirname(storePath);
|
|
772
|
-
const baseName = path.basename(storePath);
|
|
773
|
-
const backups = (await fs.promises.readdir(dir)).filter((f) => f.startsWith(`${baseName}.bak.`)).toSorted().toReversed();
|
|
774
|
-
const maxBackups = 3;
|
|
775
|
-
if (backups.length > maxBackups) {
|
|
776
|
-
const toDelete = backups.slice(maxBackups);
|
|
777
|
-
for (const old of toDelete) await fs.promises.unlink(path.join(dir, old)).catch(() => void 0);
|
|
778
|
-
log$1.info("cleaned up old session store backups", { deleted: toDelete.length });
|
|
779
|
-
}
|
|
780
|
-
} catch {}
|
|
781
|
-
return true;
|
|
782
|
-
}
|
|
783
|
-
function applySessionStoreMigrations(store) {
|
|
784
|
-
for (const entry of Object.values(store)) {
|
|
785
|
-
if (!entry || typeof entry !== "object") continue;
|
|
786
|
-
const rec = entry;
|
|
787
|
-
if (typeof rec.channel !== "string" && typeof rec.provider === "string") {
|
|
788
|
-
rec.channel = rec.provider;
|
|
789
|
-
delete rec.provider;
|
|
790
|
-
}
|
|
791
|
-
if (typeof rec.lastChannel !== "string" && typeof rec.lastProvider === "string") {
|
|
792
|
-
rec.lastChannel = rec.lastProvider;
|
|
793
|
-
delete rec.lastProvider;
|
|
794
|
-
}
|
|
795
|
-
if (typeof rec.groupChannel !== "string" && typeof rec.room === "string") {
|
|
796
|
-
rec.groupChannel = rec.room;
|
|
797
|
-
delete rec.room;
|
|
798
|
-
} else if ("room" in rec) delete rec.room;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
const log$2 = createSubsystemLogger("sessions/store");
|
|
802
|
-
function isSessionStoreRecord(value) {
|
|
803
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
804
|
-
}
|
|
805
|
-
function normalizeSessionEntryDelivery(entry) {
|
|
806
|
-
const normalized = normalizeSessionDeliveryFields({
|
|
807
|
-
channel: entry.channel,
|
|
808
|
-
lastChannel: entry.lastChannel,
|
|
809
|
-
lastTo: entry.lastTo,
|
|
810
|
-
lastAccountId: entry.lastAccountId,
|
|
811
|
-
lastThreadId: entry.lastThreadId ?? entry.deliveryContext?.threadId ?? entry.origin?.threadId,
|
|
812
|
-
deliveryContext: entry.deliveryContext
|
|
813
|
-
});
|
|
814
|
-
const nextDelivery = normalized.deliveryContext;
|
|
815
|
-
const sameDelivery = (entry.deliveryContext?.channel ?? void 0) === nextDelivery?.channel && (entry.deliveryContext?.to ?? void 0) === nextDelivery?.to && (entry.deliveryContext?.accountId ?? void 0) === nextDelivery?.accountId && (entry.deliveryContext?.threadId ?? void 0) === nextDelivery?.threadId;
|
|
816
|
-
const sameLast = entry.lastChannel === normalized.lastChannel && entry.lastTo === normalized.lastTo && entry.lastAccountId === normalized.lastAccountId && entry.lastThreadId === normalized.lastThreadId;
|
|
817
|
-
if (sameDelivery && sameLast) return entry;
|
|
818
|
-
return {
|
|
819
|
-
...entry,
|
|
820
|
-
deliveryContext: nextDelivery,
|
|
821
|
-
lastChannel: normalized.lastChannel,
|
|
822
|
-
lastTo: normalized.lastTo,
|
|
823
|
-
lastAccountId: normalized.lastAccountId,
|
|
824
|
-
lastThreadId: normalized.lastThreadId
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
function normalizeSessionStore(store) {
|
|
828
|
-
for (const [key, entry] of Object.entries(store)) {
|
|
829
|
-
if (!entry) continue;
|
|
830
|
-
const normalized = normalizeSessionEntryDelivery(normalizeSessionRuntimeModelFields(entry));
|
|
831
|
-
if (normalized !== entry) store[key] = normalized;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
function loadSessionStore(storePath, opts = {}) {
|
|
835
|
-
if (!opts.skipCache && isSessionStoreCacheEnabled()) {
|
|
836
|
-
const currentFileStat = getFileStatSnapshot(storePath);
|
|
837
|
-
const cached = readSessionStoreCache({
|
|
838
|
-
storePath,
|
|
839
|
-
mtimeMs: currentFileStat?.mtimeMs,
|
|
840
|
-
sizeBytes: currentFileStat?.sizeBytes
|
|
841
|
-
});
|
|
842
|
-
if (cached) return cached;
|
|
843
|
-
}
|
|
844
|
-
let store = {};
|
|
845
|
-
let fileStat = getFileStatSnapshot(storePath);
|
|
846
|
-
let mtimeMs = fileStat?.mtimeMs;
|
|
847
|
-
let serializedFromDisk;
|
|
848
|
-
const maxReadAttempts = process.platform === "win32" ? 3 : 1;
|
|
849
|
-
const retryBuf = maxReadAttempts > 1 ? new Int32Array(new SharedArrayBuffer(4)) : void 0;
|
|
850
|
-
for (let attempt = 0; attempt < maxReadAttempts; attempt += 1) try {
|
|
851
|
-
const raw = fs.readFileSync(storePath, "utf-8");
|
|
852
|
-
if (raw.length === 0 && attempt < maxReadAttempts - 1) {
|
|
853
|
-
Atomics.wait(retryBuf, 0, 0, 50);
|
|
854
|
-
continue;
|
|
855
|
-
}
|
|
856
|
-
const parsed = JSON.parse(raw);
|
|
857
|
-
if (isSessionStoreRecord(parsed)) {
|
|
858
|
-
store = parsed;
|
|
859
|
-
serializedFromDisk = raw;
|
|
860
|
-
}
|
|
861
|
-
fileStat = getFileStatSnapshot(storePath) ?? fileStat;
|
|
862
|
-
mtimeMs = fileStat?.mtimeMs;
|
|
863
|
-
break;
|
|
864
|
-
} catch {
|
|
865
|
-
if (attempt < maxReadAttempts - 1) {
|
|
866
|
-
Atomics.wait(retryBuf, 0, 0, 50);
|
|
867
|
-
continue;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
if (serializedFromDisk !== void 0) setSerializedSessionStore(storePath, serializedFromDisk);
|
|
871
|
-
else setSerializedSessionStore(storePath, void 0);
|
|
872
|
-
applySessionStoreMigrations(store);
|
|
873
|
-
normalizeSessionStore(store);
|
|
874
|
-
const maintenance = opts.maintenanceConfig ?? resolveMaintenanceConfigFromInput();
|
|
875
|
-
if (maintenance.mode === "enforce" && Object.keys(store).length > maintenance.maxEntries) {
|
|
876
|
-
const beforeCount = Object.keys(store).length;
|
|
877
|
-
const pruned = pruneStaleEntries(store, maintenance.pruneAfterMs, { log: false });
|
|
878
|
-
const capped = capEntryCount(store, maintenance.maxEntries, { log: false });
|
|
879
|
-
const afterCount = Object.keys(store).length;
|
|
880
|
-
if (pruned > 0 || capped > 0) {
|
|
881
|
-
serializedFromDisk = void 0;
|
|
882
|
-
setSerializedSessionStore(storePath, void 0);
|
|
883
|
-
log$2.info("applied load-time maintenance to oversized session store", {
|
|
884
|
-
storePath,
|
|
885
|
-
before: beforeCount,
|
|
886
|
-
after: afterCount,
|
|
887
|
-
pruned,
|
|
888
|
-
capped,
|
|
889
|
-
maxEntries: maintenance.maxEntries
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (!opts.skipCache && isSessionStoreCacheEnabled()) writeSessionStoreCache({
|
|
894
|
-
storePath,
|
|
895
|
-
store,
|
|
896
|
-
mtimeMs,
|
|
897
|
-
sizeBytes: fileStat?.sizeBytes,
|
|
898
|
-
serialized: serializedFromDisk
|
|
899
|
-
});
|
|
900
|
-
return structuredClone(store);
|
|
901
|
-
}
|
|
902
|
-
//#endregion
|
|
903
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/store-lock-state-DxRntb08.js
|
|
904
|
-
const LOCK_QUEUES = /* @__PURE__ */ new Map();
|
|
905
|
-
//#endregion
|
|
906
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/store-D_G4w--8.js
|
|
907
|
-
const NOOP_LOGGER = {
|
|
908
|
-
warn: () => {},
|
|
909
|
-
info: () => {}
|
|
910
|
-
};
|
|
911
|
-
function canonicalizePathForComparison(filePath) {
|
|
912
|
-
const resolved = path.resolve(filePath);
|
|
913
|
-
try {
|
|
914
|
-
return fs.realpathSync(resolved);
|
|
915
|
-
} catch {
|
|
916
|
-
return resolved;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
function measureStoreBytes(store) {
|
|
920
|
-
return Buffer.byteLength(JSON.stringify(store, null, 2), "utf-8");
|
|
921
|
-
}
|
|
922
|
-
function measureStoreEntryChunkBytes(key, entry) {
|
|
923
|
-
const singleEntryStore = JSON.stringify({ [key]: entry }, null, 2);
|
|
924
|
-
if (!singleEntryStore.startsWith("{\n") || !singleEntryStore.endsWith("\n}")) return measureStoreBytes({ [key]: entry }) - 4;
|
|
925
|
-
const chunk = singleEntryStore.slice(2, -2);
|
|
926
|
-
return Buffer.byteLength(chunk, "utf-8");
|
|
927
|
-
}
|
|
928
|
-
function buildStoreEntryChunkSizeMap(store) {
|
|
929
|
-
const out = /* @__PURE__ */ new Map();
|
|
930
|
-
for (const [key, entry] of Object.entries(store)) out.set(key, measureStoreEntryChunkBytes(key, entry));
|
|
931
|
-
return out;
|
|
932
|
-
}
|
|
933
|
-
function getEntryUpdatedAt(entry) {
|
|
934
|
-
if (!entry) return 0;
|
|
935
|
-
const updatedAt = entry.updatedAt;
|
|
936
|
-
return Number.isFinite(updatedAt) ? updatedAt : 0;
|
|
937
|
-
}
|
|
938
|
-
function buildSessionIdRefCounts(store) {
|
|
939
|
-
const counts = /* @__PURE__ */ new Map();
|
|
940
|
-
for (const entry of Object.values(store)) {
|
|
941
|
-
const sessionId = entry?.sessionId;
|
|
942
|
-
if (!sessionId) continue;
|
|
943
|
-
counts.set(sessionId, (counts.get(sessionId) ?? 0) + 1);
|
|
944
|
-
}
|
|
945
|
-
return counts;
|
|
946
|
-
}
|
|
947
|
-
function resolveSessionTranscriptPathForEntry(params) {
|
|
948
|
-
if (!params.entry.sessionId) return null;
|
|
949
|
-
try {
|
|
950
|
-
const resolved = resolveSessionFilePath(params.entry.sessionId, params.entry, { sessionsDir: params.sessionsDir });
|
|
951
|
-
const resolvedSessionsDir = canonicalizePathForComparison(params.sessionsDir);
|
|
952
|
-
const resolvedPath = canonicalizePathForComparison(resolved);
|
|
953
|
-
const relative = path.relative(resolvedSessionsDir, resolvedPath);
|
|
954
|
-
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return null;
|
|
955
|
-
return resolvedPath;
|
|
956
|
-
} catch {
|
|
957
|
-
return null;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
function resolveReferencedSessionTranscriptPaths(params) {
|
|
961
|
-
const referenced = /* @__PURE__ */ new Set();
|
|
962
|
-
for (const entry of Object.values(params.store)) {
|
|
963
|
-
const resolved = resolveSessionTranscriptPathForEntry({
|
|
964
|
-
sessionsDir: params.sessionsDir,
|
|
965
|
-
entry
|
|
966
|
-
});
|
|
967
|
-
if (resolved) referenced.add(canonicalizePathForComparison(resolved));
|
|
968
|
-
}
|
|
969
|
-
return referenced;
|
|
970
|
-
}
|
|
971
|
-
async function readSessionsDirFiles(sessionsDir) {
|
|
972
|
-
const dirEntries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
|
|
973
|
-
const files = [];
|
|
974
|
-
for (const dirent of dirEntries) {
|
|
975
|
-
if (!dirent.isFile()) continue;
|
|
976
|
-
const filePath = path.join(sessionsDir, dirent.name);
|
|
977
|
-
const stat = await fs.promises.stat(filePath).catch(() => null);
|
|
978
|
-
if (!stat?.isFile()) continue;
|
|
979
|
-
files.push({
|
|
980
|
-
path: filePath,
|
|
981
|
-
canonicalPath: canonicalizePathForComparison(filePath),
|
|
982
|
-
name: dirent.name,
|
|
983
|
-
size: stat.size,
|
|
984
|
-
mtimeMs: stat.mtimeMs
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
return files;
|
|
988
|
-
}
|
|
989
|
-
async function removeFileIfExists(filePath) {
|
|
990
|
-
const stat = await fs.promises.stat(filePath).catch(() => null);
|
|
991
|
-
if (!stat?.isFile()) return 0;
|
|
992
|
-
await fs.promises.rm(filePath, { force: true }).catch(() => void 0);
|
|
993
|
-
return stat.size;
|
|
994
|
-
}
|
|
995
|
-
async function removeFileForBudget(params) {
|
|
996
|
-
const resolvedPath = path.resolve(params.filePath);
|
|
997
|
-
const canonicalPath = params.canonicalPath ?? canonicalizePathForComparison(resolvedPath);
|
|
998
|
-
if (params.dryRun) {
|
|
999
|
-
if (params.simulatedRemovedPaths.has(canonicalPath)) return 0;
|
|
1000
|
-
const size = params.fileSizesByPath.get(canonicalPath) ?? 0;
|
|
1001
|
-
if (size <= 0) return 0;
|
|
1002
|
-
params.simulatedRemovedPaths.add(canonicalPath);
|
|
1003
|
-
return size;
|
|
1004
|
-
}
|
|
1005
|
-
return removeFileIfExists(resolvedPath);
|
|
1006
|
-
}
|
|
1007
|
-
async function enforceSessionDiskBudget(params) {
|
|
1008
|
-
const maxBytes = params.maintenance.maxDiskBytes;
|
|
1009
|
-
const highWaterBytes = params.maintenance.highWaterBytes;
|
|
1010
|
-
if (maxBytes == null || highWaterBytes == null) return null;
|
|
1011
|
-
const log = params.log ?? NOOP_LOGGER;
|
|
1012
|
-
const dryRun = params.dryRun === true;
|
|
1013
|
-
const sessionsDir = path.dirname(params.storePath);
|
|
1014
|
-
const files = await readSessionsDirFiles(sessionsDir);
|
|
1015
|
-
const fileSizesByPath = new Map(files.map((file) => [file.canonicalPath, file.size]));
|
|
1016
|
-
const simulatedRemovedPaths = /* @__PURE__ */ new Set();
|
|
1017
|
-
const resolvedStorePath = canonicalizePathForComparison(params.storePath);
|
|
1018
|
-
const storeFile = files.find((file) => file.canonicalPath === resolvedStorePath);
|
|
1019
|
-
let projectedStoreBytes = measureStoreBytes(params.store);
|
|
1020
|
-
let total = files.reduce((sum, file) => sum + file.size, 0) - (storeFile?.size ?? 0) + projectedStoreBytes;
|
|
1021
|
-
const totalBefore = total;
|
|
1022
|
-
if (total <= maxBytes) return {
|
|
1023
|
-
totalBytesBefore: totalBefore,
|
|
1024
|
-
totalBytesAfter: total,
|
|
1025
|
-
removedFiles: 0,
|
|
1026
|
-
removedEntries: 0,
|
|
1027
|
-
freedBytes: 0,
|
|
1028
|
-
maxBytes,
|
|
1029
|
-
highWaterBytes,
|
|
1030
|
-
overBudget: false
|
|
1031
|
-
};
|
|
1032
|
-
if (params.warnOnly) {
|
|
1033
|
-
log.warn("session disk budget exceeded (warn-only mode)", {
|
|
1034
|
-
sessionsDir,
|
|
1035
|
-
totalBytes: total,
|
|
1036
|
-
maxBytes,
|
|
1037
|
-
highWaterBytes
|
|
1038
|
-
});
|
|
1039
|
-
return {
|
|
1040
|
-
totalBytesBefore: totalBefore,
|
|
1041
|
-
totalBytesAfter: total,
|
|
1042
|
-
removedFiles: 0,
|
|
1043
|
-
removedEntries: 0,
|
|
1044
|
-
freedBytes: 0,
|
|
1045
|
-
maxBytes,
|
|
1046
|
-
highWaterBytes,
|
|
1047
|
-
overBudget: true
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
let removedFiles = 0;
|
|
1051
|
-
let removedEntries = 0;
|
|
1052
|
-
let freedBytes = 0;
|
|
1053
|
-
const referencedPaths = resolveReferencedSessionTranscriptPaths({
|
|
1054
|
-
sessionsDir,
|
|
1055
|
-
store: params.store
|
|
1056
|
-
});
|
|
1057
|
-
const removableFileQueue = files.filter((file) => isSessionArchiveArtifactName(file.name) || isPrimarySessionTranscriptFileName(file.name) && !referencedPaths.has(file.canonicalPath)).toSorted((a, b) => a.mtimeMs - b.mtimeMs);
|
|
1058
|
-
for (const file of removableFileQueue) {
|
|
1059
|
-
if (total <= highWaterBytes) break;
|
|
1060
|
-
const deletedBytes = await removeFileForBudget({
|
|
1061
|
-
filePath: file.path,
|
|
1062
|
-
canonicalPath: file.canonicalPath,
|
|
1063
|
-
dryRun,
|
|
1064
|
-
fileSizesByPath,
|
|
1065
|
-
simulatedRemovedPaths
|
|
1066
|
-
});
|
|
1067
|
-
if (deletedBytes <= 0) continue;
|
|
1068
|
-
total -= deletedBytes;
|
|
1069
|
-
freedBytes += deletedBytes;
|
|
1070
|
-
removedFiles += 1;
|
|
1071
|
-
}
|
|
1072
|
-
if (total > highWaterBytes) {
|
|
1073
|
-
const activeSessionKey = normalizeOptionalLowercaseString(params.activeSessionKey);
|
|
1074
|
-
const sessionIdRefCounts = buildSessionIdRefCounts(params.store);
|
|
1075
|
-
const entryChunkBytesByKey = buildStoreEntryChunkSizeMap(params.store);
|
|
1076
|
-
const keys = Object.keys(params.store).toSorted((a, b) => {
|
|
1077
|
-
return getEntryUpdatedAt(params.store[a]) - getEntryUpdatedAt(params.store[b]);
|
|
1078
|
-
});
|
|
1079
|
-
for (const key of keys) {
|
|
1080
|
-
if (total <= highWaterBytes) break;
|
|
1081
|
-
if (activeSessionKey && normalizeLowercaseStringOrEmpty(key) === activeSessionKey) continue;
|
|
1082
|
-
const entry = params.store[key];
|
|
1083
|
-
if (!entry) continue;
|
|
1084
|
-
const previousProjectedBytes = projectedStoreBytes;
|
|
1085
|
-
delete params.store[key];
|
|
1086
|
-
const chunkBytes = entryChunkBytesByKey.get(key);
|
|
1087
|
-
entryChunkBytesByKey.delete(key);
|
|
1088
|
-
if (typeof chunkBytes === "number" && Number.isFinite(chunkBytes) && chunkBytes >= 0) projectedStoreBytes = Math.max(2, projectedStoreBytes - (chunkBytes + 2));
|
|
1089
|
-
else projectedStoreBytes = measureStoreBytes(params.store);
|
|
1090
|
-
total += projectedStoreBytes - previousProjectedBytes;
|
|
1091
|
-
removedEntries += 1;
|
|
1092
|
-
const sessionId = entry.sessionId;
|
|
1093
|
-
if (!sessionId) continue;
|
|
1094
|
-
const nextRefCount = (sessionIdRefCounts.get(sessionId) ?? 1) - 1;
|
|
1095
|
-
if (nextRefCount > 0) {
|
|
1096
|
-
sessionIdRefCounts.set(sessionId, nextRefCount);
|
|
1097
|
-
continue;
|
|
1098
|
-
}
|
|
1099
|
-
sessionIdRefCounts.delete(sessionId);
|
|
1100
|
-
const transcriptPath = resolveSessionTranscriptPathForEntry({
|
|
1101
|
-
sessionsDir,
|
|
1102
|
-
entry
|
|
1103
|
-
});
|
|
1104
|
-
if (!transcriptPath) continue;
|
|
1105
|
-
const deletedBytes = await removeFileForBudget({
|
|
1106
|
-
filePath: transcriptPath,
|
|
1107
|
-
dryRun,
|
|
1108
|
-
fileSizesByPath,
|
|
1109
|
-
simulatedRemovedPaths
|
|
1110
|
-
});
|
|
1111
|
-
if (deletedBytes <= 0) continue;
|
|
1112
|
-
total -= deletedBytes;
|
|
1113
|
-
freedBytes += deletedBytes;
|
|
1114
|
-
removedFiles += 1;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (!dryRun) {
|
|
1118
|
-
if (total > highWaterBytes) log.warn("session disk budget still above high-water target after cleanup", {
|
|
1119
|
-
sessionsDir,
|
|
1120
|
-
totalBytes: total,
|
|
1121
|
-
maxBytes,
|
|
1122
|
-
highWaterBytes,
|
|
1123
|
-
removedFiles,
|
|
1124
|
-
removedEntries
|
|
1125
|
-
});
|
|
1126
|
-
else if (removedFiles > 0 || removedEntries > 0) log.info("applied session disk budget cleanup", {
|
|
1127
|
-
sessionsDir,
|
|
1128
|
-
totalBytesBefore: totalBefore,
|
|
1129
|
-
totalBytesAfter: total,
|
|
1130
|
-
maxBytes,
|
|
1131
|
-
highWaterBytes,
|
|
1132
|
-
removedFiles,
|
|
1133
|
-
removedEntries
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
return {
|
|
1137
|
-
totalBytesBefore: totalBefore,
|
|
1138
|
-
totalBytesAfter: total,
|
|
1139
|
-
removedFiles,
|
|
1140
|
-
removedEntries,
|
|
1141
|
-
freedBytes,
|
|
1142
|
-
maxBytes,
|
|
1143
|
-
highWaterBytes,
|
|
1144
|
-
overBudget: true
|
|
1145
|
-
};
|
|
1146
|
-
}
|
|
1147
|
-
function normalizeStoreSessionKey(sessionKey) {
|
|
1148
|
-
return normalizeLowercaseStringOrEmpty(sessionKey);
|
|
1149
|
-
}
|
|
1150
|
-
function resolveMaintenanceConfig() {
|
|
1151
|
-
let maintenance;
|
|
1152
|
-
try {
|
|
1153
|
-
maintenance = loadConfig().session?.maintenance;
|
|
1154
|
-
} catch {}
|
|
1155
|
-
return resolveMaintenanceConfigFromInput(maintenance);
|
|
1156
|
-
}
|
|
1157
|
-
const log = createSubsystemLogger("sessions/store");
|
|
1158
|
-
let sessionArchiveRuntimePromise = null;
|
|
1159
|
-
let sessionWriteLockAcquirerForTests = null;
|
|
1160
|
-
function loadSessionArchiveRuntime() {
|
|
1161
|
-
sessionArchiveRuntimePromise ??= import("./session-archive.runtime-B52_5vs4-BG-_IwNJ.mjs");
|
|
1162
|
-
return sessionArchiveRuntimePromise;
|
|
1163
|
-
}
|
|
1164
|
-
function updateSessionStoreWriteCaches(params) {
|
|
1165
|
-
const fileStat = getFileStatSnapshot(params.storePath);
|
|
1166
|
-
setSerializedSessionStore(params.storePath, params.serialized);
|
|
1167
|
-
if (!isSessionStoreCacheEnabled()) {
|
|
1168
|
-
dropSessionStoreObjectCache(params.storePath);
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
writeSessionStoreCache({
|
|
1172
|
-
storePath: params.storePath,
|
|
1173
|
-
store: params.store,
|
|
1174
|
-
mtimeMs: fileStat?.mtimeMs,
|
|
1175
|
-
sizeBytes: fileStat?.sizeBytes,
|
|
1176
|
-
serialized: params.serialized
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
function resolveMutableSessionStoreKey(store, sessionKey) {
|
|
1180
|
-
const trimmed = sessionKey.trim();
|
|
1181
|
-
if (!trimmed) return;
|
|
1182
|
-
if (Object.prototype.hasOwnProperty.call(store, trimmed)) return trimmed;
|
|
1183
|
-
const normalized = normalizeStoreSessionKey(trimmed);
|
|
1184
|
-
if (Object.prototype.hasOwnProperty.call(store, normalized)) return normalized;
|
|
1185
|
-
return Object.keys(store).find((key) => normalizeStoreSessionKey(key) === normalized);
|
|
1186
|
-
}
|
|
1187
|
-
function collectAcpMetadataSnapshot(store) {
|
|
1188
|
-
const snapshot = /* @__PURE__ */ new Map();
|
|
1189
|
-
for (const [sessionKey, entry] of Object.entries(store)) if (entry?.acp) snapshot.set(sessionKey, entry.acp);
|
|
1190
|
-
return snapshot;
|
|
1191
|
-
}
|
|
1192
|
-
function preserveExistingAcpMetadata(params) {
|
|
1193
|
-
const allowDrop = new Set((params.allowDropSessionKeys ?? []).map((key) => normalizeStoreSessionKey(key)));
|
|
1194
|
-
for (const [previousKey, previousAcp] of params.previousAcpByKey.entries()) {
|
|
1195
|
-
const normalizedKey = normalizeStoreSessionKey(previousKey);
|
|
1196
|
-
if (allowDrop.has(normalizedKey)) continue;
|
|
1197
|
-
const nextKey = resolveMutableSessionStoreKey(params.nextStore, previousKey);
|
|
1198
|
-
if (!nextKey) continue;
|
|
1199
|
-
const nextEntry = params.nextStore[nextKey];
|
|
1200
|
-
if (!nextEntry || nextEntry.acp) continue;
|
|
1201
|
-
params.nextStore[nextKey] = {
|
|
1202
|
-
...nextEntry,
|
|
1203
|
-
acp: previousAcp
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
async function saveSessionStoreUnlocked(storePath, store, opts) {
|
|
1208
|
-
normalizeSessionStore(store);
|
|
1209
|
-
if (!opts?.skipMaintenance) {
|
|
1210
|
-
const maintenance = opts?.maintenanceConfig ? {
|
|
1211
|
-
...opts.maintenanceConfig,
|
|
1212
|
-
...opts?.maintenanceOverride
|
|
1213
|
-
} : {
|
|
1214
|
-
...resolveMaintenanceConfig(),
|
|
1215
|
-
...opts?.maintenanceOverride
|
|
1216
|
-
};
|
|
1217
|
-
const shouldWarnOnly = maintenance.mode === "warn";
|
|
1218
|
-
const beforeCount = Object.keys(store).length;
|
|
1219
|
-
if (shouldWarnOnly) {
|
|
1220
|
-
const activeSessionKey = opts?.activeSessionKey?.trim();
|
|
1221
|
-
if (activeSessionKey) {
|
|
1222
|
-
const warning = getActiveSessionMaintenanceWarning({
|
|
1223
|
-
store,
|
|
1224
|
-
activeSessionKey,
|
|
1225
|
-
pruneAfterMs: maintenance.pruneAfterMs,
|
|
1226
|
-
maxEntries: maintenance.maxEntries
|
|
1227
|
-
});
|
|
1228
|
-
if (warning) {
|
|
1229
|
-
log.warn("session maintenance would evict active session; skipping enforcement", {
|
|
1230
|
-
activeSessionKey: warning.activeSessionKey,
|
|
1231
|
-
wouldPrune: warning.wouldPrune,
|
|
1232
|
-
wouldCap: warning.wouldCap,
|
|
1233
|
-
pruneAfterMs: warning.pruneAfterMs,
|
|
1234
|
-
maxEntries: warning.maxEntries
|
|
1235
|
-
});
|
|
1236
|
-
await opts?.onWarn?.(warning);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
const diskBudget = await enforceSessionDiskBudget({
|
|
1240
|
-
store,
|
|
1241
|
-
storePath,
|
|
1242
|
-
activeSessionKey: opts?.activeSessionKey,
|
|
1243
|
-
maintenance,
|
|
1244
|
-
warnOnly: true,
|
|
1245
|
-
log
|
|
1246
|
-
});
|
|
1247
|
-
await opts?.onMaintenanceApplied?.({
|
|
1248
|
-
mode: maintenance.mode,
|
|
1249
|
-
beforeCount,
|
|
1250
|
-
afterCount: Object.keys(store).length,
|
|
1251
|
-
pruned: 0,
|
|
1252
|
-
capped: 0,
|
|
1253
|
-
diskBudget
|
|
1254
|
-
});
|
|
1255
|
-
} else {
|
|
1256
|
-
const preserveSessionKeys = opts?.activeSessionKey ? new Set([opts.activeSessionKey]) : void 0;
|
|
1257
|
-
const removedSessionFiles = /* @__PURE__ */ new Map();
|
|
1258
|
-
const pruned = pruneStaleEntries(store, maintenance.pruneAfterMs, {
|
|
1259
|
-
onPruned: ({ entry }) => {
|
|
1260
|
-
rememberRemovedSessionFile(removedSessionFiles, entry);
|
|
1261
|
-
},
|
|
1262
|
-
preserveKeys: preserveSessionKeys
|
|
1263
|
-
});
|
|
1264
|
-
const capped = capEntryCount(store, maintenance.maxEntries, {
|
|
1265
|
-
onCapped: ({ entry }) => {
|
|
1266
|
-
rememberRemovedSessionFile(removedSessionFiles, entry);
|
|
1267
|
-
},
|
|
1268
|
-
preserveKeys: preserveSessionKeys
|
|
1269
|
-
});
|
|
1270
|
-
const archivedDirs = /* @__PURE__ */ new Set();
|
|
1271
|
-
const archivedForDeletedSessions = await archiveRemovedSessionTranscripts({
|
|
1272
|
-
removedSessionFiles,
|
|
1273
|
-
referencedSessionIds: new Set(Object.values(store).map((entry) => entry?.sessionId).filter((id) => Boolean(id))),
|
|
1274
|
-
storePath,
|
|
1275
|
-
reason: "deleted",
|
|
1276
|
-
restrictToStoreDir: true
|
|
1277
|
-
});
|
|
1278
|
-
for (const archivedDir of archivedForDeletedSessions) archivedDirs.add(archivedDir);
|
|
1279
|
-
if (archivedDirs.size > 0 || maintenance.resetArchiveRetentionMs != null) {
|
|
1280
|
-
const { cleanupArchivedSessionTranscripts } = await loadSessionArchiveRuntime();
|
|
1281
|
-
const targetDirs = archivedDirs.size > 0 ? [...archivedDirs] : [path.dirname(path.resolve(storePath))];
|
|
1282
|
-
await cleanupArchivedSessionTranscripts({
|
|
1283
|
-
directories: targetDirs,
|
|
1284
|
-
olderThanMs: maintenance.pruneAfterMs,
|
|
1285
|
-
reason: "deleted"
|
|
1286
|
-
});
|
|
1287
|
-
if (maintenance.resetArchiveRetentionMs != null) await cleanupArchivedSessionTranscripts({
|
|
1288
|
-
directories: targetDirs,
|
|
1289
|
-
olderThanMs: maintenance.resetArchiveRetentionMs,
|
|
1290
|
-
reason: "reset"
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
await rotateSessionFile(storePath, maintenance.rotateBytes);
|
|
1294
|
-
const diskBudget = await enforceSessionDiskBudget({
|
|
1295
|
-
store,
|
|
1296
|
-
storePath,
|
|
1297
|
-
activeSessionKey: opts?.activeSessionKey,
|
|
1298
|
-
maintenance,
|
|
1299
|
-
warnOnly: false,
|
|
1300
|
-
log
|
|
1301
|
-
});
|
|
1302
|
-
await opts?.onMaintenanceApplied?.({
|
|
1303
|
-
mode: maintenance.mode,
|
|
1304
|
-
beforeCount,
|
|
1305
|
-
afterCount: Object.keys(store).length,
|
|
1306
|
-
pruned,
|
|
1307
|
-
capped,
|
|
1308
|
-
diskBudget
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
await fs.promises.mkdir(path.dirname(storePath), { recursive: true });
|
|
1313
|
-
const json = JSON.stringify(store, null, 2);
|
|
1314
|
-
if (getSerializedSessionStore(storePath) === json) {
|
|
1315
|
-
updateSessionStoreWriteCaches({
|
|
1316
|
-
storePath,
|
|
1317
|
-
store,
|
|
1318
|
-
serialized: json
|
|
1319
|
-
});
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
if (process.platform === "win32") {
|
|
1323
|
-
for (let i = 0; i < 5; i++) try {
|
|
1324
|
-
await writeSessionStoreAtomic({
|
|
1325
|
-
storePath,
|
|
1326
|
-
store,
|
|
1327
|
-
serialized: json
|
|
1328
|
-
});
|
|
1329
|
-
return;
|
|
1330
|
-
} catch (err) {
|
|
1331
|
-
if (getErrorCode(err) === "ENOENT") return;
|
|
1332
|
-
if (i < 4) {
|
|
1333
|
-
await new Promise((r) => setTimeout(r, 50 * (i + 1)));
|
|
1334
|
-
continue;
|
|
1335
|
-
}
|
|
1336
|
-
log.warn(`atomic write failed after 5 attempts: ${storePath}`);
|
|
1337
|
-
}
|
|
1338
|
-
return;
|
|
1339
|
-
}
|
|
1340
|
-
try {
|
|
1341
|
-
await writeSessionStoreAtomic({
|
|
1342
|
-
storePath,
|
|
1343
|
-
store,
|
|
1344
|
-
serialized: json
|
|
1345
|
-
});
|
|
1346
|
-
} catch (err) {
|
|
1347
|
-
if (getErrorCode(err) === "ENOENT") {
|
|
1348
|
-
try {
|
|
1349
|
-
await writeSessionStoreAtomic({
|
|
1350
|
-
storePath,
|
|
1351
|
-
store,
|
|
1352
|
-
serialized: json
|
|
1353
|
-
});
|
|
1354
|
-
} catch (err2) {
|
|
1355
|
-
if (getErrorCode(err2) === "ENOENT") return;
|
|
1356
|
-
throw err2;
|
|
1357
|
-
}
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
throw err;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
async function updateSessionStore(storePath, mutator, opts) {
|
|
1364
|
-
return await withSessionStoreLock(storePath, async () => {
|
|
1365
|
-
const store = loadSessionStore(storePath, { skipCache: true });
|
|
1366
|
-
const previousAcpByKey = collectAcpMetadataSnapshot(store);
|
|
1367
|
-
const result = await mutator(store);
|
|
1368
|
-
preserveExistingAcpMetadata({
|
|
1369
|
-
previousAcpByKey,
|
|
1370
|
-
nextStore: store,
|
|
1371
|
-
allowDropSessionKeys: opts?.allowDropAcpMetaSessionKeys
|
|
1372
|
-
});
|
|
1373
|
-
await saveSessionStoreUnlocked(storePath, store, opts);
|
|
1374
|
-
return result;
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
|
-
const SESSION_STORE_LOCK_MIN_HOLD_MS = 5e3;
|
|
1378
|
-
const SESSION_STORE_LOCK_TIMEOUT_GRACE_MS = 5e3;
|
|
1379
|
-
function getErrorCode(error) {
|
|
1380
|
-
if (!error || typeof error !== "object" || !("code" in error)) return null;
|
|
1381
|
-
return String(error.code);
|
|
1382
|
-
}
|
|
1383
|
-
function rememberRemovedSessionFile(removedSessionFiles, entry) {
|
|
1384
|
-
if (!removedSessionFiles.has(entry.sessionId) || entry.sessionFile) removedSessionFiles.set(entry.sessionId, entry.sessionFile);
|
|
1385
|
-
}
|
|
1386
|
-
async function archiveRemovedSessionTranscripts(params) {
|
|
1387
|
-
const { archiveSessionTranscripts } = await loadSessionArchiveRuntime();
|
|
1388
|
-
const archivedDirs = /* @__PURE__ */ new Set();
|
|
1389
|
-
for (const [sessionId, sessionFile] of params.removedSessionFiles) {
|
|
1390
|
-
if (params.referencedSessionIds.has(sessionId)) continue;
|
|
1391
|
-
const archived = archiveSessionTranscripts({
|
|
1392
|
-
sessionId,
|
|
1393
|
-
storePath: params.storePath,
|
|
1394
|
-
sessionFile,
|
|
1395
|
-
reason: params.reason,
|
|
1396
|
-
restrictToStoreDir: params.restrictToStoreDir
|
|
1397
|
-
});
|
|
1398
|
-
for (const archivedPath of archived) archivedDirs.add(path.dirname(archivedPath));
|
|
1399
|
-
}
|
|
1400
|
-
return archivedDirs;
|
|
1401
|
-
}
|
|
1402
|
-
async function writeSessionStoreAtomic(params) {
|
|
1403
|
-
await writeTextAtomic(params.storePath, params.serialized, { mode: 384 });
|
|
1404
|
-
updateSessionStoreWriteCaches({
|
|
1405
|
-
storePath: params.storePath,
|
|
1406
|
-
store: params.store,
|
|
1407
|
-
serialized: params.serialized
|
|
1408
|
-
});
|
|
1409
|
-
}
|
|
1410
|
-
function lockTimeoutError(storePath) {
|
|
1411
|
-
return /* @__PURE__ */ new Error(`timeout waiting for session store lock: ${storePath}`);
|
|
1412
|
-
}
|
|
1413
|
-
function resolveSessionStoreLockMaxHoldMs(timeoutMs) {
|
|
1414
|
-
if (timeoutMs == null || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return;
|
|
1415
|
-
return resolveSessionLockMaxHoldFromTimeout({
|
|
1416
|
-
timeoutMs,
|
|
1417
|
-
graceMs: SESSION_STORE_LOCK_TIMEOUT_GRACE_MS,
|
|
1418
|
-
minMs: SESSION_STORE_LOCK_MIN_HOLD_MS
|
|
1419
|
-
});
|
|
1420
|
-
}
|
|
1421
|
-
function getOrCreateLockQueue(storePath) {
|
|
1422
|
-
const existing = LOCK_QUEUES.get(storePath);
|
|
1423
|
-
if (existing) return existing;
|
|
1424
|
-
const created = {
|
|
1425
|
-
running: false,
|
|
1426
|
-
pending: [],
|
|
1427
|
-
drainPromise: null
|
|
1428
|
-
};
|
|
1429
|
-
LOCK_QUEUES.set(storePath, created);
|
|
1430
|
-
return created;
|
|
1431
|
-
}
|
|
1432
|
-
async function drainSessionStoreLockQueue(storePath) {
|
|
1433
|
-
const queue = LOCK_QUEUES.get(storePath);
|
|
1434
|
-
if (!queue) return;
|
|
1435
|
-
if (queue.drainPromise) {
|
|
1436
|
-
await queue.drainPromise;
|
|
1437
|
-
return;
|
|
1438
|
-
}
|
|
1439
|
-
queue.running = true;
|
|
1440
|
-
queue.drainPromise = (async () => {
|
|
1441
|
-
try {
|
|
1442
|
-
while (queue.pending.length > 0) {
|
|
1443
|
-
const task = queue.pending.shift();
|
|
1444
|
-
if (!task) continue;
|
|
1445
|
-
const remainingTimeoutMs = task.timeoutMs ?? Number.POSITIVE_INFINITY;
|
|
1446
|
-
if (task.timeoutMs != null && remainingTimeoutMs <= 0) {
|
|
1447
|
-
task.reject(lockTimeoutError(storePath));
|
|
1448
|
-
continue;
|
|
1449
|
-
}
|
|
1450
|
-
let lock;
|
|
1451
|
-
let result;
|
|
1452
|
-
let failed;
|
|
1453
|
-
let hasFailure = false;
|
|
1454
|
-
try {
|
|
1455
|
-
lock = await (sessionWriteLockAcquirerForTests ?? acquireSessionWriteLock)({
|
|
1456
|
-
sessionFile: storePath,
|
|
1457
|
-
timeoutMs: remainingTimeoutMs,
|
|
1458
|
-
staleMs: task.staleMs,
|
|
1459
|
-
maxHoldMs: resolveSessionStoreLockMaxHoldMs(task.timeoutMs)
|
|
1460
|
-
});
|
|
1461
|
-
result = await task.fn();
|
|
1462
|
-
} catch (err) {
|
|
1463
|
-
hasFailure = true;
|
|
1464
|
-
failed = err;
|
|
1465
|
-
} finally {
|
|
1466
|
-
await lock?.release().catch(() => void 0);
|
|
1467
|
-
}
|
|
1468
|
-
if (hasFailure) {
|
|
1469
|
-
task.reject(failed);
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
task.resolve(result);
|
|
1473
|
-
}
|
|
1474
|
-
} finally {
|
|
1475
|
-
queue.running = false;
|
|
1476
|
-
queue.drainPromise = null;
|
|
1477
|
-
if (queue.pending.length === 0) LOCK_QUEUES.delete(storePath);
|
|
1478
|
-
else queueMicrotask(() => {
|
|
1479
|
-
drainSessionStoreLockQueue(storePath);
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1482
|
-
})();
|
|
1483
|
-
await queue.drainPromise;
|
|
1484
|
-
}
|
|
1485
|
-
async function withSessionStoreLock(storePath, fn, opts = {}) {
|
|
1486
|
-
if (!storePath || typeof storePath !== "string") throw new Error(`withSessionStoreLock: storePath must be a non-empty string, got ${JSON.stringify(storePath)}`);
|
|
1487
|
-
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
1488
|
-
const staleMs = opts.staleMs ?? 3e4;
|
|
1489
|
-
opts.pollIntervalMs;
|
|
1490
|
-
const hasTimeout = timeoutMs > 0 && Number.isFinite(timeoutMs);
|
|
1491
|
-
const queue = getOrCreateLockQueue(storePath);
|
|
1492
|
-
return await new Promise((resolve, reject) => {
|
|
1493
|
-
const task = {
|
|
1494
|
-
fn: async () => await fn(),
|
|
1495
|
-
resolve: (value) => resolve(value),
|
|
1496
|
-
reject,
|
|
1497
|
-
timeoutMs: hasTimeout ? timeoutMs : void 0,
|
|
1498
|
-
staleMs
|
|
1499
|
-
};
|
|
1500
|
-
queue.pending.push(task);
|
|
1501
|
-
drainSessionStoreLockQueue(storePath);
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
|
-
//#endregion
|
|
1505
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/facade-runtime-DJ_ZWLEP.js
|
|
1506
|
-
function createLazyFacadeValue(loadFacadeModule, key) {
|
|
1507
|
-
return ((...args) => {
|
|
1508
|
-
const value = loadFacadeModule()[key];
|
|
1509
|
-
if (typeof value !== "function") return value;
|
|
1510
|
-
return value(...args);
|
|
1511
|
-
});
|
|
1512
|
-
}
|
|
1513
|
-
const OPENCLAW_PACKAGE_ROOT = resolveLoaderPackageRoot({
|
|
1514
|
-
modulePath: fileURLToPath(import.meta.url),
|
|
1515
|
-
moduleUrl: import.meta.url
|
|
1516
|
-
}) ?? fileURLToPath(new URL("../..", import.meta.url));
|
|
1517
|
-
const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url);
|
|
1518
|
-
const OPENCLAW_SOURCE_EXTENSIONS_ROOT = path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions");
|
|
1519
|
-
const cachedFacadeModuleLocationsByKey = /* @__PURE__ */ new Map();
|
|
1520
|
-
function createFacadeResolutionKey(params) {
|
|
1521
|
-
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
|
1522
|
-
return createFacadeResolutionKey$1({
|
|
1523
|
-
...params,
|
|
1524
|
-
bundledPluginsDir
|
|
1525
|
-
});
|
|
1526
|
-
}
|
|
1527
|
-
function resolveRegistryPluginModuleLocation(params) {
|
|
1528
|
-
return loadFacadeActivationCheckRuntime().resolveRegistryPluginModuleLocation({
|
|
1529
|
-
...params,
|
|
1530
|
-
resolutionKey: createFacadeResolutionKey(params)
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
function resolveFacadeModuleLocationUncached(params) {
|
|
1534
|
-
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
|
1535
|
-
const bundledLocation = resolveBundledFacadeModuleLocation({
|
|
1536
|
-
...params,
|
|
1537
|
-
currentModulePath: CURRENT_MODULE_PATH,
|
|
1538
|
-
packageRoot: OPENCLAW_PACKAGE_ROOT,
|
|
1539
|
-
bundledPluginsDir
|
|
1540
|
-
});
|
|
1541
|
-
if (bundledLocation) return bundledLocation;
|
|
1542
|
-
return resolveRegistryPluginModuleLocation(params);
|
|
1543
|
-
}
|
|
1544
|
-
function resolveFacadeModuleLocation(params) {
|
|
1545
|
-
return resolveCachedFacadeModuleLocation({
|
|
1546
|
-
cache: cachedFacadeModuleLocationsByKey,
|
|
1547
|
-
key: createFacadeResolutionKey(params),
|
|
1548
|
-
resolve: () => resolveFacadeModuleLocationUncached(params)
|
|
1549
|
-
});
|
|
1550
|
-
}
|
|
1551
|
-
const nodeRequire = createRequire(import.meta.url);
|
|
1552
|
-
const FACADE_ACTIVATION_CHECK_RUNTIME_CANDIDATES = ["./facade-activation-check.runtime.js", "./facade-activation-check.runtime.ts"];
|
|
1553
|
-
let facadeActivationCheckRuntimeModule;
|
|
1554
|
-
const facadeActivationCheckRuntimeJitiLoaders = /* @__PURE__ */ new Map();
|
|
1555
|
-
function getFacadeActivationCheckRuntimeJiti(modulePath) {
|
|
1556
|
-
return getCachedPluginJitiLoader({
|
|
1557
|
-
cache: facadeActivationCheckRuntimeJitiLoaders,
|
|
1558
|
-
modulePath,
|
|
1559
|
-
importerUrl: import.meta.url,
|
|
1560
|
-
jitiFilename: import.meta.url,
|
|
1561
|
-
aliasMap: {},
|
|
1562
|
-
tryNative: false
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1565
|
-
function loadFacadeActivationCheckRuntimeFromCandidates(loadCandidate) {
|
|
1566
|
-
for (const candidate of FACADE_ACTIVATION_CHECK_RUNTIME_CANDIDATES) try {
|
|
1567
|
-
return loadCandidate(candidate);
|
|
1568
|
-
} catch {}
|
|
1569
|
-
}
|
|
1570
|
-
function loadFacadeActivationCheckRuntime() {
|
|
1571
|
-
if (facadeActivationCheckRuntimeModule) return facadeActivationCheckRuntimeModule;
|
|
1572
|
-
facadeActivationCheckRuntimeModule = loadFacadeActivationCheckRuntimeFromCandidates((candidate) => nodeRequire(candidate));
|
|
1573
|
-
if (facadeActivationCheckRuntimeModule) return facadeActivationCheckRuntimeModule;
|
|
1574
|
-
facadeActivationCheckRuntimeModule = loadFacadeActivationCheckRuntimeFromCandidates((candidate) => getFacadeActivationCheckRuntimeJiti(candidate)(candidate));
|
|
1575
|
-
if (facadeActivationCheckRuntimeModule) return facadeActivationCheckRuntimeModule;
|
|
1576
|
-
throw new Error("Unable to load facade activation check runtime");
|
|
1577
|
-
}
|
|
1578
|
-
function loadFacadeModuleAtLocationSync(params) {
|
|
1579
|
-
return loadFacadeModuleAtLocationSync$1(params);
|
|
1580
|
-
}
|
|
1581
|
-
function buildFacadeActivationCheckParams(params, location = resolveFacadeModuleLocation(params)) {
|
|
1582
|
-
return {
|
|
1583
|
-
...params,
|
|
1584
|
-
location,
|
|
1585
|
-
sourceExtensionsRoot: OPENCLAW_SOURCE_EXTENSIONS_ROOT,
|
|
1586
|
-
resolutionKey: createFacadeResolutionKey(params)
|
|
1587
|
-
};
|
|
1588
|
-
}
|
|
1589
|
-
function loadBundledPluginPublicSurfaceModuleSync(params) {
|
|
1590
|
-
const location = resolveFacadeModuleLocation(params);
|
|
1591
|
-
const trackedPluginId = () => loadFacadeActivationCheckRuntime().resolveTrackedFacadePluginId(buildFacadeActivationCheckParams(params, location));
|
|
1592
|
-
if (!location) return loadBundledPluginPublicSurfaceModuleSync$1({
|
|
1593
|
-
...params,
|
|
1594
|
-
trackedPluginId
|
|
1595
|
-
});
|
|
1596
|
-
return loadFacadeModuleAtLocationSync({
|
|
1597
|
-
location,
|
|
1598
|
-
trackedPluginId
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
function loadActivatedBundledPluginPublicSurfaceModuleSync(params) {
|
|
1602
|
-
loadFacadeActivationCheckRuntime().resolveActivatedBundledPluginPublicSurfaceAccessOrThrow(buildFacadeActivationCheckParams(params));
|
|
1603
|
-
return loadBundledPluginPublicSurfaceModuleSync(params);
|
|
1604
|
-
}
|
|
1605
|
-
function tryLoadActivatedBundledPluginPublicSurfaceModuleSync(params) {
|
|
1606
|
-
if (!loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess(buildFacadeActivationCheckParams(params)).allowed) return null;
|
|
1607
|
-
return loadBundledPluginPublicSurfaceModuleSync(params);
|
|
1608
|
-
}
|
|
1609
|
-
//#endregion
|
|
1610
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/session-conversation-DOWP59Dq.js
|
|
1611
|
-
const SESSION_KEY_API_ARTIFACT_BASENAME = "session-key-api.js";
|
|
1612
|
-
const bundledSessionConversationFallbackCache = /* @__PURE__ */ new Map();
|
|
1613
|
-
function normalizeResolvedChannel(channel) {
|
|
1614
|
-
return normalizeChannelId$1(channel) ?? normalizeChannelId(channel) ?? normalizeOptionalLowercaseString(channel) ?? "";
|
|
1615
|
-
}
|
|
1616
|
-
function getMessagingAdapter(channel) {
|
|
1617
|
-
const normalizedChannel = normalizeResolvedChannel(channel);
|
|
1618
|
-
try {
|
|
1619
|
-
return getLoadedChannelPlugin(normalizedChannel)?.messaging;
|
|
1620
|
-
} catch {
|
|
1621
|
-
return;
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
function dedupeConversationIds(values) {
|
|
1625
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1626
|
-
const resolved = [];
|
|
1627
|
-
for (const value of values) {
|
|
1628
|
-
if (typeof value !== "string") continue;
|
|
1629
|
-
const trimmed = value.trim();
|
|
1630
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
1631
|
-
seen.add(trimmed);
|
|
1632
|
-
resolved.push(trimmed);
|
|
1633
|
-
}
|
|
1634
|
-
return resolved;
|
|
1635
|
-
}
|
|
1636
|
-
function buildGenericConversationResolution(rawId) {
|
|
1637
|
-
const trimmed = rawId.trim();
|
|
1638
|
-
if (!trimmed) return null;
|
|
1639
|
-
const parsed = parseThreadSessionSuffix(trimmed);
|
|
1640
|
-
const id = (parsed.baseSessionKey ?? trimmed).trim();
|
|
1641
|
-
if (!id) return null;
|
|
1642
|
-
return {
|
|
1643
|
-
id,
|
|
1644
|
-
threadId: parsed.threadId,
|
|
1645
|
-
baseConversationId: id,
|
|
1646
|
-
parentConversationCandidates: dedupeConversationIds(parsed.threadId ? [parsed.baseSessionKey] : [])
|
|
1647
|
-
};
|
|
1648
|
-
}
|
|
1649
|
-
function normalizeSessionConversationResolution(resolved) {
|
|
1650
|
-
if (!resolved?.id?.trim()) return null;
|
|
1651
|
-
return {
|
|
1652
|
-
id: resolved.id.trim(),
|
|
1653
|
-
threadId: normalizeOptionalString(resolved.threadId),
|
|
1654
|
-
baseConversationId: normalizeOptionalString(resolved.baseConversationId) ?? dedupeConversationIds(resolved.parentConversationCandidates ?? []).at(-1) ?? resolved.id.trim(),
|
|
1655
|
-
parentConversationCandidates: dedupeConversationIds(resolved.parentConversationCandidates ?? []),
|
|
1656
|
-
hasExplicitParentConversationCandidates: Object.hasOwn(resolved, "parentConversationCandidates")
|
|
1657
|
-
};
|
|
1658
|
-
}
|
|
1659
|
-
function resolveBundledSessionConversationFallback(params) {
|
|
1660
|
-
if (isBundledSessionConversationFallbackDisabled(params.channel)) return null;
|
|
1661
|
-
const dirName = normalizeResolvedChannel(params.channel);
|
|
1662
|
-
const version = getActivePluginChannelRegistryVersion();
|
|
1663
|
-
let cached = bundledSessionConversationFallbackCache.get(dirName);
|
|
1664
|
-
if (!cached || cached.version !== version) {
|
|
1665
|
-
let resolveSessionConversation = null;
|
|
1666
|
-
try {
|
|
1667
|
-
const loaded = tryLoadActivatedBundledPluginPublicSurfaceModuleSync({
|
|
1668
|
-
dirName,
|
|
1669
|
-
artifactBasename: SESSION_KEY_API_ARTIFACT_BASENAME
|
|
1670
|
-
});
|
|
1671
|
-
resolveSessionConversation = typeof loaded?.resolveSessionConversation === "function" ? loaded.resolveSessionConversation : null;
|
|
1672
|
-
} catch {
|
|
1673
|
-
resolveSessionConversation = null;
|
|
1674
|
-
}
|
|
1675
|
-
cached = {
|
|
1676
|
-
version,
|
|
1677
|
-
resolveSessionConversation
|
|
1678
|
-
};
|
|
1679
|
-
bundledSessionConversationFallbackCache.set(dirName, cached);
|
|
1680
|
-
}
|
|
1681
|
-
if (typeof cached.resolveSessionConversation !== "function") return null;
|
|
1682
|
-
return normalizeSessionConversationResolution(cached.resolveSessionConversation({
|
|
1683
|
-
kind: params.kind,
|
|
1684
|
-
rawId: params.rawId
|
|
1685
|
-
}));
|
|
1686
|
-
}
|
|
1687
|
-
function isBundledSessionConversationFallbackDisabled(channel) {
|
|
1688
|
-
const snapshot = getRuntimeConfigSnapshot();
|
|
1689
|
-
if (!snapshot?.plugins) return false;
|
|
1690
|
-
if (snapshot.plugins.enabled === false) return true;
|
|
1691
|
-
const entry = snapshot.plugins.entries?.[normalizeResolvedChannel(channel)];
|
|
1692
|
-
return !!entry && typeof entry === "object" && entry.enabled === false;
|
|
1693
|
-
}
|
|
1694
|
-
function shouldProbeBundledSessionConversationFallback(rawId) {
|
|
1695
|
-
return rawId.includes(":");
|
|
1696
|
-
}
|
|
1697
|
-
function resolveSessionConversationResolution(params) {
|
|
1698
|
-
const rawId = params.rawId.trim();
|
|
1699
|
-
if (!rawId) return null;
|
|
1700
|
-
const messaging = getMessagingAdapter(params.channel);
|
|
1701
|
-
const pluginResolved = normalizeSessionConversationResolution(messaging?.resolveSessionConversation?.({
|
|
1702
|
-
kind: params.kind,
|
|
1703
|
-
rawId
|
|
1704
|
-
}));
|
|
1705
|
-
const shouldTryBundledFallback = params.bundledFallback !== false && !messaging && shouldProbeBundledSessionConversationFallback(rawId);
|
|
1706
|
-
const resolved = pluginResolved ?? (shouldTryBundledFallback ? resolveBundledSessionConversationFallback({
|
|
1707
|
-
channel: params.channel,
|
|
1708
|
-
kind: params.kind,
|
|
1709
|
-
rawId
|
|
1710
|
-
}) : null) ?? buildGenericConversationResolution(rawId);
|
|
1711
|
-
if (!resolved) return null;
|
|
1712
|
-
const parentConversationCandidates = dedupeConversationIds(pluginResolved?.hasExplicitParentConversationCandidates ? resolved.parentConversationCandidates : messaging?.resolveParentConversationCandidates?.({
|
|
1713
|
-
kind: params.kind,
|
|
1714
|
-
rawId
|
|
1715
|
-
}) ?? resolved.parentConversationCandidates);
|
|
1716
|
-
const baseConversationId = parentConversationCandidates.at(-1) ?? resolved.baseConversationId ?? resolved.id;
|
|
1717
|
-
return {
|
|
1718
|
-
...resolved,
|
|
1719
|
-
baseConversationId,
|
|
1720
|
-
parentConversationCandidates
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
function resolveSessionConversation(params) {
|
|
1724
|
-
return resolveSessionConversationResolution(params);
|
|
1725
|
-
}
|
|
1726
|
-
//#endregion
|
|
1727
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/transcript-events-e-pVl4cn.js
|
|
1728
|
-
const SESSION_TRANSCRIPT_LISTENERS = /* @__PURE__ */ new Set();
|
|
1729
|
-
function emitSessionTranscriptUpdate(update) {
|
|
1730
|
-
const normalized = typeof update === "string" ? { sessionFile: update } : {
|
|
1731
|
-
sessionFile: update.sessionFile,
|
|
1732
|
-
sessionKey: update.sessionKey,
|
|
1733
|
-
message: update.message,
|
|
1734
|
-
messageId: update.messageId
|
|
1735
|
-
};
|
|
1736
|
-
const trimmed = normalizeOptionalString(normalized.sessionFile);
|
|
1737
|
-
if (!trimmed) return;
|
|
1738
|
-
const nextUpdate = {
|
|
1739
|
-
sessionFile: trimmed,
|
|
1740
|
-
...normalizeOptionalString(normalized.sessionKey) ? { sessionKey: normalizeOptionalString(normalized.sessionKey) } : {},
|
|
1741
|
-
...normalized.message !== void 0 ? { message: normalized.message } : {},
|
|
1742
|
-
...normalizeOptionalString(normalized.messageId) ? { messageId: normalizeOptionalString(normalized.messageId) } : {}
|
|
1743
|
-
};
|
|
1744
|
-
for (const listener of SESSION_TRANSCRIPT_LISTENERS) try {
|
|
1745
|
-
listener(nextUpdate);
|
|
1746
|
-
} catch {}
|
|
1747
|
-
}
|
|
1748
|
-
//#endregion
|
|
1749
|
-
//#region node_modules/.pnpm/openclaw@2026.4.22_@napi-rs+canvas@0.1.99/node_modules/openclaw/dist/transcript-E_-ISOkD.js
|
|
1750
|
-
async function resolveAndPersistSessionFile(params) {
|
|
1751
|
-
const { sessionId, sessionKey, sessionStore, storePath } = params;
|
|
1752
|
-
const baseEntry = params.sessionEntry ?? sessionStore[sessionKey] ?? {
|
|
1753
|
-
sessionId,
|
|
1754
|
-
updatedAt: Date.now()
|
|
1755
|
-
};
|
|
1756
|
-
const shouldReusePersistedSessionFile = baseEntry.sessionId === sessionId;
|
|
1757
|
-
const fallbackSessionFile = params.fallbackSessionFile?.trim();
|
|
1758
|
-
const sessionFile = resolveSessionFilePath(sessionId, !shouldReusePersistedSessionFile ? fallbackSessionFile ? {
|
|
1759
|
-
...baseEntry,
|
|
1760
|
-
sessionFile: fallbackSessionFile
|
|
1761
|
-
} : {
|
|
1762
|
-
...baseEntry,
|
|
1763
|
-
sessionFile: void 0
|
|
1764
|
-
} : !baseEntry.sessionFile && fallbackSessionFile ? {
|
|
1765
|
-
...baseEntry,
|
|
1766
|
-
sessionFile: fallbackSessionFile
|
|
1767
|
-
} : baseEntry, {
|
|
1768
|
-
agentId: params.agentId,
|
|
1769
|
-
sessionsDir: params.sessionsDir
|
|
1770
|
-
});
|
|
1771
|
-
const persistedEntry = {
|
|
1772
|
-
...baseEntry,
|
|
1773
|
-
sessionId,
|
|
1774
|
-
updatedAt: Date.now(),
|
|
1775
|
-
sessionFile
|
|
1776
|
-
};
|
|
1777
|
-
if (baseEntry.sessionId !== sessionId || baseEntry.sessionFile !== sessionFile) {
|
|
1778
|
-
sessionStore[sessionKey] = persistedEntry;
|
|
1779
|
-
await updateSessionStore(storePath, (store) => {
|
|
1780
|
-
store[sessionKey] = {
|
|
1781
|
-
...store[sessionKey],
|
|
1782
|
-
...persistedEntry
|
|
1783
|
-
};
|
|
1784
|
-
}, params.activeSessionKey || params.maintenanceConfig ? {
|
|
1785
|
-
...params.activeSessionKey ? { activeSessionKey: params.activeSessionKey } : {},
|
|
1786
|
-
...params.maintenanceConfig ? { maintenanceConfig: params.maintenanceConfig } : {}
|
|
1787
|
-
} : void 0);
|
|
1788
|
-
return {
|
|
1789
|
-
sessionFile,
|
|
1790
|
-
sessionEntry: persistedEntry
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
sessionStore[sessionKey] = persistedEntry;
|
|
1794
|
-
return {
|
|
1795
|
-
sessionFile,
|
|
1796
|
-
sessionEntry: persistedEntry
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
function stripQuery(value) {
|
|
1800
|
-
const noHash = value.split("#")[0] ?? value;
|
|
1801
|
-
return noHash.split("?")[0] ?? noHash;
|
|
1802
|
-
}
|
|
1803
|
-
function extractFileNameFromMediaUrl(value) {
|
|
1804
|
-
const trimmed = value.trim();
|
|
1805
|
-
if (!trimmed) return null;
|
|
1806
|
-
const cleaned = stripQuery(trimmed);
|
|
1807
|
-
try {
|
|
1808
|
-
const parsed = new URL(cleaned);
|
|
1809
|
-
const base = path.basename(parsed.pathname);
|
|
1810
|
-
if (!base) return null;
|
|
1811
|
-
try {
|
|
1812
|
-
return decodeURIComponent(base);
|
|
1813
|
-
} catch {
|
|
1814
|
-
return base;
|
|
1815
|
-
}
|
|
1816
|
-
} catch {
|
|
1817
|
-
const base = path.basename(cleaned);
|
|
1818
|
-
if (!base || base === "/" || base === ".") return null;
|
|
1819
|
-
return base;
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
function resolveMirroredTranscriptText(params) {
|
|
1823
|
-
const mediaUrls = params.mediaUrls?.filter((url) => url && url.trim()) ?? [];
|
|
1824
|
-
if (mediaUrls.length > 0) {
|
|
1825
|
-
const names = mediaUrls.map((url) => extractFileNameFromMediaUrl(url)).filter((name) => Boolean(name && name.trim()));
|
|
1826
|
-
if (names.length > 0) return names.join(", ");
|
|
1827
|
-
return "media";
|
|
1828
|
-
}
|
|
1829
|
-
const trimmed = (params.text ?? "").trim();
|
|
1830
|
-
return trimmed ? trimmed : null;
|
|
1831
|
-
}
|
|
1832
|
-
async function ensureSessionHeader(params) {
|
|
1833
|
-
if (fs.existsSync(params.sessionFile)) return;
|
|
1834
|
-
await fs.promises.mkdir(path.dirname(params.sessionFile), { recursive: true });
|
|
1835
|
-
const header = {
|
|
1836
|
-
type: "session",
|
|
1837
|
-
version: 3,
|
|
1838
|
-
id: params.sessionId,
|
|
1839
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1840
|
-
cwd: process.cwd()
|
|
1841
|
-
};
|
|
1842
|
-
await fs.promises.writeFile(params.sessionFile, `${JSON.stringify(header)}\n`, {
|
|
1843
|
-
encoding: "utf-8",
|
|
1844
|
-
mode: 384
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
async function appendAssistantMessageToSessionTranscript(params) {
|
|
1848
|
-
const sessionKey = params.sessionKey.trim();
|
|
1849
|
-
if (!sessionKey) return {
|
|
1850
|
-
ok: false,
|
|
1851
|
-
reason: "missing sessionKey"
|
|
1852
|
-
};
|
|
1853
|
-
const mirrorText = resolveMirroredTranscriptText({
|
|
1854
|
-
text: params.text,
|
|
1855
|
-
mediaUrls: params.mediaUrls
|
|
1856
|
-
});
|
|
1857
|
-
if (!mirrorText) return {
|
|
1858
|
-
ok: false,
|
|
1859
|
-
reason: "empty text"
|
|
1860
|
-
};
|
|
1861
|
-
return appendExactAssistantMessageToSessionTranscript({
|
|
1862
|
-
agentId: params.agentId,
|
|
1863
|
-
sessionKey,
|
|
1864
|
-
storePath: params.storePath,
|
|
1865
|
-
idempotencyKey: params.idempotencyKey,
|
|
1866
|
-
updateMode: params.updateMode,
|
|
1867
|
-
message: {
|
|
1868
|
-
role: "assistant",
|
|
1869
|
-
content: [{
|
|
1870
|
-
type: "text",
|
|
1871
|
-
text: mirrorText
|
|
1872
|
-
}],
|
|
1873
|
-
api: "openai-responses",
|
|
1874
|
-
provider: "openclaw",
|
|
1875
|
-
model: "delivery-mirror",
|
|
1876
|
-
usage: {
|
|
1877
|
-
input: 0,
|
|
1878
|
-
output: 0,
|
|
1879
|
-
cacheRead: 0,
|
|
1880
|
-
cacheWrite: 0,
|
|
1881
|
-
totalTokens: 0,
|
|
1882
|
-
cost: {
|
|
1883
|
-
input: 0,
|
|
1884
|
-
output: 0,
|
|
1885
|
-
cacheRead: 0,
|
|
1886
|
-
cacheWrite: 0,
|
|
1887
|
-
total: 0
|
|
1888
|
-
}
|
|
1889
|
-
},
|
|
1890
|
-
stopReason: "stop",
|
|
1891
|
-
timestamp: Date.now()
|
|
1892
|
-
}
|
|
1893
|
-
});
|
|
1894
|
-
}
|
|
1895
|
-
async function appendExactAssistantMessageToSessionTranscript(params) {
|
|
1896
|
-
const sessionKey = params.sessionKey.trim();
|
|
1897
|
-
if (!sessionKey) return {
|
|
1898
|
-
ok: false,
|
|
1899
|
-
reason: "missing sessionKey"
|
|
1900
|
-
};
|
|
1901
|
-
if (params.message.role !== "assistant") return {
|
|
1902
|
-
ok: false,
|
|
1903
|
-
reason: "message role must be assistant"
|
|
1904
|
-
};
|
|
1905
|
-
const storePath = params.storePath ?? resolveDefaultSessionStorePath(params.agentId);
|
|
1906
|
-
const store = loadSessionStore(storePath, { skipCache: true });
|
|
1907
|
-
const entry = store[normalizeStoreSessionKey(sessionKey)] ?? store[sessionKey];
|
|
1908
|
-
if (!entry?.sessionId) return {
|
|
1909
|
-
ok: false,
|
|
1910
|
-
reason: `unknown sessionKey: ${sessionKey}`
|
|
1911
|
-
};
|
|
1912
|
-
let sessionFile;
|
|
1913
|
-
try {
|
|
1914
|
-
sessionFile = (await resolveAndPersistSessionFile({
|
|
1915
|
-
sessionId: entry.sessionId,
|
|
1916
|
-
sessionKey,
|
|
1917
|
-
sessionStore: store,
|
|
1918
|
-
storePath,
|
|
1919
|
-
sessionEntry: entry,
|
|
1920
|
-
agentId: params.agentId,
|
|
1921
|
-
sessionsDir: path.dirname(storePath)
|
|
1922
|
-
})).sessionFile;
|
|
1923
|
-
} catch (err) {
|
|
1924
|
-
return {
|
|
1925
|
-
ok: false,
|
|
1926
|
-
reason: formatErrorMessage(err)
|
|
1927
|
-
};
|
|
1928
|
-
}
|
|
1929
|
-
await ensureSessionHeader({
|
|
1930
|
-
sessionFile,
|
|
1931
|
-
sessionId: entry.sessionId
|
|
1932
|
-
});
|
|
1933
|
-
const explicitIdempotencyKey = params.idempotencyKey ?? params.message.idempotencyKey;
|
|
1934
|
-
const existingMessageId = explicitIdempotencyKey ? await transcriptHasIdempotencyKey(sessionFile, explicitIdempotencyKey) : void 0;
|
|
1935
|
-
if (existingMessageId) return {
|
|
1936
|
-
ok: true,
|
|
1937
|
-
sessionFile,
|
|
1938
|
-
messageId: existingMessageId
|
|
1939
|
-
};
|
|
1940
|
-
const latestEquivalentAssistantId = isRedundantDeliveryMirror(params.message) ? await findLatestEquivalentAssistantMessageId(sessionFile, params.message) : void 0;
|
|
1941
|
-
if (latestEquivalentAssistantId) return {
|
|
1942
|
-
ok: true,
|
|
1943
|
-
sessionFile,
|
|
1944
|
-
messageId: latestEquivalentAssistantId
|
|
1945
|
-
};
|
|
1946
|
-
const message = {
|
|
1947
|
-
...params.message,
|
|
1948
|
-
...explicitIdempotencyKey ? { idempotencyKey: explicitIdempotencyKey } : {}
|
|
1949
|
-
};
|
|
1950
|
-
const messageId = SessionManager.open(sessionFile).appendMessage(message);
|
|
1951
|
-
switch (params.updateMode ?? "inline") {
|
|
1952
|
-
case "inline":
|
|
1953
|
-
emitSessionTranscriptUpdate({
|
|
1954
|
-
sessionFile,
|
|
1955
|
-
sessionKey,
|
|
1956
|
-
message,
|
|
1957
|
-
messageId
|
|
1958
|
-
});
|
|
1959
|
-
break;
|
|
1960
|
-
case "file-only":
|
|
1961
|
-
emitSessionTranscriptUpdate(sessionFile);
|
|
1962
|
-
break;
|
|
1963
|
-
case "none": break;
|
|
1964
|
-
}
|
|
1965
|
-
return {
|
|
1966
|
-
ok: true,
|
|
1967
|
-
sessionFile,
|
|
1968
|
-
messageId
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
async function transcriptHasIdempotencyKey(transcriptPath, idempotencyKey) {
|
|
1972
|
-
try {
|
|
1973
|
-
const raw = await fs.promises.readFile(transcriptPath, "utf-8");
|
|
1974
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
1975
|
-
if (!line.trim()) continue;
|
|
1976
|
-
try {
|
|
1977
|
-
const parsed = JSON.parse(line);
|
|
1978
|
-
if (parsed.message?.idempotencyKey === idempotencyKey && typeof parsed.id === "string" && parsed.id) return parsed.id;
|
|
1979
|
-
} catch {
|
|
1980
|
-
continue;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
} catch {
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
function isRedundantDeliveryMirror(message) {
|
|
1988
|
-
return message.provider === "openclaw" && message.model === "delivery-mirror";
|
|
1989
|
-
}
|
|
1990
|
-
function extractAssistantMessageText(message) {
|
|
1991
|
-
if (!Array.isArray(message.content)) return null;
|
|
1992
|
-
const parts = message.content.filter((part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0).map((part) => part.text.trim());
|
|
1993
|
-
return parts.length > 0 ? parts.join("\n").trim() : null;
|
|
1994
|
-
}
|
|
1995
|
-
async function findLatestEquivalentAssistantMessageId(transcriptPath, message) {
|
|
1996
|
-
const expectedText = extractAssistantMessageText(message);
|
|
1997
|
-
if (!expectedText) return;
|
|
1998
|
-
try {
|
|
1999
|
-
const lines = (await fs.promises.readFile(transcriptPath, "utf-8")).split(/\r?\n/);
|
|
2000
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
2001
|
-
const line = lines[index];
|
|
2002
|
-
if (!line.trim()) continue;
|
|
2003
|
-
try {
|
|
2004
|
-
const parsed = JSON.parse(line);
|
|
2005
|
-
const candidate = parsed.message;
|
|
2006
|
-
if (!candidate || candidate.role !== "assistant") continue;
|
|
2007
|
-
if (extractAssistantMessageText(candidate) !== expectedText) return;
|
|
2008
|
-
if (typeof parsed.id === "string" && parsed.id) return parsed.id;
|
|
2009
|
-
return;
|
|
2010
|
-
} catch {
|
|
2011
|
-
continue;
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
} catch {
|
|
2015
|
-
return;
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
//#endregion
|
|
2019
|
-
export { createLazyFacadeValue as a, loadSessionStore as c, resolveSessionConversation as i, appendExactAssistantMessageToSessionTranscript as n, loadActivatedBundledPluginPublicSurfaceModuleSync as o, resolveMirroredTranscriptText as r, updateSessionStore as s, appendAssistantMessageToSessionTranscript as t };
|