@askexenow/exe-os 0.8.37 → 0.8.39
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/README.md +17 -8
- package/dist/bin/backfill-conversations.js +112 -70
- package/dist/bin/backfill-responses.js +53 -18
- package/dist/bin/backfill-vectors.js +43 -16
- package/dist/bin/cleanup-stale-review-tasks.js +38 -16
- package/dist/bin/cli.js +790 -468
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +46 -13
- package/dist/bin/exe-boot.js +288 -129
- package/dist/bin/exe-call.js +20 -10
- package/dist/bin/exe-cloud.js +135 -30
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +38 -16
- package/dist/bin/exe-export-behaviors.js +43 -21
- package/dist/bin/exe-forget.js +39 -17
- package/dist/bin/exe-gateway.js +159 -50
- package/dist/bin/exe-heartbeat.js +53 -31
- package/dist/bin/exe-kill.js +40 -18
- package/dist/bin/exe-launch-agent.js +109 -36
- package/dist/bin/exe-link.js +196 -87
- package/dist/bin/exe-new-employee.js +56 -17
- package/dist/bin/exe-pending-messages.js +47 -25
- package/dist/bin/exe-pending-notifications.js +38 -16
- package/dist/bin/exe-pending-reviews.js +51 -29
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +41 -13
- package/dist/bin/exe-search.js +57 -21
- package/dist/bin/exe-session-cleanup.js +67 -31
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +35 -13
- package/dist/bin/exe-team.js +35 -13
- package/dist/bin/git-sweep.js +45 -17
- package/dist/bin/graph-backfill.js +38 -16
- package/dist/bin/graph-export.js +38 -16
- package/dist/bin/install.js +10 -1
- package/dist/bin/scan-tasks.js +47 -19
- package/dist/bin/setup.js +444 -259
- package/dist/bin/shard-migrate.js +38 -16
- package/dist/bin/wiki-sync.js +40 -17
- package/dist/gateway/index.js +113 -48
- package/dist/hooks/bug-report-worker.js +66 -39
- package/dist/hooks/commit-complete.js +45 -17
- package/dist/hooks/error-recall.js +60 -20
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +174 -45
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +46 -17
- package/dist/hooks/notification.js +44 -15
- package/dist/hooks/post-compact.js +44 -15
- package/dist/hooks/pre-compact.js +42 -14
- package/dist/hooks/pre-tool-use.js +59 -22
- package/dist/hooks/prompt-ingest-worker.js +75 -14
- package/dist/hooks/prompt-submit.js +75 -32
- package/dist/hooks/response-ingest-worker.js +76 -15
- package/dist/hooks/session-end.js +54 -22
- package/dist/hooks/session-start.js +57 -20
- package/dist/hooks/stop.js +44 -15
- package/dist/hooks/subagent-stop.js +44 -15
- package/dist/hooks/summary-worker.js +339 -106
- package/dist/index.js +94 -23
- package/dist/lib/cloud-sync.js +191 -80
- package/dist/lib/config.js +4 -1
- package/dist/lib/consolidation.js +5 -4
- package/dist/lib/database.js +1 -0
- package/dist/lib/device-registry.js +2 -1
- package/dist/lib/embedder.js +9 -1
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +95 -36
- package/dist/lib/hybrid-search.js +57 -21
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +56 -6
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +38 -16
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +44 -16
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +280 -155
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +14 -6
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +36 -28
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +42 -8
- package/dist/tui/App.js +220 -99
- package/package.json +5 -3
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -41,7 +41,7 @@ __export(config_exports, {
|
|
|
41
41
|
migrateConfig: () => migrateConfig,
|
|
42
42
|
saveConfig: () => saveConfig
|
|
43
43
|
});
|
|
44
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
44
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
45
45
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
46
46
|
import path from "path";
|
|
47
47
|
import os from "os";
|
|
@@ -167,6 +167,9 @@ async function saveConfig(config) {
|
|
|
167
167
|
await mkdir(dir, { recursive: true });
|
|
168
168
|
const configPath = path.join(dir, "config.json");
|
|
169
169
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
170
|
+
if (config.cloud?.apiKey) {
|
|
171
|
+
await chmod(configPath, 384);
|
|
172
|
+
}
|
|
170
173
|
}
|
|
171
174
|
async function loadConfigFrom(configPath) {
|
|
172
175
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -379,6 +382,7 @@ async function ensureSchema() {
|
|
|
379
382
|
const client = getRawClient();
|
|
380
383
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
381
384
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
385
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
382
386
|
try {
|
|
383
387
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
384
388
|
} catch {
|
|
@@ -1195,12 +1199,13 @@ __export(keychain_exports, {
|
|
|
1195
1199
|
importMnemonic: () => importMnemonic,
|
|
1196
1200
|
setMasterKey: () => setMasterKey
|
|
1197
1201
|
});
|
|
1198
|
-
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
|
|
1202
|
+
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
1199
1203
|
import { existsSync as existsSync2 } from "fs";
|
|
1200
1204
|
import path2 from "path";
|
|
1205
|
+
import os2 from "os";
|
|
1201
1206
|
import crypto from "crypto";
|
|
1202
1207
|
function getKeyDir() {
|
|
1203
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(
|
|
1208
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
|
|
1204
1209
|
}
|
|
1205
1210
|
function getKeyPath() {
|
|
1206
1211
|
return path2.join(getKeyDir(), "master.key");
|
|
@@ -1248,7 +1253,7 @@ async function setMasterKey(key) {
|
|
|
1248
1253
|
await mkdir2(dir, { recursive: true });
|
|
1249
1254
|
const keyPath = getKeyPath();
|
|
1250
1255
|
await writeFile2(keyPath, b64 + "\n", "utf-8");
|
|
1251
|
-
await
|
|
1256
|
+
await chmod2(keyPath, 384);
|
|
1252
1257
|
}
|
|
1253
1258
|
async function deleteMasterKey() {
|
|
1254
1259
|
const keytar = await tryKeytar();
|
|
@@ -1346,7 +1351,7 @@ __export(shard_manager_exports, {
|
|
|
1346
1351
|
shardExists: () => shardExists
|
|
1347
1352
|
});
|
|
1348
1353
|
import path3 from "path";
|
|
1349
|
-
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
1354
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
|
|
1350
1355
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1351
1356
|
function initShardManager(encryptionKey) {
|
|
1352
1357
|
_encryptionKey = encryptionKey;
|
|
@@ -1385,8 +1390,7 @@ function shardExists(projectName) {
|
|
|
1385
1390
|
}
|
|
1386
1391
|
function listShards() {
|
|
1387
1392
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
1388
|
-
|
|
1389
|
-
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1393
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1390
1394
|
}
|
|
1391
1395
|
async function ensureShardSchema(client) {
|
|
1392
1396
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1591,6 +1595,28 @@ __export(store_exports, {
|
|
|
1591
1595
|
vectorToBlob: () => vectorToBlob,
|
|
1592
1596
|
writeMemory: () => writeMemory
|
|
1593
1597
|
});
|
|
1598
|
+
function isBusyError2(err) {
|
|
1599
|
+
if (err instanceof Error) {
|
|
1600
|
+
const msg = err.message.toLowerCase();
|
|
1601
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1602
|
+
}
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
async function retryOnBusy2(fn, label) {
|
|
1606
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1607
|
+
try {
|
|
1608
|
+
return await fn();
|
|
1609
|
+
} catch (err) {
|
|
1610
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1611
|
+
process.stderr.write(
|
|
1612
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1613
|
+
`
|
|
1614
|
+
);
|
|
1615
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
throw new Error("unreachable");
|
|
1619
|
+
}
|
|
1594
1620
|
async function initStore(options) {
|
|
1595
1621
|
if (_flushTimer !== null) {
|
|
1596
1622
|
clearInterval(_flushTimer);
|
|
@@ -1619,14 +1645,17 @@ async function initStore(options) {
|
|
|
1619
1645
|
dbPath,
|
|
1620
1646
|
encryptionKey: hexKey
|
|
1621
1647
|
});
|
|
1622
|
-
await ensureSchema();
|
|
1648
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1623
1649
|
try {
|
|
1624
1650
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1625
1651
|
initShardManager2(hexKey);
|
|
1626
1652
|
} catch {
|
|
1627
1653
|
}
|
|
1628
1654
|
const client = getClient();
|
|
1629
|
-
const vResult = await
|
|
1655
|
+
const vResult = await retryOnBusy2(
|
|
1656
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1657
|
+
"version-query"
|
|
1658
|
+
);
|
|
1630
1659
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1631
1660
|
}
|
|
1632
1661
|
function classifyTier(record) {
|
|
@@ -1669,6 +1698,12 @@ async function writeMemory(record) {
|
|
|
1669
1698
|
supersedes_id: record.supersedes_id ?? null
|
|
1670
1699
|
};
|
|
1671
1700
|
_pendingRecords.push(dbRow);
|
|
1701
|
+
const MAX_PENDING = 1e3;
|
|
1702
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
1703
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
1704
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
1705
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
1706
|
+
}
|
|
1672
1707
|
if (_flushTimer === null) {
|
|
1673
1708
|
_flushTimer = setInterval(() => {
|
|
1674
1709
|
void flushBatch();
|
|
@@ -2000,7 +2035,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
2000
2035
|
return 0;
|
|
2001
2036
|
}
|
|
2002
2037
|
}
|
|
2003
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2038
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2004
2039
|
var init_store = __esm({
|
|
2005
2040
|
"src/lib/store.ts"() {
|
|
2006
2041
|
"use strict";
|
|
@@ -2008,6 +2043,8 @@ var init_store = __esm({
|
|
|
2008
2043
|
init_database();
|
|
2009
2044
|
init_keychain();
|
|
2010
2045
|
init_config();
|
|
2046
|
+
INIT_MAX_RETRIES = 3;
|
|
2047
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
2011
2048
|
_pendingRecords = [];
|
|
2012
2049
|
_batchSize = 20;
|
|
2013
2050
|
_flushIntervalMs = 1e4;
|
|
@@ -2027,7 +2064,7 @@ __export(session_registry_exports, {
|
|
|
2027
2064
|
import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
2028
2065
|
import { execSync } from "child_process";
|
|
2029
2066
|
import path4 from "path";
|
|
2030
|
-
import
|
|
2067
|
+
import os3 from "os";
|
|
2031
2068
|
function registerSession(entry) {
|
|
2032
2069
|
const dir = path4.dirname(REGISTRY_PATH);
|
|
2033
2070
|
if (!existsSync4(dir)) {
|
|
@@ -2073,7 +2110,7 @@ var REGISTRY_PATH;
|
|
|
2073
2110
|
var init_session_registry = __esm({
|
|
2074
2111
|
"src/lib/session-registry.ts"() {
|
|
2075
2112
|
"use strict";
|
|
2076
|
-
REGISTRY_PATH = path4.join(
|
|
2113
|
+
REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
2077
2114
|
}
|
|
2078
2115
|
});
|
|
2079
2116
|
|
|
@@ -2310,7 +2347,7 @@ __export(intercom_queue_exports, {
|
|
|
2310
2347
|
});
|
|
2311
2348
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
2312
2349
|
import path5 from "path";
|
|
2313
|
-
import
|
|
2350
|
+
import os4 from "os";
|
|
2314
2351
|
function ensureDir() {
|
|
2315
2352
|
const dir = path5.dirname(QUEUE_PATH);
|
|
2316
2353
|
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
@@ -2409,10 +2446,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
2409
2446
|
var init_intercom_queue = __esm({
|
|
2410
2447
|
"src/lib/intercom-queue.ts"() {
|
|
2411
2448
|
"use strict";
|
|
2412
|
-
QUEUE_PATH = path5.join(
|
|
2449
|
+
QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
2413
2450
|
MAX_RETRIES2 = 5;
|
|
2414
2451
|
TTL_MS = 60 * 60 * 1e3;
|
|
2415
|
-
INTERCOM_LOG = path5.join(
|
|
2452
|
+
INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
2416
2453
|
}
|
|
2417
2454
|
});
|
|
2418
2455
|
|
|
@@ -2563,10 +2600,10 @@ var init_plan_limits = __esm({
|
|
|
2563
2600
|
// src/lib/notifications.ts
|
|
2564
2601
|
import crypto2 from "crypto";
|
|
2565
2602
|
import path9 from "path";
|
|
2566
|
-
import
|
|
2603
|
+
import os5 from "os";
|
|
2567
2604
|
import {
|
|
2568
2605
|
readFileSync as readFileSync7,
|
|
2569
|
-
readdirSync,
|
|
2606
|
+
readdirSync as readdirSync2,
|
|
2570
2607
|
unlinkSync,
|
|
2571
2608
|
existsSync as existsSync9,
|
|
2572
2609
|
rmdirSync
|
|
@@ -3128,7 +3165,7 @@ __export(tasks_review_exports, {
|
|
|
3128
3165
|
listPendingReviews: () => listPendingReviews
|
|
3129
3166
|
});
|
|
3130
3167
|
import path11 from "path";
|
|
3131
|
-
import { existsSync as existsSync11, readdirSync as
|
|
3168
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
3132
3169
|
async function countPendingReviews() {
|
|
3133
3170
|
const client = getClient();
|
|
3134
3171
|
const result = await client.execute({
|
|
@@ -3340,7 +3377,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3340
3377
|
try {
|
|
3341
3378
|
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3342
3379
|
if (existsSync11(cacheDir)) {
|
|
3343
|
-
for (const f of
|
|
3380
|
+
for (const f of readdirSync3(cacheDir)) {
|
|
3344
3381
|
if (f.startsWith("review-notified-")) {
|
|
3345
3382
|
unlinkSync2(path11.join(cacheDir, f));
|
|
3346
3383
|
}
|
|
@@ -4386,7 +4423,7 @@ __export(tmux_routing_exports, {
|
|
|
4386
4423
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4387
4424
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4388
4425
|
import path15 from "path";
|
|
4389
|
-
import
|
|
4426
|
+
import os6 from "os";
|
|
4390
4427
|
import { fileURLToPath } from "url";
|
|
4391
4428
|
import { unlinkSync as unlinkSync4 } from "fs";
|
|
4392
4429
|
function spawnLockPath(sessionName) {
|
|
@@ -4739,7 +4776,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4739
4776
|
const transport = getTransport();
|
|
4740
4777
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4741
4778
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4742
|
-
const logDir = path15.join(
|
|
4779
|
+
const logDir = path15.join(os6.homedir(), ".exe-os", "session-logs");
|
|
4743
4780
|
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4744
4781
|
if (!existsSync12(logDir)) {
|
|
4745
4782
|
mkdirSync6(logDir, { recursive: true });
|
|
@@ -4755,7 +4792,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4755
4792
|
} catch {
|
|
4756
4793
|
}
|
|
4757
4794
|
try {
|
|
4758
|
-
const claudeJsonPath = path15.join(
|
|
4795
|
+
const claudeJsonPath = path15.join(os6.homedir(), ".claude.json");
|
|
4759
4796
|
let claudeJson = {};
|
|
4760
4797
|
try {
|
|
4761
4798
|
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
@@ -4770,7 +4807,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4770
4807
|
} catch {
|
|
4771
4808
|
}
|
|
4772
4809
|
try {
|
|
4773
|
-
const settingsDir = path15.join(
|
|
4810
|
+
const settingsDir = path15.join(os6.homedir(), ".claude", "projects");
|
|
4774
4811
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4775
4812
|
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
4776
4813
|
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
@@ -4818,7 +4855,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4818
4855
|
let legacyFallbackWarned = false;
|
|
4819
4856
|
if (!useExeAgent && !useBinSymlink) {
|
|
4820
4857
|
const identityPath = path15.join(
|
|
4821
|
-
|
|
4858
|
+
os6.homedir(),
|
|
4822
4859
|
".exe-os",
|
|
4823
4860
|
"identity",
|
|
4824
4861
|
`${employeeName}.md`
|
|
@@ -4848,7 +4885,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4848
4885
|
}
|
|
4849
4886
|
let sessionContextFlag = "";
|
|
4850
4887
|
try {
|
|
4851
|
-
const ctxDir = path15.join(
|
|
4888
|
+
const ctxDir = path15.join(os6.homedir(), ".exe-os", "session-cache");
|
|
4852
4889
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4853
4890
|
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4854
4891
|
const ctxContent = [
|
|
@@ -4959,12 +4996,12 @@ var init_tmux_routing = __esm({
|
|
|
4959
4996
|
init_provider_table();
|
|
4960
4997
|
init_intercom_queue();
|
|
4961
4998
|
init_plan_limits();
|
|
4962
|
-
SPAWN_LOCK_DIR = path15.join(
|
|
4963
|
-
SESSION_CACHE = path15.join(
|
|
4999
|
+
SPAWN_LOCK_DIR = path15.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
5000
|
+
SESSION_CACHE = path15.join(os6.homedir(), ".exe-os", "session-cache");
|
|
4964
5001
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4965
5002
|
VERIFY_PANE_LINES = 200;
|
|
4966
5003
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4967
|
-
INTERCOM_LOG2 = path15.join(
|
|
5004
|
+
INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
4968
5005
|
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4969
5006
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4970
5007
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
@@ -5453,7 +5490,7 @@ __export(agent_signals_exports, {
|
|
|
5453
5490
|
hasUnreadInbox: () => hasUnreadInbox
|
|
5454
5491
|
});
|
|
5455
5492
|
import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
|
|
5456
|
-
import
|
|
5493
|
+
import os7 from "os";
|
|
5457
5494
|
import path16 from "path";
|
|
5458
5495
|
async function hasOpenTasks(client, agentId) {
|
|
5459
5496
|
try {
|
|
@@ -5494,7 +5531,7 @@ async function hasUnreadInbox(client, agentId) {
|
|
|
5494
5531
|
return CONSERVATIVE_ON_ERROR;
|
|
5495
5532
|
}
|
|
5496
5533
|
}
|
|
5497
|
-
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(
|
|
5534
|
+
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os7.homedir(), ".exe-os", "intercom.log")) {
|
|
5498
5535
|
if (!existsSync13(intercomLog)) return false;
|
|
5499
5536
|
try {
|
|
5500
5537
|
const raw = readFileSync10(intercomLog, "utf8");
|
|
@@ -6185,6 +6222,10 @@ import path17 from "path";
|
|
|
6185
6222
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6186
6223
|
function handleData(chunk) {
|
|
6187
6224
|
_buffer += chunk.toString();
|
|
6225
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
6226
|
+
_buffer = "";
|
|
6227
|
+
return;
|
|
6228
|
+
}
|
|
6188
6229
|
let newlineIdx;
|
|
6189
6230
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
6190
6231
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -6492,7 +6533,7 @@ function disconnectClient() {
|
|
|
6492
6533
|
entry.resolve({ error: "Client disconnected" });
|
|
6493
6534
|
}
|
|
6494
6535
|
}
|
|
6495
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
|
|
6536
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
6496
6537
|
var init_exe_daemon_client = __esm({
|
|
6497
6538
|
"src/lib/exe-daemon-client.ts"() {
|
|
6498
6539
|
"use strict";
|
|
@@ -6509,6 +6550,7 @@ var init_exe_daemon_client = __esm({
|
|
|
6509
6550
|
_requestCount = 0;
|
|
6510
6551
|
HEALTH_CHECK_INTERVAL = 100;
|
|
6511
6552
|
_pending = /* @__PURE__ */ new Map();
|
|
6553
|
+
MAX_BUFFER = 1e7;
|
|
6512
6554
|
}
|
|
6513
6555
|
});
|
|
6514
6556
|
|
|
@@ -7250,7 +7292,8 @@ async function wikiRequest(config, path21, method = "GET", body) {
|
|
|
7250
7292
|
const response = await fetch(url, {
|
|
7251
7293
|
method,
|
|
7252
7294
|
headers,
|
|
7253
|
-
body: body ? JSON.stringify(body) : void 0
|
|
7295
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
7296
|
+
signal: AbortSignal.timeout(3e4)
|
|
7254
7297
|
});
|
|
7255
7298
|
if (!response.ok) {
|
|
7256
7299
|
throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
|
|
@@ -7440,7 +7483,7 @@ __export(device_registry_exports, {
|
|
|
7440
7483
|
setFriendlyName: () => setFriendlyName
|
|
7441
7484
|
});
|
|
7442
7485
|
import crypto9 from "crypto";
|
|
7443
|
-
import
|
|
7486
|
+
import os8 from "os";
|
|
7444
7487
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
|
|
7445
7488
|
import path19 from "path";
|
|
7446
7489
|
function getDeviceInfo() {
|
|
@@ -7454,7 +7497,7 @@ function getDeviceInfo() {
|
|
|
7454
7497
|
} catch {
|
|
7455
7498
|
}
|
|
7456
7499
|
}
|
|
7457
|
-
const hostname =
|
|
7500
|
+
const hostname = os8.hostname();
|
|
7458
7501
|
const info = {
|
|
7459
7502
|
deviceId: crypto9.randomUUID(),
|
|
7460
7503
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
@@ -7952,13 +7995,24 @@ var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
|
|
|
7952
7995
|
var _context = null;
|
|
7953
7996
|
var _model = null;
|
|
7954
7997
|
var _llama = null;
|
|
7998
|
+
var MAX_QUEUE_SIZE = 1e3;
|
|
7955
7999
|
var highQueue = [];
|
|
7956
8000
|
var lowQueue = [];
|
|
7957
8001
|
var _processing = false;
|
|
7958
8002
|
var _activeConnections = 0;
|
|
7959
8003
|
var _idleTimer = null;
|
|
7960
8004
|
var _requestsServed = 0;
|
|
8005
|
+
var _droppedRequests = 0;
|
|
7961
8006
|
var _startedAt = Date.now();
|
|
8007
|
+
function enqueue(queue, entry) {
|
|
8008
|
+
if (queue.length >= MAX_QUEUE_SIZE) {
|
|
8009
|
+
queue.shift();
|
|
8010
|
+
_droppedRequests++;
|
|
8011
|
+
process.stderr.write(`[exed] Queue overflow \u2014 dropped oldest request (total dropped: ${_droppedRequests})
|
|
8012
|
+
`);
|
|
8013
|
+
}
|
|
8014
|
+
queue.push(entry);
|
|
8015
|
+
}
|
|
7962
8016
|
async function loadModel() {
|
|
7963
8017
|
const modelPath = path20.join(MODELS_DIR, MODEL_FILE);
|
|
7964
8018
|
if (!existsSync16(modelPath)) {
|
|
@@ -8106,8 +8160,13 @@ function startServer() {
|
|
|
8106
8160
|
_activeConnections++;
|
|
8107
8161
|
resetIdleTimer();
|
|
8108
8162
|
let buffer = "";
|
|
8163
|
+
const MAX_BUFFER2 = 1e7;
|
|
8109
8164
|
socket.on("data", (chunk) => {
|
|
8110
8165
|
buffer += chunk.toString();
|
|
8166
|
+
if (buffer.length > MAX_BUFFER2) {
|
|
8167
|
+
socket.destroy(new Error("Buffer overflow \u2014 payload too large"));
|
|
8168
|
+
return;
|
|
8169
|
+
}
|
|
8111
8170
|
let newlineIdx;
|
|
8112
8171
|
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
8113
8172
|
const line = buffer.slice(0, newlineIdx).trim();
|
|
@@ -8125,9 +8184,9 @@ function startServer() {
|
|
|
8125
8184
|
}
|
|
8126
8185
|
const entry = { request, socket };
|
|
8127
8186
|
if (request.priority === "high") {
|
|
8128
|
-
highQueue
|
|
8187
|
+
enqueue(highQueue, entry);
|
|
8129
8188
|
} else {
|
|
8130
|
-
lowQueue
|
|
8189
|
+
enqueue(lowQueue, entry);
|
|
8131
8190
|
}
|
|
8132
8191
|
void processQueue();
|
|
8133
8192
|
} catch (err) {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
-
}) : x)(function(x) {
|
|
6
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
-
});
|
|
9
3
|
var __esm = (fn, res) => function __init() {
|
|
10
4
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
5
|
};
|
|
@@ -111,6 +105,7 @@ async function ensureSchema() {
|
|
|
111
105
|
const client = getRawClient();
|
|
112
106
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
113
107
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
108
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
114
109
|
try {
|
|
115
110
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
116
111
|
} catch {
|
|
@@ -922,9 +917,10 @@ var init_database = __esm({
|
|
|
922
917
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
923
918
|
import { existsSync } from "fs";
|
|
924
919
|
import path from "path";
|
|
920
|
+
import os from "os";
|
|
925
921
|
import crypto from "crypto";
|
|
926
922
|
function getKeyDir() {
|
|
927
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
923
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
928
924
|
}
|
|
929
925
|
function getKeyPath() {
|
|
930
926
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -983,15 +979,15 @@ __export(config_exports, {
|
|
|
983
979
|
migrateConfig: () => migrateConfig,
|
|
984
980
|
saveConfig: () => saveConfig
|
|
985
981
|
});
|
|
986
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
982
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
987
983
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
988
984
|
import path2 from "path";
|
|
989
|
-
import
|
|
985
|
+
import os2 from "os";
|
|
990
986
|
function resolveDataDir() {
|
|
991
987
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
992
988
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
993
|
-
const newDir = path2.join(
|
|
994
|
-
const legacyDir = path2.join(
|
|
989
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
990
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
995
991
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
996
992
|
try {
|
|
997
993
|
renameSync(legacyDir, newDir);
|
|
@@ -1078,7 +1074,7 @@ async function loadConfig() {
|
|
|
1078
1074
|
normalizeAutoUpdate(migratedCfg);
|
|
1079
1075
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1080
1076
|
if (config.dbPath.startsWith("~")) {
|
|
1081
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
1077
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1082
1078
|
}
|
|
1083
1079
|
return config;
|
|
1084
1080
|
} catch {
|
|
@@ -1109,6 +1105,9 @@ async function saveConfig(config) {
|
|
|
1109
1105
|
await mkdir2(dir, { recursive: true });
|
|
1110
1106
|
const configPath = path2.join(dir, "config.json");
|
|
1111
1107
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1108
|
+
if (config.cloud?.apiKey) {
|
|
1109
|
+
await chmod2(configPath, 384);
|
|
1110
|
+
}
|
|
1112
1111
|
}
|
|
1113
1112
|
async function loadConfigFrom(configPath) {
|
|
1114
1113
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -1224,7 +1223,7 @@ __export(shard_manager_exports, {
|
|
|
1224
1223
|
shardExists: () => shardExists
|
|
1225
1224
|
});
|
|
1226
1225
|
import path3 from "path";
|
|
1227
|
-
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
1226
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
|
|
1228
1227
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1229
1228
|
function initShardManager(encryptionKey) {
|
|
1230
1229
|
_encryptionKey = encryptionKey;
|
|
@@ -1263,8 +1262,7 @@ function shardExists(projectName) {
|
|
|
1263
1262
|
}
|
|
1264
1263
|
function listShards() {
|
|
1265
1264
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
1266
|
-
|
|
1267
|
-
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1265
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1268
1266
|
}
|
|
1269
1267
|
async function ensureShardSchema(client) {
|
|
1270
1268
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1469,6 +1467,28 @@ __export(store_exports, {
|
|
|
1469
1467
|
vectorToBlob: () => vectorToBlob,
|
|
1470
1468
|
writeMemory: () => writeMemory
|
|
1471
1469
|
});
|
|
1470
|
+
function isBusyError2(err) {
|
|
1471
|
+
if (err instanceof Error) {
|
|
1472
|
+
const msg = err.message.toLowerCase();
|
|
1473
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1474
|
+
}
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
async function retryOnBusy2(fn, label) {
|
|
1478
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1479
|
+
try {
|
|
1480
|
+
return await fn();
|
|
1481
|
+
} catch (err) {
|
|
1482
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1483
|
+
process.stderr.write(
|
|
1484
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1485
|
+
`
|
|
1486
|
+
);
|
|
1487
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
throw new Error("unreachable");
|
|
1491
|
+
}
|
|
1472
1492
|
async function initStore(options) {
|
|
1473
1493
|
if (_flushTimer !== null) {
|
|
1474
1494
|
clearInterval(_flushTimer);
|
|
@@ -1497,14 +1517,17 @@ async function initStore(options) {
|
|
|
1497
1517
|
dbPath,
|
|
1498
1518
|
encryptionKey: hexKey
|
|
1499
1519
|
});
|
|
1500
|
-
await ensureSchema();
|
|
1520
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1501
1521
|
try {
|
|
1502
1522
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1503
1523
|
initShardManager2(hexKey);
|
|
1504
1524
|
} catch {
|
|
1505
1525
|
}
|
|
1506
1526
|
const client = getClient();
|
|
1507
|
-
const vResult = await
|
|
1527
|
+
const vResult = await retryOnBusy2(
|
|
1528
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1529
|
+
"version-query"
|
|
1530
|
+
);
|
|
1508
1531
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1509
1532
|
}
|
|
1510
1533
|
function classifyTier(record) {
|
|
@@ -1547,6 +1570,12 @@ async function writeMemory(record) {
|
|
|
1547
1570
|
supersedes_id: record.supersedes_id ?? null
|
|
1548
1571
|
};
|
|
1549
1572
|
_pendingRecords.push(dbRow);
|
|
1573
|
+
const MAX_PENDING = 1e3;
|
|
1574
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
1575
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
1576
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
1577
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
1578
|
+
}
|
|
1550
1579
|
if (_flushTimer === null) {
|
|
1551
1580
|
_flushTimer = setInterval(() => {
|
|
1552
1581
|
void flushBatch();
|
|
@@ -1878,7 +1907,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
1878
1907
|
return 0;
|
|
1879
1908
|
}
|
|
1880
1909
|
}
|
|
1881
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1910
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1882
1911
|
var init_store = __esm({
|
|
1883
1912
|
"src/lib/store.ts"() {
|
|
1884
1913
|
"use strict";
|
|
@@ -1886,6 +1915,8 @@ var init_store = __esm({
|
|
|
1886
1915
|
init_database();
|
|
1887
1916
|
init_keychain();
|
|
1888
1917
|
init_config();
|
|
1918
|
+
INIT_MAX_RETRIES = 3;
|
|
1919
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
1889
1920
|
_pendingRecords = [];
|
|
1890
1921
|
_batchSize = 20;
|
|
1891
1922
|
_flushIntervalMs = 1e4;
|
|
@@ -1993,6 +2024,10 @@ import path4 from "path";
|
|
|
1993
2024
|
import { fileURLToPath } from "url";
|
|
1994
2025
|
function handleData(chunk) {
|
|
1995
2026
|
_buffer += chunk.toString();
|
|
2027
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
2028
|
+
_buffer = "";
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
1996
2031
|
let newlineIdx;
|
|
1997
2032
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1998
2033
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -2300,7 +2335,7 @@ function disconnectClient() {
|
|
|
2300
2335
|
entry.resolve({ error: "Client disconnected" });
|
|
2301
2336
|
}
|
|
2302
2337
|
}
|
|
2303
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
|
|
2338
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
2304
2339
|
var init_exe_daemon_client = __esm({
|
|
2305
2340
|
"src/lib/exe-daemon-client.ts"() {
|
|
2306
2341
|
"use strict";
|
|
@@ -2317,6 +2352,7 @@ var init_exe_daemon_client = __esm({
|
|
|
2317
2352
|
_requestCount = 0;
|
|
2318
2353
|
HEALTH_CHECK_INTERVAL = 100;
|
|
2319
2354
|
_pending = /* @__PURE__ */ new Map();
|
|
2355
|
+
MAX_BUFFER = 1e7;
|
|
2320
2356
|
}
|
|
2321
2357
|
});
|
|
2322
2358
|
|
|
@@ -2445,7 +2481,7 @@ __export(file_grep_exports, {
|
|
|
2445
2481
|
grepProjectFiles: () => grepProjectFiles
|
|
2446
2482
|
});
|
|
2447
2483
|
import { execSync as execSync2 } from "child_process";
|
|
2448
|
-
import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2484
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2449
2485
|
import path6 from "path";
|
|
2450
2486
|
import crypto2 from "crypto";
|
|
2451
2487
|
function hasRipgrep() {
|
|
@@ -2590,7 +2626,7 @@ function collectFiles(root, patterns) {
|
|
|
2590
2626
|
const basename = path6.basename(dir);
|
|
2591
2627
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2592
2628
|
try {
|
|
2593
|
-
const entries =
|
|
2629
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2594
2630
|
for (const entry of entries) {
|
|
2595
2631
|
if (files.length >= MAX_FILES) return;
|
|
2596
2632
|
const rel = path6.join(relative, entry.name);
|