@askexenow/exe-os 0.9.21 → 0.9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +17 -4
- package/dist/bin/backfill-responses.js +17 -4
- package/dist/bin/backfill-vectors.js +2 -2
- package/dist/bin/cleanup-stale-review-tasks.js +17 -4
- package/dist/bin/cli.js +378 -171
- package/dist/bin/exe-assign.js +17 -4
- package/dist/bin/exe-boot.js +2 -2
- package/dist/bin/exe-dispatch.js +17 -4
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +17 -4
- package/dist/bin/exe-forget.js +17 -4
- package/dist/bin/exe-gateway.js +17 -4
- package/dist/bin/exe-heartbeat.js +17 -4
- package/dist/bin/exe-kill.js +17 -4
- package/dist/bin/exe-launch-agent.js +17 -4
- package/dist/bin/exe-pending-messages.js +17 -4
- package/dist/bin/exe-pending-notifications.js +17 -4
- package/dist/bin/exe-pending-reviews.js +17 -4
- package/dist/bin/exe-review.js +17 -4
- package/dist/bin/exe-search.js +23 -8
- package/dist/bin/exe-session-cleanup.js +17 -4
- package/dist/bin/exe-start-codex.js +209 -32
- package/dist/bin/exe-start-opencode.js +17 -4
- package/dist/bin/exe-status.js +17 -4
- package/dist/bin/exe-team.js +17 -4
- package/dist/bin/git-sweep.js +17 -4
- package/dist/bin/graph-backfill.js +17 -4
- package/dist/bin/graph-export.js +17 -4
- package/dist/bin/install.js +42 -0
- package/dist/bin/intercom-check.js +17 -4
- package/dist/bin/scan-tasks.js +17 -4
- package/dist/bin/shard-migrate.js +17 -4
- package/dist/bin/update.js +187 -42
- package/dist/gateway/index.js +17 -4
- package/dist/hooks/bug-report-worker.js +793 -150
- package/dist/hooks/codex-stop-task-finalizer.js +3020 -2375
- package/dist/hooks/commit-complete.js +156 -6
- package/dist/hooks/error-recall.js +23 -8
- package/dist/hooks/ingest.js +17 -4
- package/dist/hooks/instructions-loaded.js +17 -4
- package/dist/hooks/notification.js +17 -4
- package/dist/hooks/post-compact.js +17 -4
- package/dist/hooks/post-tool-combined.js +23 -8
- package/dist/hooks/pre-compact.js +156 -8
- package/dist/hooks/pre-tool-use.js +21 -12
- package/dist/hooks/prompt-submit.js +23 -8
- package/dist/hooks/session-end.js +156 -8
- package/dist/hooks/session-start.js +23 -8
- package/dist/hooks/stop.js +306 -9
- package/dist/hooks/subagent-stop.js +306 -9
- package/dist/hooks/summary-worker.js +2 -2
- package/dist/index.js +17 -4
- package/dist/lib/exe-daemon.js +37 -14
- package/dist/lib/hybrid-search.js +23 -8
- package/dist/lib/schedules.js +2 -2
- package/dist/lib/store.js +17 -4
- package/dist/mcp/server.js +36 -10
- package/dist/runtime/index.js +17 -4
- package/dist/tui/App.js +17 -4
- package/package.json +1 -1
package/dist/bin/update.js
CHANGED
|
@@ -16,8 +16,8 @@ var __export = (target, all) => {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
// src/lib/secure-files.ts
|
|
19
|
-
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
20
|
-
import { chmod, mkdir } from "fs/promises";
|
|
19
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
20
|
+
import { chmod, mkdir as mkdir2 } from "fs/promises";
|
|
21
21
|
var init_secure_files = __esm({
|
|
22
22
|
"src/lib/secure-files.ts"() {
|
|
23
23
|
"use strict";
|
|
@@ -25,16 +25,16 @@ var init_secure_files = __esm({
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
// src/lib/config.ts
|
|
28
|
-
import { readFile, writeFile } from "fs/promises";
|
|
29
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
function
|
|
28
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
29
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, renameSync } from "fs";
|
|
30
|
+
import path3 from "path";
|
|
31
|
+
import os2 from "os";
|
|
32
|
+
function resolveDataDir2() {
|
|
33
33
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
34
34
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
35
|
-
const newDir =
|
|
36
|
-
const legacyDir =
|
|
37
|
-
if (!
|
|
35
|
+
const newDir = path3.join(os2.homedir(), ".exe-os");
|
|
36
|
+
const legacyDir = path3.join(os2.homedir(), ".exe-mem");
|
|
37
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
38
38
|
try {
|
|
39
39
|
renameSync(legacyDir, newDir);
|
|
40
40
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -50,11 +50,11 @@ var init_config = __esm({
|
|
|
50
50
|
"src/lib/config.ts"() {
|
|
51
51
|
"use strict";
|
|
52
52
|
init_secure_files();
|
|
53
|
-
EXE_AI_DIR =
|
|
54
|
-
DB_PATH =
|
|
55
|
-
MODELS_DIR =
|
|
56
|
-
CONFIG_PATH =
|
|
57
|
-
LEGACY_LANCE_PATH =
|
|
53
|
+
EXE_AI_DIR = resolveDataDir2();
|
|
54
|
+
DB_PATH = path3.join(EXE_AI_DIR, "memories.db");
|
|
55
|
+
MODELS_DIR = path3.join(EXE_AI_DIR, "models");
|
|
56
|
+
CONFIG_PATH = path3.join(EXE_AI_DIR, "config.json");
|
|
57
|
+
LEGACY_LANCE_PATH = path3.join(EXE_AI_DIR, "local.lance");
|
|
58
58
|
CURRENT_CONFIG_VERSION = 1;
|
|
59
59
|
DEFAULT_CONFIG = {
|
|
60
60
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -133,12 +133,12 @@ __export(license_exports, {
|
|
|
133
133
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
134
134
|
validateLicense: () => validateLicense
|
|
135
135
|
});
|
|
136
|
-
import { readFileSync as readFileSync3, writeFileSync, existsSync as
|
|
136
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
137
137
|
import { randomUUID } from "crypto";
|
|
138
138
|
import { createRequire } from "module";
|
|
139
139
|
import { pathToFileURL } from "url";
|
|
140
|
-
import
|
|
141
|
-
import
|
|
140
|
+
import os3 from "os";
|
|
141
|
+
import path4 from "path";
|
|
142
142
|
import { jwtVerify, importSPKI } from "jose";
|
|
143
143
|
async function fetchRetry(url, init) {
|
|
144
144
|
try {
|
|
@@ -149,16 +149,16 @@ async function fetchRetry(url, init) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
function loadDeviceId() {
|
|
152
|
-
const deviceJsonPath =
|
|
152
|
+
const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
|
|
153
153
|
try {
|
|
154
|
-
if (
|
|
154
|
+
if (existsSync4(deviceJsonPath)) {
|
|
155
155
|
const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
|
|
156
156
|
if (data.deviceId) return data.deviceId;
|
|
157
157
|
}
|
|
158
158
|
} catch {
|
|
159
159
|
}
|
|
160
160
|
try {
|
|
161
|
-
if (
|
|
161
|
+
if (existsSync4(DEVICE_ID_PATH)) {
|
|
162
162
|
const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
|
|
163
163
|
if (id2) return id2;
|
|
164
164
|
}
|
|
@@ -171,7 +171,7 @@ function loadDeviceId() {
|
|
|
171
171
|
}
|
|
172
172
|
function loadLicense() {
|
|
173
173
|
try {
|
|
174
|
-
if (!
|
|
174
|
+
if (!existsSync4(LICENSE_PATH)) return null;
|
|
175
175
|
return readFileSync3(LICENSE_PATH, "utf8").trim();
|
|
176
176
|
} catch {
|
|
177
177
|
return null;
|
|
@@ -205,7 +205,7 @@ async function verifyLicenseJwt(token) {
|
|
|
205
205
|
}
|
|
206
206
|
async function getCachedLicense() {
|
|
207
207
|
try {
|
|
208
|
-
if (!
|
|
208
|
+
if (!existsSync4(CACHE_PATH)) return null;
|
|
209
209
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
210
210
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
211
211
|
return await verifyLicenseJwt(raw.token);
|
|
@@ -215,7 +215,7 @@ async function getCachedLicense() {
|
|
|
215
215
|
}
|
|
216
216
|
function readCachedToken() {
|
|
217
217
|
try {
|
|
218
|
-
if (!
|
|
218
|
+
if (!existsSync4(CACHE_PATH)) return null;
|
|
219
219
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
220
220
|
return typeof raw.token === "string" ? raw.token : null;
|
|
221
221
|
} catch {
|
|
@@ -258,8 +258,8 @@ function loadPrismaForLicense() {
|
|
|
258
258
|
if (_prismaFailed) return null;
|
|
259
259
|
const dbUrl = process.env.DATABASE_URL;
|
|
260
260
|
if (!dbUrl) {
|
|
261
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
262
|
-
if (!
|
|
261
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
262
|
+
if (!existsSync4(path4.join(exeDbRoot, "package.json"))) {
|
|
263
263
|
_prismaFailed = true;
|
|
264
264
|
return null;
|
|
265
265
|
}
|
|
@@ -273,8 +273,8 @@ function loadPrismaForLicense() {
|
|
|
273
273
|
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
274
274
|
return new Ctor2();
|
|
275
275
|
}
|
|
276
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
277
|
-
const req = createRequire(
|
|
276
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
277
|
+
const req = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
278
278
|
const entry = req.resolve("@prisma/client");
|
|
279
279
|
const mod = await import(pathToFileURL(entry).href);
|
|
280
280
|
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
@@ -363,7 +363,7 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
363
363
|
const cached = await getCachedLicense();
|
|
364
364
|
if (cached) return cached;
|
|
365
365
|
try {
|
|
366
|
-
if (
|
|
366
|
+
if (existsSync4(CACHE_PATH)) {
|
|
367
367
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
368
368
|
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
369
369
|
return raw.pgLicense;
|
|
@@ -388,8 +388,8 @@ async function checkLicense() {
|
|
|
388
388
|
let key = loadLicense();
|
|
389
389
|
if (!key) {
|
|
390
390
|
try {
|
|
391
|
-
const configPath =
|
|
392
|
-
if (
|
|
391
|
+
const configPath = path4.join(EXE_AI_DIR, "config.json");
|
|
392
|
+
if (existsSync4(configPath)) {
|
|
393
393
|
const raw = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
394
394
|
const cloud = raw.cloud;
|
|
395
395
|
if (cloud?.apiKey) {
|
|
@@ -549,9 +549,9 @@ var init_license = __esm({
|
|
|
549
549
|
"src/lib/license.ts"() {
|
|
550
550
|
"use strict";
|
|
551
551
|
init_config();
|
|
552
|
-
LICENSE_PATH =
|
|
553
|
-
CACHE_PATH =
|
|
554
|
-
DEVICE_ID_PATH =
|
|
552
|
+
LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
|
|
553
|
+
CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
|
|
554
|
+
DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
|
|
555
555
|
API_BASE = "https://askexe.com/cloud";
|
|
556
556
|
RETRY_DELAY_MS = 500;
|
|
557
557
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -601,12 +601,102 @@ function isMainModule(importMetaUrl) {
|
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
// src/lib/update-backup.ts
|
|
605
|
+
import { copyFile, readFile, readdir, writeFile, rm, mkdir, cp } from "fs/promises";
|
|
606
|
+
import { existsSync } from "fs";
|
|
607
|
+
import path from "path";
|
|
608
|
+
import os from "os";
|
|
609
|
+
var BACKUP_DIR_NAME = ".update-backup";
|
|
610
|
+
function resolveDataDir() {
|
|
611
|
+
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
612
|
+
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
613
|
+
return path.join(os.homedir(), ".exe-os");
|
|
614
|
+
}
|
|
615
|
+
var BACKUP_TARGETS = [
|
|
616
|
+
{ name: "config.json", type: "file" },
|
|
617
|
+
{ name: "exe-employees.json", type: "file" },
|
|
618
|
+
{ name: "master.key", type: "file" },
|
|
619
|
+
{ name: "identity", type: "dir" }
|
|
620
|
+
// All .db files (SQLCipher databases) — matched dynamically
|
|
621
|
+
];
|
|
622
|
+
async function createUpdateBackup(currentVersion, dataDir) {
|
|
623
|
+
const dir = dataDir ?? resolveDataDir();
|
|
624
|
+
const backupDir = path.join(dir, BACKUP_DIR_NAME);
|
|
625
|
+
if (existsSync(backupDir)) {
|
|
626
|
+
await rm(backupDir, { recursive: true, force: true });
|
|
627
|
+
}
|
|
628
|
+
await mkdir(backupDir, { recursive: true });
|
|
629
|
+
const backedUpFiles = [];
|
|
630
|
+
for (const target of BACKUP_TARGETS) {
|
|
631
|
+
const src = path.join(dir, target.name);
|
|
632
|
+
if (!existsSync(src)) continue;
|
|
633
|
+
const dest = path.join(backupDir, target.name);
|
|
634
|
+
if (target.type === "file") {
|
|
635
|
+
await copyFile(src, dest);
|
|
636
|
+
} else {
|
|
637
|
+
await cp(src, dest, { recursive: true });
|
|
638
|
+
}
|
|
639
|
+
backedUpFiles.push(target.name);
|
|
640
|
+
}
|
|
641
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
642
|
+
for (const entry of entries) {
|
|
643
|
+
if (entry.isFile() && entry.name.endsWith(".db") && entry.name !== BACKUP_DIR_NAME) {
|
|
644
|
+
const src = path.join(dir, entry.name);
|
|
645
|
+
const dest = path.join(backupDir, entry.name);
|
|
646
|
+
await copyFile(src, dest);
|
|
647
|
+
backedUpFiles.push(entry.name);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const manifest = {
|
|
651
|
+
version: currentVersion,
|
|
652
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
653
|
+
files: backedUpFiles
|
|
654
|
+
};
|
|
655
|
+
await writeFile(
|
|
656
|
+
path.join(backupDir, "manifest.json"),
|
|
657
|
+
JSON.stringify(manifest, null, 2) + "\n"
|
|
658
|
+
);
|
|
659
|
+
return manifest;
|
|
660
|
+
}
|
|
661
|
+
async function restoreFromBackup(dataDir) {
|
|
662
|
+
const dir = dataDir ?? resolveDataDir();
|
|
663
|
+
const backupDir = path.join(dir, BACKUP_DIR_NAME);
|
|
664
|
+
const manifestPath = path.join(backupDir, "manifest.json");
|
|
665
|
+
if (!existsSync(manifestPath)) {
|
|
666
|
+
throw new Error(
|
|
667
|
+
`No backup found at ${backupDir}. Nothing to restore.`
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
const manifest = JSON.parse(
|
|
671
|
+
await readFile(manifestPath, "utf-8")
|
|
672
|
+
);
|
|
673
|
+
for (const fileName of manifest.files) {
|
|
674
|
+
const src = path.join(backupDir, fileName);
|
|
675
|
+
const dest = path.join(dir, fileName);
|
|
676
|
+
if (!existsSync(src)) continue;
|
|
677
|
+
const stat = await import("fs/promises").then((m) => m.stat(src));
|
|
678
|
+
if (stat.isDirectory()) {
|
|
679
|
+
await cp(src, dest, { recursive: true, force: true });
|
|
680
|
+
} else {
|
|
681
|
+
await copyFile(src, dest);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return manifest;
|
|
685
|
+
}
|
|
686
|
+
async function deleteBackup(dataDir) {
|
|
687
|
+
const dir = dataDir ?? resolveDataDir();
|
|
688
|
+
const backupDir = path.join(dir, BACKUP_DIR_NAME);
|
|
689
|
+
if (existsSync(backupDir)) {
|
|
690
|
+
await rm(backupDir, { recursive: true, force: true });
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
604
694
|
// src/lib/update-check.ts
|
|
605
695
|
import { execSync } from "child_process";
|
|
606
696
|
import { readFileSync } from "fs";
|
|
607
|
-
import
|
|
697
|
+
import path2 from "path";
|
|
608
698
|
function getLocalVersion(packageRoot) {
|
|
609
|
-
const pkgPath =
|
|
699
|
+
const pkgPath = path2.join(packageRoot, "package.json");
|
|
610
700
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
611
701
|
return pkg.version;
|
|
612
702
|
}
|
|
@@ -639,10 +729,47 @@ function checkForUpdate(packageRoot) {
|
|
|
639
729
|
}
|
|
640
730
|
|
|
641
731
|
// src/bin/update.ts
|
|
732
|
+
async function runRestore() {
|
|
733
|
+
console.log("\n\u{1F504} Restoring from update backup...");
|
|
734
|
+
try {
|
|
735
|
+
const manifest = await restoreFromBackup();
|
|
736
|
+
console.log(` Restored ${manifest.files.length} file(s) from v${manifest.version} backup.`);
|
|
737
|
+
console.log(` Backup was created at ${manifest.timestamp}`);
|
|
738
|
+
console.log(`
|
|
739
|
+
\u{1F4E5} Reinstalling @askexenow/exe-os@${manifest.version}...`);
|
|
740
|
+
try {
|
|
741
|
+
execSync2(`npm install -g @askexenow/exe-os@${manifest.version}`, {
|
|
742
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
743
|
+
timeout: 3e5
|
|
744
|
+
});
|
|
745
|
+
console.log(`
|
|
746
|
+
\u2705 Restored to v${manifest.version}`);
|
|
747
|
+
} catch {
|
|
748
|
+
console.error(`
|
|
749
|
+
\u26A0\uFE0F Could not reinstall v${manifest.version} via npm.`);
|
|
750
|
+
console.error(` Your data files have been restored. Try manually:`);
|
|
751
|
+
console.error(` npm install -g @askexenow/exe-os@${manifest.version}`);
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
await deleteBackup();
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
console.log("\n\u{1F680} Restore complete. Restart your sessions.\n");
|
|
758
|
+
} catch (err) {
|
|
759
|
+
console.error("\u274C Restore failed.");
|
|
760
|
+
if (err instanceof Error) console.error(` ${err.message}`);
|
|
761
|
+
process.exit(1);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
642
764
|
async function runUpdate(cliArgs) {
|
|
643
765
|
const args = cliArgs ?? process.argv.slice(2);
|
|
644
766
|
const autoMode = args.includes("--auto") || args.includes("-y");
|
|
645
767
|
const checkOnly = args.includes("--check");
|
|
768
|
+
const restoreMode = args.includes("--restore");
|
|
769
|
+
if (restoreMode) {
|
|
770
|
+
await runRestore();
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
646
773
|
const packageRoot = new URL("../..", import.meta.url).pathname;
|
|
647
774
|
const result = checkForUpdate(packageRoot);
|
|
648
775
|
if (result.error) {
|
|
@@ -677,7 +804,16 @@ async function runUpdate(cliArgs) {
|
|
|
677
804
|
console.log("Update skipped.");
|
|
678
805
|
process.exit(0);
|
|
679
806
|
}
|
|
680
|
-
console.log("\n\u{
|
|
807
|
+
console.log("\n\u{1F4BE} Backing up customer data...");
|
|
808
|
+
try {
|
|
809
|
+
const backupResult = await createUpdateBackup(result.localVersion);
|
|
810
|
+
console.log(` Backed up ${backupResult.files.length} file(s) to ${BACKUP_DIR_NAME}/`);
|
|
811
|
+
} catch (err) {
|
|
812
|
+
console.error("\u274C Backup failed \u2014 aborting update to protect your data.");
|
|
813
|
+
if (err instanceof Error) console.error(` ${err.message}`);
|
|
814
|
+
process.exit(1);
|
|
815
|
+
}
|
|
816
|
+
console.log("\u{1F9F9} Clearing npm cache...");
|
|
681
817
|
try {
|
|
682
818
|
execSync2("npm cache clean --force", { stdio: "pipe" });
|
|
683
819
|
console.log(" Done");
|
|
@@ -692,8 +828,9 @@ async function runUpdate(cliArgs) {
|
|
|
692
828
|
timeout: 3e5
|
|
693
829
|
});
|
|
694
830
|
} catch (err) {
|
|
695
|
-
console.error("\n\u274C Update failed.");
|
|
696
|
-
console.error(
|
|
831
|
+
console.error("\n\u274C Update failed. Your backup is preserved.");
|
|
832
|
+
console.error(` Restore with: exe-os update --restore`);
|
|
833
|
+
console.error(" Or try manually: npm install -g @askexenow/exe-os@latest");
|
|
697
834
|
if (err instanceof Error && err.message) {
|
|
698
835
|
console.error(` Error: ${err.message.split("\n")[0]}`);
|
|
699
836
|
}
|
|
@@ -712,30 +849,38 @@ async function runUpdate(cliArgs) {
|
|
|
712
849
|
}
|
|
713
850
|
}
|
|
714
851
|
const remoteVersion = result.remoteVersion;
|
|
852
|
+
const updateSucceeded = newVersion !== result.localVersion;
|
|
715
853
|
if (newVersion === remoteVersion) {
|
|
716
854
|
console.log(`
|
|
717
855
|
\u2705 Updated to v${newVersion}`);
|
|
718
|
-
} else if (
|
|
856
|
+
} else if (updateSucceeded) {
|
|
719
857
|
console.log(`
|
|
720
858
|
\u2705 Updated to v${newVersion} (latest: v${remoteVersion})`);
|
|
721
859
|
} else {
|
|
722
860
|
console.log(`
|
|
723
861
|
\u26A0\uFE0F Version unchanged (v${newVersion}). npm cache may be stale.`);
|
|
862
|
+
console.log(" Backup preserved. Restore with: exe-os update --restore");
|
|
724
863
|
console.log(" Try: npm cache clean --force && npm install -g @askexenow/exe-os@latest");
|
|
725
864
|
}
|
|
865
|
+
if (updateSucceeded) {
|
|
866
|
+
try {
|
|
867
|
+
await deleteBackup();
|
|
868
|
+
} catch {
|
|
869
|
+
}
|
|
870
|
+
}
|
|
726
871
|
console.log(" Hooks re-wired, daemon restarted automatically.");
|
|
727
872
|
console.log("");
|
|
728
873
|
console.log(" \x1B[33m\u26A1 Run /mcp in each active Claude Code session to pick up new tools.\x1B[0m");
|
|
729
874
|
console.log(" \x1B[2m(MCP servers can't hot-reload \u2014 Claude Code needs to reconnect them.)\x1B[0m");
|
|
730
875
|
try {
|
|
731
|
-
const { existsSync: exists, readFileSync:
|
|
876
|
+
const { existsSync: exists, readFileSync: readFile3 } = await import("fs");
|
|
732
877
|
const p = await import("path");
|
|
733
878
|
const { homedir: home } = await import("os");
|
|
734
879
|
const exeDir = p.default.join(home(), ".exe-os");
|
|
735
880
|
const licKeyPath = p.default.join(exeDir, "license.key");
|
|
736
881
|
const configPath = p.default.join(exeDir, "config.json");
|
|
737
882
|
if (!exists(licKeyPath) && exists(configPath)) {
|
|
738
|
-
const cfg = JSON.parse(
|
|
883
|
+
const cfg = JSON.parse(readFile3(configPath, "utf8"));
|
|
739
884
|
const cloud = cfg.cloud;
|
|
740
885
|
if (cloud?.apiKey) {
|
|
741
886
|
const { mirrorLicenseKey: mirrorLicenseKey2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
package/dist/gateway/index.js
CHANGED
|
@@ -3580,8 +3580,8 @@ function getShardClient(projectName) {
|
|
|
3580
3580
|
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3581
3581
|
}
|
|
3582
3582
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3583
|
-
if (!safeName) {
|
|
3584
|
-
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
3583
|
+
if (!safeName || safeName === "unknown") {
|
|
3584
|
+
throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
|
|
3585
3585
|
}
|
|
3586
3586
|
const cached = _shards.get(safeName);
|
|
3587
3587
|
if (cached) {
|
|
@@ -4450,19 +4450,32 @@ async function flushBatch() {
|
|
|
4450
4450
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
4451
4451
|
if (isShardingEnabled2()) {
|
|
4452
4452
|
const byProject = /* @__PURE__ */ new Map();
|
|
4453
|
+
let skippedUnknown = 0;
|
|
4453
4454
|
for (const row of batch) {
|
|
4454
|
-
const proj = row.project_name
|
|
4455
|
+
const proj = row.project_name?.trim();
|
|
4456
|
+
if (!proj) {
|
|
4457
|
+
skippedUnknown++;
|
|
4458
|
+
continue;
|
|
4459
|
+
}
|
|
4455
4460
|
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
4456
4461
|
byProject.get(proj).push(row);
|
|
4457
4462
|
}
|
|
4463
|
+
if (skippedUnknown > 0) {
|
|
4464
|
+
process.stderr.write(
|
|
4465
|
+
`[store] Shard skip: ${skippedUnknown} record(s) with empty project_name (kept in main DB only)
|
|
4466
|
+
`
|
|
4467
|
+
);
|
|
4468
|
+
}
|
|
4458
4469
|
for (const [project, rows] of byProject) {
|
|
4459
4470
|
try {
|
|
4460
4471
|
const shardClient = await getReadyShardClient2(project);
|
|
4461
4472
|
const shardStmts = rows.map(buildStmt);
|
|
4462
4473
|
await shardClient.batch(shardStmts, "write");
|
|
4463
4474
|
} catch (err) {
|
|
4475
|
+
const fullError = err instanceof Error ? `${err.name}: ${err.message}${err.stack ? `
|
|
4476
|
+
${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
|
|
4464
4477
|
process.stderr.write(
|
|
4465
|
-
`[store] Shard write failed for ${project}
|
|
4478
|
+
`[store] Shard write failed for ${project} (${rows.length} records): ${fullError}
|
|
4466
4479
|
`
|
|
4467
4480
|
);
|
|
4468
4481
|
}
|