@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
package/dist/bin/exe-status.js
CHANGED
|
@@ -140,6 +140,17 @@ function normalizeOrchestration(raw) {
|
|
|
140
140
|
const userOrg = raw.orchestration ?? {};
|
|
141
141
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
142
142
|
}
|
|
143
|
+
function normalizeCloudEndpoint(raw) {
|
|
144
|
+
const cloud = raw.cloud;
|
|
145
|
+
if (!cloud?.endpoint) return;
|
|
146
|
+
const ep = String(cloud.endpoint);
|
|
147
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
148
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
149
|
+
process.stderr.write(
|
|
150
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
143
154
|
async function loadConfig() {
|
|
144
155
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
145
156
|
await ensurePrivateDir(dir);
|
|
@@ -165,6 +176,7 @@ async function loadConfig() {
|
|
|
165
176
|
normalizeSessionLifecycle(migratedCfg);
|
|
166
177
|
normalizeAutoUpdate(migratedCfg);
|
|
167
178
|
normalizeOrchestration(migratedCfg);
|
|
179
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
168
180
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
169
181
|
if (config.dbPath.startsWith("~")) {
|
|
170
182
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -266,11 +278,176 @@ var init_config = __esm({
|
|
|
266
278
|
}
|
|
267
279
|
});
|
|
268
280
|
|
|
281
|
+
// src/lib/runtime-table.ts
|
|
282
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
283
|
+
var init_runtime_table = __esm({
|
|
284
|
+
"src/lib/runtime-table.ts"() {
|
|
285
|
+
"use strict";
|
|
286
|
+
RUNTIME_TABLE = {
|
|
287
|
+
codex: {
|
|
288
|
+
binary: "codex",
|
|
289
|
+
launchMode: "interactive",
|
|
290
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
291
|
+
inlineFlag: "--no-alt-screen",
|
|
292
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
293
|
+
defaultModel: "gpt-5.5"
|
|
294
|
+
},
|
|
295
|
+
opencode: {
|
|
296
|
+
binary: "opencode",
|
|
297
|
+
launchMode: "exec",
|
|
298
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
299
|
+
inlineFlag: "",
|
|
300
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
301
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
DEFAULT_RUNTIME = "claude";
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// src/lib/agent-config.ts
|
|
309
|
+
var agent_config_exports = {};
|
|
310
|
+
__export(agent_config_exports, {
|
|
311
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
312
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
313
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
314
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
315
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
316
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
317
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
318
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
319
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
320
|
+
setAgentMcps: () => setAgentMcps,
|
|
321
|
+
setAgentRuntime: () => setAgentRuntime
|
|
322
|
+
});
|
|
323
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
324
|
+
import path2 from "path";
|
|
325
|
+
function loadAgentConfig() {
|
|
326
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
327
|
+
try {
|
|
328
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
329
|
+
} catch {
|
|
330
|
+
return {};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function saveAgentConfig(config) {
|
|
334
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
335
|
+
ensurePrivateDirSync(dir);
|
|
336
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
337
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
338
|
+
}
|
|
339
|
+
function getAgentRuntime(agentId) {
|
|
340
|
+
const config = loadAgentConfig();
|
|
341
|
+
const entry = config[agentId];
|
|
342
|
+
if (entry) return entry;
|
|
343
|
+
const orgDefault = config["default"];
|
|
344
|
+
if (orgDefault) return orgDefault;
|
|
345
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
346
|
+
}
|
|
347
|
+
function normalizeCcModelName(model) {
|
|
348
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
349
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
350
|
+
ccModel += "[1m]";
|
|
351
|
+
}
|
|
352
|
+
return ccModel;
|
|
353
|
+
}
|
|
354
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
355
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
356
|
+
if (!knownModels) {
|
|
357
|
+
return {
|
|
358
|
+
ok: false,
|
|
359
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
if (!knownModels.includes(model)) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const config = loadAgentConfig();
|
|
369
|
+
const existing = config[agentId];
|
|
370
|
+
const entry = { runtime, model };
|
|
371
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
372
|
+
if (mcps !== void 0) {
|
|
373
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
374
|
+
} else if (existing?.mcps) {
|
|
375
|
+
entry.mcps = existing.mcps;
|
|
376
|
+
}
|
|
377
|
+
config[agentId] = entry;
|
|
378
|
+
saveAgentConfig(config);
|
|
379
|
+
return { ok: true };
|
|
380
|
+
}
|
|
381
|
+
function setAgentMcps(agentId, mcps) {
|
|
382
|
+
const config = loadAgentConfig();
|
|
383
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
384
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
385
|
+
config[agentId] = existing;
|
|
386
|
+
saveAgentConfig(config);
|
|
387
|
+
return { ok: true };
|
|
388
|
+
}
|
|
389
|
+
function clearAgentRuntime(agentId) {
|
|
390
|
+
const config = loadAgentConfig();
|
|
391
|
+
delete config[agentId];
|
|
392
|
+
saveAgentConfig(config);
|
|
393
|
+
}
|
|
394
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
395
|
+
var init_agent_config = __esm({
|
|
396
|
+
"src/lib/agent-config.ts"() {
|
|
397
|
+
"use strict";
|
|
398
|
+
init_config();
|
|
399
|
+
init_runtime_table();
|
|
400
|
+
init_secure_files();
|
|
401
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
402
|
+
KNOWN_RUNTIMES = {
|
|
403
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
404
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
405
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
406
|
+
};
|
|
407
|
+
RUNTIME_LABELS = {
|
|
408
|
+
claude: "Claude Code (Anthropic)",
|
|
409
|
+
codex: "Codex (OpenAI)",
|
|
410
|
+
opencode: "OpenCode (open source)"
|
|
411
|
+
};
|
|
412
|
+
DEFAULT_MODELS = {
|
|
413
|
+
claude: "claude-opus-4.6",
|
|
414
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
415
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
269
420
|
// src/lib/employees.ts
|
|
421
|
+
var employees_exports = {};
|
|
422
|
+
__export(employees_exports, {
|
|
423
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
424
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
425
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
426
|
+
addEmployee: () => addEmployee,
|
|
427
|
+
baseAgentName: () => baseAgentName,
|
|
428
|
+
canCoordinate: () => canCoordinate,
|
|
429
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
430
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
431
|
+
getEmployee: () => getEmployee,
|
|
432
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
433
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
434
|
+
hasRole: () => hasRole,
|
|
435
|
+
hireEmployee: () => hireEmployee,
|
|
436
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
437
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
438
|
+
isMultiInstance: () => isMultiInstance,
|
|
439
|
+
loadEmployees: () => loadEmployees,
|
|
440
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
441
|
+
normalizeRole: () => normalizeRole,
|
|
442
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
443
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
444
|
+
saveEmployees: () => saveEmployees,
|
|
445
|
+
validateEmployeeName: () => validateEmployeeName
|
|
446
|
+
});
|
|
270
447
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
271
|
-
import { existsSync as
|
|
448
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
272
449
|
import { execSync } from "child_process";
|
|
273
|
-
import
|
|
450
|
+
import path3 from "path";
|
|
274
451
|
import os2 from "os";
|
|
275
452
|
function normalizeRole(role) {
|
|
276
453
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -284,8 +461,30 @@ function getCoordinatorEmployee(employees) {
|
|
|
284
461
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
285
462
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
286
463
|
}
|
|
464
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
465
|
+
if (!agentName) return false;
|
|
466
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
467
|
+
}
|
|
468
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
469
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
470
|
+
}
|
|
471
|
+
function validateEmployeeName(name) {
|
|
472
|
+
if (!name) {
|
|
473
|
+
return { valid: false, error: "Name is required" };
|
|
474
|
+
}
|
|
475
|
+
if (name.length > 32) {
|
|
476
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
477
|
+
}
|
|
478
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
479
|
+
return {
|
|
480
|
+
valid: false,
|
|
481
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
return { valid: true };
|
|
485
|
+
}
|
|
287
486
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
288
|
-
if (!
|
|
487
|
+
if (!existsSync4(employeesPath)) {
|
|
289
488
|
return [];
|
|
290
489
|
}
|
|
291
490
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -295,23 +494,183 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
295
494
|
return [];
|
|
296
495
|
}
|
|
297
496
|
}
|
|
497
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
498
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
499
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
500
|
+
}
|
|
298
501
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
299
|
-
if (!
|
|
502
|
+
if (!existsSync4(employeesPath)) return [];
|
|
300
503
|
try {
|
|
301
|
-
return JSON.parse(
|
|
504
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
302
505
|
} catch {
|
|
303
506
|
return [];
|
|
304
507
|
}
|
|
305
508
|
}
|
|
306
|
-
|
|
509
|
+
function getEmployee(employees, name) {
|
|
510
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
511
|
+
}
|
|
512
|
+
function getEmployeeByRole(employees, role) {
|
|
513
|
+
const lower = role.toLowerCase();
|
|
514
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
515
|
+
}
|
|
516
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
517
|
+
const lower = role.toLowerCase();
|
|
518
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
519
|
+
}
|
|
520
|
+
function hasRole(agentName, role) {
|
|
521
|
+
const employees = loadEmployeesSync();
|
|
522
|
+
const emp = getEmployee(employees, agentName);
|
|
523
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
524
|
+
}
|
|
525
|
+
function baseAgentName(name, employees) {
|
|
526
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
527
|
+
if (!match) return name;
|
|
528
|
+
const base = match[1];
|
|
529
|
+
const roster = employees ?? loadEmployeesSync();
|
|
530
|
+
if (getEmployee(roster, base)) return base;
|
|
531
|
+
return name;
|
|
532
|
+
}
|
|
533
|
+
function isMultiInstance(agentName, employees) {
|
|
534
|
+
const roster = employees ?? loadEmployeesSync();
|
|
535
|
+
const emp = getEmployee(roster, agentName);
|
|
536
|
+
if (!emp) return false;
|
|
537
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
538
|
+
}
|
|
539
|
+
function addEmployee(employees, employee) {
|
|
540
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
541
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
542
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
543
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
544
|
+
}
|
|
545
|
+
return [...employees, normalized];
|
|
546
|
+
}
|
|
547
|
+
function appendToCoordinatorTeam(employee) {
|
|
548
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
549
|
+
if (!coordinator) return;
|
|
550
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
551
|
+
if (!existsSync4(idPath)) return;
|
|
552
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
553
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
554
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
555
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
556
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
557
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
558
|
+
const entry = `
|
|
559
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
560
|
+
`;
|
|
561
|
+
let updated;
|
|
562
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
563
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
564
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
565
|
+
} else {
|
|
566
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
567
|
+
}
|
|
568
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
569
|
+
}
|
|
570
|
+
function capitalize(s) {
|
|
571
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
572
|
+
}
|
|
573
|
+
async function hireEmployee(employee) {
|
|
574
|
+
const employees = await loadEmployees();
|
|
575
|
+
const updated = addEmployee(employees, employee);
|
|
576
|
+
await saveEmployees(updated);
|
|
577
|
+
try {
|
|
578
|
+
appendToCoordinatorTeam(employee);
|
|
579
|
+
} catch {
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
583
|
+
const config = loadAgentConfig2();
|
|
584
|
+
const name = employee.name.toLowerCase();
|
|
585
|
+
if (!config[name] && config["default"]) {
|
|
586
|
+
config[name] = { ...config["default"] };
|
|
587
|
+
saveAgentConfig2(config);
|
|
588
|
+
}
|
|
589
|
+
} catch {
|
|
590
|
+
}
|
|
591
|
+
return updated;
|
|
592
|
+
}
|
|
593
|
+
async function normalizeRosterCase(rosterPath) {
|
|
594
|
+
const employees = await loadEmployees(rosterPath);
|
|
595
|
+
let changed = false;
|
|
596
|
+
for (const emp of employees) {
|
|
597
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
598
|
+
const oldName = emp.name;
|
|
599
|
+
emp.name = emp.name.toLowerCase();
|
|
600
|
+
changed = true;
|
|
601
|
+
try {
|
|
602
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
603
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
604
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
605
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
606
|
+
renameSync2(oldPath, newPath);
|
|
607
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
608
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
609
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
610
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
611
|
+
unlinkSync(oldPath);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
} catch {
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (changed) {
|
|
619
|
+
await saveEmployees(employees, rosterPath);
|
|
620
|
+
}
|
|
621
|
+
return changed;
|
|
622
|
+
}
|
|
623
|
+
function findExeBin() {
|
|
624
|
+
try {
|
|
625
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
626
|
+
} catch {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function registerBinSymlinks(name) {
|
|
631
|
+
const created = [];
|
|
632
|
+
const skipped = [];
|
|
633
|
+
const errors = [];
|
|
634
|
+
const exeBinPath = findExeBin();
|
|
635
|
+
if (!exeBinPath) {
|
|
636
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
637
|
+
return { created, skipped, errors };
|
|
638
|
+
}
|
|
639
|
+
const binDir = path3.dirname(exeBinPath);
|
|
640
|
+
let target;
|
|
641
|
+
try {
|
|
642
|
+
target = readlinkSync(exeBinPath);
|
|
643
|
+
} catch {
|
|
644
|
+
errors.push("Could not read 'exe' symlink");
|
|
645
|
+
return { created, skipped, errors };
|
|
646
|
+
}
|
|
647
|
+
for (const suffix of ["", "-opencode"]) {
|
|
648
|
+
const linkName = `${name}${suffix}`;
|
|
649
|
+
const linkPath = path3.join(binDir, linkName);
|
|
650
|
+
if (existsSync4(linkPath)) {
|
|
651
|
+
skipped.push(linkName);
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
try {
|
|
655
|
+
symlinkSync(target, linkPath);
|
|
656
|
+
created.push(linkName);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return { created, skipped, errors };
|
|
662
|
+
}
|
|
663
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
307
664
|
var init_employees = __esm({
|
|
308
665
|
"src/lib/employees.ts"() {
|
|
309
666
|
"use strict";
|
|
310
667
|
init_config();
|
|
311
|
-
EMPLOYEES_PATH =
|
|
668
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
312
669
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
313
670
|
COORDINATOR_ROLE = "COO";
|
|
314
|
-
|
|
671
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
672
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
673
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
315
674
|
}
|
|
316
675
|
});
|
|
317
676
|
|
|
@@ -372,7 +731,7 @@ var init_db_retry = __esm({
|
|
|
372
731
|
|
|
373
732
|
// src/lib/database-adapter.ts
|
|
374
733
|
import os3 from "os";
|
|
375
|
-
import
|
|
734
|
+
import path4 from "path";
|
|
376
735
|
import { createRequire } from "module";
|
|
377
736
|
import { pathToFileURL } from "url";
|
|
378
737
|
function quotedIdentifier(identifier) {
|
|
@@ -683,8 +1042,8 @@ async function loadPrismaClient() {
|
|
|
683
1042
|
}
|
|
684
1043
|
return new PrismaClient2();
|
|
685
1044
|
}
|
|
686
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
687
|
-
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"));
|
|
688
1047
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
689
1048
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
690
1049
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -965,8 +1324,8 @@ var init_memory = __esm({
|
|
|
965
1324
|
|
|
966
1325
|
// src/lib/daemon-auth.ts
|
|
967
1326
|
import crypto from "crypto";
|
|
968
|
-
import
|
|
969
|
-
import { existsSync as
|
|
1327
|
+
import path5 from "path";
|
|
1328
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
970
1329
|
function normalizeToken(token) {
|
|
971
1330
|
if (!token) return null;
|
|
972
1331
|
const trimmed = token.trim();
|
|
@@ -974,8 +1333,8 @@ function normalizeToken(token) {
|
|
|
974
1333
|
}
|
|
975
1334
|
function readDaemonToken() {
|
|
976
1335
|
try {
|
|
977
|
-
if (!
|
|
978
|
-
return normalizeToken(
|
|
1336
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1337
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
979
1338
|
} catch {
|
|
980
1339
|
return null;
|
|
981
1340
|
}
|
|
@@ -985,7 +1344,7 @@ function ensureDaemonToken(seed) {
|
|
|
985
1344
|
if (existing) return existing;
|
|
986
1345
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
987
1346
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
988
|
-
|
|
1347
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
989
1348
|
`, "utf8");
|
|
990
1349
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
991
1350
|
return token;
|
|
@@ -996,7 +1355,7 @@ var init_daemon_auth = __esm({
|
|
|
996
1355
|
"use strict";
|
|
997
1356
|
init_config();
|
|
998
1357
|
init_secure_files();
|
|
999
|
-
DAEMON_TOKEN_PATH =
|
|
1358
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1000
1359
|
}
|
|
1001
1360
|
});
|
|
1002
1361
|
|
|
@@ -1016,8 +1375,8 @@ import net from "net";
|
|
|
1016
1375
|
import os4 from "os";
|
|
1017
1376
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1018
1377
|
import { randomUUID } from "crypto";
|
|
1019
|
-
import { existsSync as
|
|
1020
|
-
import
|
|
1378
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1379
|
+
import path6 from "path";
|
|
1021
1380
|
import { fileURLToPath } from "url";
|
|
1022
1381
|
function handleData(chunk) {
|
|
1023
1382
|
_buffer += chunk.toString();
|
|
@@ -1053,9 +1412,9 @@ function isZombie(pid) {
|
|
|
1053
1412
|
}
|
|
1054
1413
|
}
|
|
1055
1414
|
function cleanupStaleFiles() {
|
|
1056
|
-
if (
|
|
1415
|
+
if (existsSync6(PID_PATH)) {
|
|
1057
1416
|
try {
|
|
1058
|
-
const pid = parseInt(
|
|
1417
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1059
1418
|
if (pid > 0) {
|
|
1060
1419
|
try {
|
|
1061
1420
|
process.kill(pid, 0);
|
|
@@ -1080,11 +1439,11 @@ function cleanupStaleFiles() {
|
|
|
1080
1439
|
}
|
|
1081
1440
|
}
|
|
1082
1441
|
function findPackageRoot() {
|
|
1083
|
-
let dir =
|
|
1084
|
-
const { root } =
|
|
1442
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1443
|
+
const { root } = path6.parse(dir);
|
|
1085
1444
|
while (dir !== root) {
|
|
1086
|
-
if (
|
|
1087
|
-
dir =
|
|
1445
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1446
|
+
dir = path6.dirname(dir);
|
|
1088
1447
|
}
|
|
1089
1448
|
return null;
|
|
1090
1449
|
}
|
|
@@ -1102,8 +1461,8 @@ function spawnDaemon() {
|
|
|
1102
1461
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1103
1462
|
return;
|
|
1104
1463
|
}
|
|
1105
|
-
const daemonPath =
|
|
1106
|
-
if (!
|
|
1464
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1465
|
+
if (!existsSync6(daemonPath)) {
|
|
1107
1466
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1108
1467
|
`);
|
|
1109
1468
|
return;
|
|
@@ -1112,7 +1471,7 @@ function spawnDaemon() {
|
|
|
1112
1471
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1113
1472
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1114
1473
|
`);
|
|
1115
|
-
const logPath =
|
|
1474
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1116
1475
|
let stderrFd = "ignore";
|
|
1117
1476
|
try {
|
|
1118
1477
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1277,9 +1636,9 @@ function killAndRespawnDaemon() {
|
|
|
1277
1636
|
}
|
|
1278
1637
|
try {
|
|
1279
1638
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1280
|
-
if (
|
|
1639
|
+
if (existsSync6(PID_PATH)) {
|
|
1281
1640
|
try {
|
|
1282
|
-
const pid = parseInt(
|
|
1641
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1283
1642
|
if (pid > 0) {
|
|
1284
1643
|
try {
|
|
1285
1644
|
process.kill(pid, "SIGKILL");
|
|
@@ -1425,9 +1784,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1425
1784
|
"use strict";
|
|
1426
1785
|
init_config();
|
|
1427
1786
|
init_daemon_auth();
|
|
1428
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1429
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1430
|
-
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");
|
|
1431
1790
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1432
1791
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1433
1792
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1695,7 +2054,7 @@ __export(database_exports, {
|
|
|
1695
2054
|
isInitialized: () => isInitialized,
|
|
1696
2055
|
setExternalClient: () => setExternalClient
|
|
1697
2056
|
});
|
|
1698
|
-
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";
|
|
1699
2058
|
import { createClient } from "@libsql/client";
|
|
1700
2059
|
import { homedir } from "os";
|
|
1701
2060
|
import { join } from "path";
|
|
@@ -1748,11 +2107,11 @@ function releaseDbLock() {
|
|
|
1748
2107
|
}
|
|
1749
2108
|
async function initDatabase(config) {
|
|
1750
2109
|
acquireDbLock();
|
|
1751
|
-
if (
|
|
2110
|
+
if (existsSync7(config.dbPath)) {
|
|
1752
2111
|
const dbStat = statSync2(config.dbPath);
|
|
1753
2112
|
if (dbStat.size === 0) {
|
|
1754
2113
|
const walPath = config.dbPath + "-wal";
|
|
1755
|
-
if (
|
|
2114
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1756
2115
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1757
2116
|
copyFileSync(config.dbPath, backupPath);
|
|
1758
2117
|
unlinkSync3(config.dbPath);
|
|
@@ -3338,16 +3697,16 @@ var init_database = __esm({
|
|
|
3338
3697
|
});
|
|
3339
3698
|
|
|
3340
3699
|
// src/lib/keychain.ts
|
|
3341
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3342
|
-
import { existsSync as
|
|
3700
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3701
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3343
3702
|
import { execSync as execSync3 } from "child_process";
|
|
3344
|
-
import
|
|
3703
|
+
import path7 from "path";
|
|
3345
3704
|
import os5 from "os";
|
|
3346
3705
|
function getKeyDir() {
|
|
3347
|
-
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");
|
|
3348
3707
|
}
|
|
3349
3708
|
function getKeyPath() {
|
|
3350
|
-
return
|
|
3709
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3351
3710
|
}
|
|
3352
3711
|
function nativeKeychainAllowed() {
|
|
3353
3712
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -3373,12 +3732,14 @@ function linuxSecretAvailable() {
|
|
|
3373
3732
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3374
3733
|
if (process.platform !== "linux") return false;
|
|
3375
3734
|
try {
|
|
3376
|
-
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3377
3735
|
const st = statSync3(keyPath);
|
|
3378
3736
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3737
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3379
3738
|
if (uid === 0) return true;
|
|
3380
3739
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3381
|
-
|
|
3740
|
+
if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
|
|
3741
|
+
if (!linuxSecretAvailable()) return true;
|
|
3742
|
+
return false;
|
|
3382
3743
|
} catch {
|
|
3383
3744
|
return false;
|
|
3384
3745
|
}
|
|
@@ -3528,15 +3889,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3528
3889
|
await mkdir3(dir, { recursive: true });
|
|
3529
3890
|
const keyPath = getKeyPath();
|
|
3530
3891
|
const machineKey = deriveMachineKey();
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3892
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
3893
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
3894
|
+
const tmpPath = keyPath + ".tmp";
|
|
3895
|
+
try {
|
|
3896
|
+
if (existsSync8(keyPath)) {
|
|
3897
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3898
|
+
});
|
|
3899
|
+
}
|
|
3900
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
3901
|
+
await chmod2(tmpPath, 384);
|
|
3902
|
+
await rename(tmpPath, keyPath);
|
|
3903
|
+
} catch (err) {
|
|
3904
|
+
try {
|
|
3905
|
+
await unlink(tmpPath);
|
|
3906
|
+
} catch {
|
|
3907
|
+
}
|
|
3908
|
+
throw err;
|
|
3536
3909
|
}
|
|
3537
|
-
|
|
3538
|
-
await chmod2(keyPath, 384);
|
|
3539
|
-
return "plaintext";
|
|
3910
|
+
return result;
|
|
3540
3911
|
}
|
|
3541
3912
|
async function getMasterKey() {
|
|
3542
3913
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -3575,7 +3946,7 @@ async function getMasterKey() {
|
|
|
3575
3946
|
}
|
|
3576
3947
|
}
|
|
3577
3948
|
const keyPath = getKeyPath();
|
|
3578
|
-
if (!
|
|
3949
|
+
if (!existsSync8(keyPath)) {
|
|
3579
3950
|
process.stderr.write(
|
|
3580
3951
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3581
3952
|
`
|
|
@@ -3603,7 +3974,7 @@ async function getMasterKey() {
|
|
|
3603
3974
|
b64Value = content;
|
|
3604
3975
|
}
|
|
3605
3976
|
const key = Buffer.from(b64Value, "base64");
|
|
3606
|
-
if (
|
|
3977
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3607
3978
|
return key;
|
|
3608
3979
|
}
|
|
3609
3980
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -3909,14 +4280,14 @@ __export(shard_manager_exports, {
|
|
|
3909
4280
|
listShards: () => listShards,
|
|
3910
4281
|
shardExists: () => shardExists
|
|
3911
4282
|
});
|
|
3912
|
-
import
|
|
3913
|
-
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";
|
|
3914
4285
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3915
4286
|
function initShardManager(encryptionKey) {
|
|
3916
4287
|
_encryptionKey = encryptionKey;
|
|
3917
4288
|
_keyValidated = false;
|
|
3918
4289
|
_keyValidationPromise = null;
|
|
3919
|
-
if (!
|
|
4290
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3920
4291
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3921
4292
|
}
|
|
3922
4293
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3937,7 +4308,7 @@ async function validateEncryptionKey() {
|
|
|
3937
4308
|
return true;
|
|
3938
4309
|
}
|
|
3939
4310
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3940
|
-
const dbPath =
|
|
4311
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3941
4312
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3942
4313
|
try {
|
|
3943
4314
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -3980,7 +4351,7 @@ function getShardClient(projectName) {
|
|
|
3980
4351
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3981
4352
|
evictLRU();
|
|
3982
4353
|
}
|
|
3983
|
-
const dbPath =
|
|
4354
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3984
4355
|
const client = createClient2({
|
|
3985
4356
|
url: `file:${dbPath}`,
|
|
3986
4357
|
encryptionKey: _encryptionKey
|
|
@@ -3991,13 +4362,13 @@ function getShardClient(projectName) {
|
|
|
3991
4362
|
}
|
|
3992
4363
|
function shardExists(projectName) {
|
|
3993
4364
|
const safeName = safeShardName(projectName);
|
|
3994
|
-
return
|
|
4365
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3995
4366
|
}
|
|
3996
4367
|
function safeShardName(projectName) {
|
|
3997
4368
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3998
4369
|
}
|
|
3999
4370
|
function listShards() {
|
|
4000
|
-
if (!
|
|
4371
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
4001
4372
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4002
4373
|
}
|
|
4003
4374
|
async function auditShardHealth(options = {}) {
|
|
@@ -4009,7 +4380,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4009
4380
|
const names = listShards();
|
|
4010
4381
|
const shards = [];
|
|
4011
4382
|
for (const name of names) {
|
|
4012
|
-
const dbPath =
|
|
4383
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4013
4384
|
const stat = statSync4(dbPath);
|
|
4014
4385
|
const item = {
|
|
4015
4386
|
name,
|
|
@@ -4044,7 +4415,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4044
4415
|
_shards.delete(name);
|
|
4045
4416
|
_shardLastAccess.delete(name);
|
|
4046
4417
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4047
|
-
const archivedPath =
|
|
4418
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4048
4419
|
renameSync3(dbPath, archivedPath);
|
|
4049
4420
|
item.archivedPath = archivedPath;
|
|
4050
4421
|
}
|
|
@@ -4272,11 +4643,11 @@ async function getReadyShardClient(projectName) {
|
|
|
4272
4643
|
client.close();
|
|
4273
4644
|
_shards.delete(safeName);
|
|
4274
4645
|
_shardLastAccess.delete(safeName);
|
|
4275
|
-
const dbPath =
|
|
4276
|
-
if (
|
|
4646
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4647
|
+
if (existsSync9(dbPath)) {
|
|
4277
4648
|
const stat = statSync4(dbPath);
|
|
4278
4649
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4279
|
-
const archivedPath =
|
|
4650
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4280
4651
|
renameSync3(dbPath, archivedPath);
|
|
4281
4652
|
process.stderr.write(
|
|
4282
4653
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -4344,7 +4715,7 @@ var init_shard_manager = __esm({
|
|
|
4344
4715
|
"src/lib/shard-manager.ts"() {
|
|
4345
4716
|
"use strict";
|
|
4346
4717
|
init_config();
|
|
4347
|
-
SHARDS_DIR =
|
|
4718
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
4348
4719
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4349
4720
|
MAX_OPEN_SHARDS = 10;
|
|
4350
4721
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -5838,13 +6209,13 @@ var init_store = __esm({
|
|
|
5838
6209
|
});
|
|
5839
6210
|
|
|
5840
6211
|
// src/lib/session-registry.ts
|
|
5841
|
-
import
|
|
6212
|
+
import path9 from "path";
|
|
5842
6213
|
import os6 from "os";
|
|
5843
6214
|
var REGISTRY_PATH;
|
|
5844
6215
|
var init_session_registry = __esm({
|
|
5845
6216
|
"src/lib/session-registry.ts"() {
|
|
5846
6217
|
"use strict";
|
|
5847
|
-
REGISTRY_PATH =
|
|
6218
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
5848
6219
|
}
|
|
5849
6220
|
});
|
|
5850
6221
|
|
|
@@ -6062,51 +6433,6 @@ var init_provider_table = __esm({
|
|
|
6062
6433
|
}
|
|
6063
6434
|
});
|
|
6064
6435
|
|
|
6065
|
-
// src/lib/runtime-table.ts
|
|
6066
|
-
var RUNTIME_TABLE;
|
|
6067
|
-
var init_runtime_table = __esm({
|
|
6068
|
-
"src/lib/runtime-table.ts"() {
|
|
6069
|
-
"use strict";
|
|
6070
|
-
RUNTIME_TABLE = {
|
|
6071
|
-
codex: {
|
|
6072
|
-
binary: "codex",
|
|
6073
|
-
launchMode: "interactive",
|
|
6074
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
6075
|
-
inlineFlag: "--no-alt-screen",
|
|
6076
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
6077
|
-
defaultModel: "gpt-5.5"
|
|
6078
|
-
},
|
|
6079
|
-
opencode: {
|
|
6080
|
-
binary: "opencode",
|
|
6081
|
-
launchMode: "exec",
|
|
6082
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
6083
|
-
inlineFlag: "",
|
|
6084
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
6085
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
6086
|
-
}
|
|
6087
|
-
};
|
|
6088
|
-
}
|
|
6089
|
-
});
|
|
6090
|
-
|
|
6091
|
-
// src/lib/agent-config.ts
|
|
6092
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
|
|
6093
|
-
import path9 from "path";
|
|
6094
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
6095
|
-
var init_agent_config = __esm({
|
|
6096
|
-
"src/lib/agent-config.ts"() {
|
|
6097
|
-
"use strict";
|
|
6098
|
-
init_config();
|
|
6099
|
-
init_runtime_table();
|
|
6100
|
-
init_secure_files();
|
|
6101
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
6102
|
-
DEFAULT_MODELS = {
|
|
6103
|
-
claude: "claude-opus-4.6",
|
|
6104
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
6105
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
6106
|
-
};
|
|
6107
|
-
}
|
|
6108
|
-
});
|
|
6109
|
-
|
|
6110
6436
|
// src/lib/intercom-queue.ts
|
|
6111
6437
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
6112
6438
|
import path10 from "path";
|
|
@@ -6187,6 +6513,21 @@ function isRootSession(name) {
|
|
|
6187
6513
|
function extractRootExe(name) {
|
|
6188
6514
|
if (!name) return null;
|
|
6189
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
|
+
}
|
|
6190
6531
|
const parts = name.split("-").filter(Boolean);
|
|
6191
6532
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6192
6533
|
}
|
|
@@ -6205,6 +6546,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6205
6546
|
function getParentExe(sessionKey) {
|
|
6206
6547
|
try {
|
|
6207
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
|
+
}
|
|
6208
6553
|
return data.parentExe || null;
|
|
6209
6554
|
} catch {
|
|
6210
6555
|
return null;
|
|
@@ -6277,7 +6622,7 @@ function resolveExeSession() {
|
|
|
6277
6622
|
}
|
|
6278
6623
|
return candidate;
|
|
6279
6624
|
}
|
|
6280
|
-
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;
|
|
6281
6626
|
var init_tmux_routing = __esm({
|
|
6282
6627
|
"src/lib/tmux-routing.ts"() {
|
|
6283
6628
|
"use strict";
|
|
@@ -6295,6 +6640,7 @@ var init_tmux_routing = __esm({
|
|
|
6295
6640
|
init_agent_symlinks();
|
|
6296
6641
|
SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6297
6642
|
SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
|
|
6643
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
6298
6644
|
INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6299
6645
|
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6300
6646
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|