@askexenow/exe-os 0.8.21 → 0.8.22
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/backfill-conversations.js +120 -13
- package/dist/bin/cli.js +13 -5
- package/package.json +1 -1
|
@@ -446,11 +446,110 @@ var init_shard_manager = __esm({
|
|
|
446
446
|
}
|
|
447
447
|
});
|
|
448
448
|
|
|
449
|
+
// src/lib/employees.ts
|
|
450
|
+
var employees_exports = {};
|
|
451
|
+
__export(employees_exports, {
|
|
452
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
453
|
+
addEmployee: () => addEmployee,
|
|
454
|
+
getEmployee: () => getEmployee,
|
|
455
|
+
loadEmployees: () => loadEmployees,
|
|
456
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
457
|
+
saveEmployees: () => saveEmployees,
|
|
458
|
+
validateEmployeeName: () => validateEmployeeName
|
|
459
|
+
});
|
|
460
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
461
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
|
|
462
|
+
import { execSync } from "child_process";
|
|
463
|
+
import path5 from "path";
|
|
464
|
+
function validateEmployeeName(name) {
|
|
465
|
+
if (!name) {
|
|
466
|
+
return { valid: false, error: "Name is required" };
|
|
467
|
+
}
|
|
468
|
+
if (name.length > 32) {
|
|
469
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
470
|
+
}
|
|
471
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
472
|
+
return {
|
|
473
|
+
valid: false,
|
|
474
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return { valid: true };
|
|
478
|
+
}
|
|
479
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
480
|
+
if (!existsSync5(employeesPath)) {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
const raw = await readFile3(employeesPath, "utf-8");
|
|
484
|
+
try {
|
|
485
|
+
return JSON.parse(raw);
|
|
486
|
+
} catch {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
491
|
+
await mkdir3(path5.dirname(employeesPath), { recursive: true });
|
|
492
|
+
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
493
|
+
}
|
|
494
|
+
function getEmployee(employees, name) {
|
|
495
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
496
|
+
}
|
|
497
|
+
function addEmployee(employees, employee) {
|
|
498
|
+
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
499
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
500
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
501
|
+
}
|
|
502
|
+
return [...employees, normalized];
|
|
503
|
+
}
|
|
504
|
+
function registerBinSymlinks(name) {
|
|
505
|
+
const created = [];
|
|
506
|
+
const skipped = [];
|
|
507
|
+
const errors = [];
|
|
508
|
+
let exeBinPath;
|
|
509
|
+
try {
|
|
510
|
+
exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
|
|
511
|
+
} catch {
|
|
512
|
+
errors.push("Could not find 'exe' in PATH");
|
|
513
|
+
return { created, skipped, errors };
|
|
514
|
+
}
|
|
515
|
+
const binDir = path5.dirname(exeBinPath);
|
|
516
|
+
let target;
|
|
517
|
+
try {
|
|
518
|
+
target = readlinkSync(exeBinPath);
|
|
519
|
+
} catch {
|
|
520
|
+
errors.push("Could not read 'exe' symlink");
|
|
521
|
+
return { created, skipped, errors };
|
|
522
|
+
}
|
|
523
|
+
for (const suffix of ["", "-opencode"]) {
|
|
524
|
+
const linkName = `${name}${suffix}`;
|
|
525
|
+
const linkPath = path5.join(binDir, linkName);
|
|
526
|
+
if (existsSync5(linkPath)) {
|
|
527
|
+
skipped.push(linkName);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
symlinkSync(target, linkPath);
|
|
532
|
+
created.push(linkName);
|
|
533
|
+
} catch (err) {
|
|
534
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return { created, skipped, errors };
|
|
538
|
+
}
|
|
539
|
+
var EMPLOYEES_PATH;
|
|
540
|
+
var init_employees = __esm({
|
|
541
|
+
"src/lib/employees.ts"() {
|
|
542
|
+
"use strict";
|
|
543
|
+
init_config();
|
|
544
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
449
548
|
// src/bin/backfill-conversations.ts
|
|
450
549
|
import crypto2 from "crypto";
|
|
451
550
|
import { createReadStream } from "fs";
|
|
452
551
|
import { readdir, stat } from "fs/promises";
|
|
453
|
-
import
|
|
552
|
+
import path6 from "path";
|
|
454
553
|
import { createInterface } from "readline";
|
|
455
554
|
import { homedir } from "os";
|
|
456
555
|
|
|
@@ -1876,7 +1975,7 @@ function isMainModule(importMetaUrl) {
|
|
|
1876
1975
|
// src/bin/backfill-conversations.ts
|
|
1877
1976
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
1878
1977
|
async function findJsonlFiles(cutoffMs) {
|
|
1879
|
-
const projectsDir =
|
|
1978
|
+
const projectsDir = path6.join(homedir(), ".claude", "projects");
|
|
1880
1979
|
const files = [];
|
|
1881
1980
|
async function walk(dir) {
|
|
1882
1981
|
let entries;
|
|
@@ -1886,7 +1985,7 @@ async function findJsonlFiles(cutoffMs) {
|
|
|
1886
1985
|
return;
|
|
1887
1986
|
}
|
|
1888
1987
|
for (const entry of entries) {
|
|
1889
|
-
const full =
|
|
1988
|
+
const full = path6.join(dir, entry.name);
|
|
1890
1989
|
if (entry.isDirectory()) {
|
|
1891
1990
|
await walk(full);
|
|
1892
1991
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
@@ -1902,9 +2001,9 @@ async function findJsonlFiles(cutoffMs) {
|
|
|
1902
2001
|
return files;
|
|
1903
2002
|
}
|
|
1904
2003
|
function projectNameFromPath(filePath) {
|
|
1905
|
-
const projectsDir =
|
|
1906
|
-
const relative =
|
|
1907
|
-
const projectDir = relative.split(
|
|
2004
|
+
const projectsDir = path6.join(homedir(), ".claude", "projects");
|
|
2005
|
+
const relative = path6.relative(projectsDir, filePath);
|
|
2006
|
+
const projectDir = relative.split(path6.sep)[0] ?? relative;
|
|
1908
2007
|
const homeEncoded = homedir().replaceAll("/", "-");
|
|
1909
2008
|
if (projectDir.startsWith(homeEncoded + "-")) {
|
|
1910
2009
|
return projectDir.slice(homeEncoded.length + 1);
|
|
@@ -1967,14 +2066,14 @@ async function extractConversationPairs(filePath, projectFilter) {
|
|
|
1967
2066
|
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
1968
2067
|
const assistantText = extractAssistantText(entry.message.content);
|
|
1969
2068
|
if (!assistantText.trim()) continue;
|
|
1970
|
-
const resolvedProject = fileCwd ?
|
|
2069
|
+
const resolvedProject = fileCwd ? path6.basename(fileCwd) : fallbackProject;
|
|
1971
2070
|
const userText = pendingUser?.text ?? "";
|
|
1972
2071
|
const timestamp = entry.timestamp ?? pendingUser?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1973
2072
|
pairs.push({
|
|
1974
2073
|
userText,
|
|
1975
2074
|
assistantText,
|
|
1976
2075
|
timestamp,
|
|
1977
|
-
sessionId: fileSessionId ||
|
|
2076
|
+
sessionId: fileSessionId || path6.basename(filePath, ".jsonl"),
|
|
1978
2077
|
project: resolvedProject,
|
|
1979
2078
|
cwd: fileCwd || fallbackProject
|
|
1980
2079
|
});
|
|
@@ -2013,7 +2112,7 @@ async function loadExistingHashes(cutoffIso) {
|
|
|
2013
2112
|
}
|
|
2014
2113
|
var MAX_CONTENT_LENGTH = 8e3;
|
|
2015
2114
|
var MIN_ASSISTANT_LENGTH = 50;
|
|
2016
|
-
async function storePair(pair, daemonConnected, stats) {
|
|
2115
|
+
async function storePair(pair, daemonConnected, stats, agentId) {
|
|
2017
2116
|
const client = getClient();
|
|
2018
2117
|
const id = crypto2.randomUUID();
|
|
2019
2118
|
const userTrunc = pair.userText.length > MAX_CONTENT_LENGTH ? pair.userText.slice(0, MAX_CONTENT_LENGTH) : pair.userText;
|
|
@@ -2062,8 +2161,8 @@ async function storePair(pair, daemonConnected, stats) {
|
|
|
2062
2161
|
}
|
|
2063
2162
|
await writeMemory({
|
|
2064
2163
|
id: crypto2.randomUUID(),
|
|
2065
|
-
agent_id:
|
|
2066
|
-
agent_role: "
|
|
2164
|
+
agent_id: agentId,
|
|
2165
|
+
agent_role: "coo",
|
|
2067
2166
|
session_id: pair.sessionId,
|
|
2068
2167
|
timestamp: pair.timestamp,
|
|
2069
2168
|
tool_name: "ConversationBackfill",
|
|
@@ -2086,7 +2185,15 @@ async function backfillConversations(options) {
|
|
|
2086
2185
|
};
|
|
2087
2186
|
const cutoffMs = Date.now() - options.days * 24 * 60 * 60 * 1e3;
|
|
2088
2187
|
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
2089
|
-
|
|
2188
|
+
let cooAgentId = "exe";
|
|
2189
|
+
try {
|
|
2190
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
2191
|
+
const employees = await loadEmployees2();
|
|
2192
|
+
const coo = employees.find((e) => e.role === "COO");
|
|
2193
|
+
if (coo) cooAgentId = coo.name;
|
|
2194
|
+
} catch {
|
|
2195
|
+
}
|
|
2196
|
+
process.stderr.write(`[backfill-conversations] Scanning last ${options.days} days (agent: ${cooAgentId})...
|
|
2090
2197
|
`);
|
|
2091
2198
|
if (!options.dryRun) {
|
|
2092
2199
|
process.stderr.write("[backfill-conversations] Initializing store...\n");
|
|
@@ -2123,7 +2230,7 @@ async function backfillConversations(options) {
|
|
|
2123
2230
|
}
|
|
2124
2231
|
seenHashes.add(hash);
|
|
2125
2232
|
if (!options.dryRun) {
|
|
2126
|
-
await storePair(pair, daemonConnected, stats);
|
|
2233
|
+
await storePair(pair, daemonConnected, stats, cooAgentId);
|
|
2127
2234
|
} else {
|
|
2128
2235
|
stats.conversationsStored++;
|
|
2129
2236
|
stats.memoriesStored++;
|
package/dist/bin/cli.js
CHANGED
|
@@ -3085,7 +3085,7 @@ async function loadExistingHashes(cutoffIso) {
|
|
|
3085
3085
|
}
|
|
3086
3086
|
return hashes;
|
|
3087
3087
|
}
|
|
3088
|
-
async function storePair(pair, daemonConnected, stats) {
|
|
3088
|
+
async function storePair(pair, daemonConnected, stats, agentId) {
|
|
3089
3089
|
const client = getClient();
|
|
3090
3090
|
const id = crypto2.randomUUID();
|
|
3091
3091
|
const userTrunc = pair.userText.length > MAX_CONTENT_LENGTH ? pair.userText.slice(0, MAX_CONTENT_LENGTH) : pair.userText;
|
|
@@ -3134,8 +3134,8 @@ async function storePair(pair, daemonConnected, stats) {
|
|
|
3134
3134
|
}
|
|
3135
3135
|
await writeMemory({
|
|
3136
3136
|
id: crypto2.randomUUID(),
|
|
3137
|
-
agent_id:
|
|
3138
|
-
agent_role: "
|
|
3137
|
+
agent_id: agentId,
|
|
3138
|
+
agent_role: "coo",
|
|
3139
3139
|
session_id: pair.sessionId,
|
|
3140
3140
|
timestamp: pair.timestamp,
|
|
3141
3141
|
tool_name: "ConversationBackfill",
|
|
@@ -3158,7 +3158,15 @@ async function backfillConversations(options) {
|
|
|
3158
3158
|
};
|
|
3159
3159
|
const cutoffMs = Date.now() - options.days * 24 * 60 * 60 * 1e3;
|
|
3160
3160
|
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
3161
|
-
|
|
3161
|
+
let cooAgentId = "exe";
|
|
3162
|
+
try {
|
|
3163
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
3164
|
+
const employees = await loadEmployees2();
|
|
3165
|
+
const coo = employees.find((e) => e.role === "COO");
|
|
3166
|
+
if (coo) cooAgentId = coo.name;
|
|
3167
|
+
} catch {
|
|
3168
|
+
}
|
|
3169
|
+
process.stderr.write(`[backfill-conversations] Scanning last ${options.days} days (agent: ${cooAgentId})...
|
|
3162
3170
|
`);
|
|
3163
3171
|
if (!options.dryRun) {
|
|
3164
3172
|
process.stderr.write("[backfill-conversations] Initializing store...\n");
|
|
@@ -3195,7 +3203,7 @@ async function backfillConversations(options) {
|
|
|
3195
3203
|
}
|
|
3196
3204
|
seenHashes.add(hash);
|
|
3197
3205
|
if (!options.dryRun) {
|
|
3198
|
-
await storePair(pair, daemonConnected, stats);
|
|
3206
|
+
await storePair(pair, daemonConnected, stats, cooAgentId);
|
|
3199
3207
|
} else {
|
|
3200
3208
|
stats.conversationsStored++;
|
|
3201
3209
|
stats.memoriesStored++;
|
package/package.json
CHANGED