@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/bin/exe-call.js
CHANGED
|
@@ -10,7 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
13
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
14
14
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
15
15
|
import path from "path";
|
|
16
16
|
import os from "os";
|
|
@@ -193,15 +193,20 @@ function addEmployee(employees, employee) {
|
|
|
193
193
|
}
|
|
194
194
|
return [...employees, normalized];
|
|
195
195
|
}
|
|
196
|
+
function findExeBin() {
|
|
197
|
+
try {
|
|
198
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
196
203
|
function registerBinSymlinks(name) {
|
|
197
204
|
const created = [];
|
|
198
205
|
const skipped = [];
|
|
199
206
|
const errors = [];
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
} catch {
|
|
204
|
-
errors.push("Could not find 'exe' in PATH");
|
|
207
|
+
const exeBinPath = findExeBin();
|
|
208
|
+
if (!exeBinPath) {
|
|
209
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
205
210
|
return { created, skipped, errors };
|
|
206
211
|
}
|
|
207
212
|
const binDir = path2.dirname(exeBinPath);
|
|
@@ -249,6 +254,7 @@ __export(employee_templates_exports, {
|
|
|
249
254
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
250
255
|
getSessionPrompt: () => getSessionPrompt,
|
|
251
256
|
getTemplate: () => getTemplate,
|
|
257
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
252
258
|
personalizePrompt: () => personalizePrompt,
|
|
253
259
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
254
260
|
});
|
|
@@ -264,6 +270,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
264
270
|
function getTemplate(name) {
|
|
265
271
|
return TEMPLATES[name];
|
|
266
272
|
}
|
|
273
|
+
function getTemplateByRole(role) {
|
|
274
|
+
const lower = role.toLowerCase();
|
|
275
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
276
|
+
}
|
|
267
277
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
268
278
|
if (templateName === actualName) return prompt;
|
|
269
279
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -907,7 +917,8 @@ if (isMainModule(import.meta.url)) {
|
|
|
907
917
|
const bootPath = path3.join(__dirname, "exe-boot.js");
|
|
908
918
|
try {
|
|
909
919
|
execSync2(`node "${bootPath}"`, { stdio: "inherit" });
|
|
910
|
-
} catch {
|
|
920
|
+
} catch (err) {
|
|
921
|
+
console.error(`Failed to boot exe: ${err instanceof Error ? err.message : String(err)}`);
|
|
911
922
|
process.exit(1);
|
|
912
923
|
}
|
|
913
924
|
process.exit(0);
|
|
@@ -944,9 +955,8 @@ if (isMainModule(import.meta.url)) {
|
|
|
944
955
|
);
|
|
945
956
|
execSync2("claude --dangerously-skip-permissions", { stdio: "inherit", env });
|
|
946
957
|
} catch (err) {
|
|
947
|
-
console.error(
|
|
948
|
-
|
|
949
|
-
);
|
|
958
|
+
console.error(`Failed to launch employee session: ${err instanceof Error ? err.message : String(err)}`);
|
|
959
|
+
console.error("Try running: exe-os team");
|
|
950
960
|
process.exit(1);
|
|
951
961
|
}
|
|
952
962
|
}
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -16,15 +16,15 @@ var __export = (target, all) => {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
// src/lib/config.ts
|
|
19
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
19
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
20
20
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
21
21
|
import path2 from "path";
|
|
22
|
-
import
|
|
22
|
+
import os2 from "os";
|
|
23
23
|
function resolveDataDir() {
|
|
24
24
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
25
25
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
26
|
-
const newDir = path2.join(
|
|
27
|
-
const legacyDir = path2.join(
|
|
26
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
27
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
28
28
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
29
29
|
try {
|
|
30
30
|
renameSync(legacyDir, newDir);
|
|
@@ -111,7 +111,7 @@ async function loadConfig() {
|
|
|
111
111
|
normalizeAutoUpdate(migratedCfg);
|
|
112
112
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
113
113
|
if (config.dbPath.startsWith("~")) {
|
|
114
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
114
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
115
115
|
}
|
|
116
116
|
return config;
|
|
117
117
|
} catch {
|
|
@@ -123,6 +123,9 @@ async function saveConfig(config) {
|
|
|
123
123
|
await mkdir2(dir, { recursive: true });
|
|
124
124
|
const configPath = path2.join(dir, "config.json");
|
|
125
125
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
126
|
+
if (config.cloud?.apiKey) {
|
|
127
|
+
await chmod2(configPath, 384);
|
|
128
|
+
}
|
|
126
129
|
}
|
|
127
130
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
128
131
|
var init_config = __esm({
|
|
@@ -223,24 +226,34 @@ __export(license_exports, {
|
|
|
223
226
|
loadLicense: () => loadLicense,
|
|
224
227
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
225
228
|
saveLicense: () => saveLicense,
|
|
229
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
230
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
226
231
|
validateLicense: () => validateLicense
|
|
227
232
|
});
|
|
228
|
-
import { readFileSync as
|
|
233
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync } from "fs";
|
|
229
234
|
import { randomUUID } from "crypto";
|
|
230
|
-
import
|
|
235
|
+
import path4 from "path";
|
|
231
236
|
import { jwtVerify, importSPKI } from "jose";
|
|
237
|
+
async function fetchRetry(url, init) {
|
|
238
|
+
try {
|
|
239
|
+
return await fetch(url, init);
|
|
240
|
+
} catch {
|
|
241
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
242
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
232
245
|
function loadDeviceId() {
|
|
233
|
-
const deviceJsonPath =
|
|
246
|
+
const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
|
|
234
247
|
try {
|
|
235
|
-
if (
|
|
236
|
-
const data = JSON.parse(
|
|
248
|
+
if (existsSync4(deviceJsonPath)) {
|
|
249
|
+
const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
|
|
237
250
|
if (data.deviceId) return data.deviceId;
|
|
238
251
|
}
|
|
239
252
|
} catch {
|
|
240
253
|
}
|
|
241
254
|
try {
|
|
242
|
-
if (
|
|
243
|
-
const id2 =
|
|
255
|
+
if (existsSync4(DEVICE_ID_PATH)) {
|
|
256
|
+
const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
|
|
244
257
|
if (id2) return id2;
|
|
245
258
|
}
|
|
246
259
|
} catch {
|
|
@@ -252,15 +265,15 @@ function loadDeviceId() {
|
|
|
252
265
|
}
|
|
253
266
|
function loadLicense() {
|
|
254
267
|
try {
|
|
255
|
-
if (!
|
|
256
|
-
return
|
|
268
|
+
if (!existsSync4(LICENSE_PATH)) return null;
|
|
269
|
+
return readFileSync3(LICENSE_PATH, "utf8").trim();
|
|
257
270
|
} catch {
|
|
258
271
|
return null;
|
|
259
272
|
}
|
|
260
273
|
}
|
|
261
274
|
function saveLicense(apiKey) {
|
|
262
275
|
mkdirSync(EXE_AI_DIR, { recursive: true });
|
|
263
|
-
writeFileSync(LICENSE_PATH, apiKey.trim(), "utf8");
|
|
276
|
+
writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
264
277
|
}
|
|
265
278
|
async function verifyLicenseJwt(token) {
|
|
266
279
|
try {
|
|
@@ -286,8 +299,8 @@ async function verifyLicenseJwt(token) {
|
|
|
286
299
|
}
|
|
287
300
|
async function getCachedLicense() {
|
|
288
301
|
try {
|
|
289
|
-
if (!
|
|
290
|
-
const raw = JSON.parse(
|
|
302
|
+
if (!existsSync4(CACHE_PATH)) return null;
|
|
303
|
+
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
291
304
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
292
305
|
return await verifyLicenseJwt(raw.token);
|
|
293
306
|
} catch {
|
|
@@ -296,8 +309,8 @@ async function getCachedLicense() {
|
|
|
296
309
|
}
|
|
297
310
|
function readCachedToken() {
|
|
298
311
|
try {
|
|
299
|
-
if (!
|
|
300
|
-
const raw = JSON.parse(
|
|
312
|
+
if (!existsSync4(CACHE_PATH)) return null;
|
|
313
|
+
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
301
314
|
return typeof raw.token === "string" ? raw.token : null;
|
|
302
315
|
} catch {
|
|
303
316
|
return null;
|
|
@@ -312,7 +325,7 @@ function cacheResponse(token) {
|
|
|
312
325
|
async function validateLicense(apiKey, deviceId) {
|
|
313
326
|
const did = deviceId ?? loadDeviceId();
|
|
314
327
|
try {
|
|
315
|
-
const res = await
|
|
328
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
316
329
|
method: "POST",
|
|
317
330
|
headers: { "Content-Type": "application/json" },
|
|
318
331
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -347,14 +360,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
347
360
|
} catch {
|
|
348
361
|
const cached = await getCachedLicense();
|
|
349
362
|
if (cached) return cached;
|
|
350
|
-
return FREE_LICENSE;
|
|
363
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function getCacheAgeMs() {
|
|
367
|
+
try {
|
|
368
|
+
const { statSync } = __require("fs");
|
|
369
|
+
const s = statSync(CACHE_PATH);
|
|
370
|
+
return Date.now() - s.mtimeMs;
|
|
371
|
+
} catch {
|
|
372
|
+
return Infinity;
|
|
351
373
|
}
|
|
352
374
|
}
|
|
353
375
|
async function checkLicense() {
|
|
354
376
|
const key = loadLicense();
|
|
355
377
|
if (!key) return FREE_LICENSE;
|
|
356
378
|
const cached = await getCachedLicense();
|
|
357
|
-
if (cached) return cached;
|
|
379
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
358
380
|
const deviceId = loadDeviceId();
|
|
359
381
|
return validateLicense(key, deviceId);
|
|
360
382
|
}
|
|
@@ -394,7 +416,7 @@ async function assertVpsLicense(opts) {
|
|
|
394
416
|
let explicitRejection = false;
|
|
395
417
|
let transientFailure = false;
|
|
396
418
|
try {
|
|
397
|
-
const res = await
|
|
419
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
398
420
|
method: "POST",
|
|
399
421
|
headers: { "Content-Type": "application/json" },
|
|
400
422
|
body: JSON.stringify({ apiKey, deviceId }),
|
|
@@ -475,15 +497,37 @@ async function assertVpsLicense(opts) {
|
|
|
475
497
|
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
476
498
|
);
|
|
477
499
|
}
|
|
478
|
-
|
|
500
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
501
|
+
if (_revalTimer) return;
|
|
502
|
+
_revalTimer = setInterval(async () => {
|
|
503
|
+
try {
|
|
504
|
+
const license = await checkLicense();
|
|
505
|
+
if (!license.valid) {
|
|
506
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
507
|
+
}
|
|
508
|
+
} catch {
|
|
509
|
+
}
|
|
510
|
+
}, intervalMs);
|
|
511
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
512
|
+
_revalTimer.unref();
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function stopLicenseRevalidation() {
|
|
516
|
+
if (_revalTimer) {
|
|
517
|
+
clearInterval(_revalTimer);
|
|
518
|
+
_revalTimer = null;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
479
522
|
var init_license = __esm({
|
|
480
523
|
"src/lib/license.ts"() {
|
|
481
524
|
"use strict";
|
|
482
525
|
init_config();
|
|
483
|
-
LICENSE_PATH =
|
|
484
|
-
CACHE_PATH =
|
|
485
|
-
DEVICE_ID_PATH =
|
|
526
|
+
LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
|
|
527
|
+
CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
|
|
528
|
+
DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
|
|
486
529
|
API_BASE = "https://askexe.com/cloud";
|
|
530
|
+
RETRY_DELAY_MS = 500;
|
|
487
531
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
488
532
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
489
533
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -505,6 +549,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
505
549
|
employeeLimit: 1,
|
|
506
550
|
memoryLimit: 5e3
|
|
507
551
|
};
|
|
552
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
553
|
+
_revalTimer = null;
|
|
508
554
|
}
|
|
509
555
|
});
|
|
510
556
|
|
|
@@ -515,11 +561,12 @@ import { createInterface } from "readline";
|
|
|
515
561
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
516
562
|
import { existsSync } from "fs";
|
|
517
563
|
import path from "path";
|
|
564
|
+
import os from "os";
|
|
518
565
|
import crypto from "crypto";
|
|
519
566
|
var SERVICE = "exe-mem";
|
|
520
567
|
var ACCOUNT = "master-key";
|
|
521
568
|
function getKeyDir() {
|
|
522
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
569
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
523
570
|
}
|
|
524
571
|
function getKeyPath() {
|
|
525
572
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -662,6 +709,58 @@ function isMainModule(importMetaUrl) {
|
|
|
662
709
|
}
|
|
663
710
|
}
|
|
664
711
|
|
|
712
|
+
// src/lib/cloud-sync.ts
|
|
713
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
|
|
714
|
+
import path6 from "path";
|
|
715
|
+
import { homedir } from "os";
|
|
716
|
+
|
|
717
|
+
// src/lib/database.ts
|
|
718
|
+
import { createClient } from "@libsql/client";
|
|
719
|
+
|
|
720
|
+
// src/lib/crypto.ts
|
|
721
|
+
import crypto3 from "crypto";
|
|
722
|
+
|
|
723
|
+
// src/lib/compress.ts
|
|
724
|
+
import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
|
|
725
|
+
|
|
726
|
+
// src/lib/plan-limits.ts
|
|
727
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
728
|
+
import path5 from "path";
|
|
729
|
+
|
|
730
|
+
// src/lib/employees.ts
|
|
731
|
+
init_config();
|
|
732
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
733
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
|
|
734
|
+
import { execSync } from "child_process";
|
|
735
|
+
import path3 from "path";
|
|
736
|
+
var EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
737
|
+
|
|
738
|
+
// src/lib/plan-limits.ts
|
|
739
|
+
init_license();
|
|
740
|
+
init_config();
|
|
741
|
+
var CACHE_PATH2 = path5.join(EXE_AI_DIR, "license-cache.json");
|
|
742
|
+
|
|
743
|
+
// src/lib/cloud-sync.ts
|
|
744
|
+
init_license();
|
|
745
|
+
init_config();
|
|
746
|
+
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
747
|
+
var ROSTER_LOCK_PATH = path6.join(EXE_AI_DIR, "roster-merge.lock");
|
|
748
|
+
function assertSecureEndpoint(endpoint) {
|
|
749
|
+
if (endpoint.startsWith("https://")) return;
|
|
750
|
+
if (endpoint.startsWith("http://")) {
|
|
751
|
+
try {
|
|
752
|
+
const parsed = new URL(endpoint);
|
|
753
|
+
if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
|
|
754
|
+
} catch {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
throw new Error(
|
|
758
|
+
`Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
var ROSTER_DELETIONS_PATH = path6.join(EXE_AI_DIR, "roster-deletions.json");
|
|
763
|
+
|
|
665
764
|
// src/bin/exe-cloud.ts
|
|
666
765
|
var BAR = "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550";
|
|
667
766
|
function ask(rl, question) {
|
|
@@ -709,6 +808,7 @@ async function setupMode() {
|
|
|
709
808
|
const orgId = deriveOrgId(masterKey);
|
|
710
809
|
const authTokenHash = hashAuthToken(deriveWsAuthToken(masterKey));
|
|
711
810
|
try {
|
|
811
|
+
assertSecureEndpoint(config.cloud.endpoint);
|
|
712
812
|
const regResp = await fetch(`${config.cloud.endpoint}/api/register-org`, {
|
|
713
813
|
method: "POST",
|
|
714
814
|
headers: { Authorization: `Bearer ${config.cloud.apiKey}`, "Content-Type": "application/json" },
|
|
@@ -730,6 +830,7 @@ async function setupMode() {
|
|
|
730
830
|
const endpoint = await ask(rl, " Endpoint [https://askexe.com/cloud]: ") || "https://askexe.com/cloud";
|
|
731
831
|
console.log(" Validating...");
|
|
732
832
|
try {
|
|
833
|
+
assertSecureEndpoint(endpoint);
|
|
733
834
|
const resp = await fetch(`${endpoint}/auth/verify`, {
|
|
734
835
|
method: "POST",
|
|
735
836
|
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
|
|
@@ -740,7 +841,8 @@ async function setupMode() {
|
|
|
740
841
|
try {
|
|
741
842
|
const { mirrorLicenseKey: mirrorLicenseKey2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
742
843
|
mirrorLicenseKey2(apiKey);
|
|
743
|
-
} catch {
|
|
844
|
+
} catch (err) {
|
|
845
|
+
console.log(` \u26A0 License mirror failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
744
846
|
}
|
|
745
847
|
console.log(" \u2713 Cloud sync configured.\n");
|
|
746
848
|
const masterKey = await getMasterKey();
|
|
@@ -749,6 +851,7 @@ async function setupMode() {
|
|
|
749
851
|
const orgId = deriveOrgId(masterKey);
|
|
750
852
|
const authTokenHash = hashAuthToken(deriveWsAuthToken(masterKey));
|
|
751
853
|
try {
|
|
854
|
+
assertSecureEndpoint(endpoint);
|
|
752
855
|
const regResp = await fetch(`${endpoint}/api/register-org`, {
|
|
753
856
|
method: "POST",
|
|
754
857
|
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
@@ -796,9 +899,11 @@ async function syncMode() {
|
|
|
796
899
|
console.log(" 24-word recovery phrase:");
|
|
797
900
|
console.log(` ${mnemonic}
|
|
798
901
|
`);
|
|
902
|
+
console.log(" \u26A0 Clear your terminal history after copying.\n");
|
|
799
903
|
if (config.cloud?.apiKey) {
|
|
904
|
+
const maskedKey = config.cloud.apiKey.slice(0, 10) + "..." + config.cloud.apiKey.slice(-4);
|
|
800
905
|
console.log(" Cloud API key:");
|
|
801
|
-
console.log(` ${
|
|
906
|
+
console.log(` ${maskedKey}
|
|
802
907
|
`);
|
|
803
908
|
console.log(" Endpoint:");
|
|
804
909
|
console.log(` ${config.cloud.endpoint}
|
package/dist/bin/exe-dispatch.js
CHANGED
|
@@ -335,7 +335,7 @@ var init_database = __esm({
|
|
|
335
335
|
});
|
|
336
336
|
|
|
337
337
|
// src/lib/config.ts
|
|
338
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
338
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
339
339
|
import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
|
|
340
340
|
import path3 from "path";
|
|
341
341
|
import os3 from "os";
|
package/dist/bin/exe-doctor.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -16,15 +10,15 @@ var __export = (target, all) => {
|
|
|
16
10
|
};
|
|
17
11
|
|
|
18
12
|
// src/lib/config.ts
|
|
19
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
20
14
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
21
15
|
import path2 from "path";
|
|
22
|
-
import
|
|
16
|
+
import os2 from "os";
|
|
23
17
|
function resolveDataDir() {
|
|
24
18
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
25
19
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
26
|
-
const newDir = path2.join(
|
|
27
|
-
const legacyDir = path2.join(
|
|
20
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
21
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
28
22
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
29
23
|
try {
|
|
30
24
|
renameSync(legacyDir, newDir);
|
|
@@ -111,7 +105,7 @@ async function loadConfig() {
|
|
|
111
105
|
normalizeAutoUpdate(migratedCfg);
|
|
112
106
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
113
107
|
if (config.dbPath.startsWith("~")) {
|
|
114
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
108
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
115
109
|
}
|
|
116
110
|
return config;
|
|
117
111
|
} catch {
|
|
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
|
|
|
218
212
|
shardExists: () => shardExists
|
|
219
213
|
});
|
|
220
214
|
import path3 from "path";
|
|
221
|
-
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
215
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
|
|
222
216
|
import { createClient as createClient2 } from "@libsql/client";
|
|
223
217
|
function initShardManager(encryptionKey) {
|
|
224
218
|
_encryptionKey = encryptionKey;
|
|
@@ -257,7 +251,6 @@ function shardExists(projectName) {
|
|
|
257
251
|
}
|
|
258
252
|
function listShards() {
|
|
259
253
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
260
|
-
const { readdirSync } = __require("fs");
|
|
261
254
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
262
255
|
}
|
|
263
256
|
async function ensureShardSchema(client) {
|
|
@@ -533,6 +526,7 @@ async function ensureSchema() {
|
|
|
533
526
|
const client = getRawClient();
|
|
534
527
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
535
528
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
529
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
536
530
|
try {
|
|
537
531
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
538
532
|
} catch {
|
|
@@ -1326,11 +1320,12 @@ async function ensureSchema() {
|
|
|
1326
1320
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1327
1321
|
import { existsSync } from "fs";
|
|
1328
1322
|
import path from "path";
|
|
1323
|
+
import os from "os";
|
|
1329
1324
|
import crypto from "crypto";
|
|
1330
1325
|
var SERVICE = "exe-mem";
|
|
1331
1326
|
var ACCOUNT = "master-key";
|
|
1332
1327
|
function getKeyDir() {
|
|
1333
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1328
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1334
1329
|
}
|
|
1335
1330
|
function getKeyPath() {
|
|
1336
1331
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1367,6 +1362,30 @@ async function getMasterKey() {
|
|
|
1367
1362
|
|
|
1368
1363
|
// src/lib/store.ts
|
|
1369
1364
|
init_config();
|
|
1365
|
+
var INIT_MAX_RETRIES = 3;
|
|
1366
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
1367
|
+
function isBusyError2(err) {
|
|
1368
|
+
if (err instanceof Error) {
|
|
1369
|
+
const msg = err.message.toLowerCase();
|
|
1370
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1371
|
+
}
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
async function retryOnBusy2(fn, label) {
|
|
1375
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1376
|
+
try {
|
|
1377
|
+
return await fn();
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1380
|
+
process.stderr.write(
|
|
1381
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1382
|
+
`
|
|
1383
|
+
);
|
|
1384
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
throw new Error("unreachable");
|
|
1388
|
+
}
|
|
1370
1389
|
var _pendingRecords = [];
|
|
1371
1390
|
var _batchSize = 20;
|
|
1372
1391
|
var _flushIntervalMs = 1e4;
|
|
@@ -1401,14 +1420,17 @@ async function initStore(options) {
|
|
|
1401
1420
|
dbPath,
|
|
1402
1421
|
encryptionKey: hexKey
|
|
1403
1422
|
});
|
|
1404
|
-
await ensureSchema();
|
|
1423
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1405
1424
|
try {
|
|
1406
1425
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1407
1426
|
initShardManager2(hexKey);
|
|
1408
1427
|
} catch {
|
|
1409
1428
|
}
|
|
1410
1429
|
const client = getClient();
|
|
1411
|
-
const vResult = await
|
|
1430
|
+
const vResult = await retryOnBusy2(
|
|
1431
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1432
|
+
"version-query"
|
|
1433
|
+
);
|
|
1412
1434
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1413
1435
|
}
|
|
1414
1436
|
|