@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/cli.js
CHANGED
|
@@ -42,7 +42,7 @@ __export(config_exports, {
|
|
|
42
42
|
migrateConfig: () => migrateConfig,
|
|
43
43
|
saveConfig: () => saveConfig
|
|
44
44
|
});
|
|
45
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
45
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
46
46
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
47
47
|
import path from "path";
|
|
48
48
|
import os from "os";
|
|
@@ -168,6 +168,9 @@ async function saveConfig(config) {
|
|
|
168
168
|
await mkdir(dir, { recursive: true });
|
|
169
169
|
const configPath = path.join(dir, "config.json");
|
|
170
170
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
171
|
+
if (config.cloud?.apiKey) {
|
|
172
|
+
await chmod(configPath, 384);
|
|
173
|
+
}
|
|
171
174
|
}
|
|
172
175
|
async function loadConfigFrom(configPath) {
|
|
173
176
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -356,15 +359,20 @@ function addEmployee(employees, employee) {
|
|
|
356
359
|
}
|
|
357
360
|
return [...employees, normalized];
|
|
358
361
|
}
|
|
362
|
+
function findExeBin() {
|
|
363
|
+
try {
|
|
364
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
359
369
|
function registerBinSymlinks(name) {
|
|
360
370
|
const created = [];
|
|
361
371
|
const skipped = [];
|
|
362
372
|
const errors = [];
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
} catch {
|
|
367
|
-
errors.push("Could not find 'exe' in PATH");
|
|
373
|
+
const exeBinPath = findExeBin();
|
|
374
|
+
if (!exeBinPath) {
|
|
375
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
368
376
|
return { created, skipped, errors };
|
|
369
377
|
}
|
|
370
378
|
const binDir = path2.dirname(exeBinPath);
|
|
@@ -621,6 +629,10 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
621
629
|
if (!settings.hooks) {
|
|
622
630
|
settings.hooks = {};
|
|
623
631
|
}
|
|
632
|
+
if (settings.hooks && typeof settings.hooks !== "object") {
|
|
633
|
+
console.warn("[exe-os] Unexpected hooks schema in settings.json \u2014 skipping hook installation");
|
|
634
|
+
return { added: 0, skipped: 0 };
|
|
635
|
+
}
|
|
624
636
|
const hooksToRegister = [
|
|
625
637
|
{
|
|
626
638
|
event: "PostToolUse",
|
|
@@ -783,6 +795,11 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
783
795
|
if (!settings.hooks[event]) {
|
|
784
796
|
settings.hooks[event] = [];
|
|
785
797
|
}
|
|
798
|
+
if (!Array.isArray(settings.hooks[event])) {
|
|
799
|
+
console.warn(`[exe-os] Hook event "${event}" has unexpected structure \u2014 skipping`);
|
|
800
|
+
skipped++;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
786
803
|
const existing = settings.hooks[event];
|
|
787
804
|
const correctCommand = group.hooks[0]?.command ?? "";
|
|
788
805
|
const alreadyCorrect = existing.some(
|
|
@@ -1087,6 +1104,7 @@ async function ensureSchema() {
|
|
|
1087
1104
|
const client = getRawClient();
|
|
1088
1105
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1089
1106
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1107
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
1090
1108
|
try {
|
|
1091
1109
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1092
1110
|
} catch {
|
|
@@ -1903,12 +1921,13 @@ __export(keychain_exports, {
|
|
|
1903
1921
|
importMnemonic: () => importMnemonic,
|
|
1904
1922
|
setMasterKey: () => setMasterKey
|
|
1905
1923
|
});
|
|
1906
|
-
import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4, chmod } from "fs/promises";
|
|
1924
|
+
import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
1907
1925
|
import { existsSync as existsSync5 } from "fs";
|
|
1908
1926
|
import path5 from "path";
|
|
1927
|
+
import os4 from "os";
|
|
1909
1928
|
import crypto from "crypto";
|
|
1910
1929
|
function getKeyDir() {
|
|
1911
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(
|
|
1930
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
|
|
1912
1931
|
}
|
|
1913
1932
|
function getKeyPath() {
|
|
1914
1933
|
return path5.join(getKeyDir(), "master.key");
|
|
@@ -1956,7 +1975,7 @@ async function setMasterKey(key) {
|
|
|
1956
1975
|
await mkdir4(dir, { recursive: true });
|
|
1957
1976
|
const keyPath = getKeyPath();
|
|
1958
1977
|
await writeFile4(keyPath, b64 + "\n", "utf-8");
|
|
1959
|
-
await
|
|
1978
|
+
await chmod2(keyPath, 384);
|
|
1960
1979
|
}
|
|
1961
1980
|
async function deleteMasterKey() {
|
|
1962
1981
|
const keytar = await tryKeytar();
|
|
@@ -2054,7 +2073,7 @@ __export(shard_manager_exports, {
|
|
|
2054
2073
|
shardExists: () => shardExists
|
|
2055
2074
|
});
|
|
2056
2075
|
import path6 from "path";
|
|
2057
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
2076
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2058
2077
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2059
2078
|
function initShardManager(encryptionKey) {
|
|
2060
2079
|
_encryptionKey = encryptionKey;
|
|
@@ -2093,8 +2112,7 @@ function shardExists(projectName) {
|
|
|
2093
2112
|
}
|
|
2094
2113
|
function listShards() {
|
|
2095
2114
|
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2096
|
-
|
|
2097
|
-
return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2115
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2098
2116
|
}
|
|
2099
2117
|
async function ensureShardSchema(client) {
|
|
2100
2118
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -2299,6 +2317,28 @@ __export(store_exports, {
|
|
|
2299
2317
|
vectorToBlob: () => vectorToBlob,
|
|
2300
2318
|
writeMemory: () => writeMemory
|
|
2301
2319
|
});
|
|
2320
|
+
function isBusyError2(err) {
|
|
2321
|
+
if (err instanceof Error) {
|
|
2322
|
+
const msg = err.message.toLowerCase();
|
|
2323
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
2324
|
+
}
|
|
2325
|
+
return false;
|
|
2326
|
+
}
|
|
2327
|
+
async function retryOnBusy2(fn, label) {
|
|
2328
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
2329
|
+
try {
|
|
2330
|
+
return await fn();
|
|
2331
|
+
} catch (err) {
|
|
2332
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
2333
|
+
process.stderr.write(
|
|
2334
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
2335
|
+
`
|
|
2336
|
+
);
|
|
2337
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
throw new Error("unreachable");
|
|
2341
|
+
}
|
|
2302
2342
|
async function initStore(options) {
|
|
2303
2343
|
if (_flushTimer !== null) {
|
|
2304
2344
|
clearInterval(_flushTimer);
|
|
@@ -2327,14 +2367,17 @@ async function initStore(options) {
|
|
|
2327
2367
|
dbPath,
|
|
2328
2368
|
encryptionKey: hexKey
|
|
2329
2369
|
});
|
|
2330
|
-
await ensureSchema();
|
|
2370
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
2331
2371
|
try {
|
|
2332
2372
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
2333
2373
|
initShardManager2(hexKey);
|
|
2334
2374
|
} catch {
|
|
2335
2375
|
}
|
|
2336
2376
|
const client = getClient();
|
|
2337
|
-
const vResult = await
|
|
2377
|
+
const vResult = await retryOnBusy2(
|
|
2378
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
2379
|
+
"version-query"
|
|
2380
|
+
);
|
|
2338
2381
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2339
2382
|
}
|
|
2340
2383
|
function classifyTier(record) {
|
|
@@ -2377,6 +2420,12 @@ async function writeMemory(record) {
|
|
|
2377
2420
|
supersedes_id: record.supersedes_id ?? null
|
|
2378
2421
|
};
|
|
2379
2422
|
_pendingRecords.push(dbRow);
|
|
2423
|
+
const MAX_PENDING = 1e3;
|
|
2424
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
2425
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
2426
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
2427
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
2428
|
+
}
|
|
2380
2429
|
if (_flushTimer === null) {
|
|
2381
2430
|
_flushTimer = setInterval(() => {
|
|
2382
2431
|
void flushBatch();
|
|
@@ -2708,7 +2757,7 @@ async function getMemoryCardinality(agentId) {
|
|
|
2708
2757
|
return 0;
|
|
2709
2758
|
}
|
|
2710
2759
|
}
|
|
2711
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2760
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
2712
2761
|
var init_store = __esm({
|
|
2713
2762
|
"src/lib/store.ts"() {
|
|
2714
2763
|
"use strict";
|
|
@@ -2716,6 +2765,8 @@ var init_store = __esm({
|
|
|
2716
2765
|
init_database();
|
|
2717
2766
|
init_keychain();
|
|
2718
2767
|
init_config();
|
|
2768
|
+
INIT_MAX_RETRIES = 3;
|
|
2769
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
2719
2770
|
_pendingRecords = [];
|
|
2720
2771
|
_batchSize = 20;
|
|
2721
2772
|
_flushIntervalMs = 1e4;
|
|
@@ -2734,6 +2785,10 @@ import path7 from "path";
|
|
|
2734
2785
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2735
2786
|
function handleData(chunk) {
|
|
2736
2787
|
_buffer += chunk.toString();
|
|
2788
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
2789
|
+
_buffer = "";
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2737
2792
|
let newlineIdx;
|
|
2738
2793
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
2739
2794
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -3041,7 +3096,7 @@ function disconnectClient() {
|
|
|
3041
3096
|
entry.resolve({ error: "Client disconnected" });
|
|
3042
3097
|
}
|
|
3043
3098
|
}
|
|
3044
|
-
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;
|
|
3099
|
+
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;
|
|
3045
3100
|
var init_exe_daemon_client = __esm({
|
|
3046
3101
|
"src/lib/exe-daemon-client.ts"() {
|
|
3047
3102
|
"use strict";
|
|
@@ -3058,6 +3113,7 @@ var init_exe_daemon_client = __esm({
|
|
|
3058
3113
|
_requestCount = 0;
|
|
3059
3114
|
HEALTH_CHECK_INTERVAL = 100;
|
|
3060
3115
|
_pending = /* @__PURE__ */ new Map();
|
|
3116
|
+
MAX_BUFFER = 1e7;
|
|
3061
3117
|
}
|
|
3062
3118
|
});
|
|
3063
3119
|
|
|
@@ -3095,7 +3151,8 @@ import { parseArgs } from "util";
|
|
|
3095
3151
|
async function findJsonlFiles(sinceDate, projectFilter) {
|
|
3096
3152
|
const projectsDir = path8.join(homedir(), ".claude", "projects");
|
|
3097
3153
|
const files = [];
|
|
3098
|
-
async function walk(dir) {
|
|
3154
|
+
async function walk(dir, depth = 0) {
|
|
3155
|
+
if (depth > MAX_WALK_DEPTH) return;
|
|
3099
3156
|
let entries;
|
|
3100
3157
|
try {
|
|
3101
3158
|
entries = await readdir2(dir, { withFileTypes: true });
|
|
@@ -3106,7 +3163,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
|
|
|
3106
3163
|
const full = path8.join(dir, entry.name);
|
|
3107
3164
|
if (entry.isDirectory()) {
|
|
3108
3165
|
if (entry.name === "subagents" || entry.name === "tool-results") continue;
|
|
3109
|
-
await walk(full);
|
|
3166
|
+
await walk(full, depth + 1);
|
|
3110
3167
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
3111
3168
|
try {
|
|
3112
3169
|
const s = await stat(full);
|
|
@@ -3347,65 +3404,72 @@ async function backfillConversations(options) {
|
|
|
3347
3404
|
process.stderr.write(`[backfill-conversations] Found ${files.length} JSONL files to process
|
|
3348
3405
|
`);
|
|
3349
3406
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3407
|
+
const BATCH_SIZE = 50;
|
|
3408
|
+
const MAX_DEDUP_SIZE = 5e4;
|
|
3409
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += BATCH_SIZE) {
|
|
3410
|
+
const batch = files.slice(batchStart, batchStart + BATCH_SIZE);
|
|
3411
|
+
for (const file of batch) {
|
|
3412
|
+
stats.filesScanned++;
|
|
3413
|
+
if (existingPaths.size < MAX_DEDUP_SIZE && existingPaths.has(file)) {
|
|
3414
|
+
stats.skippedDedup++;
|
|
3415
|
+
continue;
|
|
3416
|
+
}
|
|
3417
|
+
const conv = await parseConversation(file);
|
|
3418
|
+
if (conv.totalMessages < MIN_MESSAGES) {
|
|
3419
|
+
stats.skippedTooShort++;
|
|
3420
|
+
continue;
|
|
3421
|
+
}
|
|
3422
|
+
const summary = buildSummary(conv);
|
|
3423
|
+
if (options.dryRun) {
|
|
3424
|
+
process.stdout.write(`
|
|
3364
3425
|
\u2500\u2500\u2500 ${file} \u2500\u2500\u2500
|
|
3365
3426
|
`);
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3427
|
+
process.stdout.write(`Project: ${conv.projectName} | Messages: ${conv.totalMessages}`);
|
|
3428
|
+
process.stdout.write(` | Tools: ${Object.keys(conv.toolCounts).length}`);
|
|
3429
|
+
process.stdout.write(` | Files: ${conv.filesTouched.size}
|
|
3369
3430
|
`);
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3431
|
+
const firstPrompt = conv.userMessages[0];
|
|
3432
|
+
if (firstPrompt) {
|
|
3433
|
+
process.stdout.write(`First prompt: ${firstPrompt.slice(0, 120)}
|
|
3373
3434
|
`);
|
|
3435
|
+
}
|
|
3436
|
+
stats.conversationsStored++;
|
|
3437
|
+
continue;
|
|
3438
|
+
}
|
|
3439
|
+
let vector = null;
|
|
3440
|
+
if (daemonConnected) {
|
|
3441
|
+
try {
|
|
3442
|
+
vector = await embedViaClient(summary, "low");
|
|
3443
|
+
if (!vector) stats.embedFailed++;
|
|
3444
|
+
} catch {
|
|
3445
|
+
stats.embedFailed++;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
await writeMemory({
|
|
3449
|
+
id: crypto2.randomUUID(),
|
|
3450
|
+
agent_id: conv.agentId,
|
|
3451
|
+
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3452
|
+
session_id: conv.sessionId,
|
|
3453
|
+
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3454
|
+
tool_name: TOOL_NAME,
|
|
3455
|
+
project_name: conv.projectName,
|
|
3456
|
+
has_error: conv.errorCount > 0,
|
|
3457
|
+
raw_text: summary,
|
|
3458
|
+
vector,
|
|
3459
|
+
source_path: file,
|
|
3460
|
+
source_type: "conversation"
|
|
3461
|
+
});
|
|
3462
|
+
if (existingPaths.size < MAX_DEDUP_SIZE) {
|
|
3463
|
+
existingPaths.add(file);
|
|
3374
3464
|
}
|
|
3375
3465
|
stats.conversationsStored++;
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
if (daemonConnected) {
|
|
3380
|
-
try {
|
|
3381
|
-
vector = await embedViaClient(summary, "low");
|
|
3382
|
-
if (!vector) stats.embedFailed++;
|
|
3383
|
-
} catch {
|
|
3384
|
-
stats.embedFailed++;
|
|
3385
|
-
}
|
|
3386
|
-
}
|
|
3387
|
-
await writeMemory({
|
|
3388
|
-
id: crypto2.randomUUID(),
|
|
3389
|
-
agent_id: conv.agentId,
|
|
3390
|
-
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3391
|
-
session_id: conv.sessionId,
|
|
3392
|
-
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3393
|
-
tool_name: TOOL_NAME,
|
|
3394
|
-
project_name: conv.projectName,
|
|
3395
|
-
has_error: conv.errorCount > 0,
|
|
3396
|
-
raw_text: summary,
|
|
3397
|
-
vector,
|
|
3398
|
-
source_path: file,
|
|
3399
|
-
source_type: "conversation"
|
|
3400
|
-
});
|
|
3401
|
-
existingPaths.add(file);
|
|
3402
|
-
stats.conversationsStored++;
|
|
3403
|
-
if (stats.filesScanned % 50 === 0) {
|
|
3404
|
-
process.stderr.write(
|
|
3405
|
-
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3466
|
+
if (stats.filesScanned % 50 === 0) {
|
|
3467
|
+
process.stderr.write(
|
|
3468
|
+
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3406
3469
|
`
|
|
3407
|
-
|
|
3408
|
-
|
|
3470
|
+
);
|
|
3471
|
+
await flushBatch();
|
|
3472
|
+
}
|
|
3409
3473
|
}
|
|
3410
3474
|
}
|
|
3411
3475
|
if (!options.dryRun) {
|
|
@@ -3417,7 +3481,7 @@ async function backfillConversations(options) {
|
|
|
3417
3481
|
);
|
|
3418
3482
|
return stats;
|
|
3419
3483
|
}
|
|
3420
|
-
var TOOL_NAME, MIN_MESSAGES, MAX_SUMMARY_LENGTH;
|
|
3484
|
+
var TOOL_NAME, MIN_MESSAGES, MAX_SUMMARY_LENGTH, MAX_WALK_DEPTH;
|
|
3421
3485
|
var init_backfill_conversations = __esm({
|
|
3422
3486
|
"src/bin/backfill-conversations.ts"() {
|
|
3423
3487
|
"use strict";
|
|
@@ -3428,6 +3492,7 @@ var init_backfill_conversations = __esm({
|
|
|
3428
3492
|
TOOL_NAME = "backfill-conversation";
|
|
3429
3493
|
MIN_MESSAGES = 3;
|
|
3430
3494
|
MAX_SUMMARY_LENGTH = 4e3;
|
|
3495
|
+
MAX_WALK_DEPTH = 10;
|
|
3431
3496
|
if (isMainModule(import.meta.url)) {
|
|
3432
3497
|
const { values } = parseArgs({
|
|
3433
3498
|
options: {
|
|
@@ -3463,6 +3528,7 @@ __export(employee_templates_exports, {
|
|
|
3463
3528
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
3464
3529
|
getSessionPrompt: () => getSessionPrompt,
|
|
3465
3530
|
getTemplate: () => getTemplate,
|
|
3531
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
3466
3532
|
personalizePrompt: () => personalizePrompt,
|
|
3467
3533
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
3468
3534
|
});
|
|
@@ -3478,6 +3544,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
3478
3544
|
function getTemplate(name) {
|
|
3479
3545
|
return TEMPLATES[name];
|
|
3480
3546
|
}
|
|
3547
|
+
function getTemplateByRole(role) {
|
|
3548
|
+
const lower = role.toLowerCase();
|
|
3549
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
3550
|
+
}
|
|
3481
3551
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
3482
3552
|
if (templateName === actualName) return prompt;
|
|
3483
3553
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4201,9 +4271,17 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
4201
4271
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
4202
4272
|
}
|
|
4203
4273
|
}
|
|
4274
|
+
function findExeBin2() {
|
|
4275
|
+
try {
|
|
4276
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
4277
|
+
} catch {
|
|
4278
|
+
return null;
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4204
4281
|
function removeOldSymlinks(name) {
|
|
4205
4282
|
try {
|
|
4206
|
-
const exeBinPath =
|
|
4283
|
+
const exeBinPath = findExeBin2();
|
|
4284
|
+
if (!exeBinPath) return;
|
|
4207
4285
|
const binDir = path9.dirname(exeBinPath);
|
|
4208
4286
|
for (const suffix of ["", "-opencode"]) {
|
|
4209
4287
|
const linkPath = path9.join(binDir, `${name}${suffix}`);
|
|
@@ -4262,48 +4340,67 @@ async function downloadModel(opts) {
|
|
|
4262
4340
|
const tmpPath = destPath + ".tmp";
|
|
4263
4341
|
await mkdir5(destDir, { recursive: true });
|
|
4264
4342
|
if (existsSync9(destPath)) {
|
|
4265
|
-
const
|
|
4266
|
-
if (
|
|
4343
|
+
const hash = await fileHash(destPath);
|
|
4344
|
+
if (hash === EXPECTED_SHA256) {
|
|
4267
4345
|
return destPath;
|
|
4268
4346
|
}
|
|
4269
4347
|
}
|
|
4270
|
-
|
|
4271
|
-
const
|
|
4272
|
-
|
|
4273
|
-
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4274
|
-
}
|
|
4275
|
-
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4348
|
+
const MAX_RETRIES2 = 3;
|
|
4349
|
+
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
4350
|
+
let lastErr;
|
|
4276
4351
|
let downloaded = 0;
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
if (!
|
|
4285
|
-
|
|
4352
|
+
for (let attempt = 1; attempt <= MAX_RETRIES2; attempt++) {
|
|
4353
|
+
try {
|
|
4354
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4355
|
+
const response = await fetchFn(GGUF_URL, {
|
|
4356
|
+
redirect: "follow",
|
|
4357
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
4358
|
+
});
|
|
4359
|
+
if (!response.ok || !response.body) {
|
|
4360
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4361
|
+
}
|
|
4362
|
+
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4363
|
+
const hash = createHash("sha256");
|
|
4364
|
+
const fileStream = createWriteStream(tmpPath);
|
|
4365
|
+
const reader = response.body.getReader();
|
|
4366
|
+
try {
|
|
4367
|
+
while (true) {
|
|
4368
|
+
const { done, value } = await reader.read();
|
|
4369
|
+
if (done) break;
|
|
4370
|
+
if (!fileStream.write(value)) {
|
|
4371
|
+
await new Promise((resolve) => fileStream.once("drain", resolve));
|
|
4372
|
+
}
|
|
4373
|
+
hash.update(value);
|
|
4374
|
+
downloaded += value.byteLength;
|
|
4375
|
+
onProgress?.(downloaded, contentLength);
|
|
4376
|
+
}
|
|
4377
|
+
} finally {
|
|
4378
|
+
fileStream.end();
|
|
4379
|
+
await new Promise((resolve, reject) => {
|
|
4380
|
+
fileStream.on("finish", resolve);
|
|
4381
|
+
fileStream.on("error", reject);
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
const actualHash = hash.digest("hex");
|
|
4385
|
+
if (actualHash !== EXPECTED_SHA256) {
|
|
4386
|
+
unlinkSync3(tmpPath);
|
|
4387
|
+
throw new Error(
|
|
4388
|
+
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
renameSync3(tmpPath, destPath);
|
|
4392
|
+
return destPath;
|
|
4393
|
+
} catch (err) {
|
|
4394
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
4395
|
+
if (attempt < MAX_RETRIES2) {
|
|
4396
|
+
process.stderr.write(`
|
|
4397
|
+
Download attempt ${attempt} failed, retrying...
|
|
4398
|
+
`);
|
|
4399
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4286
4400
|
}
|
|
4287
|
-
hash.update(value);
|
|
4288
|
-
downloaded += value.byteLength;
|
|
4289
|
-
onProgress?.(downloaded, contentLength);
|
|
4290
4401
|
}
|
|
4291
|
-
} finally {
|
|
4292
|
-
fileStream.end();
|
|
4293
|
-
await new Promise((resolve, reject) => {
|
|
4294
|
-
fileStream.on("finish", resolve);
|
|
4295
|
-
fileStream.on("error", reject);
|
|
4296
|
-
});
|
|
4297
4402
|
}
|
|
4298
|
-
|
|
4299
|
-
if (actualHash !== EXPECTED_SHA256) {
|
|
4300
|
-
unlinkSync3(tmpPath);
|
|
4301
|
-
throw new Error(
|
|
4302
|
-
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4303
|
-
);
|
|
4304
|
-
}
|
|
4305
|
-
renameSync3(tmpPath, destPath);
|
|
4306
|
-
return destPath;
|
|
4403
|
+
throw lastErr;
|
|
4307
4404
|
}
|
|
4308
4405
|
async function fileHash(filePath) {
|
|
4309
4406
|
return new Promise((resolve, reject) => {
|
|
@@ -4363,8 +4460,8 @@ async function embedDirect(text) {
|
|
|
4363
4460
|
const llamaCpp = await import("node-llama-cpp");
|
|
4364
4461
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4365
4462
|
const { existsSync: existsSync22 } = await import("fs");
|
|
4366
|
-
const
|
|
4367
|
-
const modelPath =
|
|
4463
|
+
const path34 = await import("path");
|
|
4464
|
+
const modelPath = path34.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4368
4465
|
if (!existsSync22(modelPath)) {
|
|
4369
4466
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4370
4467
|
}
|
|
@@ -4406,12 +4503,22 @@ __export(license_exports, {
|
|
|
4406
4503
|
loadLicense: () => loadLicense,
|
|
4407
4504
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
4408
4505
|
saveLicense: () => saveLicense,
|
|
4506
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
4507
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4409
4508
|
validateLicense: () => validateLicense
|
|
4410
4509
|
});
|
|
4411
4510
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
|
|
4412
4511
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4413
4512
|
import path11 from "path";
|
|
4414
4513
|
import { jwtVerify, importSPKI } from "jose";
|
|
4514
|
+
async function fetchRetry(url, init) {
|
|
4515
|
+
try {
|
|
4516
|
+
return await fetch(url, init);
|
|
4517
|
+
} catch {
|
|
4518
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
4519
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4415
4522
|
function loadDeviceId() {
|
|
4416
4523
|
const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
|
|
4417
4524
|
try {
|
|
@@ -4443,7 +4550,7 @@ function loadLicense() {
|
|
|
4443
4550
|
}
|
|
4444
4551
|
function saveLicense(apiKey) {
|
|
4445
4552
|
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
4446
|
-
writeFileSync2(LICENSE_PATH, apiKey.trim(), "utf8");
|
|
4553
|
+
writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
4447
4554
|
}
|
|
4448
4555
|
async function verifyLicenseJwt(token) {
|
|
4449
4556
|
try {
|
|
@@ -4495,7 +4602,7 @@ function cacheResponse(token) {
|
|
|
4495
4602
|
async function validateLicense(apiKey, deviceId) {
|
|
4496
4603
|
const did = deviceId ?? loadDeviceId();
|
|
4497
4604
|
try {
|
|
4498
|
-
const res = await
|
|
4605
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
4499
4606
|
method: "POST",
|
|
4500
4607
|
headers: { "Content-Type": "application/json" },
|
|
4501
4608
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -4530,14 +4637,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
4530
4637
|
} catch {
|
|
4531
4638
|
const cached = await getCachedLicense();
|
|
4532
4639
|
if (cached) return cached;
|
|
4533
|
-
return FREE_LICENSE;
|
|
4640
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
function getCacheAgeMs() {
|
|
4644
|
+
try {
|
|
4645
|
+
const { statSync: statSync2 } = __require("fs");
|
|
4646
|
+
const s = statSync2(CACHE_PATH);
|
|
4647
|
+
return Date.now() - s.mtimeMs;
|
|
4648
|
+
} catch {
|
|
4649
|
+
return Infinity;
|
|
4534
4650
|
}
|
|
4535
4651
|
}
|
|
4536
4652
|
async function checkLicense() {
|
|
4537
4653
|
const key = loadLicense();
|
|
4538
4654
|
if (!key) return FREE_LICENSE;
|
|
4539
4655
|
const cached = await getCachedLicense();
|
|
4540
|
-
if (cached) return cached;
|
|
4656
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
4541
4657
|
const deviceId = loadDeviceId();
|
|
4542
4658
|
return validateLicense(key, deviceId);
|
|
4543
4659
|
}
|
|
@@ -4577,7 +4693,7 @@ async function assertVpsLicense(opts) {
|
|
|
4577
4693
|
let explicitRejection = false;
|
|
4578
4694
|
let transientFailure = false;
|
|
4579
4695
|
try {
|
|
4580
|
-
const res = await
|
|
4696
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
4581
4697
|
method: "POST",
|
|
4582
4698
|
headers: { "Content-Type": "application/json" },
|
|
4583
4699
|
body: JSON.stringify({ apiKey, deviceId }),
|
|
@@ -4658,7 +4774,28 @@ async function assertVpsLicense(opts) {
|
|
|
4658
4774
|
`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.`
|
|
4659
4775
|
);
|
|
4660
4776
|
}
|
|
4661
|
-
|
|
4777
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
4778
|
+
if (_revalTimer) return;
|
|
4779
|
+
_revalTimer = setInterval(async () => {
|
|
4780
|
+
try {
|
|
4781
|
+
const license = await checkLicense();
|
|
4782
|
+
if (!license.valid) {
|
|
4783
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
4784
|
+
}
|
|
4785
|
+
} catch {
|
|
4786
|
+
}
|
|
4787
|
+
}, intervalMs);
|
|
4788
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
4789
|
+
_revalTimer.unref();
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
function stopLicenseRevalidation() {
|
|
4793
|
+
if (_revalTimer) {
|
|
4794
|
+
clearInterval(_revalTimer);
|
|
4795
|
+
_revalTimer = null;
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
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;
|
|
4662
4799
|
var init_license = __esm({
|
|
4663
4800
|
"src/lib/license.ts"() {
|
|
4664
4801
|
"use strict";
|
|
@@ -4667,6 +4804,7 @@ var init_license = __esm({
|
|
|
4667
4804
|
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4668
4805
|
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4669
4806
|
API_BASE = "https://askexe.com/cloud";
|
|
4807
|
+
RETRY_DELAY_MS = 500;
|
|
4670
4808
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
4671
4809
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
4672
4810
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -4688,6 +4826,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
4688
4826
|
employeeLimit: 1,
|
|
4689
4827
|
memoryLimit: 5e3
|
|
4690
4828
|
};
|
|
4829
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
4830
|
+
_revalTimer = null;
|
|
4691
4831
|
}
|
|
4692
4832
|
});
|
|
4693
4833
|
|
|
@@ -4701,7 +4841,7 @@ __export(identity_exports, {
|
|
|
4701
4841
|
updateIdentity: () => updateIdentity
|
|
4702
4842
|
});
|
|
4703
4843
|
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
4704
|
-
import { readdirSync } from "fs";
|
|
4844
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
4705
4845
|
import path12 from "path";
|
|
4706
4846
|
import { createHash as createHash2 } from "crypto";
|
|
4707
4847
|
function ensureDir() {
|
|
@@ -4783,7 +4923,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4783
4923
|
}
|
|
4784
4924
|
function listIdentities() {
|
|
4785
4925
|
ensureDir();
|
|
4786
|
-
const files =
|
|
4926
|
+
const files = readdirSync2(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
4787
4927
|
const results = [];
|
|
4788
4928
|
for (const file of files) {
|
|
4789
4929
|
const agentId = file.replace(".md", "");
|
|
@@ -4830,6 +4970,7 @@ var init_identity = __esm({
|
|
|
4830
4970
|
var identity_templates_exports = {};
|
|
4831
4971
|
__export(identity_templates_exports, {
|
|
4832
4972
|
IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
|
|
4973
|
+
PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
|
|
4833
4974
|
POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
|
|
4834
4975
|
getTemplate: () => getTemplate2,
|
|
4835
4976
|
getTemplateForTitle: () => getTemplateForTitle
|
|
@@ -4849,10 +4990,18 @@ function getTemplateForTitle(title) {
|
|
|
4849
4990
|
if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
|
|
4850
4991
|
return null;
|
|
4851
4992
|
}
|
|
4852
|
-
var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4993
|
+
var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4853
4994
|
var init_identity_templates = __esm({
|
|
4854
4995
|
"src/lib/identity-templates.ts"() {
|
|
4855
4996
|
"use strict";
|
|
4997
|
+
PLAN_MODE_COMPAT = `
|
|
4998
|
+
## Plan Mode Compatibility
|
|
4999
|
+
If tool execution is unavailable (e.g., CC plan mode), switch to planning:
|
|
5000
|
+
- Reason about the task and create a written plan
|
|
5001
|
+
- Document what tools you would call and with what parameters
|
|
5002
|
+
- Output structured text that can be acted on when tools become available
|
|
5003
|
+
Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
|
|
5004
|
+
`;
|
|
4856
5005
|
POST_WORK_CHECKLIST = `
|
|
4857
5006
|
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
4858
5007
|
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
@@ -4932,7 +5081,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
4932
5081
|
- **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
|
|
4933
5082
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
4934
5083
|
- **send_message** \u2014 direct intercom to employees
|
|
4935
|
-
|
|
5084
|
+
${PLAN_MODE_COMPAT}
|
|
4936
5085
|
## Completion Workflow
|
|
4937
5086
|
|
|
4938
5087
|
1. Read the task file and verify the deliverable matches the brief
|
|
@@ -5003,7 +5152,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
|
|
|
5003
5152
|
- **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
|
|
5004
5153
|
- **get_identity** \u2014 read any agent's identity for review context
|
|
5005
5154
|
- **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
|
|
5006
|
-
|
|
5155
|
+
${PLAN_MODE_COMPAT}
|
|
5007
5156
|
## Completion Workflow
|
|
5008
5157
|
|
|
5009
5158
|
1. Read ARCHITECTURE.md before starting work on any repo
|
|
@@ -5070,7 +5219,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
|
|
|
5070
5219
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5071
5220
|
- **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
|
|
5072
5221
|
- **get_identity** \u2014 read team identities for brand-consistent communication
|
|
5073
|
-
|
|
5222
|
+
${PLAN_MODE_COMPAT}
|
|
5074
5223
|
## Completion Workflow
|
|
5075
5224
|
|
|
5076
5225
|
1. Read the task file and understand the brief \u2014 tone, format, channel requirements
|
|
@@ -5137,7 +5286,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
5137
5286
|
- **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
|
|
5138
5287
|
- **store_memory** \u2014 report completions for org visibility
|
|
5139
5288
|
- **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
|
|
5140
|
-
|
|
5289
|
+
${PLAN_MODE_COMPAT}
|
|
5141
5290
|
## Completion Workflow
|
|
5142
5291
|
|
|
5143
5292
|
1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
|
|
@@ -5197,7 +5346,7 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
5197
5346
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5198
5347
|
- **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
|
|
5199
5348
|
- **store_memory** \u2014 report completions with production decisions for future reference
|
|
5200
|
-
|
|
5349
|
+
${PLAN_MODE_COMPAT}
|
|
5201
5350
|
## Completion Workflow
|
|
5202
5351
|
|
|
5203
5352
|
1. Read the task file \u2014 understand the brief, check budget constraints
|
|
@@ -5269,7 +5418,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
|
|
|
5269
5418
|
- **update_task** \u2014 mark tasks done with analysis results
|
|
5270
5419
|
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
5271
5420
|
- **create_task** \u2014 when a feature is worth building, spec it for the CTO
|
|
5272
|
-
|
|
5421
|
+
${PLAN_MODE_COMPAT}
|
|
5273
5422
|
## Completion Workflow
|
|
5274
5423
|
|
|
5275
5424
|
1. Read the task \u2014 understand what capability is needed
|
|
@@ -5332,7 +5481,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
5332
5481
|
- **store_behavior** \u2014 record new patterns
|
|
5333
5482
|
- **update_task** \u2014 mark reviews done with structured findings
|
|
5334
5483
|
- **create_task** \u2014 assign fixes to the CTO
|
|
5335
|
-
|
|
5484
|
+
${PLAN_MODE_COMPAT}
|
|
5336
5485
|
## Completion Workflow
|
|
5337
5486
|
|
|
5338
5487
|
1. Read the task brief and understand the audit scope
|
|
@@ -5354,10 +5503,27 @@ __export(setup_wizard_exports, {
|
|
|
5354
5503
|
validateModel: () => validateModel
|
|
5355
5504
|
});
|
|
5356
5505
|
import crypto3 from "crypto";
|
|
5357
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
5358
|
-
import
|
|
5506
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
5507
|
+
import os5 from "os";
|
|
5359
5508
|
import path13 from "path";
|
|
5360
5509
|
import { createInterface as createInterface2 } from "readline";
|
|
5510
|
+
function loadSetupState() {
|
|
5511
|
+
try {
|
|
5512
|
+
return JSON.parse(readFileSync8(SETUP_STATE_PATH, "utf8"));
|
|
5513
|
+
} catch {
|
|
5514
|
+
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
function saveSetupState(state) {
|
|
5518
|
+
mkdirSync5(path13.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
5519
|
+
writeFileSync4(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5520
|
+
}
|
|
5521
|
+
function clearSetupState() {
|
|
5522
|
+
try {
|
|
5523
|
+
unlinkSync4(SETUP_STATE_PATH);
|
|
5524
|
+
} catch {
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5361
5527
|
function ask(rl, prompt) {
|
|
5362
5528
|
return new Promise((resolve) => {
|
|
5363
5529
|
const doAsk = () => {
|
|
@@ -5397,88 +5563,133 @@ async function runSetupWizard(opts = {}) {
|
|
|
5397
5563
|
rl.close();
|
|
5398
5564
|
return;
|
|
5399
5565
|
}
|
|
5566
|
+
const state = loadSetupState();
|
|
5567
|
+
if (state.completedSteps.length > 0) {
|
|
5568
|
+
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5569
|
+
}
|
|
5400
5570
|
if (existsSync12(LEGACY_LANCE_PATH)) {
|
|
5401
5571
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5402
5572
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5403
5573
|
log(" The old directory will not be modified or deleted.");
|
|
5404
5574
|
log("");
|
|
5405
5575
|
}
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5576
|
+
if (!state.completedSteps.includes(1)) {
|
|
5577
|
+
const existingKey = await getMasterKey();
|
|
5578
|
+
if (existingKey) {
|
|
5579
|
+
log("Encryption key already exists \u2014 skipping generation.");
|
|
5580
|
+
} else {
|
|
5581
|
+
log("Generating 256-bit encryption key...");
|
|
5582
|
+
const key = crypto3.randomBytes(32);
|
|
5583
|
+
await setMasterKey(key);
|
|
5584
|
+
log("Encryption key generated and stored securely.");
|
|
5585
|
+
}
|
|
5586
|
+
state.completedSteps.push(1);
|
|
5587
|
+
saveSetupState(state);
|
|
5409
5588
|
} else {
|
|
5410
|
-
log("
|
|
5411
|
-
const key = crypto3.randomBytes(32);
|
|
5412
|
-
await setMasterKey(key);
|
|
5413
|
-
log("Encryption key generated and stored securely.");
|
|
5589
|
+
log("Step 1 already complete \u2014 skipping.");
|
|
5414
5590
|
}
|
|
5415
5591
|
log("");
|
|
5416
|
-
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5417
|
-
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5418
|
-
log("only your encryption key can decrypt it.");
|
|
5419
5592
|
let cloudConfig;
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5593
|
+
if (!state.completedSteps.includes(2)) {
|
|
5594
|
+
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5595
|
+
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5596
|
+
log("only your encryption key can decrypt it.");
|
|
5597
|
+
try {
|
|
5598
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5599
|
+
const deviceId = loadDeviceId2();
|
|
5600
|
+
let res;
|
|
5601
|
+
try {
|
|
5602
|
+
res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
5603
|
+
method: "POST",
|
|
5604
|
+
headers: { "Content-Type": "application/json" },
|
|
5605
|
+
body: JSON.stringify({ deviceId }),
|
|
5606
|
+
signal: AbortSignal.timeout(1e4)
|
|
5607
|
+
});
|
|
5608
|
+
} catch {
|
|
5609
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
5610
|
+
res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
5611
|
+
method: "POST",
|
|
5612
|
+
headers: { "Content-Type": "application/json" },
|
|
5613
|
+
body: JSON.stringify({ deviceId }),
|
|
5614
|
+
signal: AbortSignal.timeout(1e4)
|
|
5615
|
+
});
|
|
5616
|
+
}
|
|
5617
|
+
if (res.ok) {
|
|
5618
|
+
const data = await res.json();
|
|
5619
|
+
if (data.apiKey) {
|
|
5620
|
+
cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
|
|
5621
|
+
const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5622
|
+
saveLicense3(data.apiKey);
|
|
5623
|
+
mirrorLicenseKey3(data.apiKey);
|
|
5624
|
+
log("Cloud sync activated automatically.");
|
|
5625
|
+
}
|
|
5437
5626
|
}
|
|
5627
|
+
} catch {
|
|
5628
|
+
log("Cloud sync will activate when online.");
|
|
5438
5629
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5630
|
+
state.completedSteps.push(2);
|
|
5631
|
+
saveSetupState(state);
|
|
5632
|
+
} else {
|
|
5633
|
+
log("Step 2 already complete \u2014 skipping.");
|
|
5441
5634
|
}
|
|
5442
5635
|
log("");
|
|
5443
|
-
if (!
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5636
|
+
if (!state.completedSteps.includes(3)) {
|
|
5637
|
+
if (!skipModel) {
|
|
5638
|
+
log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
|
|
5639
|
+
log("");
|
|
5640
|
+
await downloadModel({
|
|
5641
|
+
destDir: MODELS_DIR,
|
|
5642
|
+
onProgress: (downloaded, total) => {
|
|
5643
|
+
const pct = (downloaded / total * 100).toFixed(1);
|
|
5644
|
+
const dlMB = (downloaded / 1e6).toFixed(0);
|
|
5645
|
+
const totalMB = (total / 1e6).toFixed(0);
|
|
5646
|
+
process.stderr.write(`\rDownloading model: ${pct}% (${dlMB}/${totalMB} MB)`);
|
|
5647
|
+
}
|
|
5648
|
+
});
|
|
5649
|
+
process.stderr.write("\n");
|
|
5650
|
+
log("Model downloaded and verified.");
|
|
5651
|
+
}
|
|
5652
|
+
state.completedSteps.push(3);
|
|
5653
|
+
saveSetupState(state);
|
|
5654
|
+
} else {
|
|
5655
|
+
log("Step 3 already complete \u2014 skipping.");
|
|
5457
5656
|
}
|
|
5458
|
-
if (!
|
|
5459
|
-
|
|
5657
|
+
if (!state.completedSteps.includes(4)) {
|
|
5658
|
+
if (!skipModel && !skipModelValidation) {
|
|
5659
|
+
await validateModel(log);
|
|
5660
|
+
}
|
|
5661
|
+
state.completedSteps.push(4);
|
|
5662
|
+
saveSetupState(state);
|
|
5663
|
+
} else {
|
|
5664
|
+
log("Step 4 already complete \u2014 skipping.");
|
|
5460
5665
|
}
|
|
5461
5666
|
const config = await loadConfig();
|
|
5462
|
-
if (
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
const claudeJsonPath = path13.join(os4.homedir(), ".claude.json");
|
|
5469
|
-
let claudeJson = {};
|
|
5667
|
+
if (!state.completedSteps.includes(5)) {
|
|
5668
|
+
if (cloudConfig) {
|
|
5669
|
+
config.cloud = cloudConfig;
|
|
5670
|
+
}
|
|
5671
|
+
await saveConfig(config);
|
|
5672
|
+
log("");
|
|
5470
5673
|
try {
|
|
5471
|
-
|
|
5674
|
+
const claudeJsonPath = path13.join(os5.homedir(), ".claude.json");
|
|
5675
|
+
let claudeJson = {};
|
|
5676
|
+
try {
|
|
5677
|
+
claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
|
|
5678
|
+
} catch {
|
|
5679
|
+
}
|
|
5680
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
5681
|
+
const projects = claudeJson.projects;
|
|
5682
|
+
for (const dir of [process.cwd(), os5.homedir()]) {
|
|
5683
|
+
if (!projects[dir]) projects[dir] = {};
|
|
5684
|
+
projects[dir].hasTrustDialogAccepted = true;
|
|
5685
|
+
}
|
|
5686
|
+
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5472
5687
|
} catch {
|
|
5473
5688
|
}
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
projects[dir].hasTrustDialogAccepted = true;
|
|
5479
|
-
}
|
|
5480
|
-
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5481
|
-
} catch {
|
|
5689
|
+
state.completedSteps.push(5);
|
|
5690
|
+
saveSetupState(state);
|
|
5691
|
+
} else {
|
|
5692
|
+
log("Step 5 already complete \u2014 skipping.");
|
|
5482
5693
|
}
|
|
5483
5694
|
const {
|
|
5484
5695
|
loadEmployees: loadEmployees2,
|
|
@@ -5487,7 +5698,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5487
5698
|
registerBinSymlinks: registerBinSymlinks2,
|
|
5488
5699
|
EMPLOYEES_PATH: EMPLOYEES_PATH2
|
|
5489
5700
|
} = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
5490
|
-
const {
|
|
5701
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5491
5702
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
5492
5703
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
5493
5704
|
const {
|
|
@@ -5497,152 +5708,178 @@ async function runSetupWizard(opts = {}) {
|
|
|
5497
5708
|
validateLicense: validateLicense2
|
|
5498
5709
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5499
5710
|
const createdEmployees = [];
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5504
|
-
log("You talk to them. They coordinate everyone else.");
|
|
5505
|
-
log("");
|
|
5506
|
-
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5507
|
-
const cooName = (cooNameInput || "exe").toLowerCase();
|
|
5508
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5509
|
-
if (!employees.some((e) => e.name === cooName)) {
|
|
5510
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5511
|
-
const cooEmployee = {
|
|
5512
|
-
name: cooName,
|
|
5513
|
-
role: "COO",
|
|
5514
|
-
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5515
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5516
|
-
templateName: "exe",
|
|
5517
|
-
templateVersion: 1
|
|
5518
|
-
};
|
|
5519
|
-
employees = addEmployee2(employees, cooEmployee);
|
|
5520
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5521
|
-
}
|
|
5522
|
-
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5523
|
-
if (cooIdentityContent) {
|
|
5524
|
-
const cooIdPath = identityPath2(cooName);
|
|
5525
|
-
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5526
|
-
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5527
|
-
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5528
|
-
}
|
|
5529
|
-
registerBinSymlinks2(cooName);
|
|
5530
|
-
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5531
|
-
log("");
|
|
5532
|
-
log("=== Meet Your Specialists ===");
|
|
5533
|
-
log("");
|
|
5534
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5535
|
-
log("");
|
|
5536
|
-
log(" CTO (default: yoshi)");
|
|
5537
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5538
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5539
|
-
log("");
|
|
5540
|
-
log(" CMO (default: mari)");
|
|
5541
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5542
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5543
|
-
log("");
|
|
5544
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
5545
|
-
log("");
|
|
5546
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5547
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5548
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5549
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5550
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
5551
|
-
log("");
|
|
5552
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5553
|
-
log("instead of headcount.");
|
|
5554
|
-
log("");
|
|
5555
|
-
await ask(rl, "Press Enter to continue. ");
|
|
5556
|
-
let license;
|
|
5557
|
-
try {
|
|
5558
|
-
license = await checkLicense2();
|
|
5559
|
-
} catch {
|
|
5560
|
-
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5561
|
-
}
|
|
5562
|
-
if (license.plan === "free") {
|
|
5563
|
-
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5711
|
+
let cooName = "exe";
|
|
5712
|
+
if (!state.completedSteps.includes(6)) {
|
|
5713
|
+
log("=== Your Team ===");
|
|
5564
5714
|
log("");
|
|
5565
|
-
log("
|
|
5566
|
-
log("
|
|
5567
|
-
log("
|
|
5715
|
+
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
5716
|
+
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5717
|
+
log("You talk to them. They coordinate everyone else.");
|
|
5568
5718
|
log("");
|
|
5569
|
-
const
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
}
|
|
5719
|
+
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5720
|
+
cooName = (cooNameInput || "exe").toLowerCase();
|
|
5721
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5722
|
+
if (!employees.some((e) => e.name === cooName)) {
|
|
5723
|
+
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5724
|
+
const cooEmployee = {
|
|
5725
|
+
name: cooName,
|
|
5726
|
+
role: "COO",
|
|
5727
|
+
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5728
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5729
|
+
templateName: "exe",
|
|
5730
|
+
templateVersion: 1
|
|
5731
|
+
};
|
|
5732
|
+
employees = addEmployee2(employees, cooEmployee);
|
|
5733
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5734
|
+
}
|
|
5735
|
+
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5736
|
+
if (cooIdentityContent) {
|
|
5737
|
+
const cooIdPath = identityPath2(cooName);
|
|
5738
|
+
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5739
|
+
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5740
|
+
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5741
|
+
}
|
|
5742
|
+
registerBinSymlinks2(cooName);
|
|
5743
|
+
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5744
|
+
state.completedSteps.push(6);
|
|
5745
|
+
saveSetupState(state);
|
|
5746
|
+
} else {
|
|
5747
|
+
log("Step 6 already complete \u2014 skipping.");
|
|
5748
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5749
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
5750
|
+
if (existingCoo) cooName = existingCoo.name;
|
|
5585
5751
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
log(
|
|
5752
|
+
log("");
|
|
5753
|
+
if (!state.completedSteps.includes(7)) {
|
|
5754
|
+
log("=== Meet Your Specialists ===");
|
|
5589
5755
|
log("");
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5756
|
+
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5757
|
+
log("");
|
|
5758
|
+
log(" CTO (default: yoshi)");
|
|
5759
|
+
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5760
|
+
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5761
|
+
log("");
|
|
5762
|
+
log(" CMO (default: mari)");
|
|
5763
|
+
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5764
|
+
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5765
|
+
log("");
|
|
5766
|
+
log("Why separate roles instead of one AI that does everything?");
|
|
5767
|
+
log("");
|
|
5768
|
+
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5769
|
+
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5770
|
+
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5771
|
+
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5772
|
+
log("Your COO connects them so nothing falls through the cracks.");
|
|
5773
|
+
log("");
|
|
5774
|
+
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5775
|
+
log("instead of headcount.");
|
|
5776
|
+
log("");
|
|
5777
|
+
await ask(rl, "Press Enter to continue. ");
|
|
5778
|
+
state.completedSteps.push(7);
|
|
5779
|
+
saveSetupState(state);
|
|
5780
|
+
} else {
|
|
5781
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
5782
|
+
}
|
|
5783
|
+
if (!state.completedSteps.includes(8)) {
|
|
5784
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5785
|
+
let license;
|
|
5786
|
+
try {
|
|
5787
|
+
license = await checkLicense2();
|
|
5788
|
+
} catch {
|
|
5789
|
+
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5790
|
+
}
|
|
5791
|
+
if (license.plan === "free") {
|
|
5792
|
+
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5793
|
+
log("");
|
|
5794
|
+
log("The CTO and CMO are available on Solopreneur ($97/mo) \u2014 full engineering");
|
|
5795
|
+
log("and marketing team, unlimited tasks, priority support.");
|
|
5796
|
+
log("Get your key at https://askexe.com, then paste it here.");
|
|
5797
|
+
log("");
|
|
5798
|
+
const licenseInput = await ask(rl, "License key (or press Enter to skip): ");
|
|
5799
|
+
if (licenseInput.startsWith("exe_sk_")) {
|
|
5800
|
+
saveLicense2(licenseInput);
|
|
5801
|
+
mirrorLicenseKey2(licenseInput);
|
|
5802
|
+
try {
|
|
5803
|
+
license = await validateLicense2(licenseInput);
|
|
5804
|
+
} catch {
|
|
5805
|
+
log("Couldn't reach the license server \u2014 your key has been saved.");
|
|
5806
|
+
log("Run exe-os --activate <key> anytime to finish activation.");
|
|
5807
|
+
}
|
|
5808
|
+
} else if (!licenseInput) {
|
|
5809
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5810
|
+
} else {
|
|
5811
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
5812
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5813
|
+
}
|
|
5814
|
+
}
|
|
5815
|
+
if (license.plan !== "free") {
|
|
5816
|
+
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
5817
|
+
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
5818
|
+
log("");
|
|
5819
|
+
const createCto = await ask(rl, "Create your CTO? (Y/n): ");
|
|
5820
|
+
if (createCto.toLowerCase() !== "n") {
|
|
5821
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
5822
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
5823
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
5824
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
5825
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
5826
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5827
|
+
const ctoEmployee = {
|
|
5828
|
+
name: ctoName,
|
|
5829
|
+
role: "CTO",
|
|
5830
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
5831
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5832
|
+
};
|
|
5833
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
5834
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5835
|
+
}
|
|
5836
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
5837
|
+
if (ctoIdentityContent) {
|
|
5838
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
5839
|
+
mkdirSync5(path13.dirname(ctoIdPath), { recursive: true });
|
|
5840
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
5841
|
+
writeFileSync4(ctoIdPath, replaced, "utf-8");
|
|
5842
|
+
}
|
|
5843
|
+
registerBinSymlinks2(ctoName);
|
|
5844
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
5845
|
+
log(`Created ${ctoName} (CTO)`);
|
|
5846
|
+
}
|
|
5847
|
+
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
5848
|
+
if (createCmo.toLowerCase() !== "n") {
|
|
5849
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
5850
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
5851
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
5852
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
5853
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
5854
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5855
|
+
const cmoEmployee = {
|
|
5856
|
+
name: cmoName,
|
|
5857
|
+
role: "CMO",
|
|
5858
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
5859
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5860
|
+
};
|
|
5861
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
5862
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5863
|
+
}
|
|
5864
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
5865
|
+
if (cmoIdentityContent) {
|
|
5866
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
5867
|
+
mkdirSync5(path13.dirname(cmoIdPath), { recursive: true });
|
|
5868
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
5869
|
+
writeFileSync4(cmoIdPath, replaced, "utf-8");
|
|
5870
|
+
}
|
|
5871
|
+
registerBinSymlinks2(cmoName);
|
|
5872
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5873
|
+
log(`Created ${cmoName} (CMO)`);
|
|
5639
5874
|
}
|
|
5640
|
-
|
|
5641
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5642
|
-
log(`Created ${cmoName} (CMO)`);
|
|
5875
|
+
log("");
|
|
5643
5876
|
}
|
|
5644
|
-
|
|
5877
|
+
state.completedSteps.push(8);
|
|
5878
|
+
saveSetupState(state);
|
|
5879
|
+
} else {
|
|
5880
|
+
log("Step 8 already complete \u2014 skipping.");
|
|
5645
5881
|
}
|
|
5882
|
+
clearSetupState();
|
|
5646
5883
|
log("=== Two Ways to Work ===");
|
|
5647
5884
|
log("");
|
|
5648
5885
|
log(" 1. Claude Code mode");
|
|
@@ -5679,12 +5916,14 @@ async function runSetupWizard(opts = {}) {
|
|
|
5679
5916
|
rl.close();
|
|
5680
5917
|
}
|
|
5681
5918
|
}
|
|
5919
|
+
var SETUP_STATE_PATH;
|
|
5682
5920
|
var init_setup_wizard = __esm({
|
|
5683
5921
|
"src/lib/setup-wizard.ts"() {
|
|
5684
5922
|
"use strict";
|
|
5685
5923
|
init_config();
|
|
5686
5924
|
init_keychain();
|
|
5687
5925
|
init_model_downloader();
|
|
5926
|
+
SETUP_STATE_PATH = path13.join(os5.homedir(), ".exe-os", "setup-state.json");
|
|
5688
5927
|
}
|
|
5689
5928
|
});
|
|
5690
5929
|
|
|
@@ -10046,8 +10285,8 @@ var init_ErrorOverview = __esm({
|
|
|
10046
10285
|
"use strict";
|
|
10047
10286
|
init_Box();
|
|
10048
10287
|
init_Text();
|
|
10049
|
-
cleanupPath = (
|
|
10050
|
-
return
|
|
10288
|
+
cleanupPath = (path34) => {
|
|
10289
|
+
return path34?.replace(`file://${cwd()}/`, "");
|
|
10051
10290
|
};
|
|
10052
10291
|
stackUtils = new StackUtils({
|
|
10053
10292
|
cwd: cwd(),
|
|
@@ -12501,7 +12740,7 @@ var init_demo_data = __esm({
|
|
|
12501
12740
|
"src/tui/demo-data.ts"() {
|
|
12502
12741
|
"use strict";
|
|
12503
12742
|
DEMO_EMPLOYEES = [
|
|
12504
|
-
{ name: "exe", role: "COO", status: "active", activity: "Reviewing yoshi's task-aware behavior injection PR", memoryCount:
|
|
12743
|
+
{ name: "exe", role: "COO", status: "active", activity: "Reviewing yoshi's task-aware behavior injection PR", memoryCount: 15e3, projects: [
|
|
12505
12744
|
{ name: "exe-os", status: "active" },
|
|
12506
12745
|
{ name: "exe-create", status: "has_tasks" },
|
|
12507
12746
|
{ name: "openclaw", status: "idle" }
|
|
@@ -12510,7 +12749,7 @@ var init_demo_data = __esm({
|
|
|
12510
12749
|
"Dispatched behavior injection task",
|
|
12511
12750
|
"Approved gateway Phase 4"
|
|
12512
12751
|
] },
|
|
12513
|
-
{ name: "yoshi", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", memoryCount:
|
|
12752
|
+
{ name: "yoshi", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", memoryCount: 8e3, projects: [
|
|
12514
12753
|
{ name: "exe-os", status: "active" },
|
|
12515
12754
|
{ name: "exe-create", status: "idle" }
|
|
12516
12755
|
], recentTasks: [
|
|
@@ -12518,7 +12757,7 @@ var init_demo_data = __esm({
|
|
|
12518
12757
|
"Fixed TUI mouse listener leak",
|
|
12519
12758
|
"Built task-aware behavior injection"
|
|
12520
12759
|
] },
|
|
12521
|
-
{ name: "mari", role: "CMO", status: "idle", activity: "", memoryCount:
|
|
12760
|
+
{ name: "mari", role: "CMO", status: "idle", activity: "", memoryCount: 2e3, projects: [
|
|
12522
12761
|
{ name: "exe-build-skills", status: "has_tasks" },
|
|
12523
12762
|
{ name: "exe-os", status: "idle" }
|
|
12524
12763
|
], recentTasks: [
|
|
@@ -12526,13 +12765,13 @@ var init_demo_data = __esm({
|
|
|
12526
12765
|
"Designed exe-os UI system",
|
|
12527
12766
|
"Fixed logo layouts"
|
|
12528
12767
|
] },
|
|
12529
|
-
{ name: "tom", role: "Principal Engineer", status: "idle", activity: "", memoryCount:
|
|
12768
|
+
{ name: "tom", role: "Principal Engineer", status: "idle", activity: "", memoryCount: 700, projects: [
|
|
12530
12769
|
{ name: "exe-os", status: "idle" }
|
|
12531
12770
|
], recentTasks: [
|
|
12532
12771
|
"Implemented BashTool sandboxed execution",
|
|
12533
12772
|
"Ported session scoping to exe-agent-memory"
|
|
12534
12773
|
] },
|
|
12535
|
-
{ name: "sasha", role: "Content Production", status: "offline", activity: "", memoryCount:
|
|
12774
|
+
{ name: "sasha", role: "Content Production", status: "offline", activity: "", memoryCount: 50, projects: [
|
|
12536
12775
|
{ name: "exe-build-skills", status: "idle" }
|
|
12537
12776
|
], recentTasks: [
|
|
12538
12777
|
"Rendered carousel export prototype"
|
|
@@ -12545,7 +12784,7 @@ var init_demo_data = __esm({
|
|
|
12545
12784
|
{ time: "11:15", agent: "mari", action: "Completed exe-os UI design system" },
|
|
12546
12785
|
{ time: "10:50", agent: "exe", action: "Approved gateway Phase 4" }
|
|
12547
12786
|
];
|
|
12548
|
-
DEMO_HEALTH = { memories:
|
|
12787
|
+
DEMO_HEALTH = { memories: 25750, daemon: "running", cloud: "disabled" };
|
|
12549
12788
|
DEMO_PROJECTS = [
|
|
12550
12789
|
{
|
|
12551
12790
|
projectName: "exe-os",
|
|
@@ -14413,7 +14652,7 @@ var init_hooks = __esm({
|
|
|
14413
14652
|
|
|
14414
14653
|
// src/runtime/safety-checks.ts
|
|
14415
14654
|
import path14 from "path";
|
|
14416
|
-
import
|
|
14655
|
+
import os6 from "os";
|
|
14417
14656
|
function checkPathSafety(filePath) {
|
|
14418
14657
|
const resolved = path14.resolve(filePath);
|
|
14419
14658
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
@@ -14440,7 +14679,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
14440
14679
|
var init_safety_checks = __esm({
|
|
14441
14680
|
"src/runtime/safety-checks.ts"() {
|
|
14442
14681
|
"use strict";
|
|
14443
|
-
HOME =
|
|
14682
|
+
HOME = os6.homedir();
|
|
14444
14683
|
BYPASS_IMMUNE_PATTERNS = [
|
|
14445
14684
|
{
|
|
14446
14685
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -14672,8 +14911,13 @@ function runRipgrep(input, searchPath, context) {
|
|
|
14672
14911
|
timeout: 3e4,
|
|
14673
14912
|
stdio: ["ignore", "pipe", "pipe"]
|
|
14674
14913
|
});
|
|
14914
|
+
const MAX_OUTPUT = 1e7;
|
|
14675
14915
|
const chunks = [];
|
|
14676
|
-
|
|
14916
|
+
let totalSize = 0;
|
|
14917
|
+
child.stdout.on("data", (chunk) => {
|
|
14918
|
+
totalSize += chunk.length;
|
|
14919
|
+
if (totalSize <= MAX_OUTPUT) chunks.push(chunk);
|
|
14920
|
+
});
|
|
14677
14921
|
const onAbort = () => child.kill("SIGTERM");
|
|
14678
14922
|
context.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
14679
14923
|
child.on("close", (code) => {
|
|
@@ -15057,10 +15301,19 @@ var init_bash = __esm({
|
|
|
15057
15301
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15058
15302
|
env: { ...process.env }
|
|
15059
15303
|
});
|
|
15304
|
+
const MAX_OUTPUT_SIZE = 5242880;
|
|
15060
15305
|
const stdoutChunks = [];
|
|
15061
15306
|
const stderrChunks = [];
|
|
15062
|
-
|
|
15063
|
-
|
|
15307
|
+
let stdoutSize = 0;
|
|
15308
|
+
let stderrSize = 0;
|
|
15309
|
+
child.stdout.on("data", (chunk) => {
|
|
15310
|
+
if (stdoutSize < MAX_OUTPUT_SIZE) stdoutChunks.push(chunk);
|
|
15311
|
+
stdoutSize += chunk.length;
|
|
15312
|
+
});
|
|
15313
|
+
child.stderr.on("data", (chunk) => {
|
|
15314
|
+
if (stderrSize < MAX_OUTPUT_SIZE) stderrChunks.push(chunk);
|
|
15315
|
+
stderrSize += chunk.length;
|
|
15316
|
+
});
|
|
15064
15317
|
const onAbort = () => {
|
|
15065
15318
|
child.kill("SIGTERM");
|
|
15066
15319
|
setTimeout(() => {
|
|
@@ -15103,7 +15356,7 @@ __export(session_registry_exports, {
|
|
|
15103
15356
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync14 } from "fs";
|
|
15104
15357
|
import { execSync as execSync4 } from "child_process";
|
|
15105
15358
|
import path20 from "path";
|
|
15106
|
-
import
|
|
15359
|
+
import os7 from "os";
|
|
15107
15360
|
function registerSession(entry) {
|
|
15108
15361
|
const dir = path20.dirname(REGISTRY_PATH);
|
|
15109
15362
|
if (!existsSync14(dir)) {
|
|
@@ -15149,13 +15402,15 @@ var REGISTRY_PATH;
|
|
|
15149
15402
|
var init_session_registry = __esm({
|
|
15150
15403
|
"src/lib/session-registry.ts"() {
|
|
15151
15404
|
"use strict";
|
|
15152
|
-
REGISTRY_PATH = path20.join(
|
|
15405
|
+
REGISTRY_PATH = path20.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
15153
15406
|
}
|
|
15154
15407
|
});
|
|
15155
15408
|
|
|
15156
15409
|
// src/tui/views/CommandCenter.tsx
|
|
15157
15410
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
15158
15411
|
import TextInput from "ink-text-input";
|
|
15412
|
+
import path21 from "path";
|
|
15413
|
+
import { homedir as homedir3 } from "os";
|
|
15159
15414
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
15160
15415
|
function CommandCenterView({
|
|
15161
15416
|
onSelectProject,
|
|
@@ -15191,8 +15446,8 @@ function CommandCenterView({
|
|
|
15191
15446
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
15192
15447
|
const { readFileSync: readFileSync18, existsSync: existsSync22 } = await import("fs");
|
|
15193
15448
|
const { join } = await import("path");
|
|
15194
|
-
const { homedir:
|
|
15195
|
-
const configPath = join(
|
|
15449
|
+
const { homedir: homedir5 } = await import("os");
|
|
15450
|
+
const configPath = join(homedir5(), ".exe-os", "config.json");
|
|
15196
15451
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
15197
15452
|
let providerConfigs = {};
|
|
15198
15453
|
if (existsSync22(configPath)) {
|
|
@@ -15255,7 +15510,7 @@ function CommandCenterView({
|
|
|
15255
15510
|
registry.register(BashTool2);
|
|
15256
15511
|
let agentRole = "CTO";
|
|
15257
15512
|
try {
|
|
15258
|
-
const markerDir = join(
|
|
15513
|
+
const markerDir = join(homedir5(), ".exe-os", "session-cache");
|
|
15259
15514
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
15260
15515
|
for (const f of agentFiles) {
|
|
15261
15516
|
const data = JSON.parse(readFileSync18(join(markerDir, f), "utf8"));
|
|
@@ -15395,10 +15650,10 @@ function CommandCenterView({
|
|
|
15395
15650
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
15396
15651
|
projectName: p.projectName,
|
|
15397
15652
|
exeSession: p.exeSession,
|
|
15398
|
-
projectDir:
|
|
15653
|
+
projectDir: path21.join(homedir3(), p.projectName),
|
|
15399
15654
|
employeeCount: p.employees.length,
|
|
15400
15655
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
15401
|
-
memoryCount: p.
|
|
15656
|
+
memoryCount: p.employees.length * 4e3,
|
|
15402
15657
|
status: p.employees.some((e) => e.status === "active") ? "active" : "idle",
|
|
15403
15658
|
type: p.projectName.startsWith("exe-") ? "code" : "automation",
|
|
15404
15659
|
recentTasks: DEMO_RECENT_TASKS[p.projectName] ?? []
|
|
@@ -16223,10 +16478,10 @@ var init_provider_table = __esm({
|
|
|
16223
16478
|
|
|
16224
16479
|
// src/lib/intercom-queue.ts
|
|
16225
16480
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, renameSync as renameSync4, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
|
|
16226
|
-
import
|
|
16227
|
-
import
|
|
16481
|
+
import path22 from "path";
|
|
16482
|
+
import os8 from "os";
|
|
16228
16483
|
function ensureDir2() {
|
|
16229
|
-
const dir =
|
|
16484
|
+
const dir = path22.dirname(QUEUE_PATH);
|
|
16230
16485
|
if (!existsSync15(dir)) mkdirSync7(dir, { recursive: true });
|
|
16231
16486
|
}
|
|
16232
16487
|
function readQueue() {
|
|
@@ -16264,15 +16519,15 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
16264
16519
|
var init_intercom_queue = __esm({
|
|
16265
16520
|
"src/lib/intercom-queue.ts"() {
|
|
16266
16521
|
"use strict";
|
|
16267
|
-
QUEUE_PATH =
|
|
16522
|
+
QUEUE_PATH = path22.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
16268
16523
|
TTL_MS = 60 * 60 * 1e3;
|
|
16269
|
-
INTERCOM_LOG =
|
|
16524
|
+
INTERCOM_LOG = path22.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
16270
16525
|
}
|
|
16271
16526
|
});
|
|
16272
16527
|
|
|
16273
16528
|
// src/lib/plan-limits.ts
|
|
16274
16529
|
import { readFileSync as readFileSync12, existsSync as existsSync16 } from "fs";
|
|
16275
|
-
import
|
|
16530
|
+
import path23 from "path";
|
|
16276
16531
|
function getLicenseSync() {
|
|
16277
16532
|
try {
|
|
16278
16533
|
if (!existsSync16(CACHE_PATH2)) return freeLicense();
|
|
@@ -16344,18 +16599,18 @@ var init_plan_limits = __esm({
|
|
|
16344
16599
|
this.name = "PlanLimitError";
|
|
16345
16600
|
}
|
|
16346
16601
|
};
|
|
16347
|
-
CACHE_PATH2 =
|
|
16602
|
+
CACHE_PATH2 = path23.join(EXE_AI_DIR, "license-cache.json");
|
|
16348
16603
|
}
|
|
16349
16604
|
});
|
|
16350
16605
|
|
|
16351
16606
|
// src/lib/notifications.ts
|
|
16352
16607
|
import crypto4 from "crypto";
|
|
16353
|
-
import
|
|
16354
|
-
import
|
|
16608
|
+
import path24 from "path";
|
|
16609
|
+
import os9 from "os";
|
|
16355
16610
|
import {
|
|
16356
16611
|
readFileSync as readFileSync13,
|
|
16357
|
-
readdirSync as
|
|
16358
|
-
unlinkSync as
|
|
16612
|
+
readdirSync as readdirSync3,
|
|
16613
|
+
unlinkSync as unlinkSync5,
|
|
16359
16614
|
existsSync as existsSync17,
|
|
16360
16615
|
rmdirSync
|
|
16361
16616
|
} from "fs";
|
|
@@ -16436,7 +16691,7 @@ var init_session_kill_telemetry = __esm({
|
|
|
16436
16691
|
|
|
16437
16692
|
// src/lib/tasks-crud.ts
|
|
16438
16693
|
import crypto6 from "crypto";
|
|
16439
|
-
import
|
|
16694
|
+
import path25 from "path";
|
|
16440
16695
|
import { execSync as execSync7 } from "child_process";
|
|
16441
16696
|
import { mkdir as mkdir6, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
16442
16697
|
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
@@ -16569,8 +16824,8 @@ async function createTaskCore(input) {
|
|
|
16569
16824
|
}
|
|
16570
16825
|
if (input.baseDir) {
|
|
16571
16826
|
try {
|
|
16572
|
-
await mkdir6(
|
|
16573
|
-
await mkdir6(
|
|
16827
|
+
await mkdir6(path25.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
16828
|
+
await mkdir6(path25.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
16574
16829
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
16575
16830
|
await ensureGitignoreExe(input.baseDir);
|
|
16576
16831
|
} catch {
|
|
@@ -16778,7 +17033,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
16778
17033
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
16779
17034
|
}
|
|
16780
17035
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
16781
|
-
const archPath =
|
|
17036
|
+
const archPath = path25.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
16782
17037
|
try {
|
|
16783
17038
|
if (existsSync18(archPath)) return;
|
|
16784
17039
|
const template = [
|
|
@@ -16813,7 +17068,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
16813
17068
|
}
|
|
16814
17069
|
}
|
|
16815
17070
|
async function ensureGitignoreExe(baseDir) {
|
|
16816
|
-
const gitignorePath =
|
|
17071
|
+
const gitignorePath = path25.join(baseDir, ".gitignore");
|
|
16817
17072
|
try {
|
|
16818
17073
|
if (existsSync18(gitignorePath)) {
|
|
16819
17074
|
const content = readFileSync14(gitignorePath, "utf-8");
|
|
@@ -16836,8 +17091,8 @@ var init_tasks_crud = __esm({
|
|
|
16836
17091
|
});
|
|
16837
17092
|
|
|
16838
17093
|
// src/lib/tasks-review.ts
|
|
16839
|
-
import
|
|
16840
|
-
import { existsSync as existsSync19, readdirSync as
|
|
17094
|
+
import path26 from "path";
|
|
17095
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, unlinkSync as unlinkSync6 } from "fs";
|
|
16841
17096
|
async function countPendingReviews() {
|
|
16842
17097
|
const client = getClient();
|
|
16843
17098
|
const result = await client.execute({
|
|
@@ -16957,11 +17212,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
16957
17212
|
);
|
|
16958
17213
|
}
|
|
16959
17214
|
try {
|
|
16960
|
-
const cacheDir =
|
|
17215
|
+
const cacheDir = path26.join(EXE_AI_DIR, "session-cache");
|
|
16961
17216
|
if (existsSync19(cacheDir)) {
|
|
16962
|
-
for (const f of
|
|
17217
|
+
for (const f of readdirSync4(cacheDir)) {
|
|
16963
17218
|
if (f.startsWith("review-notified-")) {
|
|
16964
|
-
|
|
17219
|
+
unlinkSync6(path26.join(cacheDir, f));
|
|
16965
17220
|
}
|
|
16966
17221
|
}
|
|
16967
17222
|
}
|
|
@@ -16982,7 +17237,7 @@ var init_tasks_review = __esm({
|
|
|
16982
17237
|
});
|
|
16983
17238
|
|
|
16984
17239
|
// src/lib/tasks-chain.ts
|
|
16985
|
-
import
|
|
17240
|
+
import path27 from "path";
|
|
16986
17241
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
16987
17242
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
16988
17243
|
const client = getClient();
|
|
@@ -16998,7 +17253,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
16998
17253
|
});
|
|
16999
17254
|
for (const ur of unblockedRows.rows) {
|
|
17000
17255
|
try {
|
|
17001
|
-
const ubFile =
|
|
17256
|
+
const ubFile = path27.join(baseDir, String(ur.task_file));
|
|
17002
17257
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
17003
17258
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
17004
17259
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -17064,7 +17319,7 @@ var init_tasks_chain = __esm({
|
|
|
17064
17319
|
|
|
17065
17320
|
// src/lib/project-name.ts
|
|
17066
17321
|
import { execSync as execSync8 } from "child_process";
|
|
17067
|
-
import
|
|
17322
|
+
import path28 from "path";
|
|
17068
17323
|
function getProjectName(cwd2) {
|
|
17069
17324
|
const dir = cwd2 ?? process.cwd();
|
|
17070
17325
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -17077,7 +17332,7 @@ function getProjectName(cwd2) {
|
|
|
17077
17332
|
timeout: 2e3,
|
|
17078
17333
|
stdio: ["pipe", "pipe", "pipe"]
|
|
17079
17334
|
}).trim();
|
|
17080
|
-
repoRoot =
|
|
17335
|
+
repoRoot = path28.dirname(gitCommonDir);
|
|
17081
17336
|
} catch {
|
|
17082
17337
|
repoRoot = execSync8("git rev-parse --show-toplevel", {
|
|
17083
17338
|
cwd: dir,
|
|
@@ -17086,11 +17341,11 @@ function getProjectName(cwd2) {
|
|
|
17086
17341
|
stdio: ["pipe", "pipe", "pipe"]
|
|
17087
17342
|
}).trim();
|
|
17088
17343
|
}
|
|
17089
|
-
_cached2 =
|
|
17344
|
+
_cached2 = path28.basename(repoRoot);
|
|
17090
17345
|
_cachedCwd = dir;
|
|
17091
17346
|
return _cached2;
|
|
17092
17347
|
} catch {
|
|
17093
|
-
_cached2 =
|
|
17348
|
+
_cached2 = path28.basename(dir);
|
|
17094
17349
|
_cachedCwd = dir;
|
|
17095
17350
|
return _cached2;
|
|
17096
17351
|
}
|
|
@@ -17561,8 +17816,8 @@ __export(tasks_exports, {
|
|
|
17561
17816
|
updateTaskStatus: () => updateTaskStatus,
|
|
17562
17817
|
writeCheckpoint: () => writeCheckpoint
|
|
17563
17818
|
});
|
|
17564
|
-
import
|
|
17565
|
-
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as
|
|
17819
|
+
import path29 from "path";
|
|
17820
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
17566
17821
|
async function createTask(input) {
|
|
17567
17822
|
const result = await createTaskCore(input);
|
|
17568
17823
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -17581,14 +17836,14 @@ async function updateTask(input) {
|
|
|
17581
17836
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
17582
17837
|
try {
|
|
17583
17838
|
const agent = String(row.assigned_to);
|
|
17584
|
-
const cacheDir =
|
|
17585
|
-
const cachePath =
|
|
17839
|
+
const cacheDir = path29.join(EXE_AI_DIR, "session-cache");
|
|
17840
|
+
const cachePath = path29.join(cacheDir, `current-task-${agent}.json`);
|
|
17586
17841
|
if (input.status === "in_progress") {
|
|
17587
17842
|
mkdirSync8(cacheDir, { recursive: true });
|
|
17588
17843
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
17589
17844
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
17590
17845
|
try {
|
|
17591
|
-
|
|
17846
|
+
unlinkSync7(cachePath);
|
|
17592
17847
|
} catch {
|
|
17593
17848
|
}
|
|
17594
17849
|
}
|
|
@@ -18004,12 +18259,12 @@ __export(tmux_routing_exports, {
|
|
|
18004
18259
|
});
|
|
18005
18260
|
import { execFileSync as execFileSync3, execSync as execSync9 } from "child_process";
|
|
18006
18261
|
import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, existsSync as existsSync20, appendFileSync } from "fs";
|
|
18007
|
-
import
|
|
18008
|
-
import
|
|
18262
|
+
import path30 from "path";
|
|
18263
|
+
import os10 from "os";
|
|
18009
18264
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
18010
|
-
import { unlinkSync as
|
|
18265
|
+
import { unlinkSync as unlinkSync8 } from "fs";
|
|
18011
18266
|
function spawnLockPath(sessionName) {
|
|
18012
|
-
return
|
|
18267
|
+
return path30.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
18013
18268
|
}
|
|
18014
18269
|
function isProcessAlive(pid) {
|
|
18015
18270
|
try {
|
|
@@ -18039,15 +18294,15 @@ function acquireSpawnLock2(sessionName) {
|
|
|
18039
18294
|
}
|
|
18040
18295
|
function releaseSpawnLock2(sessionName) {
|
|
18041
18296
|
try {
|
|
18042
|
-
|
|
18297
|
+
unlinkSync8(spawnLockPath(sessionName));
|
|
18043
18298
|
} catch {
|
|
18044
18299
|
}
|
|
18045
18300
|
}
|
|
18046
18301
|
function resolveBehaviorsExporterScript() {
|
|
18047
18302
|
try {
|
|
18048
18303
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
18049
|
-
const scriptPath =
|
|
18050
|
-
|
|
18304
|
+
const scriptPath = path30.join(
|
|
18305
|
+
path30.dirname(thisFile),
|
|
18051
18306
|
"..",
|
|
18052
18307
|
"bin",
|
|
18053
18308
|
"exe-export-behaviors.js"
|
|
@@ -18097,7 +18352,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
18097
18352
|
mkdirSync9(SESSION_CACHE, { recursive: true });
|
|
18098
18353
|
}
|
|
18099
18354
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
18100
|
-
const filePath =
|
|
18355
|
+
const filePath = path30.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
18101
18356
|
writeFileSync8(filePath, JSON.stringify({
|
|
18102
18357
|
parentExe: rootExe,
|
|
18103
18358
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -18106,7 +18361,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
18106
18361
|
}
|
|
18107
18362
|
function getParentExe(sessionKey) {
|
|
18108
18363
|
try {
|
|
18109
|
-
const data = JSON.parse(readFileSync15(
|
|
18364
|
+
const data = JSON.parse(readFileSync15(path30.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
18110
18365
|
return data.parentExe || null;
|
|
18111
18366
|
} catch {
|
|
18112
18367
|
return null;
|
|
@@ -18115,7 +18370,7 @@ function getParentExe(sessionKey) {
|
|
|
18115
18370
|
function getDispatchedBy(sessionKey) {
|
|
18116
18371
|
try {
|
|
18117
18372
|
const data = JSON.parse(readFileSync15(
|
|
18118
|
-
|
|
18373
|
+
path30.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
18119
18374
|
"utf8"
|
|
18120
18375
|
));
|
|
18121
18376
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -18358,8 +18613,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18358
18613
|
const transport = getTransport();
|
|
18359
18614
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
18360
18615
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
18361
|
-
const logDir =
|
|
18362
|
-
const logFile =
|
|
18616
|
+
const logDir = path30.join(os10.homedir(), ".exe-os", "session-logs");
|
|
18617
|
+
const logFile = path30.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
18363
18618
|
if (!existsSync20(logDir)) {
|
|
18364
18619
|
mkdirSync9(logDir, { recursive: true });
|
|
18365
18620
|
}
|
|
@@ -18367,14 +18622,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18367
18622
|
let cleanupSuffix = "";
|
|
18368
18623
|
try {
|
|
18369
18624
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
18370
|
-
const cleanupScript =
|
|
18625
|
+
const cleanupScript = path30.join(path30.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
18371
18626
|
if (existsSync20(cleanupScript)) {
|
|
18372
18627
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
18373
18628
|
}
|
|
18374
18629
|
} catch {
|
|
18375
18630
|
}
|
|
18376
18631
|
try {
|
|
18377
|
-
const claudeJsonPath =
|
|
18632
|
+
const claudeJsonPath = path30.join(os10.homedir(), ".claude.json");
|
|
18378
18633
|
let claudeJson = {};
|
|
18379
18634
|
try {
|
|
18380
18635
|
claudeJson = JSON.parse(readFileSync15(claudeJsonPath, "utf8"));
|
|
@@ -18389,10 +18644,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18389
18644
|
} catch {
|
|
18390
18645
|
}
|
|
18391
18646
|
try {
|
|
18392
|
-
const settingsDir =
|
|
18647
|
+
const settingsDir = path30.join(os10.homedir(), ".claude", "projects");
|
|
18393
18648
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
18394
|
-
const projSettingsDir =
|
|
18395
|
-
const settingsPath =
|
|
18649
|
+
const projSettingsDir = path30.join(settingsDir, normalizedKey);
|
|
18650
|
+
const settingsPath = path30.join(projSettingsDir, "settings.json");
|
|
18396
18651
|
let settings = {};
|
|
18397
18652
|
try {
|
|
18398
18653
|
settings = JSON.parse(readFileSync15(settingsPath, "utf8"));
|
|
@@ -18436,8 +18691,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18436
18691
|
let behaviorsFlag = "";
|
|
18437
18692
|
let legacyFallbackWarned = false;
|
|
18438
18693
|
if (!useExeAgent && !useBinSymlink) {
|
|
18439
|
-
const identityPath2 =
|
|
18440
|
-
|
|
18694
|
+
const identityPath2 = path30.join(
|
|
18695
|
+
os10.homedir(),
|
|
18441
18696
|
".exe-os",
|
|
18442
18697
|
"identity",
|
|
18443
18698
|
`${employeeName}.md`
|
|
@@ -18452,7 +18707,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18452
18707
|
}
|
|
18453
18708
|
const behaviorsFile = exportBehaviorsSync(
|
|
18454
18709
|
employeeName,
|
|
18455
|
-
|
|
18710
|
+
path30.basename(spawnCwd),
|
|
18456
18711
|
sessionName
|
|
18457
18712
|
);
|
|
18458
18713
|
if (behaviorsFile) {
|
|
@@ -18467,9 +18722,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18467
18722
|
}
|
|
18468
18723
|
let sessionContextFlag = "";
|
|
18469
18724
|
try {
|
|
18470
|
-
const ctxDir =
|
|
18725
|
+
const ctxDir = path30.join(os10.homedir(), ".exe-os", "session-cache");
|
|
18471
18726
|
mkdirSync9(ctxDir, { recursive: true });
|
|
18472
|
-
const ctxFile =
|
|
18727
|
+
const ctxFile = path30.join(ctxDir, `session-context-${sessionName}.md`);
|
|
18473
18728
|
const ctxContent = [
|
|
18474
18729
|
`## Session Context`,
|
|
18475
18730
|
`You are running in tmux session: ${sessionName}.`,
|
|
@@ -18514,7 +18769,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18514
18769
|
transport.pipeLog(sessionName, logFile);
|
|
18515
18770
|
try {
|
|
18516
18771
|
const mySession = getMySession();
|
|
18517
|
-
const dispatchInfo =
|
|
18772
|
+
const dispatchInfo = path30.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
18518
18773
|
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
18519
18774
|
dispatchedBy: mySession,
|
|
18520
18775
|
rootExe: exeSession,
|
|
@@ -18578,13 +18833,13 @@ var init_tmux_routing = __esm({
|
|
|
18578
18833
|
init_provider_table();
|
|
18579
18834
|
init_intercom_queue();
|
|
18580
18835
|
init_plan_limits();
|
|
18581
|
-
SPAWN_LOCK_DIR =
|
|
18582
|
-
SESSION_CACHE =
|
|
18836
|
+
SPAWN_LOCK_DIR = path30.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
18837
|
+
SESSION_CACHE = path30.join(os10.homedir(), ".exe-os", "session-cache");
|
|
18583
18838
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
18584
18839
|
VERIFY_PANE_LINES = 200;
|
|
18585
18840
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
18586
|
-
INTERCOM_LOG2 =
|
|
18587
|
-
DEBOUNCE_FILE =
|
|
18841
|
+
INTERCOM_LOG2 = path30.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
18842
|
+
DEBOUNCE_FILE = path30.join(SESSION_CACHE, "intercom-debounce.json");
|
|
18588
18843
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
18589
18844
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
18590
18845
|
}
|
|
@@ -18972,6 +19227,8 @@ var init_useOrchestrator = __esm({
|
|
|
18972
19227
|
|
|
18973
19228
|
// src/tui/views/Sessions.tsx
|
|
18974
19229
|
import { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
19230
|
+
import path31 from "path";
|
|
19231
|
+
import { homedir as homedir4 } from "os";
|
|
18975
19232
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
18976
19233
|
function SessionsView({
|
|
18977
19234
|
initialProject,
|
|
@@ -19004,7 +19261,7 @@ function SessionsView({
|
|
|
19004
19261
|
if (demo) {
|
|
19005
19262
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
19006
19263
|
...p,
|
|
19007
|
-
projectDir:
|
|
19264
|
+
projectDir: path31.join(homedir4(), p.projectName),
|
|
19008
19265
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
19009
19266
|
})));
|
|
19010
19267
|
return;
|
|
@@ -20867,8 +21124,8 @@ __export(wiki_client_exports, {
|
|
|
20867
21124
|
listDocuments: () => listDocuments,
|
|
20868
21125
|
listWorkspaces: () => listWorkspaces
|
|
20869
21126
|
});
|
|
20870
|
-
async function wikiFetch(config,
|
|
20871
|
-
const url = `${config.baseUrl}/api/v1${
|
|
21127
|
+
async function wikiFetch(config, path34, method = "GET", body) {
|
|
21128
|
+
const url = `${config.baseUrl}/api/v1${path34}`;
|
|
20872
21129
|
const headers = {
|
|
20873
21130
|
Authorization: `Bearer ${config.apiKey}`,
|
|
20874
21131
|
"Content-Type": "application/json"
|
|
@@ -20876,14 +21133,32 @@ async function wikiFetch(config, path32, method = "GET", body) {
|
|
|
20876
21133
|
const controller = new AbortController();
|
|
20877
21134
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
20878
21135
|
try {
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
|
|
20884
|
-
|
|
21136
|
+
let response;
|
|
21137
|
+
try {
|
|
21138
|
+
response = await fetch(url, {
|
|
21139
|
+
method,
|
|
21140
|
+
headers,
|
|
21141
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
21142
|
+
signal: controller.signal
|
|
21143
|
+
});
|
|
21144
|
+
} catch {
|
|
21145
|
+
clearTimeout(timeout);
|
|
21146
|
+
const retryController = new AbortController();
|
|
21147
|
+
const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
|
|
21148
|
+
try {
|
|
21149
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
21150
|
+
response = await fetch(url, {
|
|
21151
|
+
method,
|
|
21152
|
+
headers,
|
|
21153
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
21154
|
+
signal: retryController.signal
|
|
21155
|
+
});
|
|
21156
|
+
} finally {
|
|
21157
|
+
clearTimeout(retryTimeout);
|
|
21158
|
+
}
|
|
21159
|
+
}
|
|
20885
21160
|
if (!response.ok) {
|
|
20886
|
-
throw new Error(`Wiki API ${method} ${
|
|
21161
|
+
throw new Error(`Wiki API ${method} ${path34}: ${response.status} ${response.statusText}`);
|
|
20887
21162
|
}
|
|
20888
21163
|
return response.json();
|
|
20889
21164
|
} finally {
|
|
@@ -21734,9 +22009,9 @@ var init_App2 = __esm({
|
|
|
21734
22009
|
// src/lib/update-check.ts
|
|
21735
22010
|
import { execSync as execSync11 } from "child_process";
|
|
21736
22011
|
import { readFileSync as readFileSync16 } from "fs";
|
|
21737
|
-
import
|
|
22012
|
+
import path32 from "path";
|
|
21738
22013
|
function getLocalVersion(packageRoot) {
|
|
21739
|
-
const pkgPath =
|
|
22014
|
+
const pkgPath = path32.join(packageRoot, "package.json");
|
|
21740
22015
|
const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
|
|
21741
22016
|
return pkg.version;
|
|
21742
22017
|
}
|
|
@@ -21827,9 +22102,9 @@ var init_update = __esm({
|
|
|
21827
22102
|
});
|
|
21828
22103
|
|
|
21829
22104
|
// src/bin/cli.ts
|
|
21830
|
-
import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as
|
|
21831
|
-
import
|
|
21832
|
-
import
|
|
22105
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as readdirSync5, rmSync } from "fs";
|
|
22106
|
+
import path33 from "path";
|
|
22107
|
+
import os11 from "os";
|
|
21833
22108
|
var args = process.argv.slice(2);
|
|
21834
22109
|
if (args.includes("--global")) {
|
|
21835
22110
|
process.stderr.write(
|
|
@@ -21902,12 +22177,19 @@ async function runClaudeInstall() {
|
|
|
21902
22177
|
}
|
|
21903
22178
|
}
|
|
21904
22179
|
async function runClaudeCheck() {
|
|
21905
|
-
const claudeDir =
|
|
21906
|
-
const settingsPath =
|
|
21907
|
-
const claudeJsonPath =
|
|
22180
|
+
const claudeDir = path33.join(os11.homedir(), ".claude");
|
|
22181
|
+
const settingsPath = path33.join(claudeDir, "settings.json");
|
|
22182
|
+
const claudeJsonPath = path33.join(os11.homedir(), ".claude.json");
|
|
21908
22183
|
let ok = true;
|
|
21909
22184
|
if (existsSync21(settingsPath)) {
|
|
21910
|
-
|
|
22185
|
+
let settings;
|
|
22186
|
+
try {
|
|
22187
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22188
|
+
} catch {
|
|
22189
|
+
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
22190
|
+
ok = false;
|
|
22191
|
+
settings = {};
|
|
22192
|
+
}
|
|
21911
22193
|
const hasHooks = settings.hooks && Object.keys(settings.hooks).some((k) => {
|
|
21912
22194
|
const groups = settings.hooks[k];
|
|
21913
22195
|
return Array.isArray(groups) && groups.some((g) => {
|
|
@@ -21929,7 +22211,14 @@ async function runClaudeCheck() {
|
|
|
21929
22211
|
ok = false;
|
|
21930
22212
|
}
|
|
21931
22213
|
if (existsSync21(claudeJsonPath)) {
|
|
21932
|
-
|
|
22214
|
+
let claudeJson;
|
|
22215
|
+
try {
|
|
22216
|
+
claudeJson = JSON.parse(readFileSync17(claudeJsonPath, "utf8"));
|
|
22217
|
+
} catch {
|
|
22218
|
+
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
22219
|
+
ok = false;
|
|
22220
|
+
claudeJson = {};
|
|
22221
|
+
}
|
|
21933
22222
|
const hasMcp = claudeJson.mcpServers && (claudeJson.mcpServers["exe-mem"] || claudeJson.mcpServers["exe-os"]);
|
|
21934
22223
|
if (hasMcp) {
|
|
21935
22224
|
console.log("\x1B[32m\u2713\x1B[0m MCP server configured in claude.json");
|
|
@@ -21943,7 +22232,7 @@ async function runClaudeCheck() {
|
|
|
21943
22232
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
21944
22233
|
ok = false;
|
|
21945
22234
|
}
|
|
21946
|
-
const skillsDir =
|
|
22235
|
+
const skillsDir = path33.join(claudeDir, "skills");
|
|
21947
22236
|
if (existsSync21(skillsDir)) {
|
|
21948
22237
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
21949
22238
|
} else {
|
|
@@ -21960,15 +22249,26 @@ async function runClaudeCheck() {
|
|
|
21960
22249
|
async function runClaudeUninstall(flags = []) {
|
|
21961
22250
|
const dryRun = flags.includes("--dry-run");
|
|
21962
22251
|
const purge = flags.includes("--purge");
|
|
21963
|
-
const homeDir =
|
|
21964
|
-
const claudeDir =
|
|
21965
|
-
const settingsPath =
|
|
21966
|
-
const claudeJsonPath =
|
|
21967
|
-
const exeOsDir =
|
|
22252
|
+
const homeDir = os11.homedir();
|
|
22253
|
+
const claudeDir = path33.join(homeDir, ".claude");
|
|
22254
|
+
const settingsPath = path33.join(claudeDir, "settings.json");
|
|
22255
|
+
const claudeJsonPath = path33.join(homeDir, ".claude.json");
|
|
22256
|
+
const exeOsDir = path33.join(homeDir, ".exe-os");
|
|
21968
22257
|
let removed = 0;
|
|
21969
22258
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
22259
|
+
let settings = {};
|
|
21970
22260
|
if (existsSync21(settingsPath)) {
|
|
21971
|
-
|
|
22261
|
+
try {
|
|
22262
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22263
|
+
} catch {
|
|
22264
|
+
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
22265
|
+
if (purge) {
|
|
22266
|
+
console.error("Skipping settings cleanup (--purge).");
|
|
22267
|
+
} else {
|
|
22268
|
+
console.error("Try running: exe-os claude install");
|
|
22269
|
+
process.exit(1);
|
|
22270
|
+
}
|
|
22271
|
+
}
|
|
21972
22272
|
if (settings.hooks) {
|
|
21973
22273
|
for (const key of Object.keys(settings.hooks)) {
|
|
21974
22274
|
const groups = settings.hooks[key];
|
|
@@ -22005,32 +22305,43 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22005
22305
|
}
|
|
22006
22306
|
}
|
|
22007
22307
|
if (existsSync21(claudeJsonPath)) {
|
|
22008
|
-
const
|
|
22009
|
-
if (
|
|
22010
|
-
|
|
22011
|
-
|
|
22012
|
-
|
|
22013
|
-
|
|
22014
|
-
|
|
22308
|
+
const raw = readFileSync17(claudeJsonPath, "utf8");
|
|
22309
|
+
if (raw.length > 1e6) {
|
|
22310
|
+
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
22311
|
+
} else {
|
|
22312
|
+
let claudeJson;
|
|
22313
|
+
try {
|
|
22314
|
+
claudeJson = JSON.parse(raw);
|
|
22315
|
+
} catch {
|
|
22316
|
+
console.error("claude.json is malformed JSON \u2014 skipping.");
|
|
22317
|
+
claudeJson = {};
|
|
22318
|
+
}
|
|
22319
|
+
if (claudeJson.mcpServers) {
|
|
22320
|
+
let removedMcp = false;
|
|
22321
|
+
for (const key of ["exe-mem", "exe-os"]) {
|
|
22322
|
+
if (claudeJson.mcpServers[key]) {
|
|
22323
|
+
if (!dryRun) delete claudeJson.mcpServers[key];
|
|
22324
|
+
removedMcp = true;
|
|
22325
|
+
}
|
|
22015
22326
|
}
|
|
22016
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
|
|
22327
|
+
if (removedMcp) {
|
|
22328
|
+
if (!dryRun) {
|
|
22329
|
+
writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
22330
|
+
}
|
|
22331
|
+
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
22332
|
+
removed++;
|
|
22020
22333
|
}
|
|
22021
|
-
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
22022
|
-
removed++;
|
|
22023
22334
|
}
|
|
22024
22335
|
}
|
|
22025
22336
|
}
|
|
22026
|
-
const skillsDir =
|
|
22337
|
+
const skillsDir = path33.join(claudeDir, "skills");
|
|
22027
22338
|
if (existsSync21(skillsDir)) {
|
|
22028
22339
|
let skillCount = 0;
|
|
22029
22340
|
try {
|
|
22030
|
-
const entries =
|
|
22341
|
+
const entries = readdirSync5(skillsDir);
|
|
22031
22342
|
for (const entry of entries) {
|
|
22032
22343
|
if (entry.startsWith("exe")) {
|
|
22033
|
-
const fullPath =
|
|
22344
|
+
const fullPath = path33.join(skillsDir, entry);
|
|
22034
22345
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
22035
22346
|
skillCount++;
|
|
22036
22347
|
}
|
|
@@ -22042,7 +22353,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22042
22353
|
removed++;
|
|
22043
22354
|
}
|
|
22044
22355
|
}
|
|
22045
|
-
const claudeMdPath =
|
|
22356
|
+
const claudeMdPath = path33.join(claudeDir, "CLAUDE.md");
|
|
22046
22357
|
if (existsSync21(claudeMdPath)) {
|
|
22047
22358
|
const content = readFileSync17(claudeMdPath, "utf8");
|
|
22048
22359
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
@@ -22056,13 +22367,13 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22056
22367
|
removed++;
|
|
22057
22368
|
}
|
|
22058
22369
|
}
|
|
22059
|
-
const agentsDir =
|
|
22370
|
+
const agentsDir = path33.join(claudeDir, "agents");
|
|
22060
22371
|
if (existsSync21(agentsDir)) {
|
|
22061
22372
|
let agentCount = 0;
|
|
22062
22373
|
try {
|
|
22063
|
-
const entries =
|
|
22374
|
+
const entries = readdirSync5(agentsDir).filter((f) => f.endsWith(".md"));
|
|
22064
22375
|
let knownNames = /* @__PURE__ */ new Set();
|
|
22065
|
-
const rosterPath =
|
|
22376
|
+
const rosterPath = path33.join(exeOsDir, "exe-employees.json");
|
|
22066
22377
|
if (existsSync21(rosterPath)) {
|
|
22067
22378
|
try {
|
|
22068
22379
|
const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
|
|
@@ -22073,7 +22384,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22073
22384
|
for (const entry of entries) {
|
|
22074
22385
|
const name = entry.replace(/\.md$/, "");
|
|
22075
22386
|
if (knownNames.has(name)) {
|
|
22076
|
-
if (!dryRun) rmSync(
|
|
22387
|
+
if (!dryRun) rmSync(path33.join(agentsDir, entry), { force: true });
|
|
22077
22388
|
agentCount++;
|
|
22078
22389
|
}
|
|
22079
22390
|
}
|
|
@@ -22084,13 +22395,13 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22084
22395
|
removed++;
|
|
22085
22396
|
}
|
|
22086
22397
|
}
|
|
22087
|
-
const projectsDir =
|
|
22398
|
+
const projectsDir = path33.join(claudeDir, "projects");
|
|
22088
22399
|
if (existsSync21(projectsDir)) {
|
|
22089
22400
|
let projectCount = 0;
|
|
22090
22401
|
try {
|
|
22091
|
-
const projects =
|
|
22402
|
+
const projects = readdirSync5(projectsDir);
|
|
22092
22403
|
for (const proj of projects) {
|
|
22093
|
-
const projSettings =
|
|
22404
|
+
const projSettings = path33.join(projectsDir, proj, "settings.json");
|
|
22094
22405
|
if (!existsSync21(projSettings)) continue;
|
|
22095
22406
|
try {
|
|
22096
22407
|
const pSettings = JSON.parse(readFileSync17(projSettings, "utf8"));
|
|
@@ -22118,16 +22429,24 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22118
22429
|
}
|
|
22119
22430
|
try {
|
|
22120
22431
|
const { execSync: execSync13 } = await import("child_process");
|
|
22121
|
-
const
|
|
22122
|
-
|
|
22432
|
+
const findExeBin3 = () => {
|
|
22433
|
+
try {
|
|
22434
|
+
return execSync13(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
22435
|
+
} catch {
|
|
22436
|
+
return null;
|
|
22437
|
+
}
|
|
22438
|
+
};
|
|
22439
|
+
const exeBinPath = findExeBin3();
|
|
22440
|
+
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
22441
|
+
const binDir = path33.dirname(exeBinPath);
|
|
22123
22442
|
let symlinkCount = 0;
|
|
22124
|
-
const rosterPath =
|
|
22443
|
+
const rosterPath = path33.join(exeOsDir, "exe-employees.json");
|
|
22125
22444
|
if (existsSync21(rosterPath)) {
|
|
22126
22445
|
const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
|
|
22127
22446
|
for (const emp of roster) {
|
|
22128
22447
|
if (emp.name === "exe") continue;
|
|
22129
22448
|
for (const suffix of ["", "-opencode"]) {
|
|
22130
|
-
const linkPath =
|
|
22449
|
+
const linkPath = path33.join(binDir, `${emp.name}${suffix}`);
|
|
22131
22450
|
if (existsSync21(linkPath)) {
|
|
22132
22451
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
22133
22452
|
symlinkCount++;
|
|
@@ -22166,7 +22485,7 @@ async function checkForUpdateOnBoot() {
|
|
|
22166
22485
|
const config = await loadConfig2();
|
|
22167
22486
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
22168
22487
|
const { checkForUpdate: checkForUpdate2 } = await init_update().then(() => update_exports);
|
|
22169
|
-
const packageRoot =
|
|
22488
|
+
const packageRoot = path33.resolve(
|
|
22170
22489
|
new URL("../..", import.meta.url).pathname
|
|
22171
22490
|
);
|
|
22172
22491
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -22195,13 +22514,16 @@ async function runActivate(key) {
|
|
|
22195
22514
|
mirrorLicenseKey2(key);
|
|
22196
22515
|
try {
|
|
22197
22516
|
const license = await validateLicense2(key);
|
|
22198
|
-
if (!license.valid) {
|
|
22517
|
+
if (!license.valid && license.error === "offline") {
|
|
22518
|
+
console.log("\u26A0 Activated in offline mode \u2014 will verify when connected");
|
|
22519
|
+
} else if (!license.valid) {
|
|
22199
22520
|
process.stderr.write(`License invalid: ${license.plan || "unknown"}
|
|
22200
22521
|
`);
|
|
22201
22522
|
process.exit(1);
|
|
22523
|
+
} else {
|
|
22524
|
+
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
22525
|
+
console.log(`Plan activated: ${planLabel}`);
|
|
22202
22526
|
}
|
|
22203
|
-
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
22204
|
-
console.log(`Plan activated: ${planLabel}`);
|
|
22205
22527
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
22206
22528
|
const ask2 = (prompt) => new Promise((resolve) => rl.question(prompt, (a) => resolve(a.trim())));
|
|
22207
22529
|
let employees = await loadEmployees2();
|
|
@@ -22222,7 +22544,7 @@ async function runActivate(key) {
|
|
|
22222
22544
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
22223
22545
|
if (idTemplate) {
|
|
22224
22546
|
const idPath = identityPath2(name);
|
|
22225
|
-
const dir =
|
|
22547
|
+
const dir = path33.dirname(idPath);
|
|
22226
22548
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
22227
22549
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
22228
22550
|
}
|