@askexenow/exe-os 0.8.38 → 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 +46 -10
- package/dist/bin/backfill-responses.js +46 -10
- package/dist/bin/backfill-vectors.js +42 -8
- package/dist/bin/cleanup-stale-review-tasks.js +37 -8
- package/dist/bin/cli.js +281 -154
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +39 -5
- package/dist/bin/exe-boot.js +237 -111
- package/dist/bin/exe-call.js +11 -6
- package/dist/bin/exe-cloud.js +99 -28
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +37 -8
- package/dist/bin/exe-export-behaviors.js +39 -10
- package/dist/bin/exe-forget.js +38 -9
- package/dist/bin/exe-gateway.js +109 -42
- package/dist/bin/exe-heartbeat.js +49 -20
- package/dist/bin/exe-kill.js +39 -10
- package/dist/bin/exe-launch-agent.js +58 -22
- package/dist/bin/exe-link.js +184 -85
- package/dist/bin/exe-new-employee.js +21 -7
- package/dist/bin/exe-pending-messages.js +46 -17
- package/dist/bin/exe-pending-notifications.js +37 -8
- package/dist/bin/exe-pending-reviews.js +47 -18
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +34 -5
- package/dist/bin/exe-search.js +47 -10
- package/dist/bin/exe-session-cleanup.js +56 -19
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +34 -5
- package/dist/bin/exe-team.js +34 -5
- package/dist/bin/git-sweep.js +38 -9
- package/dist/bin/graph-backfill.js +37 -8
- package/dist/bin/graph-export.js +37 -8
- package/dist/bin/install.js +1 -1
- package/dist/bin/scan-tasks.js +40 -11
- package/dist/bin/setup.js +58 -24
- package/dist/bin/shard-migrate.js +37 -8
- package/dist/bin/wiki-sync.js +39 -9
- package/dist/gateway/index.js +102 -37
- package/dist/hooks/bug-report-worker.js +62 -28
- package/dist/hooks/commit-complete.js +38 -9
- package/dist/hooks/error-recall.js +49 -8
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +151 -37
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +39 -9
- package/dist/hooks/notification.js +37 -7
- package/dist/hooks/post-compact.js +37 -7
- package/dist/hooks/pre-compact.js +35 -6
- package/dist/hooks/pre-tool-use.js +52 -14
- package/dist/hooks/prompt-ingest-worker.js +56 -10
- package/dist/hooks/prompt-submit.js +61 -23
- package/dist/hooks/response-ingest-worker.js +57 -11
- package/dist/hooks/session-end.js +43 -10
- package/dist/hooks/session-start.js +46 -8
- package/dist/hooks/stop.js +37 -7
- package/dist/hooks/subagent-stop.js +37 -7
- package/dist/hooks/summary-worker.js +317 -99
- package/dist/index.js +87 -22
- package/dist/lib/cloud-sync.js +172 -78
- 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/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +71 -28
- package/dist/lib/hybrid-search.js +47 -10
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +13 -4
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +37 -8
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +37 -8
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +97 -43
- 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 +1 -1
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +35 -6
- package/dist/tui/App.js +177 -95
- package/package.json +3 -3
|
@@ -4,15 +4,15 @@ var __esm = (fn, res) => function __init() {
|
|
|
4
4
|
};
|
|
5
5
|
|
|
6
6
|
// src/lib/config.ts
|
|
7
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
7
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
8
8
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
9
9
|
import path2 from "path";
|
|
10
|
-
import
|
|
10
|
+
import os2 from "os";
|
|
11
11
|
function resolveDataDir() {
|
|
12
12
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
13
13
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
14
|
-
const newDir = path2.join(
|
|
15
|
-
const legacyDir = path2.join(
|
|
14
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
15
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
16
16
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
17
17
|
try {
|
|
18
18
|
renameSync(legacyDir, newDir);
|
|
@@ -110,6 +110,7 @@ import { createClient } from "@libsql/client";
|
|
|
110
110
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
111
111
|
import { existsSync } from "fs";
|
|
112
112
|
import path from "path";
|
|
113
|
+
import os from "os";
|
|
113
114
|
import crypto from "crypto";
|
|
114
115
|
|
|
115
116
|
// src/lib/store.ts
|
package/dist/lib/database.js
CHANGED
|
@@ -88,6 +88,7 @@ async function ensureSchema() {
|
|
|
88
88
|
const client = getRawClient();
|
|
89
89
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
90
90
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
91
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
91
92
|
try {
|
|
92
93
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
93
94
|
} catch {
|
|
@@ -110,6 +110,7 @@ async function ensureSchema() {
|
|
|
110
110
|
const client = getRawClient();
|
|
111
111
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
112
112
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
113
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
113
114
|
try {
|
|
114
115
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
115
116
|
} catch {
|
|
@@ -924,7 +925,7 @@ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync as
|
|
|
924
925
|
import path2 from "path";
|
|
925
926
|
|
|
926
927
|
// src/lib/config.ts
|
|
927
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
928
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
928
929
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
929
930
|
import path from "path";
|
|
930
931
|
import os from "os";
|
package/dist/lib/embedder.js
CHANGED
|
@@ -24,7 +24,7 @@ __export(config_exports, {
|
|
|
24
24
|
migrateConfig: () => migrateConfig,
|
|
25
25
|
saveConfig: () => saveConfig
|
|
26
26
|
});
|
|
27
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
27
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
28
28
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
29
29
|
import path from "path";
|
|
30
30
|
import os from "os";
|
|
@@ -150,6 +150,9 @@ async function saveConfig(config) {
|
|
|
150
150
|
await mkdir(dir, { recursive: true });
|
|
151
151
|
const configPath = path.join(dir, "config.json");
|
|
152
152
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
153
|
+
if (config.cloud?.apiKey) {
|
|
154
|
+
await chmod(configPath, 384);
|
|
155
|
+
}
|
|
153
156
|
}
|
|
154
157
|
async function loadConfigFrom(configPath) {
|
|
155
158
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -274,8 +277,13 @@ var _buffer = "";
|
|
|
274
277
|
var _requestCount = 0;
|
|
275
278
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
276
279
|
var _pending = /* @__PURE__ */ new Map();
|
|
280
|
+
var MAX_BUFFER = 1e7;
|
|
277
281
|
function handleData(chunk) {
|
|
278
282
|
_buffer += chunk.toString();
|
|
283
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
284
|
+
_buffer = "";
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
279
287
|
let newlineIdx;
|
|
280
288
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
281
289
|
const line = _buffer.slice(0, newlineIdx).trim();
|
package/dist/lib/employees.js
CHANGED
|
@@ -5,7 +5,7 @@ import { execSync } from "child_process";
|
|
|
5
5
|
import path2 from "path";
|
|
6
6
|
|
|
7
7
|
// src/lib/config.ts
|
|
8
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
8
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
9
9
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
10
10
|
import path from "path";
|
|
11
11
|
import os from "os";
|
|
@@ -165,15 +165,20 @@ function addEmployee(employees, employee) {
|
|
|
165
165
|
}
|
|
166
166
|
return [...employees, normalized];
|
|
167
167
|
}
|
|
168
|
+
function findExeBin() {
|
|
169
|
+
try {
|
|
170
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
168
175
|
function registerBinSymlinks(name) {
|
|
169
176
|
const created = [];
|
|
170
177
|
const skipped = [];
|
|
171
178
|
const errors = [];
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
} catch {
|
|
176
|
-
errors.push("Could not find 'exe' in PATH");
|
|
179
|
+
const exeBinPath = findExeBin();
|
|
180
|
+
if (!exeBinPath) {
|
|
181
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
177
182
|
return { created, skipped, errors };
|
|
178
183
|
}
|
|
179
184
|
const binDir = path2.dirname(exeBinPath);
|
|
@@ -7,7 +7,7 @@ import path2 from "path";
|
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
9
|
// src/lib/config.ts
|
|
10
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
10
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
11
11
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
12
12
|
import path from "path";
|
|
13
13
|
import os from "os";
|
|
@@ -110,8 +110,13 @@ var _buffer = "";
|
|
|
110
110
|
var _requestCount = 0;
|
|
111
111
|
var HEALTH_CHECK_INTERVAL = 100;
|
|
112
112
|
var _pending = /* @__PURE__ */ new Map();
|
|
113
|
+
var MAX_BUFFER = 1e7;
|
|
113
114
|
function handleData(chunk) {
|
|
114
115
|
_buffer += chunk.toString();
|
|
116
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
117
|
+
_buffer = "";
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
115
120
|
let newlineIdx;
|
|
116
121
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
117
122
|
const line = _buffer.slice(0, newlineIdx).trim();
|
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();
|
|
@@ -1590,6 +1595,28 @@ __export(store_exports, {
|
|
|
1590
1595
|
vectorToBlob: () => vectorToBlob,
|
|
1591
1596
|
writeMemory: () => writeMemory
|
|
1592
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
|
+
}
|
|
1593
1620
|
async function initStore(options) {
|
|
1594
1621
|
if (_flushTimer !== null) {
|
|
1595
1622
|
clearInterval(_flushTimer);
|
|
@@ -1618,14 +1645,17 @@ async function initStore(options) {
|
|
|
1618
1645
|
dbPath,
|
|
1619
1646
|
encryptionKey: hexKey
|
|
1620
1647
|
});
|
|
1621
|
-
await ensureSchema();
|
|
1648
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1622
1649
|
try {
|
|
1623
1650
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1624
1651
|
initShardManager2(hexKey);
|
|
1625
1652
|
} catch {
|
|
1626
1653
|
}
|
|
1627
1654
|
const client = getClient();
|
|
1628
|
-
const vResult = await
|
|
1655
|
+
const vResult = await retryOnBusy2(
|
|
1656
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1657
|
+
"version-query"
|
|
1658
|
+
);
|
|
1629
1659
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1630
1660
|
}
|
|
1631
1661
|
function classifyTier(record) {
|
|
@@ -2005,7 +2035,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
2005
2035
|
return 0;
|
|
2006
2036
|
}
|
|
2007
2037
|
}
|
|
2008
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2038
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2009
2039
|
var init_store = __esm({
|
|
2010
2040
|
"src/lib/store.ts"() {
|
|
2011
2041
|
"use strict";
|
|
@@ -2013,6 +2043,8 @@ var init_store = __esm({
|
|
|
2013
2043
|
init_database();
|
|
2014
2044
|
init_keychain();
|
|
2015
2045
|
init_config();
|
|
2046
|
+
INIT_MAX_RETRIES = 3;
|
|
2047
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
2016
2048
|
_pendingRecords = [];
|
|
2017
2049
|
_batchSize = 20;
|
|
2018
2050
|
_flushIntervalMs = 1e4;
|
|
@@ -2032,7 +2064,7 @@ __export(session_registry_exports, {
|
|
|
2032
2064
|
import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
2033
2065
|
import { execSync } from "child_process";
|
|
2034
2066
|
import path4 from "path";
|
|
2035
|
-
import
|
|
2067
|
+
import os3 from "os";
|
|
2036
2068
|
function registerSession(entry) {
|
|
2037
2069
|
const dir = path4.dirname(REGISTRY_PATH);
|
|
2038
2070
|
if (!existsSync4(dir)) {
|
|
@@ -2078,7 +2110,7 @@ var REGISTRY_PATH;
|
|
|
2078
2110
|
var init_session_registry = __esm({
|
|
2079
2111
|
"src/lib/session-registry.ts"() {
|
|
2080
2112
|
"use strict";
|
|
2081
|
-
REGISTRY_PATH = path4.join(
|
|
2113
|
+
REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
2082
2114
|
}
|
|
2083
2115
|
});
|
|
2084
2116
|
|
|
@@ -2315,7 +2347,7 @@ __export(intercom_queue_exports, {
|
|
|
2315
2347
|
});
|
|
2316
2348
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
2317
2349
|
import path5 from "path";
|
|
2318
|
-
import
|
|
2350
|
+
import os4 from "os";
|
|
2319
2351
|
function ensureDir() {
|
|
2320
2352
|
const dir = path5.dirname(QUEUE_PATH);
|
|
2321
2353
|
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
@@ -2414,10 +2446,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
2414
2446
|
var init_intercom_queue = __esm({
|
|
2415
2447
|
"src/lib/intercom-queue.ts"() {
|
|
2416
2448
|
"use strict";
|
|
2417
|
-
QUEUE_PATH = path5.join(
|
|
2449
|
+
QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
2418
2450
|
MAX_RETRIES2 = 5;
|
|
2419
2451
|
TTL_MS = 60 * 60 * 1e3;
|
|
2420
|
-
INTERCOM_LOG = path5.join(
|
|
2452
|
+
INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
2421
2453
|
}
|
|
2422
2454
|
});
|
|
2423
2455
|
|
|
@@ -2568,7 +2600,7 @@ var init_plan_limits = __esm({
|
|
|
2568
2600
|
// src/lib/notifications.ts
|
|
2569
2601
|
import crypto2 from "crypto";
|
|
2570
2602
|
import path9 from "path";
|
|
2571
|
-
import
|
|
2603
|
+
import os5 from "os";
|
|
2572
2604
|
import {
|
|
2573
2605
|
readFileSync as readFileSync7,
|
|
2574
2606
|
readdirSync as readdirSync2,
|
|
@@ -4391,7 +4423,7 @@ __export(tmux_routing_exports, {
|
|
|
4391
4423
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4392
4424
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4393
4425
|
import path15 from "path";
|
|
4394
|
-
import
|
|
4426
|
+
import os6 from "os";
|
|
4395
4427
|
import { fileURLToPath } from "url";
|
|
4396
4428
|
import { unlinkSync as unlinkSync4 } from "fs";
|
|
4397
4429
|
function spawnLockPath(sessionName) {
|
|
@@ -4744,7 +4776,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4744
4776
|
const transport = getTransport();
|
|
4745
4777
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4746
4778
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4747
|
-
const logDir = path15.join(
|
|
4779
|
+
const logDir = path15.join(os6.homedir(), ".exe-os", "session-logs");
|
|
4748
4780
|
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4749
4781
|
if (!existsSync12(logDir)) {
|
|
4750
4782
|
mkdirSync6(logDir, { recursive: true });
|
|
@@ -4760,7 +4792,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4760
4792
|
} catch {
|
|
4761
4793
|
}
|
|
4762
4794
|
try {
|
|
4763
|
-
const claudeJsonPath = path15.join(
|
|
4795
|
+
const claudeJsonPath = path15.join(os6.homedir(), ".claude.json");
|
|
4764
4796
|
let claudeJson = {};
|
|
4765
4797
|
try {
|
|
4766
4798
|
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
@@ -4775,7 +4807,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4775
4807
|
} catch {
|
|
4776
4808
|
}
|
|
4777
4809
|
try {
|
|
4778
|
-
const settingsDir = path15.join(
|
|
4810
|
+
const settingsDir = path15.join(os6.homedir(), ".claude", "projects");
|
|
4779
4811
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4780
4812
|
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
4781
4813
|
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
@@ -4823,7 +4855,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4823
4855
|
let legacyFallbackWarned = false;
|
|
4824
4856
|
if (!useExeAgent && !useBinSymlink) {
|
|
4825
4857
|
const identityPath = path15.join(
|
|
4826
|
-
|
|
4858
|
+
os6.homedir(),
|
|
4827
4859
|
".exe-os",
|
|
4828
4860
|
"identity",
|
|
4829
4861
|
`${employeeName}.md`
|
|
@@ -4853,7 +4885,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4853
4885
|
}
|
|
4854
4886
|
let sessionContextFlag = "";
|
|
4855
4887
|
try {
|
|
4856
|
-
const ctxDir = path15.join(
|
|
4888
|
+
const ctxDir = path15.join(os6.homedir(), ".exe-os", "session-cache");
|
|
4857
4889
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4858
4890
|
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4859
4891
|
const ctxContent = [
|
|
@@ -4964,12 +4996,12 @@ var init_tmux_routing = __esm({
|
|
|
4964
4996
|
init_provider_table();
|
|
4965
4997
|
init_intercom_queue();
|
|
4966
4998
|
init_plan_limits();
|
|
4967
|
-
SPAWN_LOCK_DIR = path15.join(
|
|
4968
|
-
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");
|
|
4969
5001
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4970
5002
|
VERIFY_PANE_LINES = 200;
|
|
4971
5003
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4972
|
-
INTERCOM_LOG2 = path15.join(
|
|
5004
|
+
INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
4973
5005
|
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4974
5006
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4975
5007
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
@@ -5458,7 +5490,7 @@ __export(agent_signals_exports, {
|
|
|
5458
5490
|
hasUnreadInbox: () => hasUnreadInbox
|
|
5459
5491
|
});
|
|
5460
5492
|
import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
|
|
5461
|
-
import
|
|
5493
|
+
import os7 from "os";
|
|
5462
5494
|
import path16 from "path";
|
|
5463
5495
|
async function hasOpenTasks(client, agentId) {
|
|
5464
5496
|
try {
|
|
@@ -5499,7 +5531,7 @@ async function hasUnreadInbox(client, agentId) {
|
|
|
5499
5531
|
return CONSERVATIVE_ON_ERROR;
|
|
5500
5532
|
}
|
|
5501
5533
|
}
|
|
5502
|
-
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")) {
|
|
5503
5535
|
if (!existsSync13(intercomLog)) return false;
|
|
5504
5536
|
try {
|
|
5505
5537
|
const raw = readFileSync10(intercomLog, "utf8");
|
|
@@ -6190,6 +6222,10 @@ import path17 from "path";
|
|
|
6190
6222
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6191
6223
|
function handleData(chunk) {
|
|
6192
6224
|
_buffer += chunk.toString();
|
|
6225
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
6226
|
+
_buffer = "";
|
|
6227
|
+
return;
|
|
6228
|
+
}
|
|
6193
6229
|
let newlineIdx;
|
|
6194
6230
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
6195
6231
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -6497,7 +6533,7 @@ function disconnectClient() {
|
|
|
6497
6533
|
entry.resolve({ error: "Client disconnected" });
|
|
6498
6534
|
}
|
|
6499
6535
|
}
|
|
6500
|
-
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;
|
|
6501
6537
|
var init_exe_daemon_client = __esm({
|
|
6502
6538
|
"src/lib/exe-daemon-client.ts"() {
|
|
6503
6539
|
"use strict";
|
|
@@ -6514,6 +6550,7 @@ var init_exe_daemon_client = __esm({
|
|
|
6514
6550
|
_requestCount = 0;
|
|
6515
6551
|
HEALTH_CHECK_INTERVAL = 100;
|
|
6516
6552
|
_pending = /* @__PURE__ */ new Map();
|
|
6553
|
+
MAX_BUFFER = 1e7;
|
|
6517
6554
|
}
|
|
6518
6555
|
});
|
|
6519
6556
|
|
|
@@ -7255,7 +7292,8 @@ async function wikiRequest(config, path21, method = "GET", body) {
|
|
|
7255
7292
|
const response = await fetch(url, {
|
|
7256
7293
|
method,
|
|
7257
7294
|
headers,
|
|
7258
|
-
body: body ? JSON.stringify(body) : void 0
|
|
7295
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
7296
|
+
signal: AbortSignal.timeout(3e4)
|
|
7259
7297
|
});
|
|
7260
7298
|
if (!response.ok) {
|
|
7261
7299
|
throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
|
|
@@ -7445,7 +7483,7 @@ __export(device_registry_exports, {
|
|
|
7445
7483
|
setFriendlyName: () => setFriendlyName
|
|
7446
7484
|
});
|
|
7447
7485
|
import crypto9 from "crypto";
|
|
7448
|
-
import
|
|
7486
|
+
import os8 from "os";
|
|
7449
7487
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
|
|
7450
7488
|
import path19 from "path";
|
|
7451
7489
|
function getDeviceInfo() {
|
|
@@ -7459,7 +7497,7 @@ function getDeviceInfo() {
|
|
|
7459
7497
|
} catch {
|
|
7460
7498
|
}
|
|
7461
7499
|
}
|
|
7462
|
-
const hostname =
|
|
7500
|
+
const hostname = os8.hostname();
|
|
7463
7501
|
const info = {
|
|
7464
7502
|
deviceId: crypto9.randomUUID(),
|
|
7465
7503
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
@@ -8122,8 +8160,13 @@ function startServer() {
|
|
|
8122
8160
|
_activeConnections++;
|
|
8123
8161
|
resetIdleTimer();
|
|
8124
8162
|
let buffer = "";
|
|
8163
|
+
const MAX_BUFFER2 = 1e7;
|
|
8125
8164
|
socket.on("data", (chunk) => {
|
|
8126
8165
|
buffer += chunk.toString();
|
|
8166
|
+
if (buffer.length > MAX_BUFFER2) {
|
|
8167
|
+
socket.destroy(new Error("Buffer overflow \u2014 payload too large"));
|
|
8168
|
+
return;
|
|
8169
|
+
}
|
|
8127
8170
|
let newlineIdx;
|
|
8128
8171
|
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
8129
8172
|
const line = buffer.slice(0, newlineIdx).trim();
|
|
@@ -105,6 +105,7 @@ async function ensureSchema() {
|
|
|
105
105
|
const client = getRawClient();
|
|
106
106
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
107
107
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
108
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
108
109
|
try {
|
|
109
110
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
110
111
|
} catch {
|
|
@@ -916,9 +917,10 @@ var init_database = __esm({
|
|
|
916
917
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
917
918
|
import { existsSync } from "fs";
|
|
918
919
|
import path from "path";
|
|
920
|
+
import os from "os";
|
|
919
921
|
import crypto from "crypto";
|
|
920
922
|
function getKeyDir() {
|
|
921
|
-
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");
|
|
922
924
|
}
|
|
923
925
|
function getKeyPath() {
|
|
924
926
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -977,15 +979,15 @@ __export(config_exports, {
|
|
|
977
979
|
migrateConfig: () => migrateConfig,
|
|
978
980
|
saveConfig: () => saveConfig
|
|
979
981
|
});
|
|
980
|
-
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";
|
|
981
983
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
982
984
|
import path2 from "path";
|
|
983
|
-
import
|
|
985
|
+
import os2 from "os";
|
|
984
986
|
function resolveDataDir() {
|
|
985
987
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
986
988
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
987
|
-
const newDir = path2.join(
|
|
988
|
-
const legacyDir = path2.join(
|
|
989
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
990
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
989
991
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
990
992
|
try {
|
|
991
993
|
renameSync(legacyDir, newDir);
|
|
@@ -1072,7 +1074,7 @@ async function loadConfig() {
|
|
|
1072
1074
|
normalizeAutoUpdate(migratedCfg);
|
|
1073
1075
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1074
1076
|
if (config.dbPath.startsWith("~")) {
|
|
1075
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
1077
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1076
1078
|
}
|
|
1077
1079
|
return config;
|
|
1078
1080
|
} catch {
|
|
@@ -1103,6 +1105,9 @@ async function saveConfig(config) {
|
|
|
1103
1105
|
await mkdir2(dir, { recursive: true });
|
|
1104
1106
|
const configPath = path2.join(dir, "config.json");
|
|
1105
1107
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1108
|
+
if (config.cloud?.apiKey) {
|
|
1109
|
+
await chmod2(configPath, 384);
|
|
1110
|
+
}
|
|
1106
1111
|
}
|
|
1107
1112
|
async function loadConfigFrom(configPath) {
|
|
1108
1113
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -1462,6 +1467,28 @@ __export(store_exports, {
|
|
|
1462
1467
|
vectorToBlob: () => vectorToBlob,
|
|
1463
1468
|
writeMemory: () => writeMemory
|
|
1464
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
|
+
}
|
|
1465
1492
|
async function initStore(options) {
|
|
1466
1493
|
if (_flushTimer !== null) {
|
|
1467
1494
|
clearInterval(_flushTimer);
|
|
@@ -1490,14 +1517,17 @@ async function initStore(options) {
|
|
|
1490
1517
|
dbPath,
|
|
1491
1518
|
encryptionKey: hexKey
|
|
1492
1519
|
});
|
|
1493
|
-
await ensureSchema();
|
|
1520
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1494
1521
|
try {
|
|
1495
1522
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1496
1523
|
initShardManager2(hexKey);
|
|
1497
1524
|
} catch {
|
|
1498
1525
|
}
|
|
1499
1526
|
const client = getClient();
|
|
1500
|
-
const vResult = await
|
|
1527
|
+
const vResult = await retryOnBusy2(
|
|
1528
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1529
|
+
"version-query"
|
|
1530
|
+
);
|
|
1501
1531
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1502
1532
|
}
|
|
1503
1533
|
function classifyTier(record) {
|
|
@@ -1877,7 +1907,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
1877
1907
|
return 0;
|
|
1878
1908
|
}
|
|
1879
1909
|
}
|
|
1880
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1910
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1881
1911
|
var init_store = __esm({
|
|
1882
1912
|
"src/lib/store.ts"() {
|
|
1883
1913
|
"use strict";
|
|
@@ -1885,6 +1915,8 @@ var init_store = __esm({
|
|
|
1885
1915
|
init_database();
|
|
1886
1916
|
init_keychain();
|
|
1887
1917
|
init_config();
|
|
1918
|
+
INIT_MAX_RETRIES = 3;
|
|
1919
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
1888
1920
|
_pendingRecords = [];
|
|
1889
1921
|
_batchSize = 20;
|
|
1890
1922
|
_flushIntervalMs = 1e4;
|
|
@@ -1992,6 +2024,10 @@ import path4 from "path";
|
|
|
1992
2024
|
import { fileURLToPath } from "url";
|
|
1993
2025
|
function handleData(chunk) {
|
|
1994
2026
|
_buffer += chunk.toString();
|
|
2027
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
2028
|
+
_buffer = "";
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
1995
2031
|
let newlineIdx;
|
|
1996
2032
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1997
2033
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -2299,7 +2335,7 @@ function disconnectClient() {
|
|
|
2299
2335
|
entry.resolve({ error: "Client disconnected" });
|
|
2300
2336
|
}
|
|
2301
2337
|
}
|
|
2302
|
-
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;
|
|
2303
2339
|
var init_exe_daemon_client = __esm({
|
|
2304
2340
|
"src/lib/exe-daemon-client.ts"() {
|
|
2305
2341
|
"use strict";
|
|
@@ -2316,6 +2352,7 @@ var init_exe_daemon_client = __esm({
|
|
|
2316
2352
|
_requestCount = 0;
|
|
2317
2353
|
HEALTH_CHECK_INTERVAL = 100;
|
|
2318
2354
|
_pending = /* @__PURE__ */ new Map();
|
|
2355
|
+
MAX_BUFFER = 1e7;
|
|
2319
2356
|
}
|
|
2320
2357
|
});
|
|
2321
2358
|
|
package/dist/lib/identity.js
CHANGED
|
@@ -5,7 +5,7 @@ import path2 from "path";
|
|
|
5
5
|
import { createHash } from "crypto";
|
|
6
6
|
|
|
7
7
|
// src/lib/config.ts
|
|
8
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
8
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
9
9
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
10
10
|
import path from "path";
|
|
11
11
|
import os from "os";
|
package/dist/lib/keychain.js
CHANGED
|
@@ -9,11 +9,12 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
10
10
|
import { existsSync } from "fs";
|
|
11
11
|
import path from "path";
|
|
12
|
+
import os from "os";
|
|
12
13
|
import crypto from "crypto";
|
|
13
14
|
var SERVICE = "exe-mem";
|
|
14
15
|
var ACCOUNT = "master-key";
|
|
15
16
|
function getKeyDir() {
|
|
16
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
17
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
17
18
|
}
|
|
18
19
|
function getKeyPath() {
|
|
19
20
|
return path.join(getKeyDir(), "master.key");
|