@askexenow/exe-os 0.8.36 → 0.8.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +75 -61
- package/dist/bin/backfill-responses.js +16 -9
- package/dist/bin/backfill-vectors.js +1 -8
- package/dist/bin/cleanup-stale-review-tasks.js +1 -8
- package/dist/bin/cli.js +767 -348
- package/dist/bin/exe-assign.js +16 -9
- package/dist/bin/exe-boot.js +606 -42
- package/dist/bin/exe-call.js +9 -4
- package/dist/bin/exe-cloud.js +37 -3
- package/dist/bin/exe-dispatch.js +43 -3
- package/dist/bin/exe-doctor.js +1 -8
- package/dist/bin/exe-export-behaviors.js +11 -11
- package/dist/bin/exe-forget.js +1 -8
- package/dist/bin/exe-gateway.js +127 -37
- package/dist/bin/exe-heartbeat.js +6 -12
- package/dist/bin/exe-kill.js +8 -8
- package/dist/bin/exe-launch-agent.js +59 -15
- package/dist/bin/exe-link.js +516 -15
- package/dist/bin/exe-new-employee.js +35 -10
- package/dist/bin/exe-pending-messages.js +3 -9
- package/dist/bin/exe-pending-notifications.js +1 -8
- package/dist/bin/exe-pending-reviews.js +6 -12
- package/dist/bin/exe-review.js +16 -9
- package/dist/bin/exe-search.js +19 -12
- package/dist/bin/exe-session-cleanup.js +22 -14
- package/dist/bin/exe-status.js +1 -8
- package/dist/bin/exe-team.js +1 -8
- package/dist/bin/git-sweep.js +16 -9
- package/dist/bin/graph-backfill.js +8 -8
- package/dist/bin/graph-export.js +8 -8
- package/dist/bin/install.js +44 -5
- package/dist/bin/scan-tasks.js +16 -9
- package/dist/bin/setup.js +396 -245
- package/dist/bin/shard-migrate.js +8 -8
- package/dist/bin/wiki-sync.js +8 -8
- package/dist/gateway/index.js +85 -37
- package/dist/hooks/bug-report-worker.js +48 -15
- package/dist/hooks/commit-complete.js +16 -9
- package/dist/hooks/error-recall.js +21 -14
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +79 -16
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +17 -10
- package/dist/hooks/notification.js +17 -10
- package/dist/hooks/post-compact.js +17 -10
- package/dist/hooks/pre-compact.js +17 -10
- package/dist/hooks/pre-tool-use.js +17 -10
- package/dist/hooks/prompt-ingest-worker.js +28 -5
- package/dist/hooks/prompt-submit.js +69 -16
- package/dist/hooks/response-ingest-worker.js +29 -6
- package/dist/hooks/session-end.js +21 -14
- package/dist/hooks/session-start.js +21 -14
- package/dist/hooks/stop.js +17 -10
- package/dist/hooks/subagent-stop.js +17 -10
- package/dist/hooks/summary-worker.js +536 -22
- package/dist/index.js +76 -20
- package/dist/lib/cloud-sync.js +521 -13
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/exe-daemon.js +97 -31
- package/dist/lib/hybrid-search.js +19 -12
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/license.js +43 -2
- package/dist/lib/messaging.js +43 -3
- package/dist/lib/schedules.js +1 -8
- package/dist/lib/store.js +16 -9
- package/dist/lib/tasks.js +47 -7
- package/dist/lib/tmux-routing.js +45 -3
- package/dist/mcp/server.js +256 -128
- package/dist/mcp/tools/create-task.js +48 -8
- package/dist/mcp/tools/deactivate-behavior.js +1 -1
- package/dist/mcp/tools/list-tasks.js +37 -28
- package/dist/mcp/tools/send-message.js +46 -6
- package/dist/mcp/tools/update-task.js +3 -2
- package/dist/runtime/index.js +61 -6
- package/dist/tui/App.js +98 -9
- package/package.json +5 -3
- package/src/commands/exe/afk.md +116 -0
package/dist/bin/cli.js
CHANGED
|
@@ -621,6 +621,10 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
621
621
|
if (!settings.hooks) {
|
|
622
622
|
settings.hooks = {};
|
|
623
623
|
}
|
|
624
|
+
if (settings.hooks && typeof settings.hooks !== "object") {
|
|
625
|
+
console.warn("[exe-os] Unexpected hooks schema in settings.json \u2014 skipping hook installation");
|
|
626
|
+
return { added: 0, skipped: 0 };
|
|
627
|
+
}
|
|
624
628
|
const hooksToRegister = [
|
|
625
629
|
{
|
|
626
630
|
event: "PostToolUse",
|
|
@@ -783,6 +787,11 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
783
787
|
if (!settings.hooks[event]) {
|
|
784
788
|
settings.hooks[event] = [];
|
|
785
789
|
}
|
|
790
|
+
if (!Array.isArray(settings.hooks[event])) {
|
|
791
|
+
console.warn(`[exe-os] Hook event "${event}" has unexpected structure \u2014 skipping`);
|
|
792
|
+
skipped++;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
786
795
|
const existing = settings.hooks[event];
|
|
787
796
|
const correctCommand = group.hooks[0]?.command ?? "";
|
|
788
797
|
const alreadyCorrect = existing.some(
|
|
@@ -807,26 +816,56 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
807
816
|
permissions.allow = [];
|
|
808
817
|
}
|
|
809
818
|
const toolNames = [
|
|
819
|
+
// Core memory
|
|
810
820
|
"store_memory",
|
|
811
821
|
"recall_my_memory",
|
|
822
|
+
"commit_to_long_term_memory",
|
|
823
|
+
"consolidate_memories",
|
|
824
|
+
"ask_team_memory",
|
|
825
|
+
"get_session_context",
|
|
826
|
+
// Tasks
|
|
812
827
|
"create_task",
|
|
813
828
|
"list_tasks",
|
|
814
829
|
"get_task",
|
|
815
830
|
"update_task",
|
|
816
831
|
"close_task",
|
|
832
|
+
"checkpoint_task",
|
|
833
|
+
// Behaviors
|
|
817
834
|
"store_behavior",
|
|
818
835
|
"deactivate_behavior",
|
|
819
|
-
"
|
|
836
|
+
"list_behaviors",
|
|
837
|
+
// Identity
|
|
820
838
|
"get_identity",
|
|
821
839
|
"update_identity",
|
|
822
|
-
|
|
823
|
-
"
|
|
840
|
+
// Messaging
|
|
841
|
+
"send_message",
|
|
842
|
+
"acknowledge_messages",
|
|
843
|
+
"send_whatsapp",
|
|
844
|
+
"query_conversations",
|
|
845
|
+
// Reminders + triggers
|
|
824
846
|
"create_reminder",
|
|
825
847
|
"complete_reminder",
|
|
826
848
|
"list_reminders",
|
|
827
|
-
"
|
|
849
|
+
"create_trigger",
|
|
850
|
+
"list_triggers",
|
|
851
|
+
// GraphRAG
|
|
828
852
|
"query_relationships",
|
|
829
|
-
"
|
|
853
|
+
"merge_entities",
|
|
854
|
+
// Documents + wiki
|
|
855
|
+
"ingest_document",
|
|
856
|
+
"list_documents",
|
|
857
|
+
"purge_document",
|
|
858
|
+
"rerank_documents",
|
|
859
|
+
"set_document_importance",
|
|
860
|
+
"create_wiki_page",
|
|
861
|
+
"update_wiki_page",
|
|
862
|
+
"get_wiki_page",
|
|
863
|
+
"list_wiki_pages",
|
|
864
|
+
// System
|
|
865
|
+
"load_skill",
|
|
866
|
+
"apply_starter_pack",
|
|
867
|
+
"resume_employee",
|
|
868
|
+
"deploy_client"
|
|
830
869
|
];
|
|
831
870
|
const allowList = permissions.allow;
|
|
832
871
|
for (const tool of expandDualPrefixTools(toolNames)) {
|
|
@@ -2024,7 +2063,7 @@ __export(shard_manager_exports, {
|
|
|
2024
2063
|
shardExists: () => shardExists
|
|
2025
2064
|
});
|
|
2026
2065
|
import path6 from "path";
|
|
2027
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
2066
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2028
2067
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2029
2068
|
function initShardManager(encryptionKey) {
|
|
2030
2069
|
_encryptionKey = encryptionKey;
|
|
@@ -2063,8 +2102,7 @@ function shardExists(projectName) {
|
|
|
2063
2102
|
}
|
|
2064
2103
|
function listShards() {
|
|
2065
2104
|
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2066
|
-
|
|
2067
|
-
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2105
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2068
2106
|
}
|
|
2069
2107
|
async function ensureShardSchema(client) {
|
|
2070
2108
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -2329,7 +2367,8 @@ async function writeMemory(record) {
|
|
|
2329
2367
|
has_error: record.has_error ? 1 : 0,
|
|
2330
2368
|
raw_text: record.raw_text,
|
|
2331
2369
|
vector: record.vector,
|
|
2332
|
-
version:
|
|
2370
|
+
version: 0,
|
|
2371
|
+
// Placeholder — assigned atomically at flush time
|
|
2333
2372
|
task_id: record.task_id ?? null,
|
|
2334
2373
|
importance: record.importance ?? 5,
|
|
2335
2374
|
status: record.status ?? "active",
|
|
@@ -2346,6 +2385,12 @@ async function writeMemory(record) {
|
|
|
2346
2385
|
supersedes_id: record.supersedes_id ?? null
|
|
2347
2386
|
};
|
|
2348
2387
|
_pendingRecords.push(dbRow);
|
|
2388
|
+
const MAX_PENDING = 1e3;
|
|
2389
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
2390
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
2391
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
2392
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
2393
|
+
}
|
|
2349
2394
|
if (_flushTimer === null) {
|
|
2350
2395
|
_flushTimer = setInterval(() => {
|
|
2351
2396
|
void flushBatch();
|
|
@@ -2363,6 +2408,13 @@ async function flushBatch() {
|
|
|
2363
2408
|
_flushing = true;
|
|
2364
2409
|
try {
|
|
2365
2410
|
const batch = _pendingRecords.slice(0);
|
|
2411
|
+
const client = getClient();
|
|
2412
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
2413
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2414
|
+
for (const row of batch) {
|
|
2415
|
+
row.version = baseVersion++;
|
|
2416
|
+
}
|
|
2417
|
+
_nextVersion = baseVersion;
|
|
2366
2418
|
const buildStmt = (row) => {
|
|
2367
2419
|
const hasVector = row.vector !== null;
|
|
2368
2420
|
const taskId = row.task_id ?? null;
|
|
@@ -3309,65 +3361,72 @@ async function backfillConversations(options) {
|
|
|
3309
3361
|
process.stderr.write(`[backfill-conversations] Found ${files.length} JSONL files to process
|
|
3310
3362
|
`);
|
|
3311
3363
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3364
|
+
const BATCH_SIZE = 50;
|
|
3365
|
+
const MAX_DEDUP_SIZE = 5e4;
|
|
3366
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += BATCH_SIZE) {
|
|
3367
|
+
const batch = files.slice(batchStart, batchStart + BATCH_SIZE);
|
|
3368
|
+
for (const file of batch) {
|
|
3369
|
+
stats.filesScanned++;
|
|
3370
|
+
if (existingPaths.size < MAX_DEDUP_SIZE && existingPaths.has(file)) {
|
|
3371
|
+
stats.skippedDedup++;
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3374
|
+
const conv = await parseConversation(file);
|
|
3375
|
+
if (conv.totalMessages < MIN_MESSAGES) {
|
|
3376
|
+
stats.skippedTooShort++;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
const summary = buildSummary(conv);
|
|
3380
|
+
if (options.dryRun) {
|
|
3381
|
+
process.stdout.write(`
|
|
3326
3382
|
\u2500\u2500\u2500 ${file} \u2500\u2500\u2500
|
|
3327
3383
|
`);
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3384
|
+
process.stdout.write(`Project: ${conv.projectName} | Messages: ${conv.totalMessages}`);
|
|
3385
|
+
process.stdout.write(` | Tools: ${Object.keys(conv.toolCounts).length}`);
|
|
3386
|
+
process.stdout.write(` | Files: ${conv.filesTouched.size}
|
|
3331
3387
|
`);
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3388
|
+
const firstPrompt = conv.userMessages[0];
|
|
3389
|
+
if (firstPrompt) {
|
|
3390
|
+
process.stdout.write(`First prompt: ${firstPrompt.slice(0, 120)}
|
|
3335
3391
|
`);
|
|
3392
|
+
}
|
|
3393
|
+
stats.conversationsStored++;
|
|
3394
|
+
continue;
|
|
3395
|
+
}
|
|
3396
|
+
let vector = null;
|
|
3397
|
+
if (daemonConnected) {
|
|
3398
|
+
try {
|
|
3399
|
+
vector = await embedViaClient(summary, "low");
|
|
3400
|
+
if (!vector) stats.embedFailed++;
|
|
3401
|
+
} catch {
|
|
3402
|
+
stats.embedFailed++;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
await writeMemory({
|
|
3406
|
+
id: crypto2.randomUUID(),
|
|
3407
|
+
agent_id: conv.agentId,
|
|
3408
|
+
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3409
|
+
session_id: conv.sessionId,
|
|
3410
|
+
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3411
|
+
tool_name: TOOL_NAME,
|
|
3412
|
+
project_name: conv.projectName,
|
|
3413
|
+
has_error: conv.errorCount > 0,
|
|
3414
|
+
raw_text: summary,
|
|
3415
|
+
vector,
|
|
3416
|
+
source_path: file,
|
|
3417
|
+
source_type: "conversation"
|
|
3418
|
+
});
|
|
3419
|
+
if (existingPaths.size < MAX_DEDUP_SIZE) {
|
|
3420
|
+
existingPaths.add(file);
|
|
3336
3421
|
}
|
|
3337
3422
|
stats.conversationsStored++;
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
if (daemonConnected) {
|
|
3342
|
-
try {
|
|
3343
|
-
vector = await embedViaClient(summary, "low");
|
|
3344
|
-
if (!vector) stats.embedFailed++;
|
|
3345
|
-
} catch {
|
|
3346
|
-
stats.embedFailed++;
|
|
3347
|
-
}
|
|
3348
|
-
}
|
|
3349
|
-
await writeMemory({
|
|
3350
|
-
id: crypto2.randomUUID(),
|
|
3351
|
-
agent_id: conv.agentId,
|
|
3352
|
-
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3353
|
-
session_id: conv.sessionId,
|
|
3354
|
-
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3355
|
-
tool_name: TOOL_NAME,
|
|
3356
|
-
project_name: conv.projectName,
|
|
3357
|
-
has_error: conv.errorCount > 0,
|
|
3358
|
-
raw_text: summary,
|
|
3359
|
-
vector,
|
|
3360
|
-
source_path: file,
|
|
3361
|
-
source_type: "conversation"
|
|
3362
|
-
});
|
|
3363
|
-
existingPaths.add(file);
|
|
3364
|
-
stats.conversationsStored++;
|
|
3365
|
-
if (stats.filesScanned % 50 === 0) {
|
|
3366
|
-
process.stderr.write(
|
|
3367
|
-
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3423
|
+
if (stats.filesScanned % 50 === 0) {
|
|
3424
|
+
process.stderr.write(
|
|
3425
|
+
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3368
3426
|
`
|
|
3369
|
-
|
|
3370
|
-
|
|
3427
|
+
);
|
|
3428
|
+
await flushBatch();
|
|
3429
|
+
}
|
|
3371
3430
|
}
|
|
3372
3431
|
}
|
|
3373
3432
|
if (!options.dryRun) {
|
|
@@ -3425,6 +3484,7 @@ __export(employee_templates_exports, {
|
|
|
3425
3484
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
3426
3485
|
getSessionPrompt: () => getSessionPrompt,
|
|
3427
3486
|
getTemplate: () => getTemplate,
|
|
3487
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
3428
3488
|
personalizePrompt: () => personalizePrompt,
|
|
3429
3489
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
3430
3490
|
});
|
|
@@ -3440,6 +3500,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
3440
3500
|
function getTemplate(name) {
|
|
3441
3501
|
return TEMPLATES[name];
|
|
3442
3502
|
}
|
|
3503
|
+
function getTemplateByRole(role) {
|
|
3504
|
+
const lower = role.toLowerCase();
|
|
3505
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
3506
|
+
}
|
|
3443
3507
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
3444
3508
|
if (templateName === actualName) return prompt;
|
|
3445
3509
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4224,48 +4288,67 @@ async function downloadModel(opts) {
|
|
|
4224
4288
|
const tmpPath = destPath + ".tmp";
|
|
4225
4289
|
await mkdir5(destDir, { recursive: true });
|
|
4226
4290
|
if (existsSync9(destPath)) {
|
|
4227
|
-
const
|
|
4228
|
-
if (
|
|
4291
|
+
const hash = await fileHash(destPath);
|
|
4292
|
+
if (hash === EXPECTED_SHA256) {
|
|
4229
4293
|
return destPath;
|
|
4230
4294
|
}
|
|
4231
4295
|
}
|
|
4232
|
-
|
|
4233
|
-
const
|
|
4234
|
-
|
|
4235
|
-
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4236
|
-
}
|
|
4237
|
-
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4296
|
+
const MAX_RETRIES2 = 3;
|
|
4297
|
+
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
4298
|
+
let lastErr;
|
|
4238
4299
|
let downloaded = 0;
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
if (!
|
|
4247
|
-
|
|
4300
|
+
for (let attempt = 1; attempt <= MAX_RETRIES2; attempt++) {
|
|
4301
|
+
try {
|
|
4302
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4303
|
+
const response = await fetchFn(GGUF_URL, {
|
|
4304
|
+
redirect: "follow",
|
|
4305
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
4306
|
+
});
|
|
4307
|
+
if (!response.ok || !response.body) {
|
|
4308
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4309
|
+
}
|
|
4310
|
+
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4311
|
+
const hash = createHash("sha256");
|
|
4312
|
+
const fileStream = createWriteStream(tmpPath);
|
|
4313
|
+
const reader = response.body.getReader();
|
|
4314
|
+
try {
|
|
4315
|
+
while (true) {
|
|
4316
|
+
const { done, value } = await reader.read();
|
|
4317
|
+
if (done) break;
|
|
4318
|
+
if (!fileStream.write(value)) {
|
|
4319
|
+
await new Promise((resolve) => fileStream.once("drain", resolve));
|
|
4320
|
+
}
|
|
4321
|
+
hash.update(value);
|
|
4322
|
+
downloaded += value.byteLength;
|
|
4323
|
+
onProgress?.(downloaded, contentLength);
|
|
4324
|
+
}
|
|
4325
|
+
} finally {
|
|
4326
|
+
fileStream.end();
|
|
4327
|
+
await new Promise((resolve, reject) => {
|
|
4328
|
+
fileStream.on("finish", resolve);
|
|
4329
|
+
fileStream.on("error", reject);
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
const actualHash = hash.digest("hex");
|
|
4333
|
+
if (actualHash !== EXPECTED_SHA256) {
|
|
4334
|
+
unlinkSync3(tmpPath);
|
|
4335
|
+
throw new Error(
|
|
4336
|
+
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4337
|
+
);
|
|
4338
|
+
}
|
|
4339
|
+
renameSync3(tmpPath, destPath);
|
|
4340
|
+
return destPath;
|
|
4341
|
+
} catch (err) {
|
|
4342
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
4343
|
+
if (attempt < MAX_RETRIES2) {
|
|
4344
|
+
process.stderr.write(`
|
|
4345
|
+
Download attempt ${attempt} failed, retrying...
|
|
4346
|
+
`);
|
|
4347
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4248
4348
|
}
|
|
4249
|
-
hash.update(value);
|
|
4250
|
-
downloaded += value.byteLength;
|
|
4251
|
-
onProgress?.(downloaded, contentLength);
|
|
4252
4349
|
}
|
|
4253
|
-
} finally {
|
|
4254
|
-
fileStream.end();
|
|
4255
|
-
await new Promise((resolve, reject) => {
|
|
4256
|
-
fileStream.on("finish", resolve);
|
|
4257
|
-
fileStream.on("error", reject);
|
|
4258
|
-
});
|
|
4259
4350
|
}
|
|
4260
|
-
|
|
4261
|
-
if (actualHash !== EXPECTED_SHA256) {
|
|
4262
|
-
unlinkSync3(tmpPath);
|
|
4263
|
-
throw new Error(
|
|
4264
|
-
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4265
|
-
);
|
|
4266
|
-
}
|
|
4267
|
-
renameSync3(tmpPath, destPath);
|
|
4268
|
-
return destPath;
|
|
4351
|
+
throw lastErr;
|
|
4269
4352
|
}
|
|
4270
4353
|
async function fileHash(filePath) {
|
|
4271
4354
|
return new Promise((resolve, reject) => {
|
|
@@ -4368,6 +4451,8 @@ __export(license_exports, {
|
|
|
4368
4451
|
loadLicense: () => loadLicense,
|
|
4369
4452
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
4370
4453
|
saveLicense: () => saveLicense,
|
|
4454
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
4455
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4371
4456
|
validateLicense: () => validateLicense
|
|
4372
4457
|
});
|
|
4373
4458
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -4492,14 +4577,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
4492
4577
|
} catch {
|
|
4493
4578
|
const cached = await getCachedLicense();
|
|
4494
4579
|
if (cached) return cached;
|
|
4495
|
-
return FREE_LICENSE;
|
|
4580
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
function getCacheAgeMs() {
|
|
4584
|
+
try {
|
|
4585
|
+
const { statSync: statSync2 } = __require("fs");
|
|
4586
|
+
const s = statSync2(CACHE_PATH);
|
|
4587
|
+
return Date.now() - s.mtimeMs;
|
|
4588
|
+
} catch {
|
|
4589
|
+
return Infinity;
|
|
4496
4590
|
}
|
|
4497
4591
|
}
|
|
4498
4592
|
async function checkLicense() {
|
|
4499
4593
|
const key = loadLicense();
|
|
4500
4594
|
if (!key) return FREE_LICENSE;
|
|
4501
4595
|
const cached = await getCachedLicense();
|
|
4502
|
-
if (cached) return cached;
|
|
4596
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
4503
4597
|
const deviceId = loadDeviceId();
|
|
4504
4598
|
return validateLicense(key, deviceId);
|
|
4505
4599
|
}
|
|
@@ -4620,7 +4714,28 @@ async function assertVpsLicense(opts) {
|
|
|
4620
4714
|
`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.`
|
|
4621
4715
|
);
|
|
4622
4716
|
}
|
|
4623
|
-
|
|
4717
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
4718
|
+
if (_revalTimer) return;
|
|
4719
|
+
_revalTimer = setInterval(async () => {
|
|
4720
|
+
try {
|
|
4721
|
+
const license = await checkLicense();
|
|
4722
|
+
if (!license.valid) {
|
|
4723
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
4724
|
+
}
|
|
4725
|
+
} catch {
|
|
4726
|
+
}
|
|
4727
|
+
}, intervalMs);
|
|
4728
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
4729
|
+
_revalTimer.unref();
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
function stopLicenseRevalidation() {
|
|
4733
|
+
if (_revalTimer) {
|
|
4734
|
+
clearInterval(_revalTimer);
|
|
4735
|
+
_revalTimer = null;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
4624
4739
|
var init_license = __esm({
|
|
4625
4740
|
"src/lib/license.ts"() {
|
|
4626
4741
|
"use strict";
|
|
@@ -4650,6 +4765,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
4650
4765
|
employeeLimit: 1,
|
|
4651
4766
|
memoryLimit: 5e3
|
|
4652
4767
|
};
|
|
4768
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
4769
|
+
_revalTimer = null;
|
|
4653
4770
|
}
|
|
4654
4771
|
});
|
|
4655
4772
|
|
|
@@ -4663,7 +4780,7 @@ __export(identity_exports, {
|
|
|
4663
4780
|
updateIdentity: () => updateIdentity
|
|
4664
4781
|
});
|
|
4665
4782
|
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
4666
|
-
import { readdirSync } from "fs";
|
|
4783
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
4667
4784
|
import path12 from "path";
|
|
4668
4785
|
import { createHash as createHash2 } from "crypto";
|
|
4669
4786
|
function ensureDir() {
|
|
@@ -4745,7 +4862,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4745
4862
|
}
|
|
4746
4863
|
function listIdentities() {
|
|
4747
4864
|
ensureDir();
|
|
4748
|
-
const files =
|
|
4865
|
+
const files = readdirSync2(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
4749
4866
|
const results = [];
|
|
4750
4867
|
for (const file of files) {
|
|
4751
4868
|
const agentId = file.replace(".md", "");
|
|
@@ -4792,6 +4909,7 @@ var init_identity = __esm({
|
|
|
4792
4909
|
var identity_templates_exports = {};
|
|
4793
4910
|
__export(identity_templates_exports, {
|
|
4794
4911
|
IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
|
|
4912
|
+
PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
|
|
4795
4913
|
POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
|
|
4796
4914
|
getTemplate: () => getTemplate2,
|
|
4797
4915
|
getTemplateForTitle: () => getTemplateForTitle
|
|
@@ -4811,10 +4929,18 @@ function getTemplateForTitle(title) {
|
|
|
4811
4929
|
if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
|
|
4812
4930
|
return null;
|
|
4813
4931
|
}
|
|
4814
|
-
var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4932
|
+
var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4815
4933
|
var init_identity_templates = __esm({
|
|
4816
4934
|
"src/lib/identity-templates.ts"() {
|
|
4817
4935
|
"use strict";
|
|
4936
|
+
PLAN_MODE_COMPAT = `
|
|
4937
|
+
## Plan Mode Compatibility
|
|
4938
|
+
If tool execution is unavailable (e.g., CC plan mode), switch to planning:
|
|
4939
|
+
- Reason about the task and create a written plan
|
|
4940
|
+
- Document what tools you would call and with what parameters
|
|
4941
|
+
- Output structured text that can be acted on when tools become available
|
|
4942
|
+
Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
|
|
4943
|
+
`;
|
|
4818
4944
|
POST_WORK_CHECKLIST = `
|
|
4819
4945
|
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
4820
4946
|
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
@@ -4894,7 +5020,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
4894
5020
|
- **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
|
|
4895
5021
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
4896
5022
|
- **send_message** \u2014 direct intercom to employees
|
|
4897
|
-
|
|
5023
|
+
${PLAN_MODE_COMPAT}
|
|
4898
5024
|
## Completion Workflow
|
|
4899
5025
|
|
|
4900
5026
|
1. Read the task file and verify the deliverable matches the brief
|
|
@@ -4965,7 +5091,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
|
|
|
4965
5091
|
- **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
|
|
4966
5092
|
- **get_identity** \u2014 read any agent's identity for review context
|
|
4967
5093
|
- **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
|
|
4968
|
-
|
|
5094
|
+
${PLAN_MODE_COMPAT}
|
|
4969
5095
|
## Completion Workflow
|
|
4970
5096
|
|
|
4971
5097
|
1. Read ARCHITECTURE.md before starting work on any repo
|
|
@@ -5032,7 +5158,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
|
|
|
5032
5158
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5033
5159
|
- **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
|
|
5034
5160
|
- **get_identity** \u2014 read team identities for brand-consistent communication
|
|
5035
|
-
|
|
5161
|
+
${PLAN_MODE_COMPAT}
|
|
5036
5162
|
## Completion Workflow
|
|
5037
5163
|
|
|
5038
5164
|
1. Read the task file and understand the brief \u2014 tone, format, channel requirements
|
|
@@ -5099,7 +5225,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
5099
5225
|
- **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
|
|
5100
5226
|
- **store_memory** \u2014 report completions for org visibility
|
|
5101
5227
|
- **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
|
|
5102
|
-
|
|
5228
|
+
${PLAN_MODE_COMPAT}
|
|
5103
5229
|
## Completion Workflow
|
|
5104
5230
|
|
|
5105
5231
|
1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
|
|
@@ -5159,7 +5285,7 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
5159
5285
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5160
5286
|
- **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
|
|
5161
5287
|
- **store_memory** \u2014 report completions with production decisions for future reference
|
|
5162
|
-
|
|
5288
|
+
${PLAN_MODE_COMPAT}
|
|
5163
5289
|
## Completion Workflow
|
|
5164
5290
|
|
|
5165
5291
|
1. Read the task file \u2014 understand the brief, check budget constraints
|
|
@@ -5231,7 +5357,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
|
|
|
5231
5357
|
- **update_task** \u2014 mark tasks done with analysis results
|
|
5232
5358
|
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
5233
5359
|
- **create_task** \u2014 when a feature is worth building, spec it for the CTO
|
|
5234
|
-
|
|
5360
|
+
${PLAN_MODE_COMPAT}
|
|
5235
5361
|
## Completion Workflow
|
|
5236
5362
|
|
|
5237
5363
|
1. Read the task \u2014 understand what capability is needed
|
|
@@ -5294,7 +5420,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
5294
5420
|
- **store_behavior** \u2014 record new patterns
|
|
5295
5421
|
- **update_task** \u2014 mark reviews done with structured findings
|
|
5296
5422
|
- **create_task** \u2014 assign fixes to the CTO
|
|
5297
|
-
|
|
5423
|
+
${PLAN_MODE_COMPAT}
|
|
5298
5424
|
## Completion Workflow
|
|
5299
5425
|
|
|
5300
5426
|
1. Read the task brief and understand the audit scope
|
|
@@ -5316,10 +5442,27 @@ __export(setup_wizard_exports, {
|
|
|
5316
5442
|
validateModel: () => validateModel
|
|
5317
5443
|
});
|
|
5318
5444
|
import crypto3 from "crypto";
|
|
5319
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
5445
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
5320
5446
|
import os4 from "os";
|
|
5321
5447
|
import path13 from "path";
|
|
5322
5448
|
import { createInterface as createInterface2 } from "readline";
|
|
5449
|
+
function loadSetupState() {
|
|
5450
|
+
try {
|
|
5451
|
+
return JSON.parse(readFileSync8(SETUP_STATE_PATH, "utf8"));
|
|
5452
|
+
} catch {
|
|
5453
|
+
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
function saveSetupState(state) {
|
|
5457
|
+
mkdirSync5(path13.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
5458
|
+
writeFileSync4(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5459
|
+
}
|
|
5460
|
+
function clearSetupState() {
|
|
5461
|
+
try {
|
|
5462
|
+
unlinkSync4(SETUP_STATE_PATH);
|
|
5463
|
+
} catch {
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5323
5466
|
function ask(rl, prompt) {
|
|
5324
5467
|
return new Promise((resolve) => {
|
|
5325
5468
|
const doAsk = () => {
|
|
@@ -5359,88 +5502,122 @@ async function runSetupWizard(opts = {}) {
|
|
|
5359
5502
|
rl.close();
|
|
5360
5503
|
return;
|
|
5361
5504
|
}
|
|
5505
|
+
const state = loadSetupState();
|
|
5506
|
+
if (state.completedSteps.length > 0) {
|
|
5507
|
+
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5508
|
+
}
|
|
5362
5509
|
if (existsSync12(LEGACY_LANCE_PATH)) {
|
|
5363
5510
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5364
5511
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5365
5512
|
log(" The old directory will not be modified or deleted.");
|
|
5366
5513
|
log("");
|
|
5367
5514
|
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5515
|
+
if (!state.completedSteps.includes(1)) {
|
|
5516
|
+
const existingKey = await getMasterKey();
|
|
5517
|
+
if (existingKey) {
|
|
5518
|
+
log("Encryption key already exists \u2014 skipping generation.");
|
|
5519
|
+
} else {
|
|
5520
|
+
log("Generating 256-bit encryption key...");
|
|
5521
|
+
const key = crypto3.randomBytes(32);
|
|
5522
|
+
await setMasterKey(key);
|
|
5523
|
+
log("Encryption key generated and stored securely.");
|
|
5524
|
+
}
|
|
5525
|
+
state.completedSteps.push(1);
|
|
5526
|
+
saveSetupState(state);
|
|
5371
5527
|
} else {
|
|
5372
|
-
log("
|
|
5373
|
-
const key = crypto3.randomBytes(32);
|
|
5374
|
-
await setMasterKey(key);
|
|
5375
|
-
log("Encryption key generated and stored securely.");
|
|
5528
|
+
log("Step 1 already complete \u2014 skipping.");
|
|
5376
5529
|
}
|
|
5377
5530
|
log("");
|
|
5378
|
-
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5379
|
-
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5380
|
-
log("only your encryption key can decrypt it.");
|
|
5381
5531
|
let cloudConfig;
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5532
|
+
if (!state.completedSteps.includes(2)) {
|
|
5533
|
+
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5534
|
+
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5535
|
+
log("only your encryption key can decrypt it.");
|
|
5536
|
+
try {
|
|
5537
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5538
|
+
const deviceId = loadDeviceId2();
|
|
5539
|
+
const res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
5540
|
+
method: "POST",
|
|
5541
|
+
headers: { "Content-Type": "application/json" },
|
|
5542
|
+
body: JSON.stringify({ deviceId }),
|
|
5543
|
+
signal: AbortSignal.timeout(1e4)
|
|
5544
|
+
});
|
|
5545
|
+
if (res.ok) {
|
|
5546
|
+
const data = await res.json();
|
|
5547
|
+
if (data.apiKey) {
|
|
5548
|
+
cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
|
|
5549
|
+
const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5550
|
+
saveLicense3(data.apiKey);
|
|
5551
|
+
mirrorLicenseKey3(data.apiKey);
|
|
5552
|
+
log("Cloud sync activated automatically.");
|
|
5553
|
+
}
|
|
5399
5554
|
}
|
|
5555
|
+
} catch {
|
|
5556
|
+
log("Cloud sync will activate when online.");
|
|
5400
5557
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5558
|
+
state.completedSteps.push(2);
|
|
5559
|
+
saveSetupState(state);
|
|
5560
|
+
} else {
|
|
5561
|
+
log("Step 2 already complete \u2014 skipping.");
|
|
5403
5562
|
}
|
|
5404
5563
|
log("");
|
|
5405
|
-
if (!
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5564
|
+
if (!state.completedSteps.includes(3)) {
|
|
5565
|
+
if (!skipModel) {
|
|
5566
|
+
log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
|
|
5567
|
+
log("");
|
|
5568
|
+
await downloadModel({
|
|
5569
|
+
destDir: MODELS_DIR,
|
|
5570
|
+
onProgress: (downloaded, total) => {
|
|
5571
|
+
const pct = (downloaded / total * 100).toFixed(1);
|
|
5572
|
+
const dlMB = (downloaded / 1e6).toFixed(0);
|
|
5573
|
+
const totalMB = (total / 1e6).toFixed(0);
|
|
5574
|
+
process.stderr.write(`\rDownloading model: ${pct}% (${dlMB}/${totalMB} MB)`);
|
|
5575
|
+
}
|
|
5576
|
+
});
|
|
5577
|
+
process.stderr.write("\n");
|
|
5578
|
+
log("Model downloaded and verified.");
|
|
5579
|
+
}
|
|
5580
|
+
state.completedSteps.push(3);
|
|
5581
|
+
saveSetupState(state);
|
|
5582
|
+
} else {
|
|
5583
|
+
log("Step 3 already complete \u2014 skipping.");
|
|
5419
5584
|
}
|
|
5420
|
-
if (!
|
|
5421
|
-
|
|
5585
|
+
if (!state.completedSteps.includes(4)) {
|
|
5586
|
+
if (!skipModel && !skipModelValidation) {
|
|
5587
|
+
await validateModel(log);
|
|
5588
|
+
}
|
|
5589
|
+
state.completedSteps.push(4);
|
|
5590
|
+
saveSetupState(state);
|
|
5591
|
+
} else {
|
|
5592
|
+
log("Step 4 already complete \u2014 skipping.");
|
|
5422
5593
|
}
|
|
5423
5594
|
const config = await loadConfig();
|
|
5424
|
-
if (
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
const claudeJsonPath = path13.join(os4.homedir(), ".claude.json");
|
|
5431
|
-
let claudeJson = {};
|
|
5595
|
+
if (!state.completedSteps.includes(5)) {
|
|
5596
|
+
if (cloudConfig) {
|
|
5597
|
+
config.cloud = cloudConfig;
|
|
5598
|
+
}
|
|
5599
|
+
await saveConfig(config);
|
|
5600
|
+
log("");
|
|
5432
5601
|
try {
|
|
5433
|
-
|
|
5602
|
+
const claudeJsonPath = path13.join(os4.homedir(), ".claude.json");
|
|
5603
|
+
let claudeJson = {};
|
|
5604
|
+
try {
|
|
5605
|
+
claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
|
|
5606
|
+
} catch {
|
|
5607
|
+
}
|
|
5608
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
5609
|
+
const projects = claudeJson.projects;
|
|
5610
|
+
for (const dir of [process.cwd(), os4.homedir()]) {
|
|
5611
|
+
if (!projects[dir]) projects[dir] = {};
|
|
5612
|
+
projects[dir].hasTrustDialogAccepted = true;
|
|
5613
|
+
}
|
|
5614
|
+
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5434
5615
|
} catch {
|
|
5435
5616
|
}
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
projects[dir].hasTrustDialogAccepted = true;
|
|
5441
|
-
}
|
|
5442
|
-
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5443
|
-
} catch {
|
|
5617
|
+
state.completedSteps.push(5);
|
|
5618
|
+
saveSetupState(state);
|
|
5619
|
+
} else {
|
|
5620
|
+
log("Step 5 already complete \u2014 skipping.");
|
|
5444
5621
|
}
|
|
5445
5622
|
const {
|
|
5446
5623
|
loadEmployees: loadEmployees2,
|
|
@@ -5449,7 +5626,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5449
5626
|
registerBinSymlinks: registerBinSymlinks2,
|
|
5450
5627
|
EMPLOYEES_PATH: EMPLOYEES_PATH2
|
|
5451
5628
|
} = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
5452
|
-
const {
|
|
5629
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5453
5630
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
5454
5631
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
5455
5632
|
const {
|
|
@@ -5459,152 +5636,178 @@ async function runSetupWizard(opts = {}) {
|
|
|
5459
5636
|
validateLicense: validateLicense2
|
|
5460
5637
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5461
5638
|
const createdEmployees = [];
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5466
|
-
log("You talk to them. They coordinate everyone else.");
|
|
5467
|
-
log("");
|
|
5468
|
-
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5469
|
-
const cooName = (cooNameInput || "exe").toLowerCase();
|
|
5470
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5471
|
-
if (!employees.some((e) => e.name === cooName)) {
|
|
5472
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5473
|
-
const cooEmployee = {
|
|
5474
|
-
name: cooName,
|
|
5475
|
-
role: "COO",
|
|
5476
|
-
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5477
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5478
|
-
templateName: "exe",
|
|
5479
|
-
templateVersion: 1
|
|
5480
|
-
};
|
|
5481
|
-
employees = addEmployee2(employees, cooEmployee);
|
|
5482
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5483
|
-
}
|
|
5484
|
-
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5485
|
-
if (cooIdentityContent) {
|
|
5486
|
-
const cooIdPath = identityPath2(cooName);
|
|
5487
|
-
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5488
|
-
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5489
|
-
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5490
|
-
}
|
|
5491
|
-
registerBinSymlinks2(cooName);
|
|
5492
|
-
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5493
|
-
log("");
|
|
5494
|
-
log("=== Meet Your Specialists ===");
|
|
5495
|
-
log("");
|
|
5496
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5497
|
-
log("");
|
|
5498
|
-
log(" CTO (default: yoshi)");
|
|
5499
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5500
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5501
|
-
log("");
|
|
5502
|
-
log(" CMO (default: mari)");
|
|
5503
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5504
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5505
|
-
log("");
|
|
5506
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
5507
|
-
log("");
|
|
5508
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5509
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5510
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5511
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5512
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
5513
|
-
log("");
|
|
5514
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5515
|
-
log("instead of headcount.");
|
|
5516
|
-
log("");
|
|
5517
|
-
await ask(rl, "Press Enter to continue. ");
|
|
5518
|
-
let license;
|
|
5519
|
-
try {
|
|
5520
|
-
license = await checkLicense2();
|
|
5521
|
-
} catch {
|
|
5522
|
-
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5523
|
-
}
|
|
5524
|
-
if (license.plan === "free") {
|
|
5525
|
-
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5639
|
+
let cooName = "exe";
|
|
5640
|
+
if (!state.completedSteps.includes(6)) {
|
|
5641
|
+
log("=== Your Team ===");
|
|
5526
5642
|
log("");
|
|
5527
|
-
log("
|
|
5528
|
-
log("
|
|
5529
|
-
log("
|
|
5643
|
+
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
5644
|
+
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5645
|
+
log("You talk to them. They coordinate everyone else.");
|
|
5530
5646
|
log("");
|
|
5531
|
-
const
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
}
|
|
5647
|
+
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5648
|
+
cooName = (cooNameInput || "exe").toLowerCase();
|
|
5649
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5650
|
+
if (!employees.some((e) => e.name === cooName)) {
|
|
5651
|
+
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5652
|
+
const cooEmployee = {
|
|
5653
|
+
name: cooName,
|
|
5654
|
+
role: "COO",
|
|
5655
|
+
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5656
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5657
|
+
templateName: "exe",
|
|
5658
|
+
templateVersion: 1
|
|
5659
|
+
};
|
|
5660
|
+
employees = addEmployee2(employees, cooEmployee);
|
|
5661
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5662
|
+
}
|
|
5663
|
+
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5664
|
+
if (cooIdentityContent) {
|
|
5665
|
+
const cooIdPath = identityPath2(cooName);
|
|
5666
|
+
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5667
|
+
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5668
|
+
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5669
|
+
}
|
|
5670
|
+
registerBinSymlinks2(cooName);
|
|
5671
|
+
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5672
|
+
state.completedSteps.push(6);
|
|
5673
|
+
saveSetupState(state);
|
|
5674
|
+
} else {
|
|
5675
|
+
log("Step 6 already complete \u2014 skipping.");
|
|
5676
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5677
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
5678
|
+
if (existingCoo) cooName = existingCoo.name;
|
|
5547
5679
|
}
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
log(
|
|
5680
|
+
log("");
|
|
5681
|
+
if (!state.completedSteps.includes(7)) {
|
|
5682
|
+
log("=== Meet Your Specialists ===");
|
|
5551
5683
|
log("");
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5684
|
+
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5685
|
+
log("");
|
|
5686
|
+
log(" CTO (default: yoshi)");
|
|
5687
|
+
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5688
|
+
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5689
|
+
log("");
|
|
5690
|
+
log(" CMO (default: mari)");
|
|
5691
|
+
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5692
|
+
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5693
|
+
log("");
|
|
5694
|
+
log("Why separate roles instead of one AI that does everything?");
|
|
5695
|
+
log("");
|
|
5696
|
+
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5697
|
+
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5698
|
+
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5699
|
+
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5700
|
+
log("Your COO connects them so nothing falls through the cracks.");
|
|
5701
|
+
log("");
|
|
5702
|
+
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5703
|
+
log("instead of headcount.");
|
|
5704
|
+
log("");
|
|
5705
|
+
await ask(rl, "Press Enter to continue. ");
|
|
5706
|
+
state.completedSteps.push(7);
|
|
5707
|
+
saveSetupState(state);
|
|
5708
|
+
} else {
|
|
5709
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
5710
|
+
}
|
|
5711
|
+
if (!state.completedSteps.includes(8)) {
|
|
5712
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5713
|
+
let license;
|
|
5714
|
+
try {
|
|
5715
|
+
license = await checkLicense2();
|
|
5716
|
+
} catch {
|
|
5717
|
+
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5718
|
+
}
|
|
5719
|
+
if (license.plan === "free") {
|
|
5720
|
+
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5721
|
+
log("");
|
|
5722
|
+
log("The CTO and CMO are available on Solopreneur ($97/mo) \u2014 full engineering");
|
|
5723
|
+
log("and marketing team, unlimited tasks, priority support.");
|
|
5724
|
+
log("Get your key at https://askexe.com, then paste it here.");
|
|
5725
|
+
log("");
|
|
5726
|
+
const licenseInput = await ask(rl, "License key (or press Enter to skip): ");
|
|
5727
|
+
if (licenseInput.startsWith("exe_sk_")) {
|
|
5728
|
+
saveLicense2(licenseInput);
|
|
5729
|
+
mirrorLicenseKey2(licenseInput);
|
|
5730
|
+
try {
|
|
5731
|
+
license = await validateLicense2(licenseInput);
|
|
5732
|
+
} catch {
|
|
5733
|
+
log("Couldn't reach the license server \u2014 your key has been saved.");
|
|
5734
|
+
log("Run exe-os --activate <key> anytime to finish activation.");
|
|
5735
|
+
}
|
|
5736
|
+
} else if (!licenseInput) {
|
|
5737
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5738
|
+
} else {
|
|
5739
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
5740
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
if (license.plan !== "free") {
|
|
5744
|
+
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
5745
|
+
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
5746
|
+
log("");
|
|
5747
|
+
const createCto = await ask(rl, "Create your CTO? (Y/n): ");
|
|
5748
|
+
if (createCto.toLowerCase() !== "n") {
|
|
5749
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
5750
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
5751
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
5752
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
5753
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
5754
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5755
|
+
const ctoEmployee = {
|
|
5756
|
+
name: ctoName,
|
|
5757
|
+
role: "CTO",
|
|
5758
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
5759
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5760
|
+
};
|
|
5761
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
5762
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5763
|
+
}
|
|
5764
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
5765
|
+
if (ctoIdentityContent) {
|
|
5766
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
5767
|
+
mkdirSync5(path13.dirname(ctoIdPath), { recursive: true });
|
|
5768
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
5769
|
+
writeFileSync4(ctoIdPath, replaced, "utf-8");
|
|
5770
|
+
}
|
|
5771
|
+
registerBinSymlinks2(ctoName);
|
|
5772
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
5773
|
+
log(`Created ${ctoName} (CTO)`);
|
|
5774
|
+
}
|
|
5775
|
+
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
5776
|
+
if (createCmo.toLowerCase() !== "n") {
|
|
5777
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
5778
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
5779
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
5780
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
5781
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
5782
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5783
|
+
const cmoEmployee = {
|
|
5784
|
+
name: cmoName,
|
|
5785
|
+
role: "CMO",
|
|
5786
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
5787
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5788
|
+
};
|
|
5789
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
5790
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5791
|
+
}
|
|
5792
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
5793
|
+
if (cmoIdentityContent) {
|
|
5794
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
5795
|
+
mkdirSync5(path13.dirname(cmoIdPath), { recursive: true });
|
|
5796
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
5797
|
+
writeFileSync4(cmoIdPath, replaced, "utf-8");
|
|
5798
|
+
}
|
|
5799
|
+
registerBinSymlinks2(cmoName);
|
|
5800
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5801
|
+
log(`Created ${cmoName} (CMO)`);
|
|
5601
5802
|
}
|
|
5602
|
-
|
|
5603
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5604
|
-
log(`Created ${cmoName} (CMO)`);
|
|
5803
|
+
log("");
|
|
5605
5804
|
}
|
|
5606
|
-
|
|
5805
|
+
state.completedSteps.push(8);
|
|
5806
|
+
saveSetupState(state);
|
|
5807
|
+
} else {
|
|
5808
|
+
log("Step 8 already complete \u2014 skipping.");
|
|
5607
5809
|
}
|
|
5810
|
+
clearSetupState();
|
|
5608
5811
|
log("=== Two Ways to Work ===");
|
|
5609
5812
|
log("");
|
|
5610
5813
|
log(" 1. Claude Code mode");
|
|
@@ -5641,12 +5844,14 @@ async function runSetupWizard(opts = {}) {
|
|
|
5641
5844
|
rl.close();
|
|
5642
5845
|
}
|
|
5643
5846
|
}
|
|
5847
|
+
var SETUP_STATE_PATH;
|
|
5644
5848
|
var init_setup_wizard = __esm({
|
|
5645
5849
|
"src/lib/setup-wizard.ts"() {
|
|
5646
5850
|
"use strict";
|
|
5647
5851
|
init_config();
|
|
5648
5852
|
init_keychain();
|
|
5649
5853
|
init_model_downloader();
|
|
5854
|
+
SETUP_STATE_PATH = path13.join(os4.homedir(), ".exe-os", "setup-state.json");
|
|
5650
5855
|
}
|
|
5651
5856
|
});
|
|
5652
5857
|
|
|
@@ -16316,8 +16521,8 @@ import path23 from "path";
|
|
|
16316
16521
|
import os8 from "os";
|
|
16317
16522
|
import {
|
|
16318
16523
|
readFileSync as readFileSync13,
|
|
16319
|
-
readdirSync as
|
|
16320
|
-
unlinkSync as
|
|
16524
|
+
readdirSync as readdirSync3,
|
|
16525
|
+
unlinkSync as unlinkSync5,
|
|
16321
16526
|
existsSync as existsSync17,
|
|
16322
16527
|
rmdirSync
|
|
16323
16528
|
} from "fs";
|
|
@@ -16799,7 +17004,7 @@ var init_tasks_crud = __esm({
|
|
|
16799
17004
|
|
|
16800
17005
|
// src/lib/tasks-review.ts
|
|
16801
17006
|
import path25 from "path";
|
|
16802
|
-
import { existsSync as existsSync19, readdirSync as
|
|
17007
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, unlinkSync as unlinkSync6 } from "fs";
|
|
16803
17008
|
async function countPendingReviews() {
|
|
16804
17009
|
const client = getClient();
|
|
16805
17010
|
const result = await client.execute({
|
|
@@ -16921,9 +17126,9 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
16921
17126
|
try {
|
|
16922
17127
|
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
16923
17128
|
if (existsSync19(cacheDir)) {
|
|
16924
|
-
for (const f of
|
|
17129
|
+
for (const f of readdirSync4(cacheDir)) {
|
|
16925
17130
|
if (f.startsWith("review-notified-")) {
|
|
16926
|
-
|
|
17131
|
+
unlinkSync6(path25.join(cacheDir, f));
|
|
16927
17132
|
}
|
|
16928
17133
|
}
|
|
16929
17134
|
}
|
|
@@ -17524,7 +17729,7 @@ __export(tasks_exports, {
|
|
|
17524
17729
|
writeCheckpoint: () => writeCheckpoint
|
|
17525
17730
|
});
|
|
17526
17731
|
import path28 from "path";
|
|
17527
|
-
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as
|
|
17732
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
17528
17733
|
async function createTask(input) {
|
|
17529
17734
|
const result = await createTaskCore(input);
|
|
17530
17735
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -17550,7 +17755,7 @@ async function updateTask(input) {
|
|
|
17550
17755
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
17551
17756
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
17552
17757
|
try {
|
|
17553
|
-
|
|
17758
|
+
unlinkSync7(cachePath);
|
|
17554
17759
|
} catch {
|
|
17555
17760
|
}
|
|
17556
17761
|
}
|
|
@@ -17943,6 +18148,7 @@ var init_capacity_monitor = __esm({
|
|
|
17943
18148
|
// src/lib/tmux-routing.ts
|
|
17944
18149
|
var tmux_routing_exports = {};
|
|
17945
18150
|
__export(tmux_routing_exports, {
|
|
18151
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
17946
18152
|
employeeSessionName: () => employeeSessionName,
|
|
17947
18153
|
ensureEmployee: () => ensureEmployee,
|
|
17948
18154
|
extractRootExe: () => extractRootExe,
|
|
@@ -17957,6 +18163,7 @@ __export(tmux_routing_exports, {
|
|
|
17957
18163
|
notifyParentExe: () => notifyParentExe,
|
|
17958
18164
|
parseParentExe: () => parseParentExe,
|
|
17959
18165
|
registerParentExe: () => registerParentExe,
|
|
18166
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
17960
18167
|
resolveExeSession: () => resolveExeSession,
|
|
17961
18168
|
sendIntercom: () => sendIntercom,
|
|
17962
18169
|
spawnEmployee: () => spawnEmployee,
|
|
@@ -17967,6 +18174,42 @@ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirS
|
|
|
17967
18174
|
import path29 from "path";
|
|
17968
18175
|
import os9 from "os";
|
|
17969
18176
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
18177
|
+
import { unlinkSync as unlinkSync8 } from "fs";
|
|
18178
|
+
function spawnLockPath(sessionName) {
|
|
18179
|
+
return path29.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
18180
|
+
}
|
|
18181
|
+
function isProcessAlive(pid) {
|
|
18182
|
+
try {
|
|
18183
|
+
process.kill(pid, 0);
|
|
18184
|
+
return true;
|
|
18185
|
+
} catch {
|
|
18186
|
+
return false;
|
|
18187
|
+
}
|
|
18188
|
+
}
|
|
18189
|
+
function acquireSpawnLock2(sessionName) {
|
|
18190
|
+
if (!existsSync20(SPAWN_LOCK_DIR)) {
|
|
18191
|
+
mkdirSync9(SPAWN_LOCK_DIR, { recursive: true });
|
|
18192
|
+
}
|
|
18193
|
+
const lockFile = spawnLockPath(sessionName);
|
|
18194
|
+
if (existsSync20(lockFile)) {
|
|
18195
|
+
try {
|
|
18196
|
+
const lock = JSON.parse(readFileSync15(lockFile, "utf8"));
|
|
18197
|
+
const age = Date.now() - lock.timestamp;
|
|
18198
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
18199
|
+
return false;
|
|
18200
|
+
}
|
|
18201
|
+
} catch {
|
|
18202
|
+
}
|
|
18203
|
+
}
|
|
18204
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
18205
|
+
return true;
|
|
18206
|
+
}
|
|
18207
|
+
function releaseSpawnLock2(sessionName) {
|
|
18208
|
+
try {
|
|
18209
|
+
unlinkSync8(spawnLockPath(sessionName));
|
|
18210
|
+
} catch {
|
|
18211
|
+
}
|
|
18212
|
+
}
|
|
17970
18213
|
function resolveBehaviorsExporterScript() {
|
|
17971
18214
|
try {
|
|
17972
18215
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
@@ -18065,10 +18308,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
18065
18308
|
}
|
|
18066
18309
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
18067
18310
|
const base = employeeSessionName(employeeName, exeSession);
|
|
18068
|
-
if (!isAlive(base)) return 0;
|
|
18311
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
18069
18312
|
for (let i = 2; i <= maxInstances; i++) {
|
|
18070
18313
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
18071
|
-
if (!isAlive(candidate)) return i;
|
|
18314
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
18072
18315
|
}
|
|
18073
18316
|
return null;
|
|
18074
18317
|
}
|
|
@@ -18432,6 +18675,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18432
18675
|
command: spawnCommand
|
|
18433
18676
|
});
|
|
18434
18677
|
if (spawnResult.error) {
|
|
18678
|
+
releaseSpawnLock2(sessionName);
|
|
18435
18679
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
18436
18680
|
}
|
|
18437
18681
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -18469,6 +18713,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18469
18713
|
}
|
|
18470
18714
|
}
|
|
18471
18715
|
if (!booted) {
|
|
18716
|
+
releaseSpawnLock2(sessionName);
|
|
18472
18717
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
18473
18718
|
}
|
|
18474
18719
|
if (!useExeAgent) {
|
|
@@ -18485,9 +18730,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
18485
18730
|
pid: 0,
|
|
18486
18731
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18487
18732
|
});
|
|
18733
|
+
releaseSpawnLock2(sessionName);
|
|
18488
18734
|
return { sessionName };
|
|
18489
18735
|
}
|
|
18490
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
18736
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
18491
18737
|
var init_tmux_routing = __esm({
|
|
18492
18738
|
"src/lib/tmux-routing.ts"() {
|
|
18493
18739
|
"use strict";
|
|
@@ -18499,6 +18745,7 @@ var init_tmux_routing = __esm({
|
|
|
18499
18745
|
init_provider_table();
|
|
18500
18746
|
init_intercom_queue();
|
|
18501
18747
|
init_plan_limits();
|
|
18748
|
+
SPAWN_LOCK_DIR = path29.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
18502
18749
|
SESSION_CACHE = path29.join(os9.homedir(), ".exe-os", "session-cache");
|
|
18503
18750
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
18504
18751
|
VERIFY_PANE_LINES = 200;
|
|
@@ -21747,7 +21994,7 @@ var init_update = __esm({
|
|
|
21747
21994
|
});
|
|
21748
21995
|
|
|
21749
21996
|
// src/bin/cli.ts
|
|
21750
|
-
import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
|
|
21997
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as readdirSync5, rmSync } from "fs";
|
|
21751
21998
|
import path31 from "path";
|
|
21752
21999
|
import os10 from "os";
|
|
21753
22000
|
var args = process.argv.slice(2);
|
|
@@ -21779,7 +22026,7 @@ if (args.includes("--global")) {
|
|
|
21779
22026
|
await runClaudeCheck();
|
|
21780
22027
|
break;
|
|
21781
22028
|
case "uninstall":
|
|
21782
|
-
await runClaudeUninstall();
|
|
22029
|
+
await runClaudeUninstall(args.slice(2));
|
|
21783
22030
|
break;
|
|
21784
22031
|
default:
|
|
21785
22032
|
await runClaudeInstall();
|
|
@@ -21827,7 +22074,14 @@ async function runClaudeCheck() {
|
|
|
21827
22074
|
const claudeJsonPath = path31.join(os10.homedir(), ".claude.json");
|
|
21828
22075
|
let ok = true;
|
|
21829
22076
|
if (existsSync21(settingsPath)) {
|
|
21830
|
-
|
|
22077
|
+
let settings;
|
|
22078
|
+
try {
|
|
22079
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22080
|
+
} catch {
|
|
22081
|
+
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
22082
|
+
ok = false;
|
|
22083
|
+
settings = {};
|
|
22084
|
+
}
|
|
21831
22085
|
const hasHooks = settings.hooks && Object.keys(settings.hooks).some((k) => {
|
|
21832
22086
|
const groups = settings.hooks[k];
|
|
21833
22087
|
return Array.isArray(groups) && groups.some((g) => {
|
|
@@ -21849,7 +22103,14 @@ async function runClaudeCheck() {
|
|
|
21849
22103
|
ok = false;
|
|
21850
22104
|
}
|
|
21851
22105
|
if (existsSync21(claudeJsonPath)) {
|
|
21852
|
-
|
|
22106
|
+
let claudeJson;
|
|
22107
|
+
try {
|
|
22108
|
+
claudeJson = JSON.parse(readFileSync17(claudeJsonPath, "utf8"));
|
|
22109
|
+
} catch {
|
|
22110
|
+
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
22111
|
+
ok = false;
|
|
22112
|
+
claudeJson = {};
|
|
22113
|
+
}
|
|
21853
22114
|
const hasMcp = claudeJson.mcpServers && (claudeJson.mcpServers["exe-mem"] || claudeJson.mcpServers["exe-os"]);
|
|
21854
22115
|
if (hasMcp) {
|
|
21855
22116
|
console.log("\x1B[32m\u2713\x1B[0m MCP server configured in claude.json");
|
|
@@ -21863,11 +22124,11 @@ async function runClaudeCheck() {
|
|
|
21863
22124
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
21864
22125
|
ok = false;
|
|
21865
22126
|
}
|
|
21866
|
-
const
|
|
21867
|
-
if (existsSync21(
|
|
21868
|
-
console.log("\x1B[32m\u2713\x1B[0m Slash
|
|
22127
|
+
const skillsDir = path31.join(claudeDir, "skills");
|
|
22128
|
+
if (existsSync21(skillsDir)) {
|
|
22129
|
+
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
21869
22130
|
} else {
|
|
21870
|
-
console.log("\x1B[31m\u2717\x1B[0m Slash
|
|
22131
|
+
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
21871
22132
|
ok = false;
|
|
21872
22133
|
}
|
|
21873
22134
|
if (!ok) {
|
|
@@ -21877,13 +22138,29 @@ async function runClaudeCheck() {
|
|
|
21877
22138
|
console.log("\nAll checks passed.");
|
|
21878
22139
|
}
|
|
21879
22140
|
}
|
|
21880
|
-
async function runClaudeUninstall() {
|
|
21881
|
-
const
|
|
22141
|
+
async function runClaudeUninstall(flags = []) {
|
|
22142
|
+
const dryRun = flags.includes("--dry-run");
|
|
22143
|
+
const purge = flags.includes("--purge");
|
|
22144
|
+
const homeDir = os10.homedir();
|
|
22145
|
+
const claudeDir = path31.join(homeDir, ".claude");
|
|
21882
22146
|
const settingsPath = path31.join(claudeDir, "settings.json");
|
|
21883
|
-
const claudeJsonPath = path31.join(
|
|
22147
|
+
const claudeJsonPath = path31.join(homeDir, ".claude.json");
|
|
22148
|
+
const exeOsDir = path31.join(homeDir, ".exe-os");
|
|
21884
22149
|
let removed = 0;
|
|
22150
|
+
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
22151
|
+
let settings = {};
|
|
21885
22152
|
if (existsSync21(settingsPath)) {
|
|
21886
|
-
|
|
22153
|
+
try {
|
|
22154
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22155
|
+
} catch {
|
|
22156
|
+
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
22157
|
+
if (purge) {
|
|
22158
|
+
console.error("Skipping settings cleanup (--purge).");
|
|
22159
|
+
} else {
|
|
22160
|
+
console.error("Try running: exe-os claude install");
|
|
22161
|
+
process.exit(1);
|
|
22162
|
+
}
|
|
22163
|
+
}
|
|
21887
22164
|
if (settings.hooks) {
|
|
21888
22165
|
for (const key of Object.keys(settings.hooks)) {
|
|
21889
22166
|
const groups = settings.hooks[key];
|
|
@@ -21903,9 +22180,19 @@ async function runClaudeUninstall() {
|
|
|
21903
22180
|
delete settings.hooks[key];
|
|
21904
22181
|
}
|
|
21905
22182
|
}
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
22183
|
+
let permCount = 0;
|
|
22184
|
+
if (Array.isArray(settings.permissions?.allow)) {
|
|
22185
|
+
const before = settings.permissions.allow.length;
|
|
22186
|
+
settings.permissions.allow = settings.permissions.allow.filter(
|
|
22187
|
+
(p) => !p.startsWith("mcp__exe-mem__") && !p.startsWith("mcp__exe-os__")
|
|
22188
|
+
);
|
|
22189
|
+
permCount = before - settings.permissions.allow.length;
|
|
22190
|
+
}
|
|
22191
|
+
if (!dryRun) {
|
|
22192
|
+
writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
22193
|
+
}
|
|
22194
|
+
log("\u2713 Removed exe-os hooks from settings.json");
|
|
22195
|
+
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
21909
22196
|
removed++;
|
|
21910
22197
|
}
|
|
21911
22198
|
}
|
|
@@ -21915,25 +22202,154 @@ async function runClaudeUninstall() {
|
|
|
21915
22202
|
let removedMcp = false;
|
|
21916
22203
|
for (const key of ["exe-mem", "exe-os"]) {
|
|
21917
22204
|
if (claudeJson.mcpServers[key]) {
|
|
21918
|
-
delete claudeJson.mcpServers[key];
|
|
22205
|
+
if (!dryRun) delete claudeJson.mcpServers[key];
|
|
21919
22206
|
removedMcp = true;
|
|
21920
22207
|
}
|
|
21921
22208
|
}
|
|
21922
22209
|
if (removedMcp) {
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21927
|
-
);
|
|
21928
|
-
console.log("Removed exe-os MCP server from claude.json");
|
|
22210
|
+
if (!dryRun) {
|
|
22211
|
+
writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
22212
|
+
}
|
|
22213
|
+
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
21929
22214
|
removed++;
|
|
21930
22215
|
}
|
|
21931
22216
|
}
|
|
21932
22217
|
}
|
|
22218
|
+
const skillsDir = path31.join(claudeDir, "skills");
|
|
22219
|
+
if (existsSync21(skillsDir)) {
|
|
22220
|
+
let skillCount = 0;
|
|
22221
|
+
try {
|
|
22222
|
+
const entries = readdirSync5(skillsDir);
|
|
22223
|
+
for (const entry of entries) {
|
|
22224
|
+
if (entry.startsWith("exe")) {
|
|
22225
|
+
const fullPath = path31.join(skillsDir, entry);
|
|
22226
|
+
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
22227
|
+
skillCount++;
|
|
22228
|
+
}
|
|
22229
|
+
}
|
|
22230
|
+
} catch {
|
|
22231
|
+
}
|
|
22232
|
+
if (skillCount > 0) {
|
|
22233
|
+
log(`\u2713 Removed ${skillCount} skill directories`);
|
|
22234
|
+
removed++;
|
|
22235
|
+
}
|
|
22236
|
+
}
|
|
22237
|
+
const claudeMdPath = path31.join(claudeDir, "CLAUDE.md");
|
|
22238
|
+
if (existsSync21(claudeMdPath)) {
|
|
22239
|
+
const content = readFileSync17(claudeMdPath, "utf8");
|
|
22240
|
+
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
22241
|
+
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
22242
|
+
const startIdx = content.indexOf(startMarker);
|
|
22243
|
+
const endIdx = content.indexOf(endMarker);
|
|
22244
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
22245
|
+
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
22246
|
+
if (!dryRun) writeFileSync9(claudeMdPath, cleaned);
|
|
22247
|
+
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
22248
|
+
removed++;
|
|
22249
|
+
}
|
|
22250
|
+
}
|
|
22251
|
+
const agentsDir = path31.join(claudeDir, "agents");
|
|
22252
|
+
if (existsSync21(agentsDir)) {
|
|
22253
|
+
let agentCount = 0;
|
|
22254
|
+
try {
|
|
22255
|
+
const entries = readdirSync5(agentsDir).filter((f) => f.endsWith(".md"));
|
|
22256
|
+
let knownNames = /* @__PURE__ */ new Set();
|
|
22257
|
+
const rosterPath = path31.join(exeOsDir, "exe-employees.json");
|
|
22258
|
+
if (existsSync21(rosterPath)) {
|
|
22259
|
+
try {
|
|
22260
|
+
const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
|
|
22261
|
+
knownNames = new Set(roster.map((e) => e.name));
|
|
22262
|
+
} catch {
|
|
22263
|
+
}
|
|
22264
|
+
}
|
|
22265
|
+
for (const entry of entries) {
|
|
22266
|
+
const name = entry.replace(/\.md$/, "");
|
|
22267
|
+
if (knownNames.has(name)) {
|
|
22268
|
+
if (!dryRun) rmSync(path31.join(agentsDir, entry), { force: true });
|
|
22269
|
+
agentCount++;
|
|
22270
|
+
}
|
|
22271
|
+
}
|
|
22272
|
+
} catch {
|
|
22273
|
+
}
|
|
22274
|
+
if (agentCount > 0) {
|
|
22275
|
+
log(`\u2713 Removed ${agentCount} agent identity files`);
|
|
22276
|
+
removed++;
|
|
22277
|
+
}
|
|
22278
|
+
}
|
|
22279
|
+
const projectsDir = path31.join(claudeDir, "projects");
|
|
22280
|
+
if (existsSync21(projectsDir)) {
|
|
22281
|
+
let projectCount = 0;
|
|
22282
|
+
try {
|
|
22283
|
+
const projects = readdirSync5(projectsDir);
|
|
22284
|
+
for (const proj of projects) {
|
|
22285
|
+
const projSettings = path31.join(projectsDir, proj, "settings.json");
|
|
22286
|
+
if (!existsSync21(projSettings)) continue;
|
|
22287
|
+
try {
|
|
22288
|
+
const pSettings = JSON.parse(readFileSync17(projSettings, "utf8"));
|
|
22289
|
+
let changed = false;
|
|
22290
|
+
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
22291
|
+
const before = pSettings.permissions.allow.length;
|
|
22292
|
+
pSettings.permissions.allow = pSettings.permissions.allow.filter(
|
|
22293
|
+
(p) => !p.startsWith("mcp__exe-mem__") && !p.startsWith("mcp__exe-os__")
|
|
22294
|
+
);
|
|
22295
|
+
if (pSettings.permissions.allow.length < before) changed = true;
|
|
22296
|
+
}
|
|
22297
|
+
if (changed && !dryRun) {
|
|
22298
|
+
writeFileSync9(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
22299
|
+
}
|
|
22300
|
+
if (changed) projectCount++;
|
|
22301
|
+
} catch {
|
|
22302
|
+
}
|
|
22303
|
+
}
|
|
22304
|
+
} catch {
|
|
22305
|
+
}
|
|
22306
|
+
if (projectCount > 0) {
|
|
22307
|
+
log(`\u2713 Cleaned exe-os entries from ${projectCount} project settings`);
|
|
22308
|
+
removed++;
|
|
22309
|
+
}
|
|
22310
|
+
}
|
|
22311
|
+
try {
|
|
22312
|
+
const { execSync: execSync13 } = await import("child_process");
|
|
22313
|
+
const exeBinPath = execSync13("which exe", { encoding: "utf-8" }).trim();
|
|
22314
|
+
const binDir = path31.dirname(exeBinPath);
|
|
22315
|
+
let symlinkCount = 0;
|
|
22316
|
+
const rosterPath = path31.join(exeOsDir, "exe-employees.json");
|
|
22317
|
+
if (existsSync21(rosterPath)) {
|
|
22318
|
+
const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
|
|
22319
|
+
for (const emp of roster) {
|
|
22320
|
+
if (emp.name === "exe") continue;
|
|
22321
|
+
for (const suffix of ["", "-opencode"]) {
|
|
22322
|
+
const linkPath = path31.join(binDir, `${emp.name}${suffix}`);
|
|
22323
|
+
if (existsSync21(linkPath)) {
|
|
22324
|
+
if (!dryRun) rmSync(linkPath, { force: true });
|
|
22325
|
+
symlinkCount++;
|
|
22326
|
+
}
|
|
22327
|
+
}
|
|
22328
|
+
}
|
|
22329
|
+
}
|
|
22330
|
+
if (symlinkCount > 0) {
|
|
22331
|
+
log(`\u2713 Removed ${symlinkCount} employee symlinks`);
|
|
22332
|
+
removed++;
|
|
22333
|
+
}
|
|
22334
|
+
} catch {
|
|
22335
|
+
}
|
|
22336
|
+
if (purge && existsSync21(exeOsDir)) {
|
|
22337
|
+
if (!dryRun) {
|
|
22338
|
+
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
22339
|
+
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
22340
|
+
rmSync(exeOsDir, { recursive: true, force: true });
|
|
22341
|
+
}
|
|
22342
|
+
log("\u2713 Purged ~/.exe-os data directory");
|
|
22343
|
+
removed++;
|
|
22344
|
+
}
|
|
21933
22345
|
if (removed === 0) {
|
|
21934
22346
|
console.log("Nothing to remove \u2014 exe-os was not installed.");
|
|
21935
22347
|
} else {
|
|
21936
|
-
|
|
22348
|
+
if (dryRun) {
|
|
22349
|
+
console.log("\nDry run complete. Re-run without --dry-run to apply.");
|
|
22350
|
+
} else {
|
|
22351
|
+
console.log("\nDone. Run \x1B[36mexe-os claude install\x1B[0m to reinstall.");
|
|
22352
|
+
}
|
|
21937
22353
|
}
|
|
21938
22354
|
}
|
|
21939
22355
|
async function checkForUpdateOnBoot() {
|
|
@@ -21971,13 +22387,16 @@ async function runActivate(key) {
|
|
|
21971
22387
|
mirrorLicenseKey2(key);
|
|
21972
22388
|
try {
|
|
21973
22389
|
const license = await validateLicense2(key);
|
|
21974
|
-
if (!license.valid) {
|
|
22390
|
+
if (!license.valid && license.error === "offline") {
|
|
22391
|
+
console.log("\u26A0 Activated in offline mode \u2014 will verify when connected");
|
|
22392
|
+
} else if (!license.valid) {
|
|
21975
22393
|
process.stderr.write(`License invalid: ${license.plan || "unknown"}
|
|
21976
22394
|
`);
|
|
21977
22395
|
process.exit(1);
|
|
22396
|
+
} else {
|
|
22397
|
+
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
22398
|
+
console.log(`Plan activated: ${planLabel}`);
|
|
21978
22399
|
}
|
|
21979
|
-
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
21980
|
-
console.log(`Plan activated: ${planLabel}`);
|
|
21981
22400
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
21982
22401
|
const ask2 = (prompt) => new Promise((resolve) => rl.question(prompt, (a) => resolve(a.trim())));
|
|
21983
22402
|
let employees = await loadEmployees2();
|