@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
|
@@ -194,6 +194,17 @@ function normalizeOrchestration(raw) {
|
|
|
194
194
|
const userOrg = raw.orchestration ?? {};
|
|
195
195
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
196
196
|
}
|
|
197
|
+
function normalizeCloudEndpoint(raw) {
|
|
198
|
+
const cloud = raw.cloud;
|
|
199
|
+
if (!cloud?.endpoint) return;
|
|
200
|
+
const ep = String(cloud.endpoint);
|
|
201
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
202
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
203
|
+
process.stderr.write(
|
|
204
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
197
208
|
async function loadConfig() {
|
|
198
209
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
199
210
|
await ensurePrivateDir(dir);
|
|
@@ -219,6 +230,7 @@ async function loadConfig() {
|
|
|
219
230
|
normalizeSessionLifecycle(migratedCfg);
|
|
220
231
|
normalizeAutoUpdate(migratedCfg);
|
|
221
232
|
normalizeOrchestration(migratedCfg);
|
|
233
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
222
234
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
223
235
|
if (config.dbPath.startsWith("~")) {
|
|
224
236
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -320,11 +332,176 @@ var init_config = __esm({
|
|
|
320
332
|
}
|
|
321
333
|
});
|
|
322
334
|
|
|
335
|
+
// src/lib/runtime-table.ts
|
|
336
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
337
|
+
var init_runtime_table = __esm({
|
|
338
|
+
"src/lib/runtime-table.ts"() {
|
|
339
|
+
"use strict";
|
|
340
|
+
RUNTIME_TABLE = {
|
|
341
|
+
codex: {
|
|
342
|
+
binary: "codex",
|
|
343
|
+
launchMode: "interactive",
|
|
344
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
345
|
+
inlineFlag: "--no-alt-screen",
|
|
346
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
347
|
+
defaultModel: "gpt-5.5"
|
|
348
|
+
},
|
|
349
|
+
opencode: {
|
|
350
|
+
binary: "opencode",
|
|
351
|
+
launchMode: "exec",
|
|
352
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
353
|
+
inlineFlag: "",
|
|
354
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
355
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
DEFAULT_RUNTIME = "claude";
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// src/lib/agent-config.ts
|
|
363
|
+
var agent_config_exports = {};
|
|
364
|
+
__export(agent_config_exports, {
|
|
365
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
366
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
367
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
368
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
369
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
370
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
371
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
372
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
373
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
374
|
+
setAgentMcps: () => setAgentMcps,
|
|
375
|
+
setAgentRuntime: () => setAgentRuntime
|
|
376
|
+
});
|
|
377
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
378
|
+
import path2 from "path";
|
|
379
|
+
function loadAgentConfig() {
|
|
380
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
381
|
+
try {
|
|
382
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
383
|
+
} catch {
|
|
384
|
+
return {};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function saveAgentConfig(config) {
|
|
388
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
389
|
+
ensurePrivateDirSync(dir);
|
|
390
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
391
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
392
|
+
}
|
|
393
|
+
function getAgentRuntime(agentId) {
|
|
394
|
+
const config = loadAgentConfig();
|
|
395
|
+
const entry = config[agentId];
|
|
396
|
+
if (entry) return entry;
|
|
397
|
+
const orgDefault = config["default"];
|
|
398
|
+
if (orgDefault) return orgDefault;
|
|
399
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
400
|
+
}
|
|
401
|
+
function normalizeCcModelName(model) {
|
|
402
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
403
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
404
|
+
ccModel += "[1m]";
|
|
405
|
+
}
|
|
406
|
+
return ccModel;
|
|
407
|
+
}
|
|
408
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
409
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
410
|
+
if (!knownModels) {
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
if (!knownModels.includes(model)) {
|
|
417
|
+
return {
|
|
418
|
+
ok: false,
|
|
419
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const config = loadAgentConfig();
|
|
423
|
+
const existing = config[agentId];
|
|
424
|
+
const entry = { runtime, model };
|
|
425
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
426
|
+
if (mcps !== void 0) {
|
|
427
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
428
|
+
} else if (existing?.mcps) {
|
|
429
|
+
entry.mcps = existing.mcps;
|
|
430
|
+
}
|
|
431
|
+
config[agentId] = entry;
|
|
432
|
+
saveAgentConfig(config);
|
|
433
|
+
return { ok: true };
|
|
434
|
+
}
|
|
435
|
+
function setAgentMcps(agentId, mcps) {
|
|
436
|
+
const config = loadAgentConfig();
|
|
437
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
438
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
439
|
+
config[agentId] = existing;
|
|
440
|
+
saveAgentConfig(config);
|
|
441
|
+
return { ok: true };
|
|
442
|
+
}
|
|
443
|
+
function clearAgentRuntime(agentId) {
|
|
444
|
+
const config = loadAgentConfig();
|
|
445
|
+
delete config[agentId];
|
|
446
|
+
saveAgentConfig(config);
|
|
447
|
+
}
|
|
448
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
449
|
+
var init_agent_config = __esm({
|
|
450
|
+
"src/lib/agent-config.ts"() {
|
|
451
|
+
"use strict";
|
|
452
|
+
init_config();
|
|
453
|
+
init_runtime_table();
|
|
454
|
+
init_secure_files();
|
|
455
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
456
|
+
KNOWN_RUNTIMES = {
|
|
457
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
458
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
459
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
460
|
+
};
|
|
461
|
+
RUNTIME_LABELS = {
|
|
462
|
+
claude: "Claude Code (Anthropic)",
|
|
463
|
+
codex: "Codex (OpenAI)",
|
|
464
|
+
opencode: "OpenCode (open source)"
|
|
465
|
+
};
|
|
466
|
+
DEFAULT_MODELS = {
|
|
467
|
+
claude: "claude-opus-4.6",
|
|
468
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
469
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
323
474
|
// src/lib/employees.ts
|
|
475
|
+
var employees_exports = {};
|
|
476
|
+
__export(employees_exports, {
|
|
477
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
478
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
479
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
480
|
+
addEmployee: () => addEmployee,
|
|
481
|
+
baseAgentName: () => baseAgentName,
|
|
482
|
+
canCoordinate: () => canCoordinate,
|
|
483
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
484
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
485
|
+
getEmployee: () => getEmployee,
|
|
486
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
487
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
488
|
+
hasRole: () => hasRole,
|
|
489
|
+
hireEmployee: () => hireEmployee,
|
|
490
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
491
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
492
|
+
isMultiInstance: () => isMultiInstance,
|
|
493
|
+
loadEmployees: () => loadEmployees,
|
|
494
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
495
|
+
normalizeRole: () => normalizeRole,
|
|
496
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
497
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
498
|
+
saveEmployees: () => saveEmployees,
|
|
499
|
+
validateEmployeeName: () => validateEmployeeName
|
|
500
|
+
});
|
|
324
501
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
325
|
-
import { existsSync as
|
|
502
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
326
503
|
import { execSync } from "child_process";
|
|
327
|
-
import
|
|
504
|
+
import path3 from "path";
|
|
328
505
|
import os2 from "os";
|
|
329
506
|
function normalizeRole(role) {
|
|
330
507
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -338,29 +515,222 @@ function getCoordinatorEmployee(employees) {
|
|
|
338
515
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
339
516
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
340
517
|
}
|
|
518
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
519
|
+
if (!agentName) return false;
|
|
520
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
521
|
+
}
|
|
522
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
523
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
524
|
+
}
|
|
525
|
+
function validateEmployeeName(name) {
|
|
526
|
+
if (!name) {
|
|
527
|
+
return { valid: false, error: "Name is required" };
|
|
528
|
+
}
|
|
529
|
+
if (name.length > 32) {
|
|
530
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
531
|
+
}
|
|
532
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
533
|
+
return {
|
|
534
|
+
valid: false,
|
|
535
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
return { valid: true };
|
|
539
|
+
}
|
|
540
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
541
|
+
if (!existsSync4(employeesPath)) {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
545
|
+
try {
|
|
546
|
+
return JSON.parse(raw);
|
|
547
|
+
} catch {
|
|
548
|
+
return [];
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
552
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
553
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
554
|
+
}
|
|
341
555
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
342
|
-
if (!
|
|
556
|
+
if (!existsSync4(employeesPath)) return [];
|
|
343
557
|
try {
|
|
344
|
-
return JSON.parse(
|
|
558
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
345
559
|
} catch {
|
|
346
560
|
return [];
|
|
347
561
|
}
|
|
348
562
|
}
|
|
349
|
-
|
|
563
|
+
function getEmployee(employees, name) {
|
|
564
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
565
|
+
}
|
|
566
|
+
function getEmployeeByRole(employees, role) {
|
|
567
|
+
const lower = role.toLowerCase();
|
|
568
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
569
|
+
}
|
|
570
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
571
|
+
const lower = role.toLowerCase();
|
|
572
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
573
|
+
}
|
|
574
|
+
function hasRole(agentName, role) {
|
|
575
|
+
const employees = loadEmployeesSync();
|
|
576
|
+
const emp = getEmployee(employees, agentName);
|
|
577
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
578
|
+
}
|
|
579
|
+
function baseAgentName(name, employees) {
|
|
580
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
581
|
+
if (!match) return name;
|
|
582
|
+
const base = match[1];
|
|
583
|
+
const roster = employees ?? loadEmployeesSync();
|
|
584
|
+
if (getEmployee(roster, base)) return base;
|
|
585
|
+
return name;
|
|
586
|
+
}
|
|
587
|
+
function isMultiInstance(agentName, employees) {
|
|
588
|
+
const roster = employees ?? loadEmployeesSync();
|
|
589
|
+
const emp = getEmployee(roster, agentName);
|
|
590
|
+
if (!emp) return false;
|
|
591
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
592
|
+
}
|
|
593
|
+
function addEmployee(employees, employee) {
|
|
594
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
595
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
596
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
597
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
598
|
+
}
|
|
599
|
+
return [...employees, normalized];
|
|
600
|
+
}
|
|
601
|
+
function appendToCoordinatorTeam(employee) {
|
|
602
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
603
|
+
if (!coordinator) return;
|
|
604
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
605
|
+
if (!existsSync4(idPath)) return;
|
|
606
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
607
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
608
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
609
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
610
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
611
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
612
|
+
const entry = `
|
|
613
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
614
|
+
`;
|
|
615
|
+
let updated;
|
|
616
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
617
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
618
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
619
|
+
} else {
|
|
620
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
621
|
+
}
|
|
622
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
623
|
+
}
|
|
624
|
+
function capitalize(s) {
|
|
625
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
626
|
+
}
|
|
627
|
+
async function hireEmployee(employee) {
|
|
628
|
+
const employees = await loadEmployees();
|
|
629
|
+
const updated = addEmployee(employees, employee);
|
|
630
|
+
await saveEmployees(updated);
|
|
631
|
+
try {
|
|
632
|
+
appendToCoordinatorTeam(employee);
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
637
|
+
const config = loadAgentConfig2();
|
|
638
|
+
const name = employee.name.toLowerCase();
|
|
639
|
+
if (!config[name] && config["default"]) {
|
|
640
|
+
config[name] = { ...config["default"] };
|
|
641
|
+
saveAgentConfig2(config);
|
|
642
|
+
}
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
return updated;
|
|
646
|
+
}
|
|
647
|
+
async function normalizeRosterCase(rosterPath) {
|
|
648
|
+
const employees = await loadEmployees(rosterPath);
|
|
649
|
+
let changed = false;
|
|
650
|
+
for (const emp of employees) {
|
|
651
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
652
|
+
const oldName = emp.name;
|
|
653
|
+
emp.name = emp.name.toLowerCase();
|
|
654
|
+
changed = true;
|
|
655
|
+
try {
|
|
656
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
657
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
658
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
659
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
660
|
+
renameSync2(oldPath, newPath);
|
|
661
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
662
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
663
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
664
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
665
|
+
unlinkSync(oldPath);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
} catch {
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (changed) {
|
|
673
|
+
await saveEmployees(employees, rosterPath);
|
|
674
|
+
}
|
|
675
|
+
return changed;
|
|
676
|
+
}
|
|
677
|
+
function findExeBin() {
|
|
678
|
+
try {
|
|
679
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
680
|
+
} catch {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function registerBinSymlinks(name) {
|
|
685
|
+
const created = [];
|
|
686
|
+
const skipped = [];
|
|
687
|
+
const errors = [];
|
|
688
|
+
const exeBinPath = findExeBin();
|
|
689
|
+
if (!exeBinPath) {
|
|
690
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
691
|
+
return { created, skipped, errors };
|
|
692
|
+
}
|
|
693
|
+
const binDir = path3.dirname(exeBinPath);
|
|
694
|
+
let target;
|
|
695
|
+
try {
|
|
696
|
+
target = readlinkSync(exeBinPath);
|
|
697
|
+
} catch {
|
|
698
|
+
errors.push("Could not read 'exe' symlink");
|
|
699
|
+
return { created, skipped, errors };
|
|
700
|
+
}
|
|
701
|
+
for (const suffix of ["", "-opencode"]) {
|
|
702
|
+
const linkName = `${name}${suffix}`;
|
|
703
|
+
const linkPath = path3.join(binDir, linkName);
|
|
704
|
+
if (existsSync4(linkPath)) {
|
|
705
|
+
skipped.push(linkName);
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
symlinkSync(target, linkPath);
|
|
710
|
+
created.push(linkName);
|
|
711
|
+
} catch (err) {
|
|
712
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return { created, skipped, errors };
|
|
716
|
+
}
|
|
717
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
350
718
|
var init_employees = __esm({
|
|
351
719
|
"src/lib/employees.ts"() {
|
|
352
720
|
"use strict";
|
|
353
721
|
init_config();
|
|
354
|
-
EMPLOYEES_PATH =
|
|
722
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
355
723
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
356
724
|
COORDINATOR_ROLE = "COO";
|
|
357
|
-
|
|
725
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
726
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
727
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
358
728
|
}
|
|
359
729
|
});
|
|
360
730
|
|
|
361
731
|
// src/lib/database-adapter.ts
|
|
362
732
|
import os3 from "os";
|
|
363
|
-
import
|
|
733
|
+
import path4 from "path";
|
|
364
734
|
import { createRequire } from "module";
|
|
365
735
|
import { pathToFileURL } from "url";
|
|
366
736
|
function quotedIdentifier(identifier) {
|
|
@@ -671,8 +1041,8 @@ async function loadPrismaClient() {
|
|
|
671
1041
|
}
|
|
672
1042
|
return new PrismaClient2();
|
|
673
1043
|
}
|
|
674
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
675
|
-
const requireFromExeDb = createRequire(
|
|
1044
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1045
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
676
1046
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
677
1047
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
678
1048
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -953,8 +1323,8 @@ var init_memory = __esm({
|
|
|
953
1323
|
|
|
954
1324
|
// src/lib/daemon-auth.ts
|
|
955
1325
|
import crypto from "crypto";
|
|
956
|
-
import
|
|
957
|
-
import { existsSync as
|
|
1326
|
+
import path5 from "path";
|
|
1327
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
958
1328
|
function normalizeToken(token) {
|
|
959
1329
|
if (!token) return null;
|
|
960
1330
|
const trimmed = token.trim();
|
|
@@ -962,8 +1332,8 @@ function normalizeToken(token) {
|
|
|
962
1332
|
}
|
|
963
1333
|
function readDaemonToken() {
|
|
964
1334
|
try {
|
|
965
|
-
if (!
|
|
966
|
-
return normalizeToken(
|
|
1335
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1336
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
967
1337
|
} catch {
|
|
968
1338
|
return null;
|
|
969
1339
|
}
|
|
@@ -973,7 +1343,7 @@ function ensureDaemonToken(seed) {
|
|
|
973
1343
|
if (existing) return existing;
|
|
974
1344
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
975
1345
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
976
|
-
|
|
1346
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
977
1347
|
`, "utf8");
|
|
978
1348
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
979
1349
|
return token;
|
|
@@ -984,7 +1354,7 @@ var init_daemon_auth = __esm({
|
|
|
984
1354
|
"use strict";
|
|
985
1355
|
init_config();
|
|
986
1356
|
init_secure_files();
|
|
987
|
-
DAEMON_TOKEN_PATH =
|
|
1357
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
988
1358
|
}
|
|
989
1359
|
});
|
|
990
1360
|
|
|
@@ -1004,8 +1374,8 @@ import net from "net";
|
|
|
1004
1374
|
import os4 from "os";
|
|
1005
1375
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1006
1376
|
import { randomUUID } from "crypto";
|
|
1007
|
-
import { existsSync as
|
|
1008
|
-
import
|
|
1377
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1378
|
+
import path6 from "path";
|
|
1009
1379
|
import { fileURLToPath } from "url";
|
|
1010
1380
|
function handleData(chunk) {
|
|
1011
1381
|
_buffer += chunk.toString();
|
|
@@ -1041,9 +1411,9 @@ function isZombie(pid) {
|
|
|
1041
1411
|
}
|
|
1042
1412
|
}
|
|
1043
1413
|
function cleanupStaleFiles() {
|
|
1044
|
-
if (
|
|
1414
|
+
if (existsSync6(PID_PATH)) {
|
|
1045
1415
|
try {
|
|
1046
|
-
const pid = parseInt(
|
|
1416
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1047
1417
|
if (pid > 0) {
|
|
1048
1418
|
try {
|
|
1049
1419
|
process.kill(pid, 0);
|
|
@@ -1068,11 +1438,11 @@ function cleanupStaleFiles() {
|
|
|
1068
1438
|
}
|
|
1069
1439
|
}
|
|
1070
1440
|
function findPackageRoot() {
|
|
1071
|
-
let dir =
|
|
1072
|
-
const { root } =
|
|
1441
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1442
|
+
const { root } = path6.parse(dir);
|
|
1073
1443
|
while (dir !== root) {
|
|
1074
|
-
if (
|
|
1075
|
-
dir =
|
|
1444
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1445
|
+
dir = path6.dirname(dir);
|
|
1076
1446
|
}
|
|
1077
1447
|
return null;
|
|
1078
1448
|
}
|
|
@@ -1090,8 +1460,8 @@ function spawnDaemon() {
|
|
|
1090
1460
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1091
1461
|
return;
|
|
1092
1462
|
}
|
|
1093
|
-
const daemonPath =
|
|
1094
|
-
if (!
|
|
1463
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1464
|
+
if (!existsSync6(daemonPath)) {
|
|
1095
1465
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1096
1466
|
`);
|
|
1097
1467
|
return;
|
|
@@ -1100,7 +1470,7 @@ function spawnDaemon() {
|
|
|
1100
1470
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1101
1471
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1102
1472
|
`);
|
|
1103
|
-
const logPath =
|
|
1473
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1104
1474
|
let stderrFd = "ignore";
|
|
1105
1475
|
try {
|
|
1106
1476
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1265,9 +1635,9 @@ function killAndRespawnDaemon() {
|
|
|
1265
1635
|
}
|
|
1266
1636
|
try {
|
|
1267
1637
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1268
|
-
if (
|
|
1638
|
+
if (existsSync6(PID_PATH)) {
|
|
1269
1639
|
try {
|
|
1270
|
-
const pid = parseInt(
|
|
1640
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1271
1641
|
if (pid > 0) {
|
|
1272
1642
|
try {
|
|
1273
1643
|
process.kill(pid, "SIGKILL");
|
|
@@ -1413,9 +1783,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1413
1783
|
"use strict";
|
|
1414
1784
|
init_config();
|
|
1415
1785
|
init_daemon_auth();
|
|
1416
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1417
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1418
|
-
SPAWN_LOCK_PATH =
|
|
1786
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1787
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1788
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1419
1789
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1420
1790
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1421
1791
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1683,7 +2053,7 @@ __export(database_exports, {
|
|
|
1683
2053
|
isInitialized: () => isInitialized,
|
|
1684
2054
|
setExternalClient: () => setExternalClient
|
|
1685
2055
|
});
|
|
1686
|
-
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";
|
|
1687
2057
|
import { createClient } from "@libsql/client";
|
|
1688
2058
|
import { homedir } from "os";
|
|
1689
2059
|
import { join } from "path";
|
|
@@ -1736,11 +2106,11 @@ function releaseDbLock() {
|
|
|
1736
2106
|
}
|
|
1737
2107
|
async function initDatabase(config) {
|
|
1738
2108
|
acquireDbLock();
|
|
1739
|
-
if (
|
|
2109
|
+
if (existsSync7(config.dbPath)) {
|
|
1740
2110
|
const dbStat = statSync2(config.dbPath);
|
|
1741
2111
|
if (dbStat.size === 0) {
|
|
1742
2112
|
const walPath = config.dbPath + "-wal";
|
|
1743
|
-
if (
|
|
2113
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1744
2114
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1745
2115
|
copyFileSync(config.dbPath, backupPath);
|
|
1746
2116
|
unlinkSync3(config.dbPath);
|
|
@@ -3326,13 +3696,13 @@ var init_database = __esm({
|
|
|
3326
3696
|
});
|
|
3327
3697
|
|
|
3328
3698
|
// src/lib/session-registry.ts
|
|
3329
|
-
import
|
|
3699
|
+
import path7 from "path";
|
|
3330
3700
|
import os5 from "os";
|
|
3331
3701
|
var REGISTRY_PATH;
|
|
3332
3702
|
var init_session_registry = __esm({
|
|
3333
3703
|
"src/lib/session-registry.ts"() {
|
|
3334
3704
|
"use strict";
|
|
3335
|
-
REGISTRY_PATH =
|
|
3705
|
+
REGISTRY_PATH = path7.join(os5.homedir(), ".exe-os", "session-registry.json");
|
|
3336
3706
|
}
|
|
3337
3707
|
});
|
|
3338
3708
|
|
|
@@ -3550,51 +3920,6 @@ var init_provider_table = __esm({
|
|
|
3550
3920
|
}
|
|
3551
3921
|
});
|
|
3552
3922
|
|
|
3553
|
-
// src/lib/runtime-table.ts
|
|
3554
|
-
var RUNTIME_TABLE;
|
|
3555
|
-
var init_runtime_table = __esm({
|
|
3556
|
-
"src/lib/runtime-table.ts"() {
|
|
3557
|
-
"use strict";
|
|
3558
|
-
RUNTIME_TABLE = {
|
|
3559
|
-
codex: {
|
|
3560
|
-
binary: "codex",
|
|
3561
|
-
launchMode: "interactive",
|
|
3562
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
3563
|
-
inlineFlag: "--no-alt-screen",
|
|
3564
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
3565
|
-
defaultModel: "gpt-5.5"
|
|
3566
|
-
},
|
|
3567
|
-
opencode: {
|
|
3568
|
-
binary: "opencode",
|
|
3569
|
-
launchMode: "exec",
|
|
3570
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
3571
|
-
inlineFlag: "",
|
|
3572
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
3573
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
3574
|
-
}
|
|
3575
|
-
};
|
|
3576
|
-
}
|
|
3577
|
-
});
|
|
3578
|
-
|
|
3579
|
-
// src/lib/agent-config.ts
|
|
3580
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "fs";
|
|
3581
|
-
import path7 from "path";
|
|
3582
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
3583
|
-
var init_agent_config = __esm({
|
|
3584
|
-
"src/lib/agent-config.ts"() {
|
|
3585
|
-
"use strict";
|
|
3586
|
-
init_config();
|
|
3587
|
-
init_runtime_table();
|
|
3588
|
-
init_secure_files();
|
|
3589
|
-
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
3590
|
-
DEFAULT_MODELS = {
|
|
3591
|
-
claude: "claude-opus-4.6",
|
|
3592
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
3593
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
3594
|
-
};
|
|
3595
|
-
}
|
|
3596
|
-
});
|
|
3597
|
-
|
|
3598
3923
|
// src/lib/intercom-queue.ts
|
|
3599
3924
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
3600
3925
|
import path8 from "path";
|
|
@@ -3675,6 +4000,21 @@ function isRootSession(name) {
|
|
|
3675
4000
|
function extractRootExe(name) {
|
|
3676
4001
|
if (!name) return null;
|
|
3677
4002
|
if (!name.includes("-")) return name;
|
|
4003
|
+
try {
|
|
4004
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
4005
|
+
if (roster.length > 0) {
|
|
4006
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
4007
|
+
for (const agentName of sortedNames) {
|
|
4008
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4009
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
4010
|
+
const match = name.match(regex);
|
|
4011
|
+
if (match) {
|
|
4012
|
+
return extractRootExe(match[1]);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
} catch {
|
|
4017
|
+
}
|
|
3678
4018
|
const parts = name.split("-").filter(Boolean);
|
|
3679
4019
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3680
4020
|
}
|
|
@@ -3693,6 +4033,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3693
4033
|
function getParentExe(sessionKey) {
|
|
3694
4034
|
try {
|
|
3695
4035
|
const data = JSON.parse(readFileSync9(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4036
|
+
if (data.registeredAt) {
|
|
4037
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
4038
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
4039
|
+
}
|
|
3696
4040
|
return data.parentExe || null;
|
|
3697
4041
|
} catch {
|
|
3698
4042
|
return null;
|
|
@@ -3765,7 +4109,7 @@ function resolveExeSession() {
|
|
|
3765
4109
|
}
|
|
3766
4110
|
return candidate;
|
|
3767
4111
|
}
|
|
3768
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
4112
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
3769
4113
|
var init_tmux_routing = __esm({
|
|
3770
4114
|
"src/lib/tmux-routing.ts"() {
|
|
3771
4115
|
"use strict";
|
|
@@ -3783,6 +4127,7 @@ var init_tmux_routing = __esm({
|
|
|
3783
4127
|
init_agent_symlinks();
|
|
3784
4128
|
SPAWN_LOCK_DIR = path12.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
3785
4129
|
SESSION_CACHE = path12.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4130
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
3786
4131
|
INTERCOM_LOG2 = path12.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
3787
4132
|
DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3788
4133
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -3814,7 +4159,7 @@ var init_task_scope = __esm({
|
|
|
3814
4159
|
});
|
|
3815
4160
|
|
|
3816
4161
|
// src/lib/keychain.ts
|
|
3817
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
4162
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3818
4163
|
import { existsSync as existsSync13, statSync as statSync3 } from "fs";
|
|
3819
4164
|
import { execSync as execSync5 } from "child_process";
|
|
3820
4165
|
import path13 from "path";
|
|
@@ -3849,12 +4194,14 @@ function linuxSecretAvailable() {
|
|
|
3849
4194
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3850
4195
|
if (process.platform !== "linux") return false;
|
|
3851
4196
|
try {
|
|
3852
|
-
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3853
4197
|
const st = statSync3(keyPath);
|
|
3854
4198
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
4199
|
+
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3855
4200
|
if (uid === 0) return true;
|
|
3856
4201
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3857
|
-
|
|
4202
|
+
if (exeOsDir && path13.resolve(keyPath).startsWith(path13.resolve(exeOsDir) + path13.sep)) return true;
|
|
4203
|
+
if (!linuxSecretAvailable()) return true;
|
|
4204
|
+
return false;
|
|
3858
4205
|
} catch {
|
|
3859
4206
|
return false;
|
|
3860
4207
|
}
|
|
@@ -4004,15 +4351,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
4004
4351
|
await mkdir3(dir, { recursive: true });
|
|
4005
4352
|
const keyPath = getKeyPath();
|
|
4006
4353
|
const machineKey = deriveMachineKey();
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4354
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
4355
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
4356
|
+
const tmpPath = keyPath + ".tmp";
|
|
4357
|
+
try {
|
|
4358
|
+
if (existsSync13(keyPath)) {
|
|
4359
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
4360
|
+
});
|
|
4361
|
+
}
|
|
4362
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
4363
|
+
await chmod2(tmpPath, 384);
|
|
4364
|
+
await rename(tmpPath, keyPath);
|
|
4365
|
+
} catch (err) {
|
|
4366
|
+
try {
|
|
4367
|
+
await unlink(tmpPath);
|
|
4368
|
+
} catch {
|
|
4369
|
+
}
|
|
4370
|
+
throw err;
|
|
4012
4371
|
}
|
|
4013
|
-
|
|
4014
|
-
await chmod2(keyPath, 384);
|
|
4015
|
-
return "plaintext";
|
|
4372
|
+
return result;
|
|
4016
4373
|
}
|
|
4017
4374
|
async function getMasterKey() {
|
|
4018
4375
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4079,7 +4436,7 @@ async function getMasterKey() {
|
|
|
4079
4436
|
b64Value = content;
|
|
4080
4437
|
}
|
|
4081
4438
|
const key = Buffer.from(b64Value, "base64");
|
|
4082
|
-
if (
|
|
4439
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
4083
4440
|
return key;
|
|
4084
4441
|
}
|
|
4085
4442
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|