@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
|
@@ -195,6 +195,17 @@ function normalizeOrchestration(raw) {
|
|
|
195
195
|
const userOrg = raw.orchestration ?? {};
|
|
196
196
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
197
197
|
}
|
|
198
|
+
function normalizeCloudEndpoint(raw) {
|
|
199
|
+
const cloud = raw.cloud;
|
|
200
|
+
if (!cloud?.endpoint) return;
|
|
201
|
+
const ep = String(cloud.endpoint);
|
|
202
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
203
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
204
|
+
process.stderr.write(
|
|
205
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
198
209
|
async function loadConfig() {
|
|
199
210
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
200
211
|
await ensurePrivateDir(dir);
|
|
@@ -220,6 +231,7 @@ async function loadConfig() {
|
|
|
220
231
|
normalizeSessionLifecycle(migratedCfg);
|
|
221
232
|
normalizeAutoUpdate(migratedCfg);
|
|
222
233
|
normalizeOrchestration(migratedCfg);
|
|
234
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
223
235
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
224
236
|
if (config.dbPath.startsWith("~")) {
|
|
225
237
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -248,6 +260,7 @@ function loadConfigSync() {
|
|
|
248
260
|
normalizeSessionLifecycle(migratedCfg);
|
|
249
261
|
normalizeAutoUpdate(migratedCfg);
|
|
250
262
|
normalizeOrchestration(migratedCfg);
|
|
263
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
251
264
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
252
265
|
if (config.dbPath.startsWith("~")) {
|
|
253
266
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -349,11 +362,176 @@ var init_config = __esm({
|
|
|
349
362
|
}
|
|
350
363
|
});
|
|
351
364
|
|
|
365
|
+
// src/lib/runtime-table.ts
|
|
366
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
367
|
+
var init_runtime_table = __esm({
|
|
368
|
+
"src/lib/runtime-table.ts"() {
|
|
369
|
+
"use strict";
|
|
370
|
+
RUNTIME_TABLE = {
|
|
371
|
+
codex: {
|
|
372
|
+
binary: "codex",
|
|
373
|
+
launchMode: "interactive",
|
|
374
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
375
|
+
inlineFlag: "--no-alt-screen",
|
|
376
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
377
|
+
defaultModel: "gpt-5.5"
|
|
378
|
+
},
|
|
379
|
+
opencode: {
|
|
380
|
+
binary: "opencode",
|
|
381
|
+
launchMode: "exec",
|
|
382
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
383
|
+
inlineFlag: "",
|
|
384
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
385
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
DEFAULT_RUNTIME = "claude";
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// src/lib/agent-config.ts
|
|
393
|
+
var agent_config_exports = {};
|
|
394
|
+
__export(agent_config_exports, {
|
|
395
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
396
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
397
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
398
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
399
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
400
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
401
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
402
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
403
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
404
|
+
setAgentMcps: () => setAgentMcps,
|
|
405
|
+
setAgentRuntime: () => setAgentRuntime
|
|
406
|
+
});
|
|
407
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
408
|
+
import path2 from "path";
|
|
409
|
+
function loadAgentConfig() {
|
|
410
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
411
|
+
try {
|
|
412
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
413
|
+
} catch {
|
|
414
|
+
return {};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function saveAgentConfig(config) {
|
|
418
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
419
|
+
ensurePrivateDirSync(dir);
|
|
420
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
421
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
422
|
+
}
|
|
423
|
+
function getAgentRuntime(agentId) {
|
|
424
|
+
const config = loadAgentConfig();
|
|
425
|
+
const entry = config[agentId];
|
|
426
|
+
if (entry) return entry;
|
|
427
|
+
const orgDefault = config["default"];
|
|
428
|
+
if (orgDefault) return orgDefault;
|
|
429
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
430
|
+
}
|
|
431
|
+
function normalizeCcModelName(model) {
|
|
432
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
433
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
434
|
+
ccModel += "[1m]";
|
|
435
|
+
}
|
|
436
|
+
return ccModel;
|
|
437
|
+
}
|
|
438
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
439
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
440
|
+
if (!knownModels) {
|
|
441
|
+
return {
|
|
442
|
+
ok: false,
|
|
443
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
if (!knownModels.includes(model)) {
|
|
447
|
+
return {
|
|
448
|
+
ok: false,
|
|
449
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const config = loadAgentConfig();
|
|
453
|
+
const existing = config[agentId];
|
|
454
|
+
const entry = { runtime, model };
|
|
455
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
456
|
+
if (mcps !== void 0) {
|
|
457
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
458
|
+
} else if (existing?.mcps) {
|
|
459
|
+
entry.mcps = existing.mcps;
|
|
460
|
+
}
|
|
461
|
+
config[agentId] = entry;
|
|
462
|
+
saveAgentConfig(config);
|
|
463
|
+
return { ok: true };
|
|
464
|
+
}
|
|
465
|
+
function setAgentMcps(agentId, mcps) {
|
|
466
|
+
const config = loadAgentConfig();
|
|
467
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
468
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
469
|
+
config[agentId] = existing;
|
|
470
|
+
saveAgentConfig(config);
|
|
471
|
+
return { ok: true };
|
|
472
|
+
}
|
|
473
|
+
function clearAgentRuntime(agentId) {
|
|
474
|
+
const config = loadAgentConfig();
|
|
475
|
+
delete config[agentId];
|
|
476
|
+
saveAgentConfig(config);
|
|
477
|
+
}
|
|
478
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
479
|
+
var init_agent_config = __esm({
|
|
480
|
+
"src/lib/agent-config.ts"() {
|
|
481
|
+
"use strict";
|
|
482
|
+
init_config();
|
|
483
|
+
init_runtime_table();
|
|
484
|
+
init_secure_files();
|
|
485
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
486
|
+
KNOWN_RUNTIMES = {
|
|
487
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
488
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
489
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
490
|
+
};
|
|
491
|
+
RUNTIME_LABELS = {
|
|
492
|
+
claude: "Claude Code (Anthropic)",
|
|
493
|
+
codex: "Codex (OpenAI)",
|
|
494
|
+
opencode: "OpenCode (open source)"
|
|
495
|
+
};
|
|
496
|
+
DEFAULT_MODELS = {
|
|
497
|
+
claude: "claude-opus-4.6",
|
|
498
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
499
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
352
504
|
// src/lib/employees.ts
|
|
505
|
+
var employees_exports = {};
|
|
506
|
+
__export(employees_exports, {
|
|
507
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
508
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
509
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
510
|
+
addEmployee: () => addEmployee,
|
|
511
|
+
baseAgentName: () => baseAgentName,
|
|
512
|
+
canCoordinate: () => canCoordinate,
|
|
513
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
514
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
515
|
+
getEmployee: () => getEmployee,
|
|
516
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
517
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
518
|
+
hasRole: () => hasRole,
|
|
519
|
+
hireEmployee: () => hireEmployee,
|
|
520
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
521
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
522
|
+
isMultiInstance: () => isMultiInstance,
|
|
523
|
+
loadEmployees: () => loadEmployees,
|
|
524
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
525
|
+
normalizeRole: () => normalizeRole,
|
|
526
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
527
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
528
|
+
saveEmployees: () => saveEmployees,
|
|
529
|
+
validateEmployeeName: () => validateEmployeeName
|
|
530
|
+
});
|
|
353
531
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
354
|
-
import { existsSync as
|
|
532
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
355
533
|
import { execSync } from "child_process";
|
|
356
|
-
import
|
|
534
|
+
import path3 from "path";
|
|
357
535
|
import os2 from "os";
|
|
358
536
|
function normalizeRole(role) {
|
|
359
537
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -367,29 +545,222 @@ function getCoordinatorEmployee(employees) {
|
|
|
367
545
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
368
546
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
369
547
|
}
|
|
548
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
549
|
+
if (!agentName) return false;
|
|
550
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
551
|
+
}
|
|
552
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
553
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
554
|
+
}
|
|
555
|
+
function validateEmployeeName(name) {
|
|
556
|
+
if (!name) {
|
|
557
|
+
return { valid: false, error: "Name is required" };
|
|
558
|
+
}
|
|
559
|
+
if (name.length > 32) {
|
|
560
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
561
|
+
}
|
|
562
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
563
|
+
return {
|
|
564
|
+
valid: false,
|
|
565
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return { valid: true };
|
|
569
|
+
}
|
|
570
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
571
|
+
if (!existsSync4(employeesPath)) {
|
|
572
|
+
return [];
|
|
573
|
+
}
|
|
574
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
575
|
+
try {
|
|
576
|
+
return JSON.parse(raw);
|
|
577
|
+
} catch {
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
582
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
583
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
584
|
+
}
|
|
370
585
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
371
|
-
if (!
|
|
586
|
+
if (!existsSync4(employeesPath)) return [];
|
|
372
587
|
try {
|
|
373
|
-
return JSON.parse(
|
|
588
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
374
589
|
} catch {
|
|
375
590
|
return [];
|
|
376
591
|
}
|
|
377
592
|
}
|
|
378
|
-
|
|
593
|
+
function getEmployee(employees, name) {
|
|
594
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
595
|
+
}
|
|
596
|
+
function getEmployeeByRole(employees, role) {
|
|
597
|
+
const lower = role.toLowerCase();
|
|
598
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
599
|
+
}
|
|
600
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
601
|
+
const lower = role.toLowerCase();
|
|
602
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
603
|
+
}
|
|
604
|
+
function hasRole(agentName, role) {
|
|
605
|
+
const employees = loadEmployeesSync();
|
|
606
|
+
const emp = getEmployee(employees, agentName);
|
|
607
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
608
|
+
}
|
|
609
|
+
function baseAgentName(name, employees) {
|
|
610
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
611
|
+
if (!match) return name;
|
|
612
|
+
const base = match[1];
|
|
613
|
+
const roster = employees ?? loadEmployeesSync();
|
|
614
|
+
if (getEmployee(roster, base)) return base;
|
|
615
|
+
return name;
|
|
616
|
+
}
|
|
617
|
+
function isMultiInstance(agentName, employees) {
|
|
618
|
+
const roster = employees ?? loadEmployeesSync();
|
|
619
|
+
const emp = getEmployee(roster, agentName);
|
|
620
|
+
if (!emp) return false;
|
|
621
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
622
|
+
}
|
|
623
|
+
function addEmployee(employees, employee) {
|
|
624
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
625
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
626
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
627
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
628
|
+
}
|
|
629
|
+
return [...employees, normalized];
|
|
630
|
+
}
|
|
631
|
+
function appendToCoordinatorTeam(employee) {
|
|
632
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
633
|
+
if (!coordinator) return;
|
|
634
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
635
|
+
if (!existsSync4(idPath)) return;
|
|
636
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
637
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
638
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
639
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
640
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
641
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
642
|
+
const entry = `
|
|
643
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
644
|
+
`;
|
|
645
|
+
let updated;
|
|
646
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
647
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
648
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
649
|
+
} else {
|
|
650
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
651
|
+
}
|
|
652
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
653
|
+
}
|
|
654
|
+
function capitalize(s) {
|
|
655
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
656
|
+
}
|
|
657
|
+
async function hireEmployee(employee) {
|
|
658
|
+
const employees = await loadEmployees();
|
|
659
|
+
const updated = addEmployee(employees, employee);
|
|
660
|
+
await saveEmployees(updated);
|
|
661
|
+
try {
|
|
662
|
+
appendToCoordinatorTeam(employee);
|
|
663
|
+
} catch {
|
|
664
|
+
}
|
|
665
|
+
try {
|
|
666
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
667
|
+
const config = loadAgentConfig2();
|
|
668
|
+
const name = employee.name.toLowerCase();
|
|
669
|
+
if (!config[name] && config["default"]) {
|
|
670
|
+
config[name] = { ...config["default"] };
|
|
671
|
+
saveAgentConfig2(config);
|
|
672
|
+
}
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
return updated;
|
|
676
|
+
}
|
|
677
|
+
async function normalizeRosterCase(rosterPath) {
|
|
678
|
+
const employees = await loadEmployees(rosterPath);
|
|
679
|
+
let changed = false;
|
|
680
|
+
for (const emp of employees) {
|
|
681
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
682
|
+
const oldName = emp.name;
|
|
683
|
+
emp.name = emp.name.toLowerCase();
|
|
684
|
+
changed = true;
|
|
685
|
+
try {
|
|
686
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
687
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
688
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
689
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
690
|
+
renameSync2(oldPath, newPath);
|
|
691
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
692
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
693
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
694
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
695
|
+
unlinkSync(oldPath);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (changed) {
|
|
703
|
+
await saveEmployees(employees, rosterPath);
|
|
704
|
+
}
|
|
705
|
+
return changed;
|
|
706
|
+
}
|
|
707
|
+
function findExeBin() {
|
|
708
|
+
try {
|
|
709
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
710
|
+
} catch {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
function registerBinSymlinks(name) {
|
|
715
|
+
const created = [];
|
|
716
|
+
const skipped = [];
|
|
717
|
+
const errors = [];
|
|
718
|
+
const exeBinPath = findExeBin();
|
|
719
|
+
if (!exeBinPath) {
|
|
720
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
721
|
+
return { created, skipped, errors };
|
|
722
|
+
}
|
|
723
|
+
const binDir = path3.dirname(exeBinPath);
|
|
724
|
+
let target;
|
|
725
|
+
try {
|
|
726
|
+
target = readlinkSync(exeBinPath);
|
|
727
|
+
} catch {
|
|
728
|
+
errors.push("Could not read 'exe' symlink");
|
|
729
|
+
return { created, skipped, errors };
|
|
730
|
+
}
|
|
731
|
+
for (const suffix of ["", "-opencode"]) {
|
|
732
|
+
const linkName = `${name}${suffix}`;
|
|
733
|
+
const linkPath = path3.join(binDir, linkName);
|
|
734
|
+
if (existsSync4(linkPath)) {
|
|
735
|
+
skipped.push(linkName);
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
try {
|
|
739
|
+
symlinkSync(target, linkPath);
|
|
740
|
+
created.push(linkName);
|
|
741
|
+
} catch (err) {
|
|
742
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return { created, skipped, errors };
|
|
746
|
+
}
|
|
747
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
379
748
|
var init_employees = __esm({
|
|
380
749
|
"src/lib/employees.ts"() {
|
|
381
750
|
"use strict";
|
|
382
751
|
init_config();
|
|
383
|
-
EMPLOYEES_PATH =
|
|
752
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
384
753
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
385
754
|
COORDINATOR_ROLE = "COO";
|
|
386
|
-
|
|
755
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
756
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
757
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
387
758
|
}
|
|
388
759
|
});
|
|
389
760
|
|
|
390
761
|
// src/lib/database-adapter.ts
|
|
391
762
|
import os3 from "os";
|
|
392
|
-
import
|
|
763
|
+
import path4 from "path";
|
|
393
764
|
import { createRequire } from "module";
|
|
394
765
|
import { pathToFileURL } from "url";
|
|
395
766
|
function quotedIdentifier(identifier) {
|
|
@@ -700,8 +1071,8 @@ async function loadPrismaClient() {
|
|
|
700
1071
|
}
|
|
701
1072
|
return new PrismaClient2();
|
|
702
1073
|
}
|
|
703
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
704
|
-
const requireFromExeDb = createRequire(
|
|
1074
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1075
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
705
1076
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
706
1077
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
707
1078
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -982,8 +1353,8 @@ var init_memory = __esm({
|
|
|
982
1353
|
|
|
983
1354
|
// src/lib/daemon-auth.ts
|
|
984
1355
|
import crypto from "crypto";
|
|
985
|
-
import
|
|
986
|
-
import { existsSync as
|
|
1356
|
+
import path5 from "path";
|
|
1357
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
987
1358
|
function normalizeToken(token) {
|
|
988
1359
|
if (!token) return null;
|
|
989
1360
|
const trimmed = token.trim();
|
|
@@ -991,8 +1362,8 @@ function normalizeToken(token) {
|
|
|
991
1362
|
}
|
|
992
1363
|
function readDaemonToken() {
|
|
993
1364
|
try {
|
|
994
|
-
if (!
|
|
995
|
-
return normalizeToken(
|
|
1365
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1366
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
996
1367
|
} catch {
|
|
997
1368
|
return null;
|
|
998
1369
|
}
|
|
@@ -1002,7 +1373,7 @@ function ensureDaemonToken(seed) {
|
|
|
1002
1373
|
if (existing) return existing;
|
|
1003
1374
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1004
1375
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1005
|
-
|
|
1376
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1006
1377
|
`, "utf8");
|
|
1007
1378
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1008
1379
|
return token;
|
|
@@ -1013,7 +1384,7 @@ var init_daemon_auth = __esm({
|
|
|
1013
1384
|
"use strict";
|
|
1014
1385
|
init_config();
|
|
1015
1386
|
init_secure_files();
|
|
1016
|
-
DAEMON_TOKEN_PATH =
|
|
1387
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1017
1388
|
}
|
|
1018
1389
|
});
|
|
1019
1390
|
|
|
@@ -1033,8 +1404,8 @@ import net from "net";
|
|
|
1033
1404
|
import os4 from "os";
|
|
1034
1405
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1035
1406
|
import { randomUUID } from "crypto";
|
|
1036
|
-
import { existsSync as
|
|
1037
|
-
import
|
|
1407
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1408
|
+
import path6 from "path";
|
|
1038
1409
|
import { fileURLToPath } from "url";
|
|
1039
1410
|
function handleData(chunk) {
|
|
1040
1411
|
_buffer += chunk.toString();
|
|
@@ -1070,9 +1441,9 @@ function isZombie(pid) {
|
|
|
1070
1441
|
}
|
|
1071
1442
|
}
|
|
1072
1443
|
function cleanupStaleFiles() {
|
|
1073
|
-
if (
|
|
1444
|
+
if (existsSync6(PID_PATH)) {
|
|
1074
1445
|
try {
|
|
1075
|
-
const pid = parseInt(
|
|
1446
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1076
1447
|
if (pid > 0) {
|
|
1077
1448
|
try {
|
|
1078
1449
|
process.kill(pid, 0);
|
|
@@ -1097,11 +1468,11 @@ function cleanupStaleFiles() {
|
|
|
1097
1468
|
}
|
|
1098
1469
|
}
|
|
1099
1470
|
function findPackageRoot() {
|
|
1100
|
-
let dir =
|
|
1101
|
-
const { root } =
|
|
1471
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1472
|
+
const { root } = path6.parse(dir);
|
|
1102
1473
|
while (dir !== root) {
|
|
1103
|
-
if (
|
|
1104
|
-
dir =
|
|
1474
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1475
|
+
dir = path6.dirname(dir);
|
|
1105
1476
|
}
|
|
1106
1477
|
return null;
|
|
1107
1478
|
}
|
|
@@ -1119,8 +1490,8 @@ function spawnDaemon() {
|
|
|
1119
1490
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1120
1491
|
return;
|
|
1121
1492
|
}
|
|
1122
|
-
const daemonPath =
|
|
1123
|
-
if (!
|
|
1493
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1494
|
+
if (!existsSync6(daemonPath)) {
|
|
1124
1495
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1125
1496
|
`);
|
|
1126
1497
|
return;
|
|
@@ -1129,7 +1500,7 @@ function spawnDaemon() {
|
|
|
1129
1500
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1130
1501
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1131
1502
|
`);
|
|
1132
|
-
const logPath =
|
|
1503
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1133
1504
|
let stderrFd = "ignore";
|
|
1134
1505
|
try {
|
|
1135
1506
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1294,9 +1665,9 @@ function killAndRespawnDaemon() {
|
|
|
1294
1665
|
}
|
|
1295
1666
|
try {
|
|
1296
1667
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1297
|
-
if (
|
|
1668
|
+
if (existsSync6(PID_PATH)) {
|
|
1298
1669
|
try {
|
|
1299
|
-
const pid = parseInt(
|
|
1670
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1300
1671
|
if (pid > 0) {
|
|
1301
1672
|
try {
|
|
1302
1673
|
process.kill(pid, "SIGKILL");
|
|
@@ -1442,9 +1813,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1442
1813
|
"use strict";
|
|
1443
1814
|
init_config();
|
|
1444
1815
|
init_daemon_auth();
|
|
1445
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1446
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1447
|
-
SPAWN_LOCK_PATH =
|
|
1816
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1817
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1818
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1448
1819
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1449
1820
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1450
1821
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1712,7 +2083,7 @@ __export(database_exports, {
|
|
|
1712
2083
|
isInitialized: () => isInitialized,
|
|
1713
2084
|
setExternalClient: () => setExternalClient
|
|
1714
2085
|
});
|
|
1715
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
2086
|
+
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";
|
|
1716
2087
|
import { createClient } from "@libsql/client";
|
|
1717
2088
|
import { homedir } from "os";
|
|
1718
2089
|
import { join } from "path";
|
|
@@ -1765,11 +2136,11 @@ function releaseDbLock() {
|
|
|
1765
2136
|
}
|
|
1766
2137
|
async function initDatabase(config) {
|
|
1767
2138
|
acquireDbLock();
|
|
1768
|
-
if (
|
|
2139
|
+
if (existsSync7(config.dbPath)) {
|
|
1769
2140
|
const dbStat = statSync2(config.dbPath);
|
|
1770
2141
|
if (dbStat.size === 0) {
|
|
1771
2142
|
const walPath = config.dbPath + "-wal";
|
|
1772
|
-
if (
|
|
2143
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1773
2144
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1774
2145
|
copyFileSync(config.dbPath, backupPath);
|
|
1775
2146
|
unlinkSync3(config.dbPath);
|
|
@@ -3355,16 +3726,16 @@ var init_database = __esm({
|
|
|
3355
3726
|
});
|
|
3356
3727
|
|
|
3357
3728
|
// src/lib/keychain.ts
|
|
3358
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3359
|
-
import { existsSync as
|
|
3729
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3730
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3360
3731
|
import { execSync as execSync3 } from "child_process";
|
|
3361
|
-
import
|
|
3732
|
+
import path7 from "path";
|
|
3362
3733
|
import os5 from "os";
|
|
3363
3734
|
function getKeyDir() {
|
|
3364
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3735
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3365
3736
|
}
|
|
3366
3737
|
function getKeyPath() {
|
|
3367
|
-
return
|
|
3738
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3368
3739
|
}
|
|
3369
3740
|
function nativeKeychainAllowed() {
|
|
3370
3741
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -3390,12 +3761,14 @@ function linuxSecretAvailable() {
|
|
|
3390
3761
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3391
3762
|
if (process.platform !== "linux") return false;
|
|
3392
3763
|
try {
|
|
3393
|
-
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3394
3764
|
const st = statSync3(keyPath);
|
|
3395
3765
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3766
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3396
3767
|
if (uid === 0) return true;
|
|
3397
3768
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3398
|
-
|
|
3769
|
+
if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
|
|
3770
|
+
if (!linuxSecretAvailable()) return true;
|
|
3771
|
+
return false;
|
|
3399
3772
|
} catch {
|
|
3400
3773
|
return false;
|
|
3401
3774
|
}
|
|
@@ -3545,15 +3918,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3545
3918
|
await mkdir3(dir, { recursive: true });
|
|
3546
3919
|
const keyPath = getKeyPath();
|
|
3547
3920
|
const machineKey = deriveMachineKey();
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3921
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
3922
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
3923
|
+
const tmpPath = keyPath + ".tmp";
|
|
3924
|
+
try {
|
|
3925
|
+
if (existsSync8(keyPath)) {
|
|
3926
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
3930
|
+
await chmod2(tmpPath, 384);
|
|
3931
|
+
await rename(tmpPath, keyPath);
|
|
3932
|
+
} catch (err) {
|
|
3933
|
+
try {
|
|
3934
|
+
await unlink(tmpPath);
|
|
3935
|
+
} catch {
|
|
3936
|
+
}
|
|
3937
|
+
throw err;
|
|
3553
3938
|
}
|
|
3554
|
-
|
|
3555
|
-
await chmod2(keyPath, 384);
|
|
3556
|
-
return "plaintext";
|
|
3939
|
+
return result;
|
|
3557
3940
|
}
|
|
3558
3941
|
async function getMasterKey() {
|
|
3559
3942
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -3592,7 +3975,7 @@ async function getMasterKey() {
|
|
|
3592
3975
|
}
|
|
3593
3976
|
}
|
|
3594
3977
|
const keyPath = getKeyPath();
|
|
3595
|
-
if (!
|
|
3978
|
+
if (!existsSync8(keyPath)) {
|
|
3596
3979
|
process.stderr.write(
|
|
3597
3980
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3598
3981
|
`
|
|
@@ -3620,7 +4003,7 @@ async function getMasterKey() {
|
|
|
3620
4003
|
b64Value = content;
|
|
3621
4004
|
}
|
|
3622
4005
|
const key = Buffer.from(b64Value, "base64");
|
|
3623
|
-
if (
|
|
4006
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3624
4007
|
return key;
|
|
3625
4008
|
}
|
|
3626
4009
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -3926,14 +4309,14 @@ __export(shard_manager_exports, {
|
|
|
3926
4309
|
listShards: () => listShards,
|
|
3927
4310
|
shardExists: () => shardExists
|
|
3928
4311
|
});
|
|
3929
|
-
import
|
|
3930
|
-
import { existsSync as
|
|
4312
|
+
import path8 from "path";
|
|
4313
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
3931
4314
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3932
4315
|
function initShardManager(encryptionKey) {
|
|
3933
4316
|
_encryptionKey = encryptionKey;
|
|
3934
4317
|
_keyValidated = false;
|
|
3935
4318
|
_keyValidationPromise = null;
|
|
3936
|
-
if (!
|
|
4319
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3937
4320
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3938
4321
|
}
|
|
3939
4322
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3954,7 +4337,7 @@ async function validateEncryptionKey() {
|
|
|
3954
4337
|
return true;
|
|
3955
4338
|
}
|
|
3956
4339
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3957
|
-
const dbPath =
|
|
4340
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3958
4341
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3959
4342
|
try {
|
|
3960
4343
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -3997,7 +4380,7 @@ function getShardClient(projectName) {
|
|
|
3997
4380
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3998
4381
|
evictLRU();
|
|
3999
4382
|
}
|
|
4000
|
-
const dbPath =
|
|
4383
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4001
4384
|
const client = createClient2({
|
|
4002
4385
|
url: `file:${dbPath}`,
|
|
4003
4386
|
encryptionKey: _encryptionKey
|
|
@@ -4008,13 +4391,13 @@ function getShardClient(projectName) {
|
|
|
4008
4391
|
}
|
|
4009
4392
|
function shardExists(projectName) {
|
|
4010
4393
|
const safeName = safeShardName(projectName);
|
|
4011
|
-
return
|
|
4394
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
4012
4395
|
}
|
|
4013
4396
|
function safeShardName(projectName) {
|
|
4014
4397
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4015
4398
|
}
|
|
4016
4399
|
function listShards() {
|
|
4017
|
-
if (!
|
|
4400
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
4018
4401
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4019
4402
|
}
|
|
4020
4403
|
async function auditShardHealth(options = {}) {
|
|
@@ -4026,7 +4409,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4026
4409
|
const names = listShards();
|
|
4027
4410
|
const shards = [];
|
|
4028
4411
|
for (const name of names) {
|
|
4029
|
-
const dbPath =
|
|
4412
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4030
4413
|
const stat = statSync4(dbPath);
|
|
4031
4414
|
const item = {
|
|
4032
4415
|
name,
|
|
@@ -4061,7 +4444,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4061
4444
|
_shards.delete(name);
|
|
4062
4445
|
_shardLastAccess.delete(name);
|
|
4063
4446
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4064
|
-
const archivedPath =
|
|
4447
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4065
4448
|
renameSync3(dbPath, archivedPath);
|
|
4066
4449
|
item.archivedPath = archivedPath;
|
|
4067
4450
|
}
|
|
@@ -4289,11 +4672,11 @@ async function getReadyShardClient(projectName) {
|
|
|
4289
4672
|
client.close();
|
|
4290
4673
|
_shards.delete(safeName);
|
|
4291
4674
|
_shardLastAccess.delete(safeName);
|
|
4292
|
-
const dbPath =
|
|
4293
|
-
if (
|
|
4675
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4676
|
+
if (existsSync9(dbPath)) {
|
|
4294
4677
|
const stat = statSync4(dbPath);
|
|
4295
4678
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4296
|
-
const archivedPath =
|
|
4679
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4297
4680
|
renameSync3(dbPath, archivedPath);
|
|
4298
4681
|
process.stderr.write(
|
|
4299
4682
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -4361,7 +4744,7 @@ var init_shard_manager = __esm({
|
|
|
4361
4744
|
"src/lib/shard-manager.ts"() {
|
|
4362
4745
|
"use strict";
|
|
4363
4746
|
init_config();
|
|
4364
|
-
SHARDS_DIR =
|
|
4747
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
4365
4748
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4366
4749
|
MAX_OPEN_SHARDS = 10;
|
|
4367
4750
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -5855,13 +6238,13 @@ var init_store = __esm({
|
|
|
5855
6238
|
});
|
|
5856
6239
|
|
|
5857
6240
|
// src/lib/session-registry.ts
|
|
5858
|
-
import
|
|
6241
|
+
import path9 from "path";
|
|
5859
6242
|
import os6 from "os";
|
|
5860
6243
|
var REGISTRY_PATH;
|
|
5861
6244
|
var init_session_registry = __esm({
|
|
5862
6245
|
"src/lib/session-registry.ts"() {
|
|
5863
6246
|
"use strict";
|
|
5864
|
-
REGISTRY_PATH =
|
|
6247
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
5865
6248
|
}
|
|
5866
6249
|
});
|
|
5867
6250
|
|
|
@@ -6079,51 +6462,6 @@ var init_provider_table = __esm({
|
|
|
6079
6462
|
}
|
|
6080
6463
|
});
|
|
6081
6464
|
|
|
6082
|
-
// src/lib/runtime-table.ts
|
|
6083
|
-
var RUNTIME_TABLE;
|
|
6084
|
-
var init_runtime_table = __esm({
|
|
6085
|
-
"src/lib/runtime-table.ts"() {
|
|
6086
|
-
"use strict";
|
|
6087
|
-
RUNTIME_TABLE = {
|
|
6088
|
-
codex: {
|
|
6089
|
-
binary: "codex",
|
|
6090
|
-
launchMode: "interactive",
|
|
6091
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
6092
|
-
inlineFlag: "--no-alt-screen",
|
|
6093
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
6094
|
-
defaultModel: "gpt-5.5"
|
|
6095
|
-
},
|
|
6096
|
-
opencode: {
|
|
6097
|
-
binary: "opencode",
|
|
6098
|
-
launchMode: "exec",
|
|
6099
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
6100
|
-
inlineFlag: "",
|
|
6101
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
6102
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
6103
|
-
}
|
|
6104
|
-
};
|
|
6105
|
-
}
|
|
6106
|
-
});
|
|
6107
|
-
|
|
6108
|
-
// src/lib/agent-config.ts
|
|
6109
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
|
|
6110
|
-
import path9 from "path";
|
|
6111
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
6112
|
-
var init_agent_config = __esm({
|
|
6113
|
-
"src/lib/agent-config.ts"() {
|
|
6114
|
-
"use strict";
|
|
6115
|
-
init_config();
|
|
6116
|
-
init_runtime_table();
|
|
6117
|
-
init_secure_files();
|
|
6118
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
6119
|
-
DEFAULT_MODELS = {
|
|
6120
|
-
claude: "claude-opus-4.6",
|
|
6121
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
6122
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
6123
|
-
};
|
|
6124
|
-
}
|
|
6125
|
-
});
|
|
6126
|
-
|
|
6127
6465
|
// src/lib/intercom-queue.ts
|
|
6128
6466
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
6129
6467
|
import path10 from "path";
|
|
@@ -6204,6 +6542,21 @@ function isRootSession(name) {
|
|
|
6204
6542
|
function extractRootExe(name) {
|
|
6205
6543
|
if (!name) return null;
|
|
6206
6544
|
if (!name.includes("-")) return name;
|
|
6545
|
+
try {
|
|
6546
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
6547
|
+
if (roster.length > 0) {
|
|
6548
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
6549
|
+
for (const agentName of sortedNames) {
|
|
6550
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6551
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
6552
|
+
const match = name.match(regex);
|
|
6553
|
+
if (match) {
|
|
6554
|
+
return extractRootExe(match[1]);
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
}
|
|
6558
|
+
} catch {
|
|
6559
|
+
}
|
|
6207
6560
|
const parts = name.split("-").filter(Boolean);
|
|
6208
6561
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6209
6562
|
}
|
|
@@ -6222,6 +6575,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6222
6575
|
function getParentExe(sessionKey) {
|
|
6223
6576
|
try {
|
|
6224
6577
|
const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6578
|
+
if (data.registeredAt) {
|
|
6579
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
6580
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
6581
|
+
}
|
|
6225
6582
|
return data.parentExe || null;
|
|
6226
6583
|
} catch {
|
|
6227
6584
|
return null;
|
|
@@ -6299,7 +6656,7 @@ function isExeSession(sessionName) {
|
|
|
6299
6656
|
const coordinatorName = getCoordinatorName();
|
|
6300
6657
|
return matchesBaseWithInstance(coordinatorName);
|
|
6301
6658
|
}
|
|
6302
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
6659
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
6303
6660
|
var init_tmux_routing = __esm({
|
|
6304
6661
|
"src/lib/tmux-routing.ts"() {
|
|
6305
6662
|
"use strict";
|
|
@@ -6317,6 +6674,7 @@ var init_tmux_routing = __esm({
|
|
|
6317
6674
|
init_agent_symlinks();
|
|
6318
6675
|
SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6319
6676
|
SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6677
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
6320
6678
|
INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6321
6679
|
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6322
6680
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|