@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/mcp/server.js
CHANGED
|
@@ -50,7 +50,7 @@ __export(config_exports, {
|
|
|
50
50
|
migrateConfig: () => migrateConfig,
|
|
51
51
|
saveConfig: () => saveConfig
|
|
52
52
|
});
|
|
53
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
53
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
54
54
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
55
55
|
import path from "path";
|
|
56
56
|
import os from "os";
|
|
@@ -176,6 +176,9 @@ async function saveConfig(config2) {
|
|
|
176
176
|
await mkdir(dir, { recursive: true });
|
|
177
177
|
const configPath = path.join(dir, "config.json");
|
|
178
178
|
await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
|
|
179
|
+
if (config2.cloud?.apiKey) {
|
|
180
|
+
await chmod(configPath, 384);
|
|
181
|
+
}
|
|
179
182
|
}
|
|
180
183
|
async function loadConfigFrom(configPath) {
|
|
181
184
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -286,6 +289,10 @@ import path2 from "path";
|
|
|
286
289
|
import { fileURLToPath } from "url";
|
|
287
290
|
function handleData(chunk) {
|
|
288
291
|
_buffer += chunk.toString();
|
|
292
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
293
|
+
_buffer = "";
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
289
296
|
let newlineIdx;
|
|
290
297
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
291
298
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -593,7 +600,7 @@ function disconnectClient() {
|
|
|
593
600
|
entry.resolve({ error: "Client disconnected" });
|
|
594
601
|
}
|
|
595
602
|
}
|
|
596
|
-
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;
|
|
603
|
+
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;
|
|
597
604
|
var init_exe_daemon_client = __esm({
|
|
598
605
|
"src/lib/exe-daemon-client.ts"() {
|
|
599
606
|
"use strict";
|
|
@@ -610,6 +617,7 @@ var init_exe_daemon_client = __esm({
|
|
|
610
617
|
_requestCount = 0;
|
|
611
618
|
HEALTH_CHECK_INTERVAL = 100;
|
|
612
619
|
_pending = /* @__PURE__ */ new Map();
|
|
620
|
+
MAX_BUFFER = 1e7;
|
|
613
621
|
}
|
|
614
622
|
});
|
|
615
623
|
|
|
@@ -772,6 +780,7 @@ async function ensureSchema() {
|
|
|
772
780
|
const client = getRawClient();
|
|
773
781
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
774
782
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
783
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
775
784
|
try {
|
|
776
785
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
777
786
|
} catch {
|
|
@@ -1580,12 +1589,13 @@ var init_database = __esm({
|
|
|
1580
1589
|
});
|
|
1581
1590
|
|
|
1582
1591
|
// src/lib/keychain.ts
|
|
1583
|
-
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
|
|
1592
|
+
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
1584
1593
|
import { existsSync as existsSync3 } from "fs";
|
|
1585
1594
|
import path3 from "path";
|
|
1595
|
+
import os2 from "os";
|
|
1586
1596
|
import crypto from "crypto";
|
|
1587
1597
|
function getKeyDir() {
|
|
1588
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(
|
|
1598
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
|
|
1589
1599
|
}
|
|
1590
1600
|
function getKeyPath() {
|
|
1591
1601
|
return path3.join(getKeyDir(), "master.key");
|
|
@@ -1642,7 +1652,7 @@ __export(shard_manager_exports, {
|
|
|
1642
1652
|
shardExists: () => shardExists
|
|
1643
1653
|
});
|
|
1644
1654
|
import path4 from "path";
|
|
1645
|
-
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
1655
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1646
1656
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1647
1657
|
function initShardManager(encryptionKey) {
|
|
1648
1658
|
_encryptionKey = encryptionKey;
|
|
@@ -1681,8 +1691,7 @@ function shardExists(projectName) {
|
|
|
1681
1691
|
}
|
|
1682
1692
|
function listShards() {
|
|
1683
1693
|
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1684
|
-
|
|
1685
|
-
return readdirSync8(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1694
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1686
1695
|
}
|
|
1687
1696
|
async function ensureShardSchema(client) {
|
|
1688
1697
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1887,6 +1896,28 @@ __export(store_exports, {
|
|
|
1887
1896
|
vectorToBlob: () => vectorToBlob,
|
|
1888
1897
|
writeMemory: () => writeMemory
|
|
1889
1898
|
});
|
|
1899
|
+
function isBusyError2(err) {
|
|
1900
|
+
if (err instanceof Error) {
|
|
1901
|
+
const msg = err.message.toLowerCase();
|
|
1902
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1903
|
+
}
|
|
1904
|
+
return false;
|
|
1905
|
+
}
|
|
1906
|
+
async function retryOnBusy2(fn, label) {
|
|
1907
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1908
|
+
try {
|
|
1909
|
+
return await fn();
|
|
1910
|
+
} catch (err) {
|
|
1911
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1912
|
+
process.stderr.write(
|
|
1913
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1914
|
+
`
|
|
1915
|
+
);
|
|
1916
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
throw new Error("unreachable");
|
|
1920
|
+
}
|
|
1890
1921
|
async function initStore(options) {
|
|
1891
1922
|
if (_flushTimer !== null) {
|
|
1892
1923
|
clearInterval(_flushTimer);
|
|
@@ -1915,14 +1946,17 @@ async function initStore(options) {
|
|
|
1915
1946
|
dbPath,
|
|
1916
1947
|
encryptionKey: hexKey
|
|
1917
1948
|
});
|
|
1918
|
-
await ensureSchema();
|
|
1949
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1919
1950
|
try {
|
|
1920
1951
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1921
1952
|
initShardManager2(hexKey);
|
|
1922
1953
|
} catch {
|
|
1923
1954
|
}
|
|
1924
1955
|
const client = getClient();
|
|
1925
|
-
const vResult = await
|
|
1956
|
+
const vResult = await retryOnBusy2(
|
|
1957
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1958
|
+
"version-query"
|
|
1959
|
+
);
|
|
1926
1960
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1927
1961
|
}
|
|
1928
1962
|
function classifyTier(record) {
|
|
@@ -1965,6 +1999,12 @@ async function writeMemory(record) {
|
|
|
1965
1999
|
supersedes_id: record.supersedes_id ?? null
|
|
1966
2000
|
};
|
|
1967
2001
|
_pendingRecords.push(dbRow);
|
|
2002
|
+
const MAX_PENDING = 1e3;
|
|
2003
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
2004
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
2005
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
2006
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
2007
|
+
}
|
|
1968
2008
|
if (_flushTimer === null) {
|
|
1969
2009
|
_flushTimer = setInterval(() => {
|
|
1970
2010
|
void flushBatch();
|
|
@@ -2296,7 +2336,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
2296
2336
|
return 0;
|
|
2297
2337
|
}
|
|
2298
2338
|
}
|
|
2299
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2339
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2300
2340
|
var init_store = __esm({
|
|
2301
2341
|
"src/lib/store.ts"() {
|
|
2302
2342
|
"use strict";
|
|
@@ -2304,6 +2344,8 @@ var init_store = __esm({
|
|
|
2304
2344
|
init_database();
|
|
2305
2345
|
init_keychain();
|
|
2306
2346
|
init_config();
|
|
2347
|
+
INIT_MAX_RETRIES = 3;
|
|
2348
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
2307
2349
|
_pendingRecords = [];
|
|
2308
2350
|
_batchSize = 20;
|
|
2309
2351
|
_flushIntervalMs = 1e4;
|
|
@@ -2459,7 +2501,7 @@ __export(file_grep_exports, {
|
|
|
2459
2501
|
grepProjectFiles: () => grepProjectFiles
|
|
2460
2502
|
});
|
|
2461
2503
|
import { execSync as execSync2 } from "child_process";
|
|
2462
|
-
import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2504
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2463
2505
|
import path6 from "path";
|
|
2464
2506
|
import crypto2 from "crypto";
|
|
2465
2507
|
function hasRipgrep() {
|
|
@@ -2604,7 +2646,7 @@ function collectFiles(root, patterns) {
|
|
|
2604
2646
|
const basename = path6.basename(dir);
|
|
2605
2647
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2606
2648
|
try {
|
|
2607
|
-
const entries =
|
|
2649
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2608
2650
|
for (const entry of entries) {
|
|
2609
2651
|
if (files.length >= MAX_FILES) return;
|
|
2610
2652
|
const rel = path6.join(relative, entry.name);
|
|
@@ -2836,7 +2878,7 @@ __export(active_agent_exports, {
|
|
|
2836
2878
|
getAllActiveAgents: () => getAllActiveAgents,
|
|
2837
2879
|
writeActiveAgent: () => writeActiveAgent
|
|
2838
2880
|
});
|
|
2839
|
-
import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as
|
|
2881
|
+
import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
|
|
2840
2882
|
import { execSync as execSync4 } from "child_process";
|
|
2841
2883
|
import path8 from "path";
|
|
2842
2884
|
function getMarkerPath() {
|
|
@@ -2907,7 +2949,7 @@ function getActiveAgent() {
|
|
|
2907
2949
|
}
|
|
2908
2950
|
function getAllActiveAgents() {
|
|
2909
2951
|
try {
|
|
2910
|
-
const files =
|
|
2952
|
+
const files = readdirSync3(CACHE_DIR);
|
|
2911
2953
|
const sessions = [];
|
|
2912
2954
|
for (const file of files) {
|
|
2913
2955
|
if (!file.startsWith("active-agent-") || !file.endsWith(".json")) continue;
|
|
@@ -3050,15 +3092,20 @@ function addEmployee(employees, employee) {
|
|
|
3050
3092
|
}
|
|
3051
3093
|
return [...employees, normalized];
|
|
3052
3094
|
}
|
|
3095
|
+
function findExeBin() {
|
|
3096
|
+
try {
|
|
3097
|
+
return execSync5(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
3098
|
+
} catch {
|
|
3099
|
+
return null;
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3053
3102
|
function registerBinSymlinks(name) {
|
|
3054
3103
|
const created = [];
|
|
3055
3104
|
const skipped = [];
|
|
3056
3105
|
const errors = [];
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
} catch {
|
|
3061
|
-
errors.push("Could not find 'exe' in PATH");
|
|
3106
|
+
const exeBinPath = findExeBin();
|
|
3107
|
+
if (!exeBinPath) {
|
|
3108
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
3062
3109
|
return { created, skipped, errors };
|
|
3063
3110
|
}
|
|
3064
3111
|
const binDir = path9.dirname(exeBinPath);
|
|
@@ -3108,12 +3155,22 @@ __export(license_exports, {
|
|
|
3108
3155
|
loadLicense: () => loadLicense,
|
|
3109
3156
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
3110
3157
|
saveLicense: () => saveLicense,
|
|
3158
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
3159
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
3111
3160
|
validateLicense: () => validateLicense
|
|
3112
3161
|
});
|
|
3113
3162
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
3114
3163
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3115
3164
|
import path10 from "path";
|
|
3116
3165
|
import { jwtVerify, importSPKI } from "jose";
|
|
3166
|
+
async function fetchRetry(url, init) {
|
|
3167
|
+
try {
|
|
3168
|
+
return await fetch(url, init);
|
|
3169
|
+
} catch {
|
|
3170
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
3171
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3117
3174
|
function loadDeviceId() {
|
|
3118
3175
|
const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
|
|
3119
3176
|
try {
|
|
@@ -3145,7 +3202,7 @@ function loadLicense() {
|
|
|
3145
3202
|
}
|
|
3146
3203
|
function saveLicense(apiKey) {
|
|
3147
3204
|
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
3148
|
-
writeFileSync2(LICENSE_PATH, apiKey.trim(), "utf8");
|
|
3205
|
+
writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3149
3206
|
}
|
|
3150
3207
|
async function verifyLicenseJwt(token) {
|
|
3151
3208
|
try {
|
|
@@ -3197,7 +3254,7 @@ function cacheResponse(token) {
|
|
|
3197
3254
|
async function validateLicense(apiKey, deviceId) {
|
|
3198
3255
|
const did = deviceId ?? loadDeviceId();
|
|
3199
3256
|
try {
|
|
3200
|
-
const res = await
|
|
3257
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3201
3258
|
method: "POST",
|
|
3202
3259
|
headers: { "Content-Type": "application/json" },
|
|
3203
3260
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -3232,14 +3289,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
3232
3289
|
} catch {
|
|
3233
3290
|
const cached = await getCachedLicense();
|
|
3234
3291
|
if (cached) return cached;
|
|
3235
|
-
return FREE_LICENSE;
|
|
3292
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
function getCacheAgeMs() {
|
|
3296
|
+
try {
|
|
3297
|
+
const { statSync: statSync4 } = __require("fs");
|
|
3298
|
+
const s = statSync4(CACHE_PATH);
|
|
3299
|
+
return Date.now() - s.mtimeMs;
|
|
3300
|
+
} catch {
|
|
3301
|
+
return Infinity;
|
|
3236
3302
|
}
|
|
3237
3303
|
}
|
|
3238
3304
|
async function checkLicense() {
|
|
3239
3305
|
const key = loadLicense();
|
|
3240
3306
|
if (!key) return FREE_LICENSE;
|
|
3241
3307
|
const cached = await getCachedLicense();
|
|
3242
|
-
if (cached) return cached;
|
|
3308
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
3243
3309
|
const deviceId = loadDeviceId();
|
|
3244
3310
|
return validateLicense(key, deviceId);
|
|
3245
3311
|
}
|
|
@@ -3279,7 +3345,7 @@ async function assertVpsLicense(opts) {
|
|
|
3279
3345
|
let explicitRejection = false;
|
|
3280
3346
|
let transientFailure = false;
|
|
3281
3347
|
try {
|
|
3282
|
-
const res = await
|
|
3348
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
3283
3349
|
method: "POST",
|
|
3284
3350
|
headers: { "Content-Type": "application/json" },
|
|
3285
3351
|
body: JSON.stringify({ apiKey, deviceId }),
|
|
@@ -3360,7 +3426,28 @@ async function assertVpsLicense(opts) {
|
|
|
3360
3426
|
`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.`
|
|
3361
3427
|
);
|
|
3362
3428
|
}
|
|
3363
|
-
|
|
3429
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
3430
|
+
if (_revalTimer) return;
|
|
3431
|
+
_revalTimer = setInterval(async () => {
|
|
3432
|
+
try {
|
|
3433
|
+
const license = await checkLicense();
|
|
3434
|
+
if (!license.valid) {
|
|
3435
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
3436
|
+
}
|
|
3437
|
+
} catch {
|
|
3438
|
+
}
|
|
3439
|
+
}, intervalMs);
|
|
3440
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
3441
|
+
_revalTimer.unref();
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
function stopLicenseRevalidation() {
|
|
3445
|
+
if (_revalTimer) {
|
|
3446
|
+
clearInterval(_revalTimer);
|
|
3447
|
+
_revalTimer = null;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
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;
|
|
3364
3451
|
var init_license = __esm({
|
|
3365
3452
|
"src/lib/license.ts"() {
|
|
3366
3453
|
"use strict";
|
|
@@ -3369,6 +3456,7 @@ var init_license = __esm({
|
|
|
3369
3456
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3370
3457
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3371
3458
|
API_BASE = "https://askexe.com/cloud";
|
|
3459
|
+
RETRY_DELAY_MS = 500;
|
|
3372
3460
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
3373
3461
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
3374
3462
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -3390,6 +3478,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
3390
3478
|
employeeLimit: 1,
|
|
3391
3479
|
memoryLimit: 5e3
|
|
3392
3480
|
};
|
|
3481
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
3482
|
+
_revalTimer = null;
|
|
3393
3483
|
}
|
|
3394
3484
|
});
|
|
3395
3485
|
|
|
@@ -3521,10 +3611,10 @@ var init_plan_limits = __esm({
|
|
|
3521
3611
|
// src/lib/notifications.ts
|
|
3522
3612
|
import crypto5 from "crypto";
|
|
3523
3613
|
import path14 from "path";
|
|
3524
|
-
import
|
|
3614
|
+
import os3 from "os";
|
|
3525
3615
|
import {
|
|
3526
3616
|
readFileSync as readFileSync8,
|
|
3527
|
-
readdirSync as
|
|
3617
|
+
readdirSync as readdirSync4,
|
|
3528
3618
|
unlinkSync as unlinkSync3,
|
|
3529
3619
|
existsSync as existsSync10,
|
|
3530
3620
|
rmdirSync
|
|
@@ -3974,7 +4064,7 @@ var init_tasks_crud = __esm({
|
|
|
3974
4064
|
// src/lib/session-registry.ts
|
|
3975
4065
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync12 } from "fs";
|
|
3976
4066
|
import path16 from "path";
|
|
3977
|
-
import
|
|
4067
|
+
import os4 from "os";
|
|
3978
4068
|
function registerSession(entry) {
|
|
3979
4069
|
const dir = path16.dirname(REGISTRY_PATH);
|
|
3980
4070
|
if (!existsSync12(dir)) {
|
|
@@ -4001,7 +4091,7 @@ var REGISTRY_PATH;
|
|
|
4001
4091
|
var init_session_registry = __esm({
|
|
4002
4092
|
"src/lib/session-registry.ts"() {
|
|
4003
4093
|
"use strict";
|
|
4004
|
-
REGISTRY_PATH = path16.join(
|
|
4094
|
+
REGISTRY_PATH = path16.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
4005
4095
|
}
|
|
4006
4096
|
});
|
|
4007
4097
|
|
|
@@ -4188,7 +4278,7 @@ var init_provider_table = __esm({
|
|
|
4188
4278
|
// src/lib/intercom-queue.ts
|
|
4189
4279
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
|
|
4190
4280
|
import path17 from "path";
|
|
4191
|
-
import
|
|
4281
|
+
import os5 from "os";
|
|
4192
4282
|
function ensureDir() {
|
|
4193
4283
|
const dir = path17.dirname(QUEUE_PATH);
|
|
4194
4284
|
if (!existsSync13(dir)) mkdirSync5(dir, { recursive: true });
|
|
@@ -4228,9 +4318,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
4228
4318
|
var init_intercom_queue = __esm({
|
|
4229
4319
|
"src/lib/intercom-queue.ts"() {
|
|
4230
4320
|
"use strict";
|
|
4231
|
-
QUEUE_PATH = path17.join(
|
|
4321
|
+
QUEUE_PATH = path17.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
4232
4322
|
TTL_MS = 60 * 60 * 1e3;
|
|
4233
|
-
INTERCOM_LOG = path17.join(
|
|
4323
|
+
INTERCOM_LOG = path17.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
4234
4324
|
}
|
|
4235
4325
|
});
|
|
4236
4326
|
|
|
@@ -4238,7 +4328,7 @@ var init_intercom_queue = __esm({
|
|
|
4238
4328
|
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
4239
4329
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync } from "fs";
|
|
4240
4330
|
import path18 from "path";
|
|
4241
|
-
import
|
|
4331
|
+
import os6 from "os";
|
|
4242
4332
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4243
4333
|
import { unlinkSync as unlinkSync4 } from "fs";
|
|
4244
4334
|
function spawnLockPath(sessionName) {
|
|
@@ -4543,7 +4633,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4543
4633
|
const transport = getTransport();
|
|
4544
4634
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4545
4635
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4546
|
-
const logDir = path18.join(
|
|
4636
|
+
const logDir = path18.join(os6.homedir(), ".exe-os", "session-logs");
|
|
4547
4637
|
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4548
4638
|
if (!existsSync14(logDir)) {
|
|
4549
4639
|
mkdirSync6(logDir, { recursive: true });
|
|
@@ -4559,7 +4649,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4559
4649
|
} catch {
|
|
4560
4650
|
}
|
|
4561
4651
|
try {
|
|
4562
|
-
const claudeJsonPath = path18.join(
|
|
4652
|
+
const claudeJsonPath = path18.join(os6.homedir(), ".claude.json");
|
|
4563
4653
|
let claudeJson = {};
|
|
4564
4654
|
try {
|
|
4565
4655
|
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
@@ -4574,7 +4664,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4574
4664
|
} catch {
|
|
4575
4665
|
}
|
|
4576
4666
|
try {
|
|
4577
|
-
const settingsDir = path18.join(
|
|
4667
|
+
const settingsDir = path18.join(os6.homedir(), ".claude", "projects");
|
|
4578
4668
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4579
4669
|
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
4580
4670
|
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
@@ -4622,7 +4712,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4622
4712
|
let legacyFallbackWarned = false;
|
|
4623
4713
|
if (!useExeAgent && !useBinSymlink) {
|
|
4624
4714
|
const identityPath2 = path18.join(
|
|
4625
|
-
|
|
4715
|
+
os6.homedir(),
|
|
4626
4716
|
".exe-os",
|
|
4627
4717
|
"identity",
|
|
4628
4718
|
`${employeeName}.md`
|
|
@@ -4652,7 +4742,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4652
4742
|
}
|
|
4653
4743
|
let sessionContextFlag = "";
|
|
4654
4744
|
try {
|
|
4655
|
-
const ctxDir = path18.join(
|
|
4745
|
+
const ctxDir = path18.join(os6.homedir(), ".exe-os", "session-cache");
|
|
4656
4746
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4657
4747
|
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4658
4748
|
const ctxContent = [
|
|
@@ -4763,11 +4853,11 @@ var init_tmux_routing = __esm({
|
|
|
4763
4853
|
init_provider_table();
|
|
4764
4854
|
init_intercom_queue();
|
|
4765
4855
|
init_plan_limits();
|
|
4766
|
-
SPAWN_LOCK_DIR = path18.join(
|
|
4767
|
-
SESSION_CACHE = path18.join(
|
|
4856
|
+
SPAWN_LOCK_DIR = path18.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
4857
|
+
SESSION_CACHE = path18.join(os6.homedir(), ".exe-os", "session-cache");
|
|
4768
4858
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4769
4859
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4770
|
-
INTERCOM_LOG2 = path18.join(
|
|
4860
|
+
INTERCOM_LOG2 = path18.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
4771
4861
|
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4772
4862
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4773
4863
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
@@ -4776,7 +4866,7 @@ var init_tmux_routing = __esm({
|
|
|
4776
4866
|
|
|
4777
4867
|
// src/lib/tasks-review.ts
|
|
4778
4868
|
import path19 from "path";
|
|
4779
|
-
import { existsSync as existsSync15, readdirSync as
|
|
4869
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4780
4870
|
async function countPendingReviews() {
|
|
4781
4871
|
const client = getClient();
|
|
4782
4872
|
const result = await client.execute({
|
|
@@ -4898,7 +4988,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4898
4988
|
try {
|
|
4899
4989
|
const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
|
|
4900
4990
|
if (existsSync15(cacheDir)) {
|
|
4901
|
-
for (const f of
|
|
4991
|
+
for (const f of readdirSync5(cacheDir)) {
|
|
4902
4992
|
if (f.startsWith("review-notified-")) {
|
|
4903
4993
|
unlinkSync5(path19.join(cacheDir, f));
|
|
4904
4994
|
}
|
|
@@ -6681,50 +6771,59 @@ function registerRecallMyMemory(server2) {
|
|
|
6681
6771
|
user_id,
|
|
6682
6772
|
include_source
|
|
6683
6773
|
}) => {
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6774
|
+
try {
|
|
6775
|
+
const { agentId } = getActiveAgent();
|
|
6776
|
+
const searchOptions = {
|
|
6777
|
+
projectName: project_name,
|
|
6778
|
+
hasError: has_error,
|
|
6779
|
+
toolName: tool_name,
|
|
6780
|
+
limit,
|
|
6781
|
+
since,
|
|
6782
|
+
includeArchived: include_archived,
|
|
6783
|
+
workspaceId: workspace_id,
|
|
6784
|
+
includeSource: include_source,
|
|
6785
|
+
...user_id !== void 0 ? { userId: user_id } : {}
|
|
6786
|
+
};
|
|
6787
|
+
const results = await hybridSearch(query, agentId, searchOptions);
|
|
6788
|
+
if (results.length === 0) {
|
|
6789
|
+
return {
|
|
6790
|
+
content: [
|
|
6791
|
+
{ type: "text", text: "No matching memories found." }
|
|
6792
|
+
]
|
|
6793
|
+
};
|
|
6794
|
+
}
|
|
6795
|
+
const formatted = results.map((r) => {
|
|
6796
|
+
const header = `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}`;
|
|
6797
|
+
const body = r.raw_text.slice(0, 500);
|
|
6798
|
+
const parts = [header];
|
|
6799
|
+
if (r.source_path) {
|
|
6800
|
+
const typeTag = r.source_type && r.source_type !== "text" ? ` [${r.source_type}]` : "";
|
|
6801
|
+
parts.push(`source: ${r.source_path}${typeTag}`);
|
|
6802
|
+
} else if (include_source) {
|
|
6803
|
+
const sourceLine = formatSourceLine(r);
|
|
6804
|
+
if (sourceLine) parts.push(sourceLine);
|
|
6805
|
+
}
|
|
6806
|
+
parts.push(body);
|
|
6807
|
+
return parts.join("\n");
|
|
6808
|
+
}).join("\n\n---\n\n");
|
|
6698
6809
|
return {
|
|
6699
6810
|
content: [
|
|
6700
|
-
{
|
|
6811
|
+
{
|
|
6812
|
+
type: "text",
|
|
6813
|
+
text: `Found ${results.length} memories:
|
|
6814
|
+
|
|
6815
|
+
${formatted}`
|
|
6816
|
+
}
|
|
6701
6817
|
]
|
|
6702
6818
|
};
|
|
6819
|
+
} catch (err) {
|
|
6820
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6821
|
+
const friendly = msg.includes("SQLITE_BUSY") ? "Memory system is busy \u2014 please retry." : "Failed to search memories. Please retry.";
|
|
6822
|
+
return {
|
|
6823
|
+
content: [{ type: "text", text: friendly }],
|
|
6824
|
+
isError: true
|
|
6825
|
+
};
|
|
6703
6826
|
}
|
|
6704
|
-
const formatted = results.map((r) => {
|
|
6705
|
-
const header = `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}`;
|
|
6706
|
-
const body = r.raw_text.slice(0, 500);
|
|
6707
|
-
const parts = [header];
|
|
6708
|
-
if (r.source_path) {
|
|
6709
|
-
const typeTag = r.source_type && r.source_type !== "text" ? ` [${r.source_type}]` : "";
|
|
6710
|
-
parts.push(`source: ${r.source_path}${typeTag}`);
|
|
6711
|
-
} else if (include_source) {
|
|
6712
|
-
const sourceLine = formatSourceLine(r);
|
|
6713
|
-
if (sourceLine) parts.push(sourceLine);
|
|
6714
|
-
}
|
|
6715
|
-
parts.push(body);
|
|
6716
|
-
return parts.join("\n");
|
|
6717
|
-
}).join("\n\n---\n\n");
|
|
6718
|
-
return {
|
|
6719
|
-
content: [
|
|
6720
|
-
{
|
|
6721
|
-
type: "text",
|
|
6722
|
-
text: `Found ${results.length} memories:
|
|
6723
|
-
|
|
6724
|
-
${formatted}`
|
|
6725
|
-
}
|
|
6726
|
-
]
|
|
6727
|
-
};
|
|
6728
6827
|
}
|
|
6729
6828
|
);
|
|
6730
6829
|
}
|
|
@@ -6751,36 +6850,45 @@ function registerAskTeamMemory(server2) {
|
|
|
6751
6850
|
}
|
|
6752
6851
|
},
|
|
6753
6852
|
async ({ team_member, query, project_name, limit, since, include_archived }) => {
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6853
|
+
try {
|
|
6854
|
+
const results = await hybridSearch(query, team_member, {
|
|
6855
|
+
projectName: project_name,
|
|
6856
|
+
limit,
|
|
6857
|
+
since,
|
|
6858
|
+
includeArchived: include_archived
|
|
6859
|
+
});
|
|
6860
|
+
if (results.length === 0) {
|
|
6861
|
+
return {
|
|
6862
|
+
content: [
|
|
6863
|
+
{
|
|
6864
|
+
type: "text",
|
|
6865
|
+
text: `No memories found for team member '${team_member}'.`
|
|
6866
|
+
}
|
|
6867
|
+
]
|
|
6868
|
+
};
|
|
6869
|
+
}
|
|
6870
|
+
const formatted = results.map(
|
|
6871
|
+
(r) => `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}
|
|
6872
|
+
${r.raw_text.slice(0, 500)}`
|
|
6873
|
+
).join("\n\n---\n\n");
|
|
6761
6874
|
return {
|
|
6762
6875
|
content: [
|
|
6763
6876
|
{
|
|
6764
6877
|
type: "text",
|
|
6765
|
-
text: `
|
|
6878
|
+
text: `From ${team_member}'s memories (${results.length} results):
|
|
6879
|
+
|
|
6880
|
+
${formatted}`
|
|
6766
6881
|
}
|
|
6767
6882
|
]
|
|
6768
6883
|
};
|
|
6884
|
+
} catch (err) {
|
|
6885
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6886
|
+
const friendly = msg.includes("SQLITE_BUSY") ? "Memory system is busy \u2014 please retry." : "Failed to search team memories. Please retry.";
|
|
6887
|
+
return {
|
|
6888
|
+
content: [{ type: "text", text: friendly }],
|
|
6889
|
+
isError: true
|
|
6890
|
+
};
|
|
6769
6891
|
}
|
|
6770
|
-
const formatted = results.map(
|
|
6771
|
-
(r) => `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}
|
|
6772
|
-
${r.raw_text.slice(0, 500)}`
|
|
6773
|
-
).join("\n\n---\n\n");
|
|
6774
|
-
return {
|
|
6775
|
-
content: [
|
|
6776
|
-
{
|
|
6777
|
-
type: "text",
|
|
6778
|
-
text: `From ${team_member}'s memories (${results.length} results):
|
|
6779
|
-
|
|
6780
|
-
${formatted}`
|
|
6781
|
-
}
|
|
6782
|
-
]
|
|
6783
|
-
};
|
|
6784
6892
|
}
|
|
6785
6893
|
);
|
|
6786
6894
|
}
|
|
@@ -7428,36 +7536,44 @@ function registerListTasks(server2) {
|
|
|
7428
7536
|
}
|
|
7429
7537
|
},
|
|
7430
7538
|
async ({ assigned_to, status, project_name, priority }) => {
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7539
|
+
try {
|
|
7540
|
+
const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
|
|
7541
|
+
const tasks = await listTasks({
|
|
7542
|
+
assignedTo: assigned_to,
|
|
7543
|
+
status,
|
|
7544
|
+
projectName: resolvedProject,
|
|
7545
|
+
priority
|
|
7546
|
+
});
|
|
7547
|
+
if (tasks.length === 0) {
|
|
7548
|
+
return {
|
|
7549
|
+
content: [{ type: "text", text: "No tasks found." }]
|
|
7550
|
+
};
|
|
7551
|
+
}
|
|
7552
|
+
const lines = tasks.map((t) => {
|
|
7553
|
+
const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
|
|
7554
|
+
let budgetNote = "";
|
|
7555
|
+
if (t.budgetTokens !== null) {
|
|
7556
|
+
const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
|
|
7557
|
+
budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
|
|
7558
|
+
}
|
|
7559
|
+
return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
|
|
7560
|
+
});
|
|
7561
|
+
return {
|
|
7562
|
+
content: [
|
|
7563
|
+
{
|
|
7564
|
+
type: "text",
|
|
7565
|
+
text: `${tasks.length} task(s):
|
|
7566
|
+
${lines.join("\n")}`
|
|
7567
|
+
}
|
|
7568
|
+
]
|
|
7569
|
+
};
|
|
7570
|
+
} catch (err) {
|
|
7571
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7439
7572
|
return {
|
|
7440
|
-
content: [{ type: "text", text:
|
|
7573
|
+
content: [{ type: "text", text: `Failed to list tasks: ${msg}` }],
|
|
7574
|
+
isError: true
|
|
7441
7575
|
};
|
|
7442
7576
|
}
|
|
7443
|
-
const lines = tasks.map((t) => {
|
|
7444
|
-
const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
|
|
7445
|
-
let budgetNote = "";
|
|
7446
|
-
if (t.budgetTokens !== null) {
|
|
7447
|
-
const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
|
|
7448
|
-
budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
|
|
7449
|
-
}
|
|
7450
|
-
return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
|
|
7451
|
-
});
|
|
7452
|
-
return {
|
|
7453
|
-
content: [
|
|
7454
|
-
{
|
|
7455
|
-
type: "text",
|
|
7456
|
-
text: `${tasks.length} task(s):
|
|
7457
|
-
${lines.join("\n")}`
|
|
7458
|
-
}
|
|
7459
|
-
]
|
|
7460
|
-
};
|
|
7461
7577
|
}
|
|
7462
7578
|
);
|
|
7463
7579
|
}
|
|
@@ -7590,7 +7706,7 @@ All tasks complete. No more open tasks in your queue.`;
|
|
|
7590
7706
|
init_tasks();
|
|
7591
7707
|
init_active_agent();
|
|
7592
7708
|
import { z as z10 } from "zod";
|
|
7593
|
-
var
|
|
7709
|
+
var CLOSE_TASK_ALLOWED_ROLES = /* @__PURE__ */ new Set(["COO", "CTO"]);
|
|
7594
7710
|
function registerCloseTask(server2) {
|
|
7595
7711
|
server2.registerTool(
|
|
7596
7712
|
"close_task",
|
|
@@ -7605,7 +7721,7 @@ function registerCloseTask(server2) {
|
|
|
7605
7721
|
},
|
|
7606
7722
|
async ({ task_id, result, status }) => {
|
|
7607
7723
|
const agent = getActiveAgent();
|
|
7608
|
-
if (agent.agentId && !
|
|
7724
|
+
if (agent.agentId && agent.agentId !== "default" && !CLOSE_TASK_ALLOWED_ROLES.has(agent.agentRole ?? "")) {
|
|
7609
7725
|
return {
|
|
7610
7726
|
content: [
|
|
7611
7727
|
{
|
|
@@ -7967,7 +8083,7 @@ async function createReminder(text, dueDate) {
|
|
|
7967
8083
|
}
|
|
7968
8084
|
async function listReminders(includeCompleted = false) {
|
|
7969
8085
|
const client = getClient();
|
|
7970
|
-
const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST`;
|
|
8086
|
+
const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST LIMIT 500` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST LIMIT 500`;
|
|
7971
8087
|
const result = await client.execute(sql);
|
|
7972
8088
|
return result.rows.map((row) => ({
|
|
7973
8089
|
id: String(row.id),
|
|
@@ -7986,7 +8102,7 @@ async function completeReminder(idOrText) {
|
|
|
7986
8102
|
});
|
|
7987
8103
|
if (result.rows.length === 0) {
|
|
7988
8104
|
result = await client.execute({
|
|
7989
|
-
sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%'`,
|
|
8105
|
+
sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
|
|
7990
8106
|
args: [idOrText]
|
|
7991
8107
|
});
|
|
7992
8108
|
}
|
|
@@ -8171,7 +8287,7 @@ import { z as z20 } from "zod";
|
|
|
8171
8287
|
init_config();
|
|
8172
8288
|
init_database();
|
|
8173
8289
|
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
8174
|
-
import { readdirSync as
|
|
8290
|
+
import { readdirSync as readdirSync6 } from "fs";
|
|
8175
8291
|
import path22 from "path";
|
|
8176
8292
|
import { createHash } from "crypto";
|
|
8177
8293
|
var IDENTITY_DIR = path22.join(EXE_AI_DIR, "identity");
|
|
@@ -8254,7 +8370,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
8254
8370
|
}
|
|
8255
8371
|
function listIdentities() {
|
|
8256
8372
|
ensureDir2();
|
|
8257
|
-
const files =
|
|
8373
|
+
const files = readdirSync6(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
8258
8374
|
const results = [];
|
|
8259
8375
|
for (const file of files) {
|
|
8260
8376
|
const agentId = file.replace(".md", "");
|
|
@@ -8339,7 +8455,7 @@ function registerUpdateIdentity(server2) {
|
|
|
8339
8455
|
},
|
|
8340
8456
|
async ({ agent_id, content }) => {
|
|
8341
8457
|
const caller = getActiveAgent();
|
|
8342
|
-
const allowed = caller.agentId === "
|
|
8458
|
+
const allowed = caller.agentId === "default" || caller.agentRole === "COO";
|
|
8343
8459
|
if (!allowed) {
|
|
8344
8460
|
return {
|
|
8345
8461
|
content: [{
|
|
@@ -8388,7 +8504,7 @@ function registerDeactivateBehavior(server2) {
|
|
|
8388
8504
|
},
|
|
8389
8505
|
async ({ behavior_id }) => {
|
|
8390
8506
|
const caller = getActiveAgent();
|
|
8391
|
-
const allowed = caller.agentId === "
|
|
8507
|
+
const allowed = caller.agentId === "default" || caller.agentRole === "COO";
|
|
8392
8508
|
if (!allowed) {
|
|
8393
8509
|
return {
|
|
8394
8510
|
content: [{
|
|
@@ -8725,10 +8841,12 @@ function registerIngestDocument(server2) {
|
|
|
8725
8841
|
}]
|
|
8726
8842
|
};
|
|
8727
8843
|
} catch (err) {
|
|
8844
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
8845
|
+
const friendly = raw.includes("SQLITE_BUSY") ? "Memory system is busy, please retry in a moment." : raw.includes("SQLITE_") ? "A database error occurred. Please retry." : `ingest_document failed: ${raw}`;
|
|
8728
8846
|
return {
|
|
8729
8847
|
content: [{
|
|
8730
8848
|
type: "text",
|
|
8731
|
-
text:
|
|
8849
|
+
text: friendly
|
|
8732
8850
|
}],
|
|
8733
8851
|
isError: true
|
|
8734
8852
|
};
|
|
@@ -8917,7 +9035,8 @@ async function gqlRequest(query, variables) {
|
|
|
8917
9035
|
"Content-Type": "application/json",
|
|
8918
9036
|
Authorization: `Bearer ${config.apiToken}`
|
|
8919
9037
|
},
|
|
8920
|
-
body: JSON.stringify({ query, variables })
|
|
9038
|
+
body: JSON.stringify({ query, variables }),
|
|
9039
|
+
signal: AbortSignal.timeout(3e4)
|
|
8921
9040
|
});
|
|
8922
9041
|
if (!res.ok) {
|
|
8923
9042
|
throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
|
|
@@ -9080,7 +9199,8 @@ async function lookupPhoneByName(name) {
|
|
|
9080
9199
|
"Content-Type": "application/json",
|
|
9081
9200
|
Authorization: `Bearer ${apiToken}`
|
|
9082
9201
|
},
|
|
9083
|
-
body: JSON.stringify({ query, variables })
|
|
9202
|
+
body: JSON.stringify({ query, variables }),
|
|
9203
|
+
signal: AbortSignal.timeout(3e4)
|
|
9084
9204
|
});
|
|
9085
9205
|
if (!res.ok) return null;
|
|
9086
9206
|
const json = await res.json();
|
|
@@ -9105,11 +9225,12 @@ async function sendWhatsAppMessage(phoneNumberId, accessToken, to, text) {
|
|
|
9105
9225
|
to,
|
|
9106
9226
|
type: "text",
|
|
9107
9227
|
text: { body: text }
|
|
9108
|
-
})
|
|
9228
|
+
}),
|
|
9229
|
+
signal: AbortSignal.timeout(3e4)
|
|
9109
9230
|
});
|
|
9110
9231
|
if (!res.ok) {
|
|
9111
|
-
|
|
9112
|
-
throw new Error(`WhatsApp
|
|
9232
|
+
await res.text();
|
|
9233
|
+
throw new Error(`WhatsApp message could not be delivered (HTTP ${res.status}). Check your WhatsApp configuration.`);
|
|
9113
9234
|
}
|
|
9114
9235
|
}
|
|
9115
9236
|
function isPhoneNumber(value) {
|
|
@@ -9193,11 +9314,13 @@ function registerSendWhatsapp(server2) {
|
|
|
9193
9314
|
});
|
|
9194
9315
|
results.push({ recipient, phone, ok: true });
|
|
9195
9316
|
} catch (err) {
|
|
9317
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
9318
|
+
const friendly = raw.includes("HTTP") ? raw : `Message delivery failed. Check WhatsApp configuration.`;
|
|
9196
9319
|
results.push({
|
|
9197
9320
|
recipient,
|
|
9198
9321
|
phone,
|
|
9199
9322
|
ok: false,
|
|
9200
|
-
error:
|
|
9323
|
+
error: friendly
|
|
9201
9324
|
});
|
|
9202
9325
|
}
|
|
9203
9326
|
}
|
|
@@ -9219,8 +9342,8 @@ import { z as z29 } from "zod";
|
|
|
9219
9342
|
import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
9220
9343
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
9221
9344
|
import path23 from "path";
|
|
9222
|
-
import
|
|
9223
|
-
var TRIGGERS_PATH = path23.join(
|
|
9345
|
+
import os7 from "os";
|
|
9346
|
+
var TRIGGERS_PATH = path23.join(os7.homedir(), ".exe-os", "triggers.json");
|
|
9224
9347
|
function loadTriggers(project) {
|
|
9225
9348
|
if (!existsSync17(TRIGGERS_PATH)) return [];
|
|
9226
9349
|
try {
|
|
@@ -9522,14 +9645,14 @@ function registerListTriggers(server2) {
|
|
|
9522
9645
|
import { z as z31 } from "zod";
|
|
9523
9646
|
|
|
9524
9647
|
// src/automation/starter-packs/index.ts
|
|
9525
|
-
import { readFileSync as readFileSync16, readdirSync as
|
|
9648
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync7, existsSync as existsSync18 } from "fs";
|
|
9526
9649
|
import path24 from "path";
|
|
9527
9650
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9528
9651
|
var __dirname = path24.dirname(fileURLToPath3(import.meta.url));
|
|
9529
9652
|
function listPacks() {
|
|
9530
9653
|
const packsDir = path24.join(__dirname, ".");
|
|
9531
9654
|
if (!existsSync18(packsDir)) return [];
|
|
9532
|
-
return
|
|
9655
|
+
return readdirSync7(packsDir, { withFileTypes: true }).filter(
|
|
9533
9656
|
(d) => d.isDirectory() && existsSync18(path24.join(packsDir, d.name, "custom-objects.json"))
|
|
9534
9657
|
).map((d) => d.name);
|
|
9535
9658
|
}
|
|
@@ -9561,7 +9684,7 @@ function loadPack(industry) {
|
|
|
9561
9684
|
}
|
|
9562
9685
|
const wikiSeeds = [];
|
|
9563
9686
|
if (existsSync18(wikiDir)) {
|
|
9564
|
-
const files =
|
|
9687
|
+
const files = readdirSync7(wikiDir).filter((f) => f.endsWith(".md"));
|
|
9565
9688
|
for (const file of files) {
|
|
9566
9689
|
const content = readFileSync16(path24.join(wikiDir, file), "utf-8");
|
|
9567
9690
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
@@ -10736,7 +10859,7 @@ var HostingerApiClient = class {
|
|
|
10736
10859
|
"Content-Type": "application/json",
|
|
10737
10860
|
Accept: "application/json"
|
|
10738
10861
|
};
|
|
10739
|
-
const options = { method, headers };
|
|
10862
|
+
const options = { method, headers, signal: AbortSignal.timeout(3e4) };
|
|
10740
10863
|
if (body) {
|
|
10741
10864
|
options.body = JSON.stringify(body);
|
|
10742
10865
|
}
|
|
@@ -11047,11 +11170,13 @@ function registerQueryConversations(server2) {
|
|
|
11047
11170
|
]
|
|
11048
11171
|
};
|
|
11049
11172
|
} catch (err) {
|
|
11173
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
11174
|
+
const friendly = raw.includes("SQLITE_BUSY") ? "Memory system is busy, please retry in a moment." : raw.includes("SQLITE_") ? "A database error occurred. Please retry." : `Error querying conversations: ${raw}`;
|
|
11050
11175
|
return {
|
|
11051
11176
|
content: [
|
|
11052
11177
|
{
|
|
11053
11178
|
type: "text",
|
|
11054
|
-
text:
|
|
11179
|
+
text: friendly
|
|
11055
11180
|
}
|
|
11056
11181
|
],
|
|
11057
11182
|
isError: true
|
|
@@ -11063,13 +11188,13 @@ function registerQueryConversations(server2) {
|
|
|
11063
11188
|
|
|
11064
11189
|
// src/mcp/tools/load-skill.ts
|
|
11065
11190
|
import { z as z39 } from "zod";
|
|
11066
|
-
import { readFileSync as readFileSync17, readdirSync as
|
|
11191
|
+
import { readFileSync as readFileSync17, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
|
|
11067
11192
|
import path27 from "path";
|
|
11068
11193
|
import { homedir as homedir2 } from "os";
|
|
11069
11194
|
var SKILLS_DIR = path27.join(homedir2(), ".claude", "skills");
|
|
11070
11195
|
function listAvailableSkills() {
|
|
11071
11196
|
try {
|
|
11072
|
-
const entries =
|
|
11197
|
+
const entries = readdirSync8(SKILLS_DIR);
|
|
11073
11198
|
return entries.filter((entry) => {
|
|
11074
11199
|
try {
|
|
11075
11200
|
const entryPath = path27.join(SKILLS_DIR, entry);
|