@askexenow/exe-os 0.9.114 → 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 +13 -1
- package/dist/bin/agentic-reflection-backfill.js +13 -1
- package/dist/bin/agentic-semantic-label.js +13 -1
- package/dist/bin/backfill-conversations.js +13 -1
- package/dist/bin/backfill-responses.js +13 -1
- package/dist/bin/backfill-vectors.js +13 -1
- package/dist/bin/bulk-sync-postgres.js +13 -1
- package/dist/bin/cleanup-stale-review-tasks.js +449 -104
- package/dist/bin/cli.js +317 -40
- package/dist/bin/exe-assign.js +13 -1
- package/dist/bin/exe-boot.js +202 -39
- package/dist/bin/exe-cloud.js +13 -1
- package/dist/bin/exe-dispatch.js +315 -38
- package/dist/bin/exe-doctor.js +28 -2
- package/dist/bin/exe-export-behaviors.js +14 -1
- package/dist/bin/exe-forget.js +13 -1
- package/dist/bin/exe-gateway.js +315 -38
- package/dist/bin/exe-heartbeat.js +450 -104
- package/dist/bin/exe-kill.js +13 -1
- package/dist/bin/exe-launch-agent.js +14 -1
- package/dist/bin/exe-pending-messages.js +429 -84
- package/dist/bin/exe-pending-notifications.js +429 -84
- package/dist/bin/exe-pending-reviews.js +429 -84
- package/dist/bin/exe-rename.js +13 -1
- package/dist/bin/exe-review.js +13 -1
- package/dist/bin/exe-search.js +14 -1
- package/dist/bin/exe-session-cleanup.js +315 -38
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +14 -1
- package/dist/bin/exe-start-opencode.js +14 -1
- package/dist/bin/exe-status.js +439 -105
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +13 -1
- package/dist/bin/git-sweep.js +315 -38
- package/dist/bin/graph-backfill.js +13 -1
- package/dist/bin/graph-export.js +13 -1
- package/dist/bin/intercom-check.js +222 -38
- package/dist/bin/scan-tasks.js +315 -38
- package/dist/bin/setup.js +14 -1
- package/dist/bin/shard-migrate.js +13 -1
- package/dist/gateway/index.js +315 -38
- package/dist/hooks/bug-report-worker.js +315 -38
- package/dist/hooks/codex-stop-task-finalizer.js +277 -26
- package/dist/hooks/commit-complete.js +315 -38
- package/dist/hooks/error-recall.js +14 -1
- package/dist/hooks/ingest.js +330 -39
- package/dist/hooks/instructions-loaded.js +13 -1
- package/dist/hooks/notification.js +13 -1
- package/dist/hooks/post-compact.js +403 -61
- package/dist/hooks/post-tool-combined.js +480 -137
- package/dist/hooks/pre-compact.js +315 -38
- package/dist/hooks/pre-tool-use.js +34 -2
- package/dist/hooks/prompt-submit.js +315 -38
- package/dist/hooks/session-end.js +125 -38
- package/dist/hooks/session-start.js +35 -2
- package/dist/hooks/stop.js +397 -61
- package/dist/hooks/subagent-stop.js +396 -61
- package/dist/hooks/summary-worker.js +410 -112
- package/dist/index.js +315 -38
- package/dist/lib/cloud-sync.js +1 -1
- package/dist/lib/config.js +13 -0
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/exe-daemon.js +351 -44
- package/dist/lib/hybrid-search.js +14 -1
- package/dist/lib/keychain.js +1 -1
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +13 -1
- package/dist/lib/skill-learning.js +13 -0
- package/dist/lib/store.js +13 -1
- package/dist/lib/tasks.js +314 -37
- package/dist/lib/tmux-routing.js +314 -37
- package/dist/mcp/server.js +331 -40
- package/dist/mcp/tools/create-task.js +314 -37
- 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 +314 -37
- package/dist/runtime/index.js +315 -38
- package/dist/tui/App.js +317 -40
- 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());
|
|
@@ -321,11 +333,176 @@ var init_config = __esm({
|
|
|
321
333
|
}
|
|
322
334
|
});
|
|
323
335
|
|
|
336
|
+
// src/lib/runtime-table.ts
|
|
337
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
338
|
+
var init_runtime_table = __esm({
|
|
339
|
+
"src/lib/runtime-table.ts"() {
|
|
340
|
+
"use strict";
|
|
341
|
+
RUNTIME_TABLE = {
|
|
342
|
+
codex: {
|
|
343
|
+
binary: "codex",
|
|
344
|
+
launchMode: "interactive",
|
|
345
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
346
|
+
inlineFlag: "--no-alt-screen",
|
|
347
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
348
|
+
defaultModel: "gpt-5.5"
|
|
349
|
+
},
|
|
350
|
+
opencode: {
|
|
351
|
+
binary: "opencode",
|
|
352
|
+
launchMode: "exec",
|
|
353
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
354
|
+
inlineFlag: "",
|
|
355
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
356
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
DEFAULT_RUNTIME = "claude";
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// src/lib/agent-config.ts
|
|
364
|
+
var agent_config_exports = {};
|
|
365
|
+
__export(agent_config_exports, {
|
|
366
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
367
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
368
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
369
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
370
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
371
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
372
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
373
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
374
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
375
|
+
setAgentMcps: () => setAgentMcps,
|
|
376
|
+
setAgentRuntime: () => setAgentRuntime
|
|
377
|
+
});
|
|
378
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
379
|
+
import path2 from "path";
|
|
380
|
+
function loadAgentConfig() {
|
|
381
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
382
|
+
try {
|
|
383
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
384
|
+
} catch {
|
|
385
|
+
return {};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function saveAgentConfig(config) {
|
|
389
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
390
|
+
ensurePrivateDirSync(dir);
|
|
391
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
392
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
393
|
+
}
|
|
394
|
+
function getAgentRuntime(agentId) {
|
|
395
|
+
const config = loadAgentConfig();
|
|
396
|
+
const entry = config[agentId];
|
|
397
|
+
if (entry) return entry;
|
|
398
|
+
const orgDefault = config["default"];
|
|
399
|
+
if (orgDefault) return orgDefault;
|
|
400
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
401
|
+
}
|
|
402
|
+
function normalizeCcModelName(model) {
|
|
403
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
404
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
405
|
+
ccModel += "[1m]";
|
|
406
|
+
}
|
|
407
|
+
return ccModel;
|
|
408
|
+
}
|
|
409
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
410
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
411
|
+
if (!knownModels) {
|
|
412
|
+
return {
|
|
413
|
+
ok: false,
|
|
414
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (!knownModels.includes(model)) {
|
|
418
|
+
return {
|
|
419
|
+
ok: false,
|
|
420
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const config = loadAgentConfig();
|
|
424
|
+
const existing = config[agentId];
|
|
425
|
+
const entry = { runtime, model };
|
|
426
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
427
|
+
if (mcps !== void 0) {
|
|
428
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
429
|
+
} else if (existing?.mcps) {
|
|
430
|
+
entry.mcps = existing.mcps;
|
|
431
|
+
}
|
|
432
|
+
config[agentId] = entry;
|
|
433
|
+
saveAgentConfig(config);
|
|
434
|
+
return { ok: true };
|
|
435
|
+
}
|
|
436
|
+
function setAgentMcps(agentId, mcps) {
|
|
437
|
+
const config = loadAgentConfig();
|
|
438
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
439
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
440
|
+
config[agentId] = existing;
|
|
441
|
+
saveAgentConfig(config);
|
|
442
|
+
return { ok: true };
|
|
443
|
+
}
|
|
444
|
+
function clearAgentRuntime(agentId) {
|
|
445
|
+
const config = loadAgentConfig();
|
|
446
|
+
delete config[agentId];
|
|
447
|
+
saveAgentConfig(config);
|
|
448
|
+
}
|
|
449
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
450
|
+
var init_agent_config = __esm({
|
|
451
|
+
"src/lib/agent-config.ts"() {
|
|
452
|
+
"use strict";
|
|
453
|
+
init_config();
|
|
454
|
+
init_runtime_table();
|
|
455
|
+
init_secure_files();
|
|
456
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
457
|
+
KNOWN_RUNTIMES = {
|
|
458
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
459
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
460
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
461
|
+
};
|
|
462
|
+
RUNTIME_LABELS = {
|
|
463
|
+
claude: "Claude Code (Anthropic)",
|
|
464
|
+
codex: "Codex (OpenAI)",
|
|
465
|
+
opencode: "OpenCode (open source)"
|
|
466
|
+
};
|
|
467
|
+
DEFAULT_MODELS = {
|
|
468
|
+
claude: "claude-opus-4.6",
|
|
469
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
470
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
324
475
|
// src/lib/employees.ts
|
|
476
|
+
var employees_exports = {};
|
|
477
|
+
__export(employees_exports, {
|
|
478
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
479
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
480
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
481
|
+
addEmployee: () => addEmployee,
|
|
482
|
+
baseAgentName: () => baseAgentName,
|
|
483
|
+
canCoordinate: () => canCoordinate,
|
|
484
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
485
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
486
|
+
getEmployee: () => getEmployee,
|
|
487
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
488
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
489
|
+
hasRole: () => hasRole,
|
|
490
|
+
hireEmployee: () => hireEmployee,
|
|
491
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
492
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
493
|
+
isMultiInstance: () => isMultiInstance,
|
|
494
|
+
loadEmployees: () => loadEmployees,
|
|
495
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
496
|
+
normalizeRole: () => normalizeRole,
|
|
497
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
498
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
499
|
+
saveEmployees: () => saveEmployees,
|
|
500
|
+
validateEmployeeName: () => validateEmployeeName
|
|
501
|
+
});
|
|
325
502
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
326
|
-
import { existsSync as
|
|
503
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
327
504
|
import { execSync } from "child_process";
|
|
328
|
-
import
|
|
505
|
+
import path3 from "path";
|
|
329
506
|
import os2 from "os";
|
|
330
507
|
function normalizeRole(role) {
|
|
331
508
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -339,29 +516,222 @@ function getCoordinatorEmployee(employees) {
|
|
|
339
516
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
340
517
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
341
518
|
}
|
|
519
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
520
|
+
if (!agentName) return false;
|
|
521
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
522
|
+
}
|
|
523
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
524
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
525
|
+
}
|
|
526
|
+
function validateEmployeeName(name) {
|
|
527
|
+
if (!name) {
|
|
528
|
+
return { valid: false, error: "Name is required" };
|
|
529
|
+
}
|
|
530
|
+
if (name.length > 32) {
|
|
531
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
532
|
+
}
|
|
533
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
534
|
+
return {
|
|
535
|
+
valid: false,
|
|
536
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
return { valid: true };
|
|
540
|
+
}
|
|
541
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
542
|
+
if (!existsSync4(employeesPath)) {
|
|
543
|
+
return [];
|
|
544
|
+
}
|
|
545
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
546
|
+
try {
|
|
547
|
+
return JSON.parse(raw);
|
|
548
|
+
} catch {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
553
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
554
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
555
|
+
}
|
|
342
556
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
343
|
-
if (!
|
|
557
|
+
if (!existsSync4(employeesPath)) return [];
|
|
344
558
|
try {
|
|
345
|
-
return JSON.parse(
|
|
559
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
346
560
|
} catch {
|
|
347
561
|
return [];
|
|
348
562
|
}
|
|
349
563
|
}
|
|
350
|
-
|
|
564
|
+
function getEmployee(employees, name) {
|
|
565
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
566
|
+
}
|
|
567
|
+
function getEmployeeByRole(employees, role) {
|
|
568
|
+
const lower = role.toLowerCase();
|
|
569
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
570
|
+
}
|
|
571
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
572
|
+
const lower = role.toLowerCase();
|
|
573
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
574
|
+
}
|
|
575
|
+
function hasRole(agentName, role) {
|
|
576
|
+
const employees = loadEmployeesSync();
|
|
577
|
+
const emp = getEmployee(employees, agentName);
|
|
578
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
579
|
+
}
|
|
580
|
+
function baseAgentName(name, employees) {
|
|
581
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
582
|
+
if (!match) return name;
|
|
583
|
+
const base = match[1];
|
|
584
|
+
const roster = employees ?? loadEmployeesSync();
|
|
585
|
+
if (getEmployee(roster, base)) return base;
|
|
586
|
+
return name;
|
|
587
|
+
}
|
|
588
|
+
function isMultiInstance(agentName, employees) {
|
|
589
|
+
const roster = employees ?? loadEmployeesSync();
|
|
590
|
+
const emp = getEmployee(roster, agentName);
|
|
591
|
+
if (!emp) return false;
|
|
592
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
593
|
+
}
|
|
594
|
+
function addEmployee(employees, employee) {
|
|
595
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
596
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
597
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
598
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
599
|
+
}
|
|
600
|
+
return [...employees, normalized];
|
|
601
|
+
}
|
|
602
|
+
function appendToCoordinatorTeam(employee) {
|
|
603
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
604
|
+
if (!coordinator) return;
|
|
605
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
606
|
+
if (!existsSync4(idPath)) return;
|
|
607
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
608
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
609
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
610
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
611
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
612
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
613
|
+
const entry = `
|
|
614
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
615
|
+
`;
|
|
616
|
+
let updated;
|
|
617
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
618
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
619
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
620
|
+
} else {
|
|
621
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
622
|
+
}
|
|
623
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
624
|
+
}
|
|
625
|
+
function capitalize(s) {
|
|
626
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
627
|
+
}
|
|
628
|
+
async function hireEmployee(employee) {
|
|
629
|
+
const employees = await loadEmployees();
|
|
630
|
+
const updated = addEmployee(employees, employee);
|
|
631
|
+
await saveEmployees(updated);
|
|
632
|
+
try {
|
|
633
|
+
appendToCoordinatorTeam(employee);
|
|
634
|
+
} catch {
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
638
|
+
const config = loadAgentConfig2();
|
|
639
|
+
const name = employee.name.toLowerCase();
|
|
640
|
+
if (!config[name] && config["default"]) {
|
|
641
|
+
config[name] = { ...config["default"] };
|
|
642
|
+
saveAgentConfig2(config);
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
return updated;
|
|
647
|
+
}
|
|
648
|
+
async function normalizeRosterCase(rosterPath) {
|
|
649
|
+
const employees = await loadEmployees(rosterPath);
|
|
650
|
+
let changed = false;
|
|
651
|
+
for (const emp of employees) {
|
|
652
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
653
|
+
const oldName = emp.name;
|
|
654
|
+
emp.name = emp.name.toLowerCase();
|
|
655
|
+
changed = true;
|
|
656
|
+
try {
|
|
657
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
658
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
659
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
660
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
661
|
+
renameSync2(oldPath, newPath);
|
|
662
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
663
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
664
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
665
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
666
|
+
unlinkSync(oldPath);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (changed) {
|
|
674
|
+
await saveEmployees(employees, rosterPath);
|
|
675
|
+
}
|
|
676
|
+
return changed;
|
|
677
|
+
}
|
|
678
|
+
function findExeBin() {
|
|
679
|
+
try {
|
|
680
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
681
|
+
} catch {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function registerBinSymlinks(name) {
|
|
686
|
+
const created = [];
|
|
687
|
+
const skipped = [];
|
|
688
|
+
const errors = [];
|
|
689
|
+
const exeBinPath = findExeBin();
|
|
690
|
+
if (!exeBinPath) {
|
|
691
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
692
|
+
return { created, skipped, errors };
|
|
693
|
+
}
|
|
694
|
+
const binDir = path3.dirname(exeBinPath);
|
|
695
|
+
let target;
|
|
696
|
+
try {
|
|
697
|
+
target = readlinkSync(exeBinPath);
|
|
698
|
+
} catch {
|
|
699
|
+
errors.push("Could not read 'exe' symlink");
|
|
700
|
+
return { created, skipped, errors };
|
|
701
|
+
}
|
|
702
|
+
for (const suffix of ["", "-opencode"]) {
|
|
703
|
+
const linkName = `${name}${suffix}`;
|
|
704
|
+
const linkPath = path3.join(binDir, linkName);
|
|
705
|
+
if (existsSync4(linkPath)) {
|
|
706
|
+
skipped.push(linkName);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
symlinkSync(target, linkPath);
|
|
711
|
+
created.push(linkName);
|
|
712
|
+
} catch (err) {
|
|
713
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return { created, skipped, errors };
|
|
717
|
+
}
|
|
718
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
351
719
|
var init_employees = __esm({
|
|
352
720
|
"src/lib/employees.ts"() {
|
|
353
721
|
"use strict";
|
|
354
722
|
init_config();
|
|
355
|
-
EMPLOYEES_PATH =
|
|
723
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
356
724
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
357
725
|
COORDINATOR_ROLE = "COO";
|
|
358
|
-
|
|
726
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
727
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
728
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
359
729
|
}
|
|
360
730
|
});
|
|
361
731
|
|
|
362
732
|
// src/lib/database-adapter.ts
|
|
363
733
|
import os3 from "os";
|
|
364
|
-
import
|
|
734
|
+
import path4 from "path";
|
|
365
735
|
import { createRequire } from "module";
|
|
366
736
|
import { pathToFileURL } from "url";
|
|
367
737
|
function quotedIdentifier(identifier) {
|
|
@@ -672,8 +1042,8 @@ async function loadPrismaClient() {
|
|
|
672
1042
|
}
|
|
673
1043
|
return new PrismaClient2();
|
|
674
1044
|
}
|
|
675
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
676
|
-
const requireFromExeDb = createRequire(
|
|
1045
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1046
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
677
1047
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
678
1048
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
679
1049
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -954,8 +1324,8 @@ var init_memory = __esm({
|
|
|
954
1324
|
|
|
955
1325
|
// src/lib/daemon-auth.ts
|
|
956
1326
|
import crypto from "crypto";
|
|
957
|
-
import
|
|
958
|
-
import { existsSync as
|
|
1327
|
+
import path5 from "path";
|
|
1328
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
959
1329
|
function normalizeToken(token) {
|
|
960
1330
|
if (!token) return null;
|
|
961
1331
|
const trimmed = token.trim();
|
|
@@ -963,8 +1333,8 @@ function normalizeToken(token) {
|
|
|
963
1333
|
}
|
|
964
1334
|
function readDaemonToken() {
|
|
965
1335
|
try {
|
|
966
|
-
if (!
|
|
967
|
-
return normalizeToken(
|
|
1336
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1337
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
968
1338
|
} catch {
|
|
969
1339
|
return null;
|
|
970
1340
|
}
|
|
@@ -974,7 +1344,7 @@ function ensureDaemonToken(seed) {
|
|
|
974
1344
|
if (existing) return existing;
|
|
975
1345
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
976
1346
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
977
|
-
|
|
1347
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
978
1348
|
`, "utf8");
|
|
979
1349
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
980
1350
|
return token;
|
|
@@ -985,7 +1355,7 @@ var init_daemon_auth = __esm({
|
|
|
985
1355
|
"use strict";
|
|
986
1356
|
init_config();
|
|
987
1357
|
init_secure_files();
|
|
988
|
-
DAEMON_TOKEN_PATH =
|
|
1358
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
989
1359
|
}
|
|
990
1360
|
});
|
|
991
1361
|
|
|
@@ -1005,8 +1375,8 @@ import net from "net";
|
|
|
1005
1375
|
import os4 from "os";
|
|
1006
1376
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1007
1377
|
import { randomUUID } from "crypto";
|
|
1008
|
-
import { existsSync as
|
|
1009
|
-
import
|
|
1378
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1379
|
+
import path6 from "path";
|
|
1010
1380
|
import { fileURLToPath } from "url";
|
|
1011
1381
|
function handleData(chunk) {
|
|
1012
1382
|
_buffer += chunk.toString();
|
|
@@ -1042,9 +1412,9 @@ function isZombie(pid) {
|
|
|
1042
1412
|
}
|
|
1043
1413
|
}
|
|
1044
1414
|
function cleanupStaleFiles() {
|
|
1045
|
-
if (
|
|
1415
|
+
if (existsSync6(PID_PATH)) {
|
|
1046
1416
|
try {
|
|
1047
|
-
const pid = parseInt(
|
|
1417
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1048
1418
|
if (pid > 0) {
|
|
1049
1419
|
try {
|
|
1050
1420
|
process.kill(pid, 0);
|
|
@@ -1069,11 +1439,11 @@ function cleanupStaleFiles() {
|
|
|
1069
1439
|
}
|
|
1070
1440
|
}
|
|
1071
1441
|
function findPackageRoot() {
|
|
1072
|
-
let dir =
|
|
1073
|
-
const { root } =
|
|
1442
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1443
|
+
const { root } = path6.parse(dir);
|
|
1074
1444
|
while (dir !== root) {
|
|
1075
|
-
if (
|
|
1076
|
-
dir =
|
|
1445
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1446
|
+
dir = path6.dirname(dir);
|
|
1077
1447
|
}
|
|
1078
1448
|
return null;
|
|
1079
1449
|
}
|
|
@@ -1091,8 +1461,8 @@ function spawnDaemon() {
|
|
|
1091
1461
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1092
1462
|
return;
|
|
1093
1463
|
}
|
|
1094
|
-
const daemonPath =
|
|
1095
|
-
if (!
|
|
1464
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1465
|
+
if (!existsSync6(daemonPath)) {
|
|
1096
1466
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1097
1467
|
`);
|
|
1098
1468
|
return;
|
|
@@ -1101,7 +1471,7 @@ function spawnDaemon() {
|
|
|
1101
1471
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1102
1472
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1103
1473
|
`);
|
|
1104
|
-
const logPath =
|
|
1474
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1105
1475
|
let stderrFd = "ignore";
|
|
1106
1476
|
try {
|
|
1107
1477
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1266,9 +1636,9 @@ function killAndRespawnDaemon() {
|
|
|
1266
1636
|
}
|
|
1267
1637
|
try {
|
|
1268
1638
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1269
|
-
if (
|
|
1639
|
+
if (existsSync6(PID_PATH)) {
|
|
1270
1640
|
try {
|
|
1271
|
-
const pid = parseInt(
|
|
1641
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1272
1642
|
if (pid > 0) {
|
|
1273
1643
|
try {
|
|
1274
1644
|
process.kill(pid, "SIGKILL");
|
|
@@ -1414,9 +1784,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1414
1784
|
"use strict";
|
|
1415
1785
|
init_config();
|
|
1416
1786
|
init_daemon_auth();
|
|
1417
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1418
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1419
|
-
SPAWN_LOCK_PATH =
|
|
1787
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1788
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1789
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1420
1790
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1421
1791
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1422
1792
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1684,7 +2054,7 @@ __export(database_exports, {
|
|
|
1684
2054
|
isInitialized: () => isInitialized,
|
|
1685
2055
|
setExternalClient: () => setExternalClient
|
|
1686
2056
|
});
|
|
1687
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
2057
|
+
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";
|
|
1688
2058
|
import { createClient } from "@libsql/client";
|
|
1689
2059
|
import { homedir } from "os";
|
|
1690
2060
|
import { join } from "path";
|
|
@@ -1737,11 +2107,11 @@ function releaseDbLock() {
|
|
|
1737
2107
|
}
|
|
1738
2108
|
async function initDatabase(config) {
|
|
1739
2109
|
acquireDbLock();
|
|
1740
|
-
if (
|
|
2110
|
+
if (existsSync7(config.dbPath)) {
|
|
1741
2111
|
const dbStat = statSync2(config.dbPath);
|
|
1742
2112
|
if (dbStat.size === 0) {
|
|
1743
2113
|
const walPath = config.dbPath + "-wal";
|
|
1744
|
-
if (
|
|
2114
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1745
2115
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1746
2116
|
copyFileSync(config.dbPath, backupPath);
|
|
1747
2117
|
unlinkSync3(config.dbPath);
|
|
@@ -3328,15 +3698,15 @@ var init_database = __esm({
|
|
|
3328
3698
|
|
|
3329
3699
|
// src/lib/keychain.ts
|
|
3330
3700
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3331
|
-
import { existsSync as
|
|
3701
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3332
3702
|
import { execSync as execSync3 } from "child_process";
|
|
3333
|
-
import
|
|
3703
|
+
import path7 from "path";
|
|
3334
3704
|
import os5 from "os";
|
|
3335
3705
|
function getKeyDir() {
|
|
3336
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3706
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3337
3707
|
}
|
|
3338
3708
|
function getKeyPath() {
|
|
3339
|
-
return
|
|
3709
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3340
3710
|
}
|
|
3341
3711
|
function nativeKeychainAllowed() {
|
|
3342
3712
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -3367,7 +3737,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
|
3367
3737
|
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3368
3738
|
if (uid === 0) return true;
|
|
3369
3739
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3370
|
-
if (exeOsDir &&
|
|
3740
|
+
if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
|
|
3371
3741
|
if (!linuxSecretAvailable()) return true;
|
|
3372
3742
|
return false;
|
|
3373
3743
|
} catch {
|
|
@@ -3523,7 +3893,7 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3523
3893
|
const result = machineKey ? "encrypted" : "plaintext";
|
|
3524
3894
|
const tmpPath = keyPath + ".tmp";
|
|
3525
3895
|
try {
|
|
3526
|
-
if (
|
|
3896
|
+
if (existsSync8(keyPath)) {
|
|
3527
3897
|
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3528
3898
|
});
|
|
3529
3899
|
}
|
|
@@ -3576,7 +3946,7 @@ async function getMasterKey() {
|
|
|
3576
3946
|
}
|
|
3577
3947
|
}
|
|
3578
3948
|
const keyPath = getKeyPath();
|
|
3579
|
-
if (!
|
|
3949
|
+
if (!existsSync8(keyPath)) {
|
|
3580
3950
|
process.stderr.write(
|
|
3581
3951
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3582
3952
|
`
|
|
@@ -3604,7 +3974,7 @@ async function getMasterKey() {
|
|
|
3604
3974
|
b64Value = content;
|
|
3605
3975
|
}
|
|
3606
3976
|
const key = Buffer.from(b64Value, "base64");
|
|
3607
|
-
if (
|
|
3977
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3608
3978
|
return key;
|
|
3609
3979
|
}
|
|
3610
3980
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -3910,14 +4280,14 @@ __export(shard_manager_exports, {
|
|
|
3910
4280
|
listShards: () => listShards,
|
|
3911
4281
|
shardExists: () => shardExists
|
|
3912
4282
|
});
|
|
3913
|
-
import
|
|
3914
|
-
import { existsSync as
|
|
4283
|
+
import path8 from "path";
|
|
4284
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
3915
4285
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3916
4286
|
function initShardManager(encryptionKey) {
|
|
3917
4287
|
_encryptionKey = encryptionKey;
|
|
3918
4288
|
_keyValidated = false;
|
|
3919
4289
|
_keyValidationPromise = null;
|
|
3920
|
-
if (!
|
|
4290
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3921
4291
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3922
4292
|
}
|
|
3923
4293
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3938,7 +4308,7 @@ async function validateEncryptionKey() {
|
|
|
3938
4308
|
return true;
|
|
3939
4309
|
}
|
|
3940
4310
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3941
|
-
const dbPath =
|
|
4311
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3942
4312
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3943
4313
|
try {
|
|
3944
4314
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -3981,7 +4351,7 @@ function getShardClient(projectName) {
|
|
|
3981
4351
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3982
4352
|
evictLRU();
|
|
3983
4353
|
}
|
|
3984
|
-
const dbPath =
|
|
4354
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3985
4355
|
const client = createClient2({
|
|
3986
4356
|
url: `file:${dbPath}`,
|
|
3987
4357
|
encryptionKey: _encryptionKey
|
|
@@ -3992,13 +4362,13 @@ function getShardClient(projectName) {
|
|
|
3992
4362
|
}
|
|
3993
4363
|
function shardExists(projectName) {
|
|
3994
4364
|
const safeName = safeShardName(projectName);
|
|
3995
|
-
return
|
|
4365
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3996
4366
|
}
|
|
3997
4367
|
function safeShardName(projectName) {
|
|
3998
4368
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3999
4369
|
}
|
|
4000
4370
|
function listShards() {
|
|
4001
|
-
if (!
|
|
4371
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
4002
4372
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4003
4373
|
}
|
|
4004
4374
|
async function auditShardHealth(options = {}) {
|
|
@@ -4010,7 +4380,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4010
4380
|
const names = listShards();
|
|
4011
4381
|
const shards = [];
|
|
4012
4382
|
for (const name of names) {
|
|
4013
|
-
const dbPath =
|
|
4383
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4014
4384
|
const stat = statSync4(dbPath);
|
|
4015
4385
|
const item = {
|
|
4016
4386
|
name,
|
|
@@ -4045,7 +4415,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4045
4415
|
_shards.delete(name);
|
|
4046
4416
|
_shardLastAccess.delete(name);
|
|
4047
4417
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4048
|
-
const archivedPath =
|
|
4418
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4049
4419
|
renameSync3(dbPath, archivedPath);
|
|
4050
4420
|
item.archivedPath = archivedPath;
|
|
4051
4421
|
}
|
|
@@ -4273,11 +4643,11 @@ async function getReadyShardClient(projectName) {
|
|
|
4273
4643
|
client.close();
|
|
4274
4644
|
_shards.delete(safeName);
|
|
4275
4645
|
_shardLastAccess.delete(safeName);
|
|
4276
|
-
const dbPath =
|
|
4277
|
-
if (
|
|
4646
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4647
|
+
if (existsSync9(dbPath)) {
|
|
4278
4648
|
const stat = statSync4(dbPath);
|
|
4279
4649
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4280
|
-
const archivedPath =
|
|
4650
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4281
4651
|
renameSync3(dbPath, archivedPath);
|
|
4282
4652
|
process.stderr.write(
|
|
4283
4653
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -4345,7 +4715,7 @@ var init_shard_manager = __esm({
|
|
|
4345
4715
|
"src/lib/shard-manager.ts"() {
|
|
4346
4716
|
"use strict";
|
|
4347
4717
|
init_config();
|
|
4348
|
-
SHARDS_DIR =
|
|
4718
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
4349
4719
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4350
4720
|
MAX_OPEN_SHARDS = 10;
|
|
4351
4721
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -5839,13 +6209,13 @@ var init_store = __esm({
|
|
|
5839
6209
|
});
|
|
5840
6210
|
|
|
5841
6211
|
// src/lib/session-registry.ts
|
|
5842
|
-
import
|
|
6212
|
+
import path9 from "path";
|
|
5843
6213
|
import os6 from "os";
|
|
5844
6214
|
var REGISTRY_PATH;
|
|
5845
6215
|
var init_session_registry = __esm({
|
|
5846
6216
|
"src/lib/session-registry.ts"() {
|
|
5847
6217
|
"use strict";
|
|
5848
|
-
REGISTRY_PATH =
|
|
6218
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
5849
6219
|
}
|
|
5850
6220
|
});
|
|
5851
6221
|
|
|
@@ -6063,51 +6433,6 @@ var init_provider_table = __esm({
|
|
|
6063
6433
|
}
|
|
6064
6434
|
});
|
|
6065
6435
|
|
|
6066
|
-
// src/lib/runtime-table.ts
|
|
6067
|
-
var RUNTIME_TABLE;
|
|
6068
|
-
var init_runtime_table = __esm({
|
|
6069
|
-
"src/lib/runtime-table.ts"() {
|
|
6070
|
-
"use strict";
|
|
6071
|
-
RUNTIME_TABLE = {
|
|
6072
|
-
codex: {
|
|
6073
|
-
binary: "codex",
|
|
6074
|
-
launchMode: "interactive",
|
|
6075
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
6076
|
-
inlineFlag: "--no-alt-screen",
|
|
6077
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
6078
|
-
defaultModel: "gpt-5.5"
|
|
6079
|
-
},
|
|
6080
|
-
opencode: {
|
|
6081
|
-
binary: "opencode",
|
|
6082
|
-
launchMode: "exec",
|
|
6083
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
6084
|
-
inlineFlag: "",
|
|
6085
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
6086
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
6087
|
-
}
|
|
6088
|
-
};
|
|
6089
|
-
}
|
|
6090
|
-
});
|
|
6091
|
-
|
|
6092
|
-
// src/lib/agent-config.ts
|
|
6093
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
|
|
6094
|
-
import path9 from "path";
|
|
6095
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
6096
|
-
var init_agent_config = __esm({
|
|
6097
|
-
"src/lib/agent-config.ts"() {
|
|
6098
|
-
"use strict";
|
|
6099
|
-
init_config();
|
|
6100
|
-
init_runtime_table();
|
|
6101
|
-
init_secure_files();
|
|
6102
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
6103
|
-
DEFAULT_MODELS = {
|
|
6104
|
-
claude: "claude-opus-4.6",
|
|
6105
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
6106
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
6107
|
-
};
|
|
6108
|
-
}
|
|
6109
|
-
});
|
|
6110
|
-
|
|
6111
6436
|
// src/lib/intercom-queue.ts
|
|
6112
6437
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
6113
6438
|
import path10 from "path";
|
|
@@ -6188,6 +6513,21 @@ function isRootSession(name) {
|
|
|
6188
6513
|
function extractRootExe(name) {
|
|
6189
6514
|
if (!name) return null;
|
|
6190
6515
|
if (!name.includes("-")) return name;
|
|
6516
|
+
try {
|
|
6517
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
6518
|
+
if (roster.length > 0) {
|
|
6519
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
6520
|
+
for (const agentName of sortedNames) {
|
|
6521
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6522
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
6523
|
+
const match = name.match(regex);
|
|
6524
|
+
if (match) {
|
|
6525
|
+
return extractRootExe(match[1]);
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
} catch {
|
|
6530
|
+
}
|
|
6191
6531
|
const parts = name.split("-").filter(Boolean);
|
|
6192
6532
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6193
6533
|
}
|
|
@@ -6206,6 +6546,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6206
6546
|
function getParentExe(sessionKey) {
|
|
6207
6547
|
try {
|
|
6208
6548
|
const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6549
|
+
if (data.registeredAt) {
|
|
6550
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
6551
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
6552
|
+
}
|
|
6209
6553
|
return data.parentExe || null;
|
|
6210
6554
|
} catch {
|
|
6211
6555
|
return null;
|
|
@@ -6278,7 +6622,7 @@ function resolveExeSession() {
|
|
|
6278
6622
|
}
|
|
6279
6623
|
return candidate;
|
|
6280
6624
|
}
|
|
6281
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
6625
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
6282
6626
|
var init_tmux_routing = __esm({
|
|
6283
6627
|
"src/lib/tmux-routing.ts"() {
|
|
6284
6628
|
"use strict";
|
|
@@ -6296,6 +6640,7 @@ var init_tmux_routing = __esm({
|
|
|
6296
6640
|
init_agent_symlinks();
|
|
6297
6641
|
SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6298
6642
|
SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6643
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
6299
6644
|
INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6300
6645
|
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6301
6646
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|