chat-recall 0.5.29 → 0.5.30
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/auto-update.d.ts +24 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +71 -1
- package/dist/auto-update.js.map +1 -1
- package/dist/cli.js +658 -529
- package/dist/cli.js.map +1 -1
- package/dist/env-needed.d.ts +17 -0
- package/dist/env-needed.d.ts.map +1 -0
- package/dist/env-needed.js +93 -0
- package/dist/env-needed.js.map +1 -0
- package/dist/intent-drain.d.ts.map +1 -1
- package/dist/intent-drain.js +2 -1
- package/dist/intent-drain.js.map +1 -1
- package/dist/mcp.js +59 -23
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +4 -2
- package/dist/sync-client.js.map +1 -1
- package/dist/watch.js +68 -31
- package/package.json +1 -1
package/dist/watch.js
CHANGED
|
@@ -7265,7 +7265,7 @@ async function archiveRawSession(store, sessionId) {
|
|
|
7265
7265
|
return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
|
|
7266
7266
|
}
|
|
7267
7267
|
function detectForkPredecessor(sessionPath) {
|
|
7268
|
-
const { readFileSync: readFileSync47, readdirSync:
|
|
7268
|
+
const { readFileSync: readFileSync47, readdirSync: readdirSync35 } = requireFs();
|
|
7269
7269
|
const { dirname: dirname26, join: join56, basename: basename28 } = requirePath();
|
|
7270
7270
|
let text;
|
|
7271
7271
|
try {
|
|
@@ -7295,7 +7295,7 @@ function detectForkPredecessor(sessionPath) {
|
|
|
7295
7295
|
const self = basename28(sessionPath);
|
|
7296
7296
|
let entries;
|
|
7297
7297
|
try {
|
|
7298
|
-
entries =
|
|
7298
|
+
entries = readdirSync35(dir);
|
|
7299
7299
|
} catch {
|
|
7300
7300
|
return null;
|
|
7301
7301
|
}
|
|
@@ -19233,7 +19233,7 @@ var init_http = __esm({
|
|
|
19233
19233
|
CLI_VERSION = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
19234
19234
|
} catch {
|
|
19235
19235
|
try {
|
|
19236
|
-
if (true) CLI_VERSION = "0.5.
|
|
19236
|
+
if (true) CLI_VERSION = "0.5.30";
|
|
19237
19237
|
} catch {
|
|
19238
19238
|
}
|
|
19239
19239
|
}
|
|
@@ -19741,7 +19741,7 @@ var init_telemetry = __esm({
|
|
|
19741
19741
|
init_sync_client();
|
|
19742
19742
|
cliVersion = () => {
|
|
19743
19743
|
try {
|
|
19744
|
-
return true ? "0.5.
|
|
19744
|
+
return true ? "0.5.30" : "dev";
|
|
19745
19745
|
} catch {
|
|
19746
19746
|
return "dev";
|
|
19747
19747
|
}
|
|
@@ -20018,12 +20018,13 @@ __export(auto_update_exports, {
|
|
|
20018
20018
|
planAutoUpdate: () => planAutoUpdate,
|
|
20019
20019
|
runAutoUpdate: () => runAutoUpdate,
|
|
20020
20020
|
runningPrefix: () => runningPrefix,
|
|
20021
|
+
sweepStaleStaging: () => sweepStaleStaging,
|
|
20021
20022
|
tarballUrl: () => tarballUrl
|
|
20022
20023
|
});
|
|
20023
20024
|
import { execSync as execSync2 } from "node:child_process";
|
|
20024
20025
|
import { readFileSync as readFileSync36 } from "node:fs";
|
|
20025
20026
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
20026
|
-
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3 } from "node:fs";
|
|
20027
|
+
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync32, statSync as statSync38 } from "node:fs";
|
|
20027
20028
|
import { join as join43, dirname as dirname18, sep as sep2 } from "node:path";
|
|
20028
20029
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
20029
20030
|
import { createHash as createHash11 } from "node:crypto";
|
|
@@ -20062,6 +20063,31 @@ function planAutoUpdate(base2, caps, ownVersion3, flag) {
|
|
|
20062
20063
|
if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
|
|
20063
20064
|
return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base2), sha256: rel.sha256, from: ownVersion3, to: rel.version };
|
|
20064
20065
|
}
|
|
20066
|
+
function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
|
|
20067
|
+
const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
|
|
20068
|
+
const COLD_MS = 60 * 60 * 1e3;
|
|
20069
|
+
let removed = 0;
|
|
20070
|
+
let entries;
|
|
20071
|
+
try {
|
|
20072
|
+
entries = readdirSync32(root);
|
|
20073
|
+
} catch {
|
|
20074
|
+
return 0;
|
|
20075
|
+
}
|
|
20076
|
+
for (const name of entries) {
|
|
20077
|
+
if (!MKDTEMP_SUFFIX.test(name)) continue;
|
|
20078
|
+
const dir = join43(root, name);
|
|
20079
|
+
try {
|
|
20080
|
+
const st = statSync38(dir);
|
|
20081
|
+
if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
|
|
20082
|
+
const inside = readdirSync32(dir);
|
|
20083
|
+
if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
|
|
20084
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
20085
|
+
removed++;
|
|
20086
|
+
} catch {
|
|
20087
|
+
}
|
|
20088
|
+
}
|
|
20089
|
+
return removed;
|
|
20090
|
+
}
|
|
20065
20091
|
async function executeAutoUpdate(plan, deps) {
|
|
20066
20092
|
if (!plan.update || !plan.url || !plan.sha256) return { updated: false, reason: plan.reason };
|
|
20067
20093
|
let buf;
|
|
@@ -20073,13 +20099,21 @@ async function executeAutoUpdate(plan, deps) {
|
|
|
20073
20099
|
const got = createHash11("sha256").update(buf).digest("hex");
|
|
20074
20100
|
if (got !== plan.sha256) return { updated: false, reason: `checksum mismatch (expected ${plan.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026) \u2014 NOT installing` };
|
|
20075
20101
|
const dir = mkdtempSync3(join43(tmpdir2(), "cr-update-"));
|
|
20102
|
+
const discardStaging = () => {
|
|
20103
|
+
try {
|
|
20104
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
20105
|
+
} catch {
|
|
20106
|
+
}
|
|
20107
|
+
};
|
|
20076
20108
|
const tgz = join43(dir, "chat-recall.tgz");
|
|
20077
20109
|
writeFileSync8(tgz, buf);
|
|
20078
20110
|
try {
|
|
20079
20111
|
deps.install(tgz);
|
|
20080
20112
|
} catch (e) {
|
|
20113
|
+
discardStaging();
|
|
20081
20114
|
return { updated: false, reason: `install failed: ${e instanceof Error ? e.message : e}` };
|
|
20082
20115
|
}
|
|
20116
|
+
discardStaging();
|
|
20083
20117
|
const landed = (deps.verify ?? installedVersion)();
|
|
20084
20118
|
if (landed && plan.to && compareVersions(landed, plan.to) < 0) {
|
|
20085
20119
|
return {
|
|
@@ -20161,7 +20195,7 @@ var init_auto_update = __esm({
|
|
|
20161
20195
|
|
|
20162
20196
|
// src/sync-client.ts
|
|
20163
20197
|
import { join as join44, dirname as dirname19, basename as basename22 } from "node:path";
|
|
20164
|
-
import { readFileSync as readFileSync37, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as
|
|
20198
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync33, statSync as statSync39 } from "node:fs";
|
|
20165
20199
|
import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
|
|
20166
20200
|
import { createHash as createHash12 } from "node:crypto";
|
|
20167
20201
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -20480,7 +20514,7 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
|
|
|
20480
20514
|
let reaped = 0;
|
|
20481
20515
|
let names;
|
|
20482
20516
|
try {
|
|
20483
|
-
names =
|
|
20517
|
+
names = readdirSync33(dir);
|
|
20484
20518
|
} catch {
|
|
20485
20519
|
return 0;
|
|
20486
20520
|
}
|
|
@@ -20488,8 +20522,8 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
|
|
|
20488
20522
|
if (!name.startsWith("cr-batchscan-")) continue;
|
|
20489
20523
|
const p = join44(dir, name);
|
|
20490
20524
|
try {
|
|
20491
|
-
if (now -
|
|
20492
|
-
|
|
20525
|
+
if (now - statSync39(p).mtimeMs < maxAgeMs) continue;
|
|
20526
|
+
rmSync6(p, { recursive: true, force: true });
|
|
20493
20527
|
reaped++;
|
|
20494
20528
|
} catch {
|
|
20495
20529
|
}
|
|
@@ -20521,7 +20555,7 @@ function batchScanExternal(items, opts = {}) {
|
|
|
20521
20555
|
slices++;
|
|
20522
20556
|
}
|
|
20523
20557
|
try {
|
|
20524
|
-
|
|
20558
|
+
rmSync6(scanDir, { recursive: true, force: true });
|
|
20525
20559
|
} catch {
|
|
20526
20560
|
}
|
|
20527
20561
|
safeToId = /* @__PURE__ */ new Map();
|
|
@@ -20546,7 +20580,7 @@ function batchScanExternal(items, opts = {}) {
|
|
|
20546
20580
|
flushSlice();
|
|
20547
20581
|
} finally {
|
|
20548
20582
|
try {
|
|
20549
|
-
|
|
20583
|
+
rmSync6(scanDir, { recursive: true, force: true });
|
|
20550
20584
|
} catch {
|
|
20551
20585
|
}
|
|
20552
20586
|
}
|
|
@@ -20674,9 +20708,10 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20674
20708
|
setServerIngestConcurrency(base2, Math.floor(adv));
|
|
20675
20709
|
}
|
|
20676
20710
|
if (body2.cli && body2.cli.version) {
|
|
20677
|
-
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20711
|
+
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20678
20712
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20679
20713
|
const plan = planAutoUpdate2(base2, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20714
|
+
sweepStaleStaging2();
|
|
20680
20715
|
if (plan.update) {
|
|
20681
20716
|
void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
|
|
20682
20717
|
});
|
|
@@ -20802,9 +20837,10 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20802
20837
|
if (!res.ok) throw new Error(`append sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
20803
20838
|
const body = await res.json().catch(() => ({}));
|
|
20804
20839
|
if (body.cli && body.cli.version) {
|
|
20805
|
-
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20840
|
+
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20806
20841
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20807
20842
|
const plan = planAutoUpdate2(base2, { cli: body.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20843
|
+
sweepStaleStaging2();
|
|
20808
20844
|
if (plan.update) {
|
|
20809
20845
|
void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
|
|
20810
20846
|
});
|
|
@@ -21606,7 +21642,7 @@ var init_sync_client = __esm({
|
|
|
21606
21642
|
ownVersion = JSON.parse(readFileSync37(join44(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21607
21643
|
} catch {
|
|
21608
21644
|
try {
|
|
21609
|
-
if (true) ownVersion = "0.5.
|
|
21645
|
+
if (true) ownVersion = "0.5.30";
|
|
21610
21646
|
} catch {
|
|
21611
21647
|
}
|
|
21612
21648
|
}
|
|
@@ -22205,11 +22241,11 @@ var init_toolkit_sync = __esm({
|
|
|
22205
22241
|
});
|
|
22206
22242
|
|
|
22207
22243
|
// ../engine/dist/core/which.js
|
|
22208
|
-
import { existsSync as existsSync45, statSync as
|
|
22244
|
+
import { existsSync as existsSync45, statSync as statSync41, accessSync, constants } from "node:fs";
|
|
22209
22245
|
import { join as join48, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
|
|
22210
22246
|
function executable(p) {
|
|
22211
22247
|
try {
|
|
22212
|
-
if (!
|
|
22248
|
+
if (!statSync41(p).isFile())
|
|
22213
22249
|
return false;
|
|
22214
22250
|
} catch {
|
|
22215
22251
|
return false;
|
|
@@ -22271,11 +22307,11 @@ __export(toolkit_pull_exports, {
|
|
|
22271
22307
|
planPull: () => planPull,
|
|
22272
22308
|
portableCommand: () => portableCommand
|
|
22273
22309
|
});
|
|
22274
|
-
import { existsSync as existsSync46, mkdirSync as mkdirSync12, statSync as
|
|
22310
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync12, statSync as statSync42, writeFileSync as writeFileSync12 } from "node:fs";
|
|
22275
22311
|
import { dirname as dirname22, join as join49, basename as basename25, isAbsolute as isAbsolute3 } from "node:path";
|
|
22276
22312
|
function isExecutableFile(p) {
|
|
22277
22313
|
try {
|
|
22278
|
-
return
|
|
22314
|
+
return statSync42(p).isFile();
|
|
22279
22315
|
} catch {
|
|
22280
22316
|
return false;
|
|
22281
22317
|
}
|
|
@@ -22769,14 +22805,14 @@ __export(shadow_prune_exports, {
|
|
|
22769
22805
|
DEFAULT_MAX_TOTAL_BYTES: () => DEFAULT_MAX_TOTAL_BYTES,
|
|
22770
22806
|
pruneShadow: () => pruneShadow
|
|
22771
22807
|
});
|
|
22772
|
-
import { readdirSync as
|
|
22808
|
+
import { readdirSync as readdirSync34, statSync as statSync44, unlinkSync as unlinkSync3 } from "fs";
|
|
22773
22809
|
import { join as join52 } from "path";
|
|
22774
22810
|
function listShadows(isFullyAcked) {
|
|
22775
22811
|
const out = [];
|
|
22776
22812
|
const root = shadowRoot();
|
|
22777
22813
|
let tools;
|
|
22778
22814
|
try {
|
|
22779
|
-
tools =
|
|
22815
|
+
tools = readdirSync34(root);
|
|
22780
22816
|
} catch {
|
|
22781
22817
|
return out;
|
|
22782
22818
|
}
|
|
@@ -22784,7 +22820,7 @@ function listShadows(isFullyAcked) {
|
|
|
22784
22820
|
const dir = join52(root, tool);
|
|
22785
22821
|
let names;
|
|
22786
22822
|
try {
|
|
22787
|
-
names =
|
|
22823
|
+
names = readdirSync34(dir);
|
|
22788
22824
|
} catch {
|
|
22789
22825
|
continue;
|
|
22790
22826
|
}
|
|
@@ -22794,7 +22830,7 @@ function listShadows(isFullyAcked) {
|
|
|
22794
22830
|
const path = join52(dir, name);
|
|
22795
22831
|
let st;
|
|
22796
22832
|
try {
|
|
22797
|
-
st =
|
|
22833
|
+
st = statSync44(path);
|
|
22798
22834
|
} catch {
|
|
22799
22835
|
continue;
|
|
22800
22836
|
}
|
|
@@ -22909,7 +22945,7 @@ var init_code_intel = __esm({
|
|
|
22909
22945
|
});
|
|
22910
22946
|
|
|
22911
22947
|
// ../engine/dist/core/companions.js
|
|
22912
|
-
import { existsSync as existsSync49, mkdirSync as mkdirSync15, statSync as
|
|
22948
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync15, statSync as statSync45, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync44, writeFileSync as writeFileSync15 } from "node:fs";
|
|
22913
22949
|
import { homedir as homedir19 } from "node:os";
|
|
22914
22950
|
import { join as join53 } from "node:path";
|
|
22915
22951
|
import { execSync as execSync3 } from "node:child_process";
|
|
@@ -22960,7 +22996,7 @@ function checkCodeindexStatus() {
|
|
|
22960
22996
|
status.installed = true;
|
|
22961
22997
|
status.path = foundPath;
|
|
22962
22998
|
try {
|
|
22963
|
-
status.size =
|
|
22999
|
+
status.size = statSync45(foundPath).size;
|
|
22964
23000
|
} catch {
|
|
22965
23001
|
}
|
|
22966
23002
|
try {
|
|
@@ -24122,7 +24158,7 @@ import { homedir as homedir18 } from "node:os";
|
|
|
24122
24158
|
|
|
24123
24159
|
// src/project-tasks.ts
|
|
24124
24160
|
init_http();
|
|
24125
|
-
import { existsSync as existsSync44, readFileSync as readFileSync40, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, statSync as
|
|
24161
|
+
import { existsSync as existsSync44, readFileSync as readFileSync40, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, statSync as statSync40 } from "node:fs";
|
|
24126
24162
|
import { join as join47, dirname as dirname21 } from "node:path";
|
|
24127
24163
|
import { homedir as homedir17 } from "node:os";
|
|
24128
24164
|
|
|
@@ -24233,7 +24269,7 @@ async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
|
|
|
24233
24269
|
if (!existsSync44(file)) continue;
|
|
24234
24270
|
let mtime;
|
|
24235
24271
|
try {
|
|
24236
|
-
mtime =
|
|
24272
|
+
mtime = statSync40(file).mtimeMs;
|
|
24237
24273
|
} catch {
|
|
24238
24274
|
continue;
|
|
24239
24275
|
}
|
|
@@ -24394,7 +24430,7 @@ try {
|
|
|
24394
24430
|
ownVersion2 = JSON.parse(readFileSync41(join50(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
24395
24431
|
} catch {
|
|
24396
24432
|
try {
|
|
24397
|
-
if (true) ownVersion2 = "0.5.
|
|
24433
|
+
if (true) ownVersion2 = "0.5.30";
|
|
24398
24434
|
} catch {
|
|
24399
24435
|
}
|
|
24400
24436
|
}
|
|
@@ -24410,8 +24446,9 @@ async function drainSyncIntents(opts = {}) {
|
|
|
24410
24446
|
const data = await res.json();
|
|
24411
24447
|
pending = data.intents || [];
|
|
24412
24448
|
if (data.cli && data.cli.version) {
|
|
24413
|
-
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
24449
|
+
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
24414
24450
|
const plan = planAutoUpdate2(base2, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
24451
|
+
sweepStaleStaging2();
|
|
24415
24452
|
if (plan.update) {
|
|
24416
24453
|
void runAutoUpdate2(base2, authHeaders, ownVersion2).catch(() => {
|
|
24417
24454
|
});
|
|
@@ -24678,11 +24715,11 @@ function checkSelfRestart(opts) {
|
|
|
24678
24715
|
}
|
|
24679
24716
|
|
|
24680
24717
|
// src/log-rotate.ts
|
|
24681
|
-
import { statSync as
|
|
24718
|
+
import { statSync as statSync43, copyFileSync as copyFileSync3, truncateSync } from "fs";
|
|
24682
24719
|
var LOG_MAX_BYTES = 32 * 1024 * 1024;
|
|
24683
24720
|
function rotateLogIfLarge(logPath, maxBytes = LOG_MAX_BYTES) {
|
|
24684
24721
|
try {
|
|
24685
|
-
const size =
|
|
24722
|
+
const size = statSync43(logPath).size;
|
|
24686
24723
|
if (size < maxBytes) return { rotated: false, sizeBefore: 0 };
|
|
24687
24724
|
copyFileSync3(logPath, `${logPath}.1`);
|
|
24688
24725
|
truncateSync(logPath, 0);
|
|
@@ -24738,7 +24775,7 @@ async function runCollectorMigration(opts) {
|
|
|
24738
24775
|
init_sync_ledger();
|
|
24739
24776
|
init_collector_health();
|
|
24740
24777
|
import { existsSync as existsSync50, readFileSync as readFileSync46 } from "fs";
|
|
24741
|
-
var CLI_VERSION3 = true ? "0.5.
|
|
24778
|
+
var CLI_VERSION3 = true ? "0.5.30" : "0.0.0";
|
|
24742
24779
|
var DEBOUNCE_MS = 5e3;
|
|
24743
24780
|
var CLAUDE_DIR = claudeBackend.projectsDir();
|
|
24744
24781
|
var PLANS_DIR = claudeBackend.plansDir();
|