@askexenow/exe-os 0.9.113 → 0.9.115
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/agentic-ontology-backfill.js +36 -12
- package/dist/bin/agentic-reflection-backfill.js +36 -12
- package/dist/bin/agentic-semantic-label.js +36 -12
- package/dist/bin/backfill-conversations.js +36 -12
- package/dist/bin/backfill-responses.js +36 -12
- package/dist/bin/backfill-vectors.js +36 -12
- package/dist/bin/bulk-sync-postgres.js +36 -12
- package/dist/bin/cleanup-stale-review-tasks.js +470 -113
- package/dist/bin/cli.js +413 -62
- package/dist/bin/exe-agent.js +27 -0
- package/dist/bin/exe-assign.js +36 -12
- package/dist/bin/exe-boot.js +246 -54
- package/dist/bin/exe-call.js +8 -0
- package/dist/bin/exe-cloud.js +47 -12
- package/dist/bin/exe-dispatch.js +348 -53
- package/dist/bin/exe-doctor.js +51 -13
- package/dist/bin/exe-export-behaviors.js +37 -12
- package/dist/bin/exe-forget.js +36 -12
- package/dist/bin/exe-gateway.js +348 -53
- package/dist/bin/exe-heartbeat.js +471 -113
- package/dist/bin/exe-kill.js +36 -12
- package/dist/bin/exe-launch-agent.js +117 -18
- package/dist/bin/exe-new-employee.js +9 -1
- package/dist/bin/exe-pending-messages.js +452 -95
- package/dist/bin/exe-pending-notifications.js +452 -95
- package/dist/bin/exe-pending-reviews.js +452 -95
- package/dist/bin/exe-rename.js +36 -12
- package/dist/bin/exe-review.js +36 -12
- package/dist/bin/exe-search.js +37 -12
- package/dist/bin/exe-session-cleanup.js +348 -53
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +46 -13
- package/dist/bin/exe-start-opencode.js +46 -13
- package/dist/bin/exe-status.js +460 -114
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +36 -12
- package/dist/bin/git-sweep.js +348 -53
- package/dist/bin/graph-backfill.js +36 -12
- package/dist/bin/graph-export.js +36 -12
- package/dist/bin/install.js +9 -1
- package/dist/bin/intercom-check.js +255 -53
- package/dist/bin/scan-tasks.js +348 -53
- package/dist/bin/setup.js +74 -12
- package/dist/bin/shard-migrate.js +36 -12
- package/dist/gateway/index.js +348 -53
- package/dist/hooks/bug-report-worker.js +348 -53
- package/dist/hooks/codex-stop-task-finalizer.js +308 -37
- package/dist/hooks/commit-complete.js +348 -53
- package/dist/hooks/error-recall.js +37 -12
- package/dist/hooks/ingest.js +363 -54
- package/dist/hooks/instructions-loaded.js +36 -12
- package/dist/hooks/notification.js +36 -12
- package/dist/hooks/post-compact.js +426 -72
- package/dist/hooks/post-tool-combined.js +501 -146
- package/dist/hooks/pre-compact.js +348 -53
- package/dist/hooks/pre-tool-use.js +92 -13
- package/dist/hooks/prompt-submit.js +348 -53
- package/dist/hooks/session-end.js +158 -53
- package/dist/hooks/session-start.js +66 -13
- package/dist/hooks/stop.js +420 -72
- package/dist/hooks/subagent-stop.js +419 -72
- package/dist/hooks/summary-worker.js +442 -121
- package/dist/index.js +375 -53
- package/dist/lib/agent-config.js +8 -0
- package/dist/lib/cloud-sync.js +35 -12
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +9 -1
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employees.js +8 -0
- package/dist/lib/exe-daemon.js +524 -60
- package/dist/lib/hybrid-search.js +37 -12
- package/dist/lib/keychain.js +25 -13
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +36 -12
- package/dist/lib/skill-learning.js +21 -0
- package/dist/lib/store.js +36 -12
- package/dist/lib/tasks.js +324 -41
- package/dist/lib/tmux-routing.js +324 -41
- package/dist/mcp/server.js +374 -54
- package/dist/mcp/tools/create-task.js +324 -41
- package/dist/mcp/tools/list-tasks.js +406 -57
- package/dist/mcp/tools/send-message.js +395 -74
- package/dist/mcp/tools/update-task.js +324 -41
- package/dist/runtime/index.js +375 -53
- package/dist/tui/App.js +377 -55
- package/package.json +1 -1
|
@@ -218,6 +218,17 @@ function normalizeOrchestration(raw) {
|
|
|
218
218
|
const userOrg = raw.orchestration ?? {};
|
|
219
219
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
220
220
|
}
|
|
221
|
+
function normalizeCloudEndpoint(raw) {
|
|
222
|
+
const cloud = raw.cloud;
|
|
223
|
+
if (!cloud?.endpoint) return;
|
|
224
|
+
const ep = String(cloud.endpoint);
|
|
225
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
226
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
227
|
+
process.stderr.write(
|
|
228
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
221
232
|
async function loadConfig() {
|
|
222
233
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
223
234
|
await ensurePrivateDir(dir);
|
|
@@ -243,6 +254,7 @@ async function loadConfig() {
|
|
|
243
254
|
normalizeSessionLifecycle(migratedCfg);
|
|
244
255
|
normalizeAutoUpdate(migratedCfg);
|
|
245
256
|
normalizeOrchestration(migratedCfg);
|
|
257
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
246
258
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
247
259
|
if (config.dbPath.startsWith("~")) {
|
|
248
260
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -271,6 +283,7 @@ function loadConfigSync() {
|
|
|
271
283
|
normalizeSessionLifecycle(migratedCfg);
|
|
272
284
|
normalizeAutoUpdate(migratedCfg);
|
|
273
285
|
normalizeOrchestration(migratedCfg);
|
|
286
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
274
287
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
275
288
|
if (config.dbPath.startsWith("~")) {
|
|
276
289
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -394,11 +407,176 @@ var init_config = __esm({
|
|
|
394
407
|
}
|
|
395
408
|
});
|
|
396
409
|
|
|
410
|
+
// src/lib/runtime-table.ts
|
|
411
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
412
|
+
var init_runtime_table = __esm({
|
|
413
|
+
"src/lib/runtime-table.ts"() {
|
|
414
|
+
"use strict";
|
|
415
|
+
RUNTIME_TABLE = {
|
|
416
|
+
codex: {
|
|
417
|
+
binary: "codex",
|
|
418
|
+
launchMode: "interactive",
|
|
419
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
420
|
+
inlineFlag: "--no-alt-screen",
|
|
421
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
422
|
+
defaultModel: "gpt-5.5"
|
|
423
|
+
},
|
|
424
|
+
opencode: {
|
|
425
|
+
binary: "opencode",
|
|
426
|
+
launchMode: "exec",
|
|
427
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
428
|
+
inlineFlag: "",
|
|
429
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
430
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
DEFAULT_RUNTIME = "claude";
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// src/lib/agent-config.ts
|
|
438
|
+
var agent_config_exports = {};
|
|
439
|
+
__export(agent_config_exports, {
|
|
440
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
441
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
442
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
443
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
444
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
445
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
446
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
447
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
448
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
449
|
+
setAgentMcps: () => setAgentMcps,
|
|
450
|
+
setAgentRuntime: () => setAgentRuntime
|
|
451
|
+
});
|
|
452
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
453
|
+
import path2 from "path";
|
|
454
|
+
function loadAgentConfig() {
|
|
455
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
456
|
+
try {
|
|
457
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
458
|
+
} catch {
|
|
459
|
+
return {};
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function saveAgentConfig(config) {
|
|
463
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
464
|
+
ensurePrivateDirSync(dir);
|
|
465
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
466
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
467
|
+
}
|
|
468
|
+
function getAgentRuntime(agentId) {
|
|
469
|
+
const config = loadAgentConfig();
|
|
470
|
+
const entry = config[agentId];
|
|
471
|
+
if (entry) return entry;
|
|
472
|
+
const orgDefault = config["default"];
|
|
473
|
+
if (orgDefault) return orgDefault;
|
|
474
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
475
|
+
}
|
|
476
|
+
function normalizeCcModelName(model) {
|
|
477
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
478
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
479
|
+
ccModel += "[1m]";
|
|
480
|
+
}
|
|
481
|
+
return ccModel;
|
|
482
|
+
}
|
|
483
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
484
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
485
|
+
if (!knownModels) {
|
|
486
|
+
return {
|
|
487
|
+
ok: false,
|
|
488
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
if (!knownModels.includes(model)) {
|
|
492
|
+
return {
|
|
493
|
+
ok: false,
|
|
494
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
const config = loadAgentConfig();
|
|
498
|
+
const existing = config[agentId];
|
|
499
|
+
const entry = { runtime, model };
|
|
500
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
501
|
+
if (mcps !== void 0) {
|
|
502
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
503
|
+
} else if (existing?.mcps) {
|
|
504
|
+
entry.mcps = existing.mcps;
|
|
505
|
+
}
|
|
506
|
+
config[agentId] = entry;
|
|
507
|
+
saveAgentConfig(config);
|
|
508
|
+
return { ok: true };
|
|
509
|
+
}
|
|
510
|
+
function setAgentMcps(agentId, mcps) {
|
|
511
|
+
const config = loadAgentConfig();
|
|
512
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
513
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
514
|
+
config[agentId] = existing;
|
|
515
|
+
saveAgentConfig(config);
|
|
516
|
+
return { ok: true };
|
|
517
|
+
}
|
|
518
|
+
function clearAgentRuntime(agentId) {
|
|
519
|
+
const config = loadAgentConfig();
|
|
520
|
+
delete config[agentId];
|
|
521
|
+
saveAgentConfig(config);
|
|
522
|
+
}
|
|
523
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
524
|
+
var init_agent_config = __esm({
|
|
525
|
+
"src/lib/agent-config.ts"() {
|
|
526
|
+
"use strict";
|
|
527
|
+
init_config();
|
|
528
|
+
init_runtime_table();
|
|
529
|
+
init_secure_files();
|
|
530
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
531
|
+
KNOWN_RUNTIMES = {
|
|
532
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
533
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
534
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
535
|
+
};
|
|
536
|
+
RUNTIME_LABELS = {
|
|
537
|
+
claude: "Claude Code (Anthropic)",
|
|
538
|
+
codex: "Codex (OpenAI)",
|
|
539
|
+
opencode: "OpenCode (open source)"
|
|
540
|
+
};
|
|
541
|
+
DEFAULT_MODELS = {
|
|
542
|
+
claude: "claude-opus-4.6",
|
|
543
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
544
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
397
549
|
// src/lib/employees.ts
|
|
550
|
+
var employees_exports = {};
|
|
551
|
+
__export(employees_exports, {
|
|
552
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
553
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
554
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
555
|
+
addEmployee: () => addEmployee,
|
|
556
|
+
baseAgentName: () => baseAgentName,
|
|
557
|
+
canCoordinate: () => canCoordinate,
|
|
558
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
559
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
560
|
+
getEmployee: () => getEmployee,
|
|
561
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
562
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
563
|
+
hasRole: () => hasRole,
|
|
564
|
+
hireEmployee: () => hireEmployee,
|
|
565
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
566
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
567
|
+
isMultiInstance: () => isMultiInstance,
|
|
568
|
+
loadEmployees: () => loadEmployees,
|
|
569
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
570
|
+
normalizeRole: () => normalizeRole,
|
|
571
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
572
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
573
|
+
saveEmployees: () => saveEmployees,
|
|
574
|
+
validateEmployeeName: () => validateEmployeeName
|
|
575
|
+
});
|
|
398
576
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
399
|
-
import { existsSync as
|
|
577
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
400
578
|
import { execSync } from "child_process";
|
|
401
|
-
import
|
|
579
|
+
import path3 from "path";
|
|
402
580
|
import os2 from "os";
|
|
403
581
|
function normalizeRole(role) {
|
|
404
582
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -419,8 +597,23 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
419
597
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
420
598
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
421
599
|
}
|
|
600
|
+
function validateEmployeeName(name) {
|
|
601
|
+
if (!name) {
|
|
602
|
+
return { valid: false, error: "Name is required" };
|
|
603
|
+
}
|
|
604
|
+
if (name.length > 32) {
|
|
605
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
606
|
+
}
|
|
607
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
608
|
+
return {
|
|
609
|
+
valid: false,
|
|
610
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return { valid: true };
|
|
614
|
+
}
|
|
422
615
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
423
|
-
if (!
|
|
616
|
+
if (!existsSync4(employeesPath)) {
|
|
424
617
|
return [];
|
|
425
618
|
}
|
|
426
619
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -431,17 +624,131 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
431
624
|
}
|
|
432
625
|
}
|
|
433
626
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
434
|
-
await mkdir2(
|
|
627
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
435
628
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
436
629
|
}
|
|
437
630
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
438
|
-
if (!
|
|
631
|
+
if (!existsSync4(employeesPath)) return [];
|
|
439
632
|
try {
|
|
440
|
-
return JSON.parse(
|
|
633
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
441
634
|
} catch {
|
|
442
635
|
return [];
|
|
443
636
|
}
|
|
444
637
|
}
|
|
638
|
+
function getEmployee(employees, name) {
|
|
639
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
640
|
+
}
|
|
641
|
+
function getEmployeeByRole(employees, role) {
|
|
642
|
+
const lower = role.toLowerCase();
|
|
643
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
644
|
+
}
|
|
645
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
646
|
+
const lower = role.toLowerCase();
|
|
647
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
648
|
+
}
|
|
649
|
+
function hasRole(agentName, role) {
|
|
650
|
+
const employees = loadEmployeesSync();
|
|
651
|
+
const emp = getEmployee(employees, agentName);
|
|
652
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
653
|
+
}
|
|
654
|
+
function baseAgentName(name, employees) {
|
|
655
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
656
|
+
if (!match) return name;
|
|
657
|
+
const base = match[1];
|
|
658
|
+
const roster = employees ?? loadEmployeesSync();
|
|
659
|
+
if (getEmployee(roster, base)) return base;
|
|
660
|
+
return name;
|
|
661
|
+
}
|
|
662
|
+
function isMultiInstance(agentName, employees) {
|
|
663
|
+
const roster = employees ?? loadEmployeesSync();
|
|
664
|
+
const emp = getEmployee(roster, agentName);
|
|
665
|
+
if (!emp) return false;
|
|
666
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
667
|
+
}
|
|
668
|
+
function addEmployee(employees, employee) {
|
|
669
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
670
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
671
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
672
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
673
|
+
}
|
|
674
|
+
return [...employees, normalized];
|
|
675
|
+
}
|
|
676
|
+
function appendToCoordinatorTeam(employee) {
|
|
677
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
678
|
+
if (!coordinator) return;
|
|
679
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
680
|
+
if (!existsSync4(idPath)) return;
|
|
681
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
682
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
683
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
684
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
685
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
686
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
687
|
+
const entry = `
|
|
688
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
689
|
+
`;
|
|
690
|
+
let updated;
|
|
691
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
692
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
693
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
694
|
+
} else {
|
|
695
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
696
|
+
}
|
|
697
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
698
|
+
}
|
|
699
|
+
function capitalize(s) {
|
|
700
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
701
|
+
}
|
|
702
|
+
async function hireEmployee(employee) {
|
|
703
|
+
const employees = await loadEmployees();
|
|
704
|
+
const updated = addEmployee(employees, employee);
|
|
705
|
+
await saveEmployees(updated);
|
|
706
|
+
try {
|
|
707
|
+
appendToCoordinatorTeam(employee);
|
|
708
|
+
} catch {
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
712
|
+
const config = loadAgentConfig2();
|
|
713
|
+
const name = employee.name.toLowerCase();
|
|
714
|
+
if (!config[name] && config["default"]) {
|
|
715
|
+
config[name] = { ...config["default"] };
|
|
716
|
+
saveAgentConfig2(config);
|
|
717
|
+
}
|
|
718
|
+
} catch {
|
|
719
|
+
}
|
|
720
|
+
return updated;
|
|
721
|
+
}
|
|
722
|
+
async function normalizeRosterCase(rosterPath) {
|
|
723
|
+
const employees = await loadEmployees(rosterPath);
|
|
724
|
+
let changed = false;
|
|
725
|
+
for (const emp of employees) {
|
|
726
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
727
|
+
const oldName = emp.name;
|
|
728
|
+
emp.name = emp.name.toLowerCase();
|
|
729
|
+
changed = true;
|
|
730
|
+
try {
|
|
731
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
732
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
733
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
734
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
735
|
+
renameSync2(oldPath, newPath);
|
|
736
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
737
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
738
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
739
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
740
|
+
unlinkSync(oldPath);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
} catch {
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (changed) {
|
|
748
|
+
await saveEmployees(employees, rosterPath);
|
|
749
|
+
}
|
|
750
|
+
return changed;
|
|
751
|
+
}
|
|
445
752
|
function findExeBin() {
|
|
446
753
|
try {
|
|
447
754
|
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
@@ -458,7 +765,7 @@ function registerBinSymlinks(name) {
|
|
|
458
765
|
errors.push("Could not find 'exe-os' in PATH");
|
|
459
766
|
return { created, skipped, errors };
|
|
460
767
|
}
|
|
461
|
-
const binDir =
|
|
768
|
+
const binDir = path3.dirname(exeBinPath);
|
|
462
769
|
let target;
|
|
463
770
|
try {
|
|
464
771
|
target = readlinkSync(exeBinPath);
|
|
@@ -468,8 +775,8 @@ function registerBinSymlinks(name) {
|
|
|
468
775
|
}
|
|
469
776
|
for (const suffix of ["", "-opencode"]) {
|
|
470
777
|
const linkName = `${name}${suffix}`;
|
|
471
|
-
const linkPath =
|
|
472
|
-
if (
|
|
778
|
+
const linkPath = path3.join(binDir, linkName);
|
|
779
|
+
if (existsSync4(linkPath)) {
|
|
473
780
|
skipped.push(linkName);
|
|
474
781
|
continue;
|
|
475
782
|
}
|
|
@@ -482,21 +789,23 @@ function registerBinSymlinks(name) {
|
|
|
482
789
|
}
|
|
483
790
|
return { created, skipped, errors };
|
|
484
791
|
}
|
|
485
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
792
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
486
793
|
var init_employees = __esm({
|
|
487
794
|
"src/lib/employees.ts"() {
|
|
488
795
|
"use strict";
|
|
489
796
|
init_config();
|
|
490
|
-
EMPLOYEES_PATH =
|
|
797
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
491
798
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
492
799
|
COORDINATOR_ROLE = "COO";
|
|
493
|
-
|
|
800
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
801
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
802
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
494
803
|
}
|
|
495
804
|
});
|
|
496
805
|
|
|
497
806
|
// src/lib/database-adapter.ts
|
|
498
807
|
import os3 from "os";
|
|
499
|
-
import
|
|
808
|
+
import path4 from "path";
|
|
500
809
|
import { createRequire } from "module";
|
|
501
810
|
import { pathToFileURL } from "url";
|
|
502
811
|
function quotedIdentifier(identifier) {
|
|
@@ -807,8 +1116,8 @@ async function loadPrismaClient() {
|
|
|
807
1116
|
}
|
|
808
1117
|
return new PrismaClient2();
|
|
809
1118
|
}
|
|
810
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
811
|
-
const requireFromExeDb = createRequire(
|
|
1119
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1120
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
812
1121
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
813
1122
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
814
1123
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -1080,8 +1389,8 @@ var init_database_adapter = __esm({
|
|
|
1080
1389
|
|
|
1081
1390
|
// src/lib/daemon-auth.ts
|
|
1082
1391
|
import crypto from "crypto";
|
|
1083
|
-
import
|
|
1084
|
-
import { existsSync as
|
|
1392
|
+
import path5 from "path";
|
|
1393
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1085
1394
|
function normalizeToken(token) {
|
|
1086
1395
|
if (!token) return null;
|
|
1087
1396
|
const trimmed = token.trim();
|
|
@@ -1089,8 +1398,8 @@ function normalizeToken(token) {
|
|
|
1089
1398
|
}
|
|
1090
1399
|
function readDaemonToken() {
|
|
1091
1400
|
try {
|
|
1092
|
-
if (!
|
|
1093
|
-
return normalizeToken(
|
|
1401
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1402
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
1094
1403
|
} catch {
|
|
1095
1404
|
return null;
|
|
1096
1405
|
}
|
|
@@ -1100,7 +1409,7 @@ function ensureDaemonToken(seed) {
|
|
|
1100
1409
|
if (existing) return existing;
|
|
1101
1410
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1102
1411
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1103
|
-
|
|
1412
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1104
1413
|
`, "utf8");
|
|
1105
1414
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1106
1415
|
return token;
|
|
@@ -1111,7 +1420,7 @@ var init_daemon_auth = __esm({
|
|
|
1111
1420
|
"use strict";
|
|
1112
1421
|
init_config();
|
|
1113
1422
|
init_secure_files();
|
|
1114
|
-
DAEMON_TOKEN_PATH =
|
|
1423
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1115
1424
|
}
|
|
1116
1425
|
});
|
|
1117
1426
|
|
|
@@ -1120,8 +1429,8 @@ import net from "net";
|
|
|
1120
1429
|
import os4 from "os";
|
|
1121
1430
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1122
1431
|
import { randomUUID } from "crypto";
|
|
1123
|
-
import { existsSync as
|
|
1124
|
-
import
|
|
1432
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1433
|
+
import path6 from "path";
|
|
1125
1434
|
import { fileURLToPath } from "url";
|
|
1126
1435
|
function handleData(chunk) {
|
|
1127
1436
|
_buffer += chunk.toString();
|
|
@@ -1157,9 +1466,9 @@ function isZombie(pid) {
|
|
|
1157
1466
|
}
|
|
1158
1467
|
}
|
|
1159
1468
|
function cleanupStaleFiles() {
|
|
1160
|
-
if (
|
|
1469
|
+
if (existsSync6(PID_PATH)) {
|
|
1161
1470
|
try {
|
|
1162
|
-
const pid = parseInt(
|
|
1471
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1163
1472
|
if (pid > 0) {
|
|
1164
1473
|
try {
|
|
1165
1474
|
process.kill(pid, 0);
|
|
@@ -1184,11 +1493,11 @@ function cleanupStaleFiles() {
|
|
|
1184
1493
|
}
|
|
1185
1494
|
}
|
|
1186
1495
|
function findPackageRoot() {
|
|
1187
|
-
let dir =
|
|
1188
|
-
const { root } =
|
|
1496
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1497
|
+
const { root } = path6.parse(dir);
|
|
1189
1498
|
while (dir !== root) {
|
|
1190
|
-
if (
|
|
1191
|
-
dir =
|
|
1499
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1500
|
+
dir = path6.dirname(dir);
|
|
1192
1501
|
}
|
|
1193
1502
|
return null;
|
|
1194
1503
|
}
|
|
@@ -1206,8 +1515,8 @@ function spawnDaemon() {
|
|
|
1206
1515
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1207
1516
|
return;
|
|
1208
1517
|
}
|
|
1209
|
-
const daemonPath =
|
|
1210
|
-
if (!
|
|
1518
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1519
|
+
if (!existsSync6(daemonPath)) {
|
|
1211
1520
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1212
1521
|
`);
|
|
1213
1522
|
return;
|
|
@@ -1216,7 +1525,7 @@ function spawnDaemon() {
|
|
|
1216
1525
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1217
1526
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1218
1527
|
`);
|
|
1219
|
-
const logPath =
|
|
1528
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1220
1529
|
let stderrFd = "ignore";
|
|
1221
1530
|
try {
|
|
1222
1531
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1381,9 +1690,9 @@ function killAndRespawnDaemon() {
|
|
|
1381
1690
|
}
|
|
1382
1691
|
try {
|
|
1383
1692
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1384
|
-
if (
|
|
1693
|
+
if (existsSync6(PID_PATH)) {
|
|
1385
1694
|
try {
|
|
1386
|
-
const pid = parseInt(
|
|
1695
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1387
1696
|
if (pid > 0) {
|
|
1388
1697
|
try {
|
|
1389
1698
|
process.kill(pid, "SIGKILL");
|
|
@@ -1509,9 +1818,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1509
1818
|
"use strict";
|
|
1510
1819
|
init_config();
|
|
1511
1820
|
init_daemon_auth();
|
|
1512
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1513
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1514
|
-
SPAWN_LOCK_PATH =
|
|
1821
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1822
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1823
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1515
1824
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1516
1825
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1517
1826
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1744,7 +2053,7 @@ __export(database_exports, {
|
|
|
1744
2053
|
isInitialized: () => isInitialized,
|
|
1745
2054
|
setExternalClient: () => setExternalClient
|
|
1746
2055
|
});
|
|
1747
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
2056
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1748
2057
|
import { createClient } from "@libsql/client";
|
|
1749
2058
|
import { homedir } from "os";
|
|
1750
2059
|
import { join } from "path";
|
|
@@ -1797,11 +2106,11 @@ function releaseDbLock() {
|
|
|
1797
2106
|
}
|
|
1798
2107
|
async function initDatabase(config) {
|
|
1799
2108
|
acquireDbLock();
|
|
1800
|
-
if (
|
|
2109
|
+
if (existsSync7(config.dbPath)) {
|
|
1801
2110
|
const dbStat = statSync2(config.dbPath);
|
|
1802
2111
|
if (dbStat.size === 0) {
|
|
1803
2112
|
const walPath = config.dbPath + "-wal";
|
|
1804
|
-
if (
|
|
2113
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1805
2114
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1806
2115
|
copyFileSync(config.dbPath, backupPath);
|
|
1807
2116
|
unlinkSync3(config.dbPath);
|
|
@@ -3396,16 +3705,16 @@ __export(keychain_exports, {
|
|
|
3396
3705
|
importMnemonic: () => importMnemonic,
|
|
3397
3706
|
setMasterKey: () => setMasterKey
|
|
3398
3707
|
});
|
|
3399
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3400
|
-
import { existsSync as
|
|
3708
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3709
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3401
3710
|
import { execSync as execSync3 } from "child_process";
|
|
3402
|
-
import
|
|
3711
|
+
import path7 from "path";
|
|
3403
3712
|
import os5 from "os";
|
|
3404
3713
|
function getKeyDir() {
|
|
3405
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3714
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3406
3715
|
}
|
|
3407
3716
|
function getKeyPath() {
|
|
3408
|
-
return
|
|
3717
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3409
3718
|
}
|
|
3410
3719
|
function nativeKeychainAllowed() {
|
|
3411
3720
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -3431,12 +3740,14 @@ function linuxSecretAvailable() {
|
|
|
3431
3740
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3432
3741
|
if (process.platform !== "linux") return false;
|
|
3433
3742
|
try {
|
|
3434
|
-
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3435
3743
|
const st = statSync3(keyPath);
|
|
3436
3744
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3745
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3437
3746
|
if (uid === 0) return true;
|
|
3438
3747
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3439
|
-
|
|
3748
|
+
if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
|
|
3749
|
+
if (!linuxSecretAvailable()) return true;
|
|
3750
|
+
return false;
|
|
3440
3751
|
} catch {
|
|
3441
3752
|
return false;
|
|
3442
3753
|
}
|
|
@@ -3586,15 +3897,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3586
3897
|
await mkdir3(dir, { recursive: true });
|
|
3587
3898
|
const keyPath = getKeyPath();
|
|
3588
3899
|
const machineKey = deriveMachineKey();
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3900
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
3901
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
3902
|
+
const tmpPath = keyPath + ".tmp";
|
|
3903
|
+
try {
|
|
3904
|
+
if (existsSync8(keyPath)) {
|
|
3905
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
3909
|
+
await chmod2(tmpPath, 384);
|
|
3910
|
+
await rename(tmpPath, keyPath);
|
|
3911
|
+
} catch (err) {
|
|
3912
|
+
try {
|
|
3913
|
+
await unlink(tmpPath);
|
|
3914
|
+
} catch {
|
|
3915
|
+
}
|
|
3916
|
+
throw err;
|
|
3594
3917
|
}
|
|
3595
|
-
|
|
3596
|
-
await chmod2(keyPath, 384);
|
|
3597
|
-
return "plaintext";
|
|
3918
|
+
return result;
|
|
3598
3919
|
}
|
|
3599
3920
|
async function getMasterKey() {
|
|
3600
3921
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -3633,7 +3954,7 @@ async function getMasterKey() {
|
|
|
3633
3954
|
}
|
|
3634
3955
|
}
|
|
3635
3956
|
const keyPath = getKeyPath();
|
|
3636
|
-
if (!
|
|
3957
|
+
if (!existsSync8(keyPath)) {
|
|
3637
3958
|
process.stderr.write(
|
|
3638
3959
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3639
3960
|
`
|
|
@@ -3661,7 +3982,7 @@ async function getMasterKey() {
|
|
|
3661
3982
|
b64Value = content;
|
|
3662
3983
|
}
|
|
3663
3984
|
const key = Buffer.from(b64Value, "base64");
|
|
3664
|
-
if (
|
|
3985
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3665
3986
|
return key;
|
|
3666
3987
|
}
|
|
3667
3988
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -3741,7 +4062,7 @@ async function getKeyStorageInfo() {
|
|
|
3741
4062
|
}
|
|
3742
4063
|
}
|
|
3743
4064
|
const keyPath = getKeyPath();
|
|
3744
|
-
if (!
|
|
4065
|
+
if (!existsSync8(keyPath)) {
|
|
3745
4066
|
return {
|
|
3746
4067
|
kind: "missing",
|
|
3747
4068
|
secure: false,
|
|
@@ -3837,7 +4158,7 @@ async function deleteMasterKey() {
|
|
|
3837
4158
|
}
|
|
3838
4159
|
}
|
|
3839
4160
|
const keyPath = getKeyPath();
|
|
3840
|
-
if (
|
|
4161
|
+
if (existsSync8(keyPath)) {
|
|
3841
4162
|
await unlink(keyPath);
|
|
3842
4163
|
}
|
|
3843
4164
|
}
|
|
@@ -3952,14 +4273,14 @@ __export(shard_manager_exports, {
|
|
|
3952
4273
|
listShards: () => listShards,
|
|
3953
4274
|
shardExists: () => shardExists
|
|
3954
4275
|
});
|
|
3955
|
-
import
|
|
3956
|
-
import { existsSync as
|
|
4276
|
+
import path8 from "path";
|
|
4277
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
3957
4278
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3958
4279
|
function initShardManager(encryptionKey) {
|
|
3959
4280
|
_encryptionKey = encryptionKey;
|
|
3960
4281
|
_keyValidated = false;
|
|
3961
4282
|
_keyValidationPromise = null;
|
|
3962
|
-
if (!
|
|
4283
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3963
4284
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3964
4285
|
}
|
|
3965
4286
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3980,7 +4301,7 @@ async function validateEncryptionKey() {
|
|
|
3980
4301
|
return true;
|
|
3981
4302
|
}
|
|
3982
4303
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3983
|
-
const dbPath =
|
|
4304
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3984
4305
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3985
4306
|
try {
|
|
3986
4307
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -4023,7 +4344,7 @@ function getShardClient(projectName) {
|
|
|
4023
4344
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
4024
4345
|
evictLRU();
|
|
4025
4346
|
}
|
|
4026
|
-
const dbPath =
|
|
4347
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4027
4348
|
const client = createClient2({
|
|
4028
4349
|
url: `file:${dbPath}`,
|
|
4029
4350
|
encryptionKey: _encryptionKey
|
|
@@ -4034,13 +4355,13 @@ function getShardClient(projectName) {
|
|
|
4034
4355
|
}
|
|
4035
4356
|
function shardExists(projectName) {
|
|
4036
4357
|
const safeName = safeShardName(projectName);
|
|
4037
|
-
return
|
|
4358
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
4038
4359
|
}
|
|
4039
4360
|
function safeShardName(projectName) {
|
|
4040
4361
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4041
4362
|
}
|
|
4042
4363
|
function listShards() {
|
|
4043
|
-
if (!
|
|
4364
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
4044
4365
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4045
4366
|
}
|
|
4046
4367
|
async function auditShardHealth(options = {}) {
|
|
@@ -4052,7 +4373,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4052
4373
|
const names = listShards();
|
|
4053
4374
|
const shards = [];
|
|
4054
4375
|
for (const name of names) {
|
|
4055
|
-
const dbPath =
|
|
4376
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4056
4377
|
const stat = statSync4(dbPath);
|
|
4057
4378
|
const item = {
|
|
4058
4379
|
name,
|
|
@@ -4087,7 +4408,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4087
4408
|
_shards.delete(name);
|
|
4088
4409
|
_shardLastAccess.delete(name);
|
|
4089
4410
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4090
|
-
const archivedPath =
|
|
4411
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4091
4412
|
renameSync3(dbPath, archivedPath);
|
|
4092
4413
|
item.archivedPath = archivedPath;
|
|
4093
4414
|
}
|
|
@@ -4315,11 +4636,11 @@ async function getReadyShardClient(projectName) {
|
|
|
4315
4636
|
client.close();
|
|
4316
4637
|
_shards.delete(safeName);
|
|
4317
4638
|
_shardLastAccess.delete(safeName);
|
|
4318
|
-
const dbPath =
|
|
4319
|
-
if (
|
|
4639
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4640
|
+
if (existsSync9(dbPath)) {
|
|
4320
4641
|
const stat = statSync4(dbPath);
|
|
4321
4642
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4322
|
-
const archivedPath =
|
|
4643
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4323
4644
|
renameSync3(dbPath, archivedPath);
|
|
4324
4645
|
process.stderr.write(
|
|
4325
4646
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -4387,7 +4708,7 @@ var init_shard_manager = __esm({
|
|
|
4387
4708
|
"src/lib/shard-manager.ts"() {
|
|
4388
4709
|
"use strict";
|
|
4389
4710
|
init_config();
|
|
4390
|
-
SHARDS_DIR =
|
|
4711
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
4391
4712
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4392
4713
|
MAX_OPEN_SHARDS = 10;
|
|
4393
4714
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -4777,13 +5098,13 @@ ${p.content}`).join("\n\n");
|
|
|
4777
5098
|
});
|
|
4778
5099
|
|
|
4779
5100
|
// src/lib/session-registry.ts
|
|
4780
|
-
import
|
|
5101
|
+
import path9 from "path";
|
|
4781
5102
|
import os6 from "os";
|
|
4782
5103
|
var REGISTRY_PATH;
|
|
4783
5104
|
var init_session_registry = __esm({
|
|
4784
5105
|
"src/lib/session-registry.ts"() {
|
|
4785
5106
|
"use strict";
|
|
4786
|
-
REGISTRY_PATH =
|
|
5107
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
4787
5108
|
}
|
|
4788
5109
|
});
|
|
4789
5110
|
|
|
@@ -5001,51 +5322,6 @@ var init_provider_table = __esm({
|
|
|
5001
5322
|
}
|
|
5002
5323
|
});
|
|
5003
5324
|
|
|
5004
|
-
// src/lib/runtime-table.ts
|
|
5005
|
-
var RUNTIME_TABLE;
|
|
5006
|
-
var init_runtime_table = __esm({
|
|
5007
|
-
"src/lib/runtime-table.ts"() {
|
|
5008
|
-
"use strict";
|
|
5009
|
-
RUNTIME_TABLE = {
|
|
5010
|
-
codex: {
|
|
5011
|
-
binary: "codex",
|
|
5012
|
-
launchMode: "interactive",
|
|
5013
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
5014
|
-
inlineFlag: "--no-alt-screen",
|
|
5015
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
5016
|
-
defaultModel: "gpt-5.5"
|
|
5017
|
-
},
|
|
5018
|
-
opencode: {
|
|
5019
|
-
binary: "opencode",
|
|
5020
|
-
launchMode: "exec",
|
|
5021
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
5022
|
-
inlineFlag: "",
|
|
5023
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
5024
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
5025
|
-
}
|
|
5026
|
-
};
|
|
5027
|
-
}
|
|
5028
|
-
});
|
|
5029
|
-
|
|
5030
|
-
// src/lib/agent-config.ts
|
|
5031
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
|
|
5032
|
-
import path9 from "path";
|
|
5033
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
5034
|
-
var init_agent_config = __esm({
|
|
5035
|
-
"src/lib/agent-config.ts"() {
|
|
5036
|
-
"use strict";
|
|
5037
|
-
init_config();
|
|
5038
|
-
init_runtime_table();
|
|
5039
|
-
init_secure_files();
|
|
5040
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
5041
|
-
DEFAULT_MODELS = {
|
|
5042
|
-
claude: "claude-opus-4.6",
|
|
5043
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
5044
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
5045
|
-
};
|
|
5046
|
-
}
|
|
5047
|
-
});
|
|
5048
|
-
|
|
5049
5325
|
// src/lib/intercom-queue.ts
|
|
5050
5326
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
5051
5327
|
import path10 from "path";
|
|
@@ -5683,6 +5959,21 @@ function isRootSession(name) {
|
|
|
5683
5959
|
function extractRootExe(name) {
|
|
5684
5960
|
if (!name) return null;
|
|
5685
5961
|
if (!name.includes("-")) return name;
|
|
5962
|
+
try {
|
|
5963
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
5964
|
+
if (roster.length > 0) {
|
|
5965
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
5966
|
+
for (const agentName of sortedNames) {
|
|
5967
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5968
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
5969
|
+
const match = name.match(regex);
|
|
5970
|
+
if (match) {
|
|
5971
|
+
return extractRootExe(match[1]);
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
}
|
|
5975
|
+
} catch {
|
|
5976
|
+
}
|
|
5686
5977
|
const parts = name.split("-").filter(Boolean);
|
|
5687
5978
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5688
5979
|
}
|
|
@@ -5701,6 +5992,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5701
5992
|
function getParentExe(sessionKey) {
|
|
5702
5993
|
try {
|
|
5703
5994
|
const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5995
|
+
if (data.registeredAt) {
|
|
5996
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
5997
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
5998
|
+
}
|
|
5704
5999
|
return data.parentExe || null;
|
|
5705
6000
|
} catch {
|
|
5706
6001
|
return null;
|
|
@@ -5773,7 +6068,7 @@ function resolveExeSession() {
|
|
|
5773
6068
|
}
|
|
5774
6069
|
return candidate;
|
|
5775
6070
|
}
|
|
5776
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
6071
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
5777
6072
|
var init_tmux_routing = __esm({
|
|
5778
6073
|
"src/lib/tmux-routing.ts"() {
|
|
5779
6074
|
"use strict";
|
|
@@ -5791,6 +6086,7 @@ var init_tmux_routing = __esm({
|
|
|
5791
6086
|
init_agent_symlinks();
|
|
5792
6087
|
SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
5793
6088
|
SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6089
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
5794
6090
|
INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
5795
6091
|
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5796
6092
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -5957,7 +6253,21 @@ function tryAcquireWorkerSlot() {
|
|
|
5957
6253
|
for (const f of files) {
|
|
5958
6254
|
if (!f.endsWith(".pid")) continue;
|
|
5959
6255
|
if (f.startsWith("res-")) {
|
|
5960
|
-
|
|
6256
|
+
const resParts = f.replace(".pid", "").split("-");
|
|
6257
|
+
const resPid = parseInt(resParts[1] ?? "", 10);
|
|
6258
|
+
if (!isNaN(resPid) && resPid > 0) {
|
|
6259
|
+
try {
|
|
6260
|
+
process.kill(resPid, 0);
|
|
6261
|
+
alive++;
|
|
6262
|
+
} catch {
|
|
6263
|
+
try {
|
|
6264
|
+
unlinkSync6(path17.join(WORKER_PID_DIR, f));
|
|
6265
|
+
} catch {
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
} else {
|
|
6269
|
+
alive++;
|
|
6270
|
+
}
|
|
5961
6271
|
continue;
|
|
5962
6272
|
}
|
|
5963
6273
|
const dashIdx = f.lastIndexOf("-");
|
|
@@ -6500,6 +6810,7 @@ __export(cloud_sync_exports, {
|
|
|
6500
6810
|
markCloudReuploadRequired: () => markCloudReuploadRequired,
|
|
6501
6811
|
mergeConfig: () => mergeConfig,
|
|
6502
6812
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
6813
|
+
migrateEndpoint: () => migrateEndpoint,
|
|
6503
6814
|
pushToPostgres: () => pushToPostgres,
|
|
6504
6815
|
recordRosterDeletion: () => recordRosterDeletion
|
|
6505
6816
|
});
|
|
@@ -6670,6 +6981,15 @@ async function fetchWithRetry(url, init) {
|
|
|
6670
6981
|
}
|
|
6671
6982
|
throw lastError;
|
|
6672
6983
|
}
|
|
6984
|
+
function migrateEndpoint(endpoint) {
|
|
6985
|
+
if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
|
|
6986
|
+
process.stderr.write(
|
|
6987
|
+
"[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
|
|
6988
|
+
);
|
|
6989
|
+
return "https://cloud.askexe.com";
|
|
6990
|
+
}
|
|
6991
|
+
return endpoint;
|
|
6992
|
+
}
|
|
6673
6993
|
function assertSecureEndpoint(endpoint) {
|
|
6674
6994
|
if (endpoint.startsWith("https://")) return;
|
|
6675
6995
|
if (endpoint.startsWith("http://")) {
|
|
@@ -6807,6 +7127,7 @@ async function markCloudReuploadRequired(client = getClient()) {
|
|
|
6807
7127
|
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
6808
7128
|
}
|
|
6809
7129
|
async function cloudSync(config) {
|
|
7130
|
+
config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
|
|
6810
7131
|
if (!isSyncCryptoInitialized()) {
|
|
6811
7132
|
try {
|
|
6812
7133
|
const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|