@askexenow/exe-os 0.9.67 → 0.9.69
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/age-ontology-load.js +263 -0
- package/dist/bin/agentic-ontology-backfill.js +178 -17
- package/dist/bin/agentic-reflection-backfill.js +165 -10
- package/dist/bin/agentic-semantic-label.js +173 -12
- package/dist/bin/backfill-vectors.js +176 -13
- package/dist/bin/cc-doctor.js +293 -30
- package/dist/bin/cli.js +1175 -855
- package/dist/bin/exe-healthcheck.js +293 -30
- package/dist/bin/graph-backfill.js +176 -17
- package/dist/bin/postgres-agentic-reflection-backfill.js +270 -0
- package/dist/bin/postgres-agentic-semantic-backfill.js +271 -1
- package/dist/lib/exe-daemon.js +1714 -985
- package/dist/mcp/server.js +728 -530
- package/package.json +1 -1
|
@@ -1,4 +1,113 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/lib/secure-files.ts
|
|
8
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
9
|
+
import { chmod, mkdir } from "fs/promises";
|
|
10
|
+
var init_secure_files = __esm({
|
|
11
|
+
"src/lib/secure-files.ts"() {
|
|
12
|
+
"use strict";
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// src/lib/config.ts
|
|
17
|
+
import { readFile, writeFile } from "fs/promises";
|
|
18
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
import os from "os";
|
|
21
|
+
function resolveDataDir() {
|
|
22
|
+
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
23
|
+
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
24
|
+
const newDir = path.join(os.homedir(), ".exe-os");
|
|
25
|
+
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
26
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
27
|
+
try {
|
|
28
|
+
renameSync(legacyDir, newDir);
|
|
29
|
+
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
30
|
+
`);
|
|
31
|
+
} catch {
|
|
32
|
+
return legacyDir;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return newDir;
|
|
36
|
+
}
|
|
37
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG;
|
|
38
|
+
var init_config = __esm({
|
|
39
|
+
"src/lib/config.ts"() {
|
|
40
|
+
"use strict";
|
|
41
|
+
init_secure_files();
|
|
42
|
+
EXE_AI_DIR = resolveDataDir();
|
|
43
|
+
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
44
|
+
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
45
|
+
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
46
|
+
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
47
|
+
CURRENT_CONFIG_VERSION = 1;
|
|
48
|
+
DEFAULT_CONFIG = {
|
|
49
|
+
config_version: CURRENT_CONFIG_VERSION,
|
|
50
|
+
dbPath: DB_PATH,
|
|
51
|
+
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
52
|
+
embeddingDim: 1024,
|
|
53
|
+
batchSize: 20,
|
|
54
|
+
flushIntervalMs: 1e4,
|
|
55
|
+
autoIngestion: true,
|
|
56
|
+
autoRetrieval: true,
|
|
57
|
+
searchMode: "hybrid",
|
|
58
|
+
hookSearchMode: "hybrid",
|
|
59
|
+
fileGrepEnabled: true,
|
|
60
|
+
splashEffect: true,
|
|
61
|
+
consolidationEnabled: true,
|
|
62
|
+
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
63
|
+
consolidationModel: "claude-haiku-4-5-20251001",
|
|
64
|
+
consolidationMaxCallsPerRun: 20,
|
|
65
|
+
selfQueryRouter: true,
|
|
66
|
+
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
67
|
+
rerankerEnabled: true,
|
|
68
|
+
scalingRoadmap: {
|
|
69
|
+
rerankerAutoTrigger: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
broadQueryMinCardinality: 5e4,
|
|
72
|
+
fetchTopK: 200,
|
|
73
|
+
returnTopK: 20
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
graphRagEnabled: true,
|
|
77
|
+
wikiEnabled: false,
|
|
78
|
+
wikiUrl: "",
|
|
79
|
+
wikiApiKey: "",
|
|
80
|
+
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
81
|
+
wikiWorkspaceMapping: {},
|
|
82
|
+
wikiAutoUpdate: true,
|
|
83
|
+
wikiAutoUpdateThreshold: 0.5,
|
|
84
|
+
wikiAutoUpdateCreateNew: true,
|
|
85
|
+
skillLearning: true,
|
|
86
|
+
skillThreshold: 3,
|
|
87
|
+
skillModel: "claude-haiku-4-5-20251001",
|
|
88
|
+
exeHeartbeat: {
|
|
89
|
+
enabled: true,
|
|
90
|
+
intervalSeconds: 60,
|
|
91
|
+
staleInProgressThresholdHours: 2
|
|
92
|
+
},
|
|
93
|
+
sessionLifecycle: {
|
|
94
|
+
idleKillEnabled: true,
|
|
95
|
+
idleKillTicksRequired: 3,
|
|
96
|
+
idleKillIntercomAckWindowMs: 1e4,
|
|
97
|
+
maxAutoInstances: 10
|
|
98
|
+
},
|
|
99
|
+
autoUpdate: {
|
|
100
|
+
checkOnBoot: true,
|
|
101
|
+
autoInstall: false,
|
|
102
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
103
|
+
},
|
|
104
|
+
orchestration: {
|
|
105
|
+
phase: "phase_1_coo",
|
|
106
|
+
phaseSetBy: "default"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
2
111
|
|
|
3
112
|
// src/bin/postgres-agentic-semantic-backfill.ts
|
|
4
113
|
import { Client } from "pg";
|
|
@@ -136,6 +245,157 @@ function inferSemanticLabel(row) {
|
|
|
136
245
|
};
|
|
137
246
|
}
|
|
138
247
|
|
|
248
|
+
// src/lib/background-jobs.ts
|
|
249
|
+
init_config();
|
|
250
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync, unlinkSync } from "fs";
|
|
251
|
+
import { execFileSync } from "child_process";
|
|
252
|
+
import os2 from "os";
|
|
253
|
+
import path2 from "path";
|
|
254
|
+
var JOB_DIR = path2.join(EXE_AI_DIR, "jobs");
|
|
255
|
+
var JOBS_FILE = path2.join(JOB_DIR, "jobs.json");
|
|
256
|
+
var LOCK_DIR = path2.join(JOB_DIR, "locks");
|
|
257
|
+
var DEFAULT_LOCK_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
258
|
+
var MAX_HISTORY = 200;
|
|
259
|
+
function ensureDirs() {
|
|
260
|
+
mkdirSync2(LOCK_DIR, { recursive: true });
|
|
261
|
+
}
|
|
262
|
+
function now() {
|
|
263
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
264
|
+
}
|
|
265
|
+
function isAlive(pid) {
|
|
266
|
+
if (!pid || pid <= 0) return false;
|
|
267
|
+
try {
|
|
268
|
+
process.kill(pid, 0);
|
|
269
|
+
return true;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function readJobsRaw() {
|
|
275
|
+
ensureDirs();
|
|
276
|
+
if (!existsSync3(JOBS_FILE)) return [];
|
|
277
|
+
try {
|
|
278
|
+
const parsed = JSON.parse(readFileSync2(JOBS_FILE, "utf8"));
|
|
279
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
280
|
+
} catch {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function writeJobsRaw(jobs) {
|
|
285
|
+
ensureDirs();
|
|
286
|
+
const running = jobs.filter((j) => j.status === "running");
|
|
287
|
+
const rest = jobs.filter((j) => j.status !== "running").slice(-MAX_HISTORY);
|
|
288
|
+
writeFileSync(JOBS_FILE, JSON.stringify([...rest, ...running], null, 2) + "\n");
|
|
289
|
+
}
|
|
290
|
+
function lockPath(type) {
|
|
291
|
+
return path2.join(LOCK_DIR, `${type.replace(/[^a-zA-Z0-9_.-]/g, "_")}.lock`);
|
|
292
|
+
}
|
|
293
|
+
function acquireJobLock(type, ttlMs = DEFAULT_LOCK_TTL_MS) {
|
|
294
|
+
ensureDirs();
|
|
295
|
+
const file = lockPath(type);
|
|
296
|
+
if (existsSync3(file)) {
|
|
297
|
+
try {
|
|
298
|
+
const lock = JSON.parse(readFileSync2(file, "utf8"));
|
|
299
|
+
const age = Date.now() - Date.parse(lock.updatedAt ?? "");
|
|
300
|
+
if (lock.pid && isAlive(lock.pid) && Number.isFinite(age) && age < ttlMs) return false;
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
unlinkSync(file);
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
writeFileSync(file, JSON.stringify({ pid: process.pid, updatedAt: now() }, null, 2) + "\n", { flag: "wx" });
|
|
310
|
+
return true;
|
|
311
|
+
} catch {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function releaseJobLock(type) {
|
|
316
|
+
const file = lockPath(type);
|
|
317
|
+
try {
|
|
318
|
+
if (!existsSync3(file)) return;
|
|
319
|
+
const lock = JSON.parse(readFileSync2(file, "utf8"));
|
|
320
|
+
if (lock.pid === process.pid || !lock.pid || !isAlive(lock.pid)) unlinkSync(file);
|
|
321
|
+
} catch {
|
|
322
|
+
try {
|
|
323
|
+
unlinkSync(file);
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function startManagedJob(options) {
|
|
329
|
+
const lowPriority = options.lowPriority ?? true;
|
|
330
|
+
if (!acquireJobLock(options.type, options.lockTtlMs)) return null;
|
|
331
|
+
if (lowPriority) {
|
|
332
|
+
try {
|
|
333
|
+
os2.setPriority(process.pid, 10);
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const id = `${options.type}-${Date.now()}-${process.pid}`.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
338
|
+
const record = {
|
|
339
|
+
id,
|
|
340
|
+
type: options.type,
|
|
341
|
+
name: options.name,
|
|
342
|
+
pid: process.pid,
|
|
343
|
+
command: options.command ?? process.argv.join(" "),
|
|
344
|
+
cwd: process.cwd(),
|
|
345
|
+
status: "running",
|
|
346
|
+
startedAt: now(),
|
|
347
|
+
updatedAt: now(),
|
|
348
|
+
lastHeartbeatAt: now(),
|
|
349
|
+
cancelCommand: `exe-os jobs cancel ${id}`,
|
|
350
|
+
lowPriority
|
|
351
|
+
};
|
|
352
|
+
const upsert = (patch) => {
|
|
353
|
+
const jobs = readJobsRaw().filter((j) => j.id !== id);
|
|
354
|
+
Object.assign(record, patch, { updatedAt: now() });
|
|
355
|
+
writeJobsRaw([...jobs, record]);
|
|
356
|
+
const file = lockPath(options.type);
|
|
357
|
+
try {
|
|
358
|
+
writeFileSync(file, JSON.stringify({ pid: process.pid, jobId: id, updatedAt: record.updatedAt }, null, 2) + "\n");
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
upsert({});
|
|
363
|
+
const timer = setInterval(() => upsert({ lastHeartbeatAt: now() }), 3e4);
|
|
364
|
+
timer.unref?.();
|
|
365
|
+
const cleanup = (status, error) => {
|
|
366
|
+
clearInterval(timer);
|
|
367
|
+
upsert({ status, error, lastHeartbeatAt: now() });
|
|
368
|
+
releaseJobLock(options.type);
|
|
369
|
+
};
|
|
370
|
+
process.once("SIGTERM", () => {
|
|
371
|
+
cleanup("cancelled");
|
|
372
|
+
process.exit(0);
|
|
373
|
+
});
|
|
374
|
+
process.once("SIGINT", () => {
|
|
375
|
+
cleanup("cancelled");
|
|
376
|
+
process.exit(130);
|
|
377
|
+
});
|
|
378
|
+
process.once("exit", () => releaseJobLock(options.type));
|
|
379
|
+
return {
|
|
380
|
+
id,
|
|
381
|
+
update(progress) {
|
|
382
|
+
upsert({ progressCurrent: progress.current, progressTotal: progress.total, progressLabel: progress.label, lastHeartbeatAt: now() });
|
|
383
|
+
},
|
|
384
|
+
complete() {
|
|
385
|
+
cleanup("completed");
|
|
386
|
+
},
|
|
387
|
+
fail(err) {
|
|
388
|
+
cleanup("failed", err instanceof Error ? err.message : String(err));
|
|
389
|
+
},
|
|
390
|
+
cancel() {
|
|
391
|
+
cleanup("cancelled");
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
async function politeBatchPause(ms = 250) {
|
|
396
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
397
|
+
}
|
|
398
|
+
|
|
139
399
|
// src/bin/postgres-agentic-semantic-backfill.ts
|
|
140
400
|
function arg(name) {
|
|
141
401
|
const i = process.argv.indexOf(name);
|
|
@@ -160,6 +420,11 @@ function scrubJson(value) {
|
|
|
160
420
|
return JSON.stringify(value).replace(/\u0000/g, "").replace(/\\u0000/gi, "").replace(/\\\\u0000/gi, "").replace(/\\u00[0-1][0-9a-f]/gi, " ");
|
|
161
421
|
}
|
|
162
422
|
async function main() {
|
|
423
|
+
const job = startManagedJob({ type: "postgres-agentic-semantic-backfill", name: "Postgres semantic label backfill", lowPriority: true });
|
|
424
|
+
if (!job) {
|
|
425
|
+
process.stderr.write("[postgres-agentic-semantic-backfill] Another Postgres semantic backfill is already running.\n");
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
163
428
|
const url = process.env.DATABASE_URL || process.env.EXED_DATABASE_URL;
|
|
164
429
|
if (!url) throw new Error("DATABASE_URL or EXED_DATABASE_URL is required");
|
|
165
430
|
const limit = Number(arg("--limit") ?? "20000");
|
|
@@ -221,11 +486,16 @@ async function main() {
|
|
|
221
486
|
]);
|
|
222
487
|
}
|
|
223
488
|
count++;
|
|
224
|
-
if (count % 5e3 === 0)
|
|
489
|
+
if (count % 5e3 === 0) {
|
|
490
|
+
process.stderr.write(`[postgres-agentic-semantic-backfill] labeled ${count}
|
|
225
491
|
`);
|
|
492
|
+
job.update({ current: count, total: limit, label: `Labeled ${count}/${limit}` });
|
|
493
|
+
await politeBatchPause(1e3);
|
|
494
|
+
}
|
|
226
495
|
}
|
|
227
496
|
process.stderr.write(`[postgres-agentic-semantic-backfill] Complete: ${count} semantic labels.
|
|
228
497
|
`);
|
|
498
|
+
job.complete();
|
|
229
499
|
} finally {
|
|
230
500
|
await client.end();
|
|
231
501
|
}
|