@askexenow/exe-os 0.8.83 → 0.8.86
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 +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +154 -21
- package/dist/bin/cli.js +14678 -12676
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +100 -91
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1420 -485
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +572 -271
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +102 -3
- package/dist/bin/exe-gateway.js +796 -292
- package/dist/bin/exe-healthcheck.js +134 -1
- package/dist/bin/exe-heartbeat.js +172 -36
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +927 -82
- package/dist/bin/exe-new-employee.js +60 -8
- package/dist/bin/exe-pending-messages.js +151 -19
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +155 -22
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +995 -228
- package/dist/bin/exe-session-cleanup.js +4930 -1664
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +154 -21
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +1180 -363
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +60 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +1185 -367
- package/dist/bin/setup.js +914 -270
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +792 -285
- package/dist/hooks/bug-report-worker.js +445 -135
- package/dist/hooks/commit-complete.js +1178 -361
- package/dist/hooks/error-recall.js +994 -228
- package/dist/hooks/ingest-worker.js +1799 -1234
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +757 -109
- package/dist/hooks/pre-compact.js +1061 -244
- package/dist/hooks/pre-tool-use.js +787 -130
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +1121 -299
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +4063 -397
- package/dist/hooks/session-start.js +1071 -254
- package/dist/hooks/stop.js +768 -120
- package/dist/hooks/subagent-stop.js +757 -109
- package/dist/hooks/summary-worker.js +1706 -1011
- package/dist/index.js +1821 -1098
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +932 -88
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +2733 -1575
- package/dist/lib/hybrid-search.js +995 -228
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +103 -40
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +348 -134
- package/dist/lib/tmux-routing.js +422 -208
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5742 -696
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +375 -152
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +99 -31
- package/dist/mcp/tools/send-message.js +108 -45
- package/dist/mcp/tools/update-task.js +162 -77
- package/dist/runtime/index.js +1075 -258
- package/dist/tui/App.js +1333 -506
- package/package.json +6 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/bin/setup.js
CHANGED
|
@@ -294,12 +294,20 @@ async function getMasterKey() {
|
|
|
294
294
|
}
|
|
295
295
|
const keyPath = getKeyPath();
|
|
296
296
|
if (!existsSync2(keyPath)) {
|
|
297
|
+
process.stderr.write(
|
|
298
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os2.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
299
|
+
`
|
|
300
|
+
);
|
|
297
301
|
return null;
|
|
298
302
|
}
|
|
299
303
|
try {
|
|
300
304
|
const content = await readFile2(keyPath, "utf-8");
|
|
301
305
|
return Buffer.from(content.trim(), "base64");
|
|
302
|
-
} catch {
|
|
306
|
+
} catch (err) {
|
|
307
|
+
process.stderr.write(
|
|
308
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
309
|
+
`
|
|
310
|
+
);
|
|
303
311
|
return null;
|
|
304
312
|
}
|
|
305
313
|
}
|
|
@@ -399,10 +407,12 @@ function handleData(chunk) {
|
|
|
399
407
|
if (!line) continue;
|
|
400
408
|
try {
|
|
401
409
|
const response = JSON.parse(line);
|
|
402
|
-
const
|
|
410
|
+
const id = response.id;
|
|
411
|
+
if (!id) continue;
|
|
412
|
+
const entry = _pending.get(id);
|
|
403
413
|
if (entry) {
|
|
404
414
|
clearTimeout(entry.timer);
|
|
405
|
-
_pending.delete(
|
|
415
|
+
_pending.delete(id);
|
|
406
416
|
entry.resolve(response);
|
|
407
417
|
}
|
|
408
418
|
} catch {
|
|
@@ -573,6 +583,9 @@ async function connectEmbedDaemon() {
|
|
|
573
583
|
return false;
|
|
574
584
|
}
|
|
575
585
|
function sendRequest(texts, priority) {
|
|
586
|
+
return sendDaemonRequest({ texts, priority });
|
|
587
|
+
}
|
|
588
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
576
589
|
return new Promise((resolve) => {
|
|
577
590
|
if (!_socket || !_connected) {
|
|
578
591
|
resolve({ error: "Not connected" });
|
|
@@ -582,10 +595,10 @@ function sendRequest(texts, priority) {
|
|
|
582
595
|
const timer = setTimeout(() => {
|
|
583
596
|
_pending.delete(id);
|
|
584
597
|
resolve({ error: "Request timeout" });
|
|
585
|
-
},
|
|
598
|
+
}, timeoutMs);
|
|
586
599
|
_pending.set(id, { resolve, timer });
|
|
587
600
|
try {
|
|
588
|
-
_socket.write(JSON.stringify({ id,
|
|
601
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
589
602
|
} catch {
|
|
590
603
|
clearTimeout(timer);
|
|
591
604
|
_pending.delete(id);
|
|
@@ -595,30 +608,11 @@ function sendRequest(texts, priority) {
|
|
|
595
608
|
}
|
|
596
609
|
async function pingDaemon() {
|
|
597
610
|
if (!_socket || !_connected) return null;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}, 5e3);
|
|
604
|
-
_pending.set(id, {
|
|
605
|
-
resolve: (data) => {
|
|
606
|
-
if (data.health) {
|
|
607
|
-
resolve(data.health);
|
|
608
|
-
} else {
|
|
609
|
-
resolve(null);
|
|
610
|
-
}
|
|
611
|
-
},
|
|
612
|
-
timer
|
|
613
|
-
});
|
|
614
|
-
try {
|
|
615
|
-
_socket.write(JSON.stringify({ id, type: "health" }) + "\n");
|
|
616
|
-
} catch {
|
|
617
|
-
clearTimeout(timer);
|
|
618
|
-
_pending.delete(id);
|
|
619
|
-
resolve(null);
|
|
620
|
-
}
|
|
621
|
-
});
|
|
611
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
612
|
+
if (response.health) {
|
|
613
|
+
return response.health;
|
|
614
|
+
}
|
|
615
|
+
return null;
|
|
622
616
|
}
|
|
623
617
|
function killAndRespawnDaemon() {
|
|
624
618
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
@@ -703,6 +697,9 @@ function disconnectClient() {
|
|
|
703
697
|
entry.resolve({ error: "Client disconnected" });
|
|
704
698
|
}
|
|
705
699
|
}
|
|
700
|
+
function isClientConnected() {
|
|
701
|
+
return _connected;
|
|
702
|
+
}
|
|
706
703
|
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
707
704
|
var init_exe_daemon_client = __esm({
|
|
708
705
|
"src/lib/exe-daemon-client.ts"() {
|
|
@@ -761,10 +758,10 @@ async function disposeEmbedder() {
|
|
|
761
758
|
async function embedDirect(text) {
|
|
762
759
|
const llamaCpp = await import("node-llama-cpp");
|
|
763
760
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
764
|
-
const { existsSync:
|
|
765
|
-
const
|
|
766
|
-
const modelPath =
|
|
767
|
-
if (!
|
|
761
|
+
const { existsSync: existsSync12 } = await import("fs");
|
|
762
|
+
const path12 = await import("path");
|
|
763
|
+
const modelPath = path12.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
764
|
+
if (!existsSync12(modelPath)) {
|
|
768
765
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
769
766
|
}
|
|
770
767
|
const llama = await llamaCpp.getLlama();
|
|
@@ -1300,6 +1297,7 @@ __export(employees_exports, {
|
|
|
1300
1297
|
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
1301
1298
|
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
1302
1299
|
addEmployee: () => addEmployee,
|
|
1300
|
+
baseAgentName: () => baseAgentName,
|
|
1303
1301
|
canCoordinate: () => canCoordinate,
|
|
1304
1302
|
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
1305
1303
|
getCoordinatorName: () => getCoordinatorName,
|
|
@@ -1396,6 +1394,14 @@ function hasRole(agentName, role) {
|
|
|
1396
1394
|
const emp = getEmployee(employees, agentName);
|
|
1397
1395
|
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
1398
1396
|
}
|
|
1397
|
+
function baseAgentName(name, employees) {
|
|
1398
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
1399
|
+
if (!match) return name;
|
|
1400
|
+
const base = match[1];
|
|
1401
|
+
const roster = employees ?? loadEmployeesSync();
|
|
1402
|
+
if (getEmployee(roster, base)) return base;
|
|
1403
|
+
return name;
|
|
1404
|
+
}
|
|
1399
1405
|
function isMultiInstance(agentName, employees) {
|
|
1400
1406
|
const roster = employees ?? loadEmployeesSync();
|
|
1401
1407
|
const emp = getEmployee(roster, agentName);
|
|
@@ -1491,6 +1497,205 @@ var init_employees = __esm({
|
|
|
1491
1497
|
}
|
|
1492
1498
|
});
|
|
1493
1499
|
|
|
1500
|
+
// src/lib/daemon-protocol.ts
|
|
1501
|
+
function serializeValue(v) {
|
|
1502
|
+
if (v === null || v === void 0) return null;
|
|
1503
|
+
if (typeof v === "bigint") return Number(v);
|
|
1504
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
1505
|
+
if (v instanceof Uint8Array) {
|
|
1506
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1507
|
+
}
|
|
1508
|
+
if (ArrayBuffer.isView(v)) {
|
|
1509
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
1510
|
+
}
|
|
1511
|
+
if (v instanceof ArrayBuffer) {
|
|
1512
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1513
|
+
}
|
|
1514
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1515
|
+
return String(v);
|
|
1516
|
+
}
|
|
1517
|
+
function deserializeValue(v) {
|
|
1518
|
+
if (v === null) return null;
|
|
1519
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
1520
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
1521
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
1522
|
+
}
|
|
1523
|
+
return v;
|
|
1524
|
+
}
|
|
1525
|
+
function deserializeResultSet(srs) {
|
|
1526
|
+
const rows = srs.rows.map((obj) => {
|
|
1527
|
+
const values = srs.columns.map(
|
|
1528
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
1529
|
+
);
|
|
1530
|
+
const row = values;
|
|
1531
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
1532
|
+
const col = srs.columns[i];
|
|
1533
|
+
if (col !== void 0) {
|
|
1534
|
+
row[col] = values[i] ?? null;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
Object.defineProperty(row, "length", {
|
|
1538
|
+
value: values.length,
|
|
1539
|
+
enumerable: false
|
|
1540
|
+
});
|
|
1541
|
+
return row;
|
|
1542
|
+
});
|
|
1543
|
+
return {
|
|
1544
|
+
columns: srs.columns,
|
|
1545
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1546
|
+
rows,
|
|
1547
|
+
rowsAffected: srs.rowsAffected,
|
|
1548
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
1549
|
+
toJSON: () => ({
|
|
1550
|
+
columns: srs.columns,
|
|
1551
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1552
|
+
rows: srs.rows,
|
|
1553
|
+
rowsAffected: srs.rowsAffected,
|
|
1554
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
1555
|
+
})
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
var init_daemon_protocol = __esm({
|
|
1559
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1560
|
+
"use strict";
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
// src/lib/db-daemon-client.ts
|
|
1565
|
+
var db_daemon_client_exports = {};
|
|
1566
|
+
__export(db_daemon_client_exports, {
|
|
1567
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1568
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1569
|
+
});
|
|
1570
|
+
function normalizeStatement(stmt) {
|
|
1571
|
+
if (typeof stmt === "string") {
|
|
1572
|
+
return { sql: stmt, args: [] };
|
|
1573
|
+
}
|
|
1574
|
+
const sql = stmt.sql;
|
|
1575
|
+
let args2 = [];
|
|
1576
|
+
if (Array.isArray(stmt.args)) {
|
|
1577
|
+
args2 = stmt.args.map((v) => serializeValue(v));
|
|
1578
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1579
|
+
const named = {};
|
|
1580
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1581
|
+
named[key] = serializeValue(val);
|
|
1582
|
+
}
|
|
1583
|
+
return { sql, args: named };
|
|
1584
|
+
}
|
|
1585
|
+
return { sql, args: args2 };
|
|
1586
|
+
}
|
|
1587
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1588
|
+
let _useDaemon = false;
|
|
1589
|
+
const client = {
|
|
1590
|
+
async execute(stmt) {
|
|
1591
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1592
|
+
return fallbackClient.execute(stmt);
|
|
1593
|
+
}
|
|
1594
|
+
const { sql, args: args2 } = normalizeStatement(stmt);
|
|
1595
|
+
const response = await sendDaemonRequest({
|
|
1596
|
+
type: "db-execute",
|
|
1597
|
+
sql,
|
|
1598
|
+
args: args2
|
|
1599
|
+
});
|
|
1600
|
+
if (response.error) {
|
|
1601
|
+
const errMsg = String(response.error);
|
|
1602
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1603
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1604
|
+
`);
|
|
1605
|
+
return fallbackClient.execute(stmt);
|
|
1606
|
+
}
|
|
1607
|
+
throw new Error(errMsg);
|
|
1608
|
+
}
|
|
1609
|
+
if (response.db) {
|
|
1610
|
+
return deserializeResultSet(response.db);
|
|
1611
|
+
}
|
|
1612
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1613
|
+
return fallbackClient.execute(stmt);
|
|
1614
|
+
},
|
|
1615
|
+
async batch(stmts, mode) {
|
|
1616
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1617
|
+
return fallbackClient.batch(stmts, mode);
|
|
1618
|
+
}
|
|
1619
|
+
const statements = stmts.map(normalizeStatement);
|
|
1620
|
+
const response = await sendDaemonRequest({
|
|
1621
|
+
type: "db-batch",
|
|
1622
|
+
statements,
|
|
1623
|
+
mode: mode ?? "deferred"
|
|
1624
|
+
});
|
|
1625
|
+
if (response.error) {
|
|
1626
|
+
const errMsg = String(response.error);
|
|
1627
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1628
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1629
|
+
`);
|
|
1630
|
+
return fallbackClient.batch(stmts, mode);
|
|
1631
|
+
}
|
|
1632
|
+
throw new Error(errMsg);
|
|
1633
|
+
}
|
|
1634
|
+
const batchResults = response["db-batch"];
|
|
1635
|
+
if (batchResults) {
|
|
1636
|
+
return batchResults.map(deserializeResultSet);
|
|
1637
|
+
}
|
|
1638
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1639
|
+
return fallbackClient.batch(stmts, mode);
|
|
1640
|
+
},
|
|
1641
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1642
|
+
async transaction(mode) {
|
|
1643
|
+
return fallbackClient.transaction(mode);
|
|
1644
|
+
},
|
|
1645
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1646
|
+
async executeMultiple(sql) {
|
|
1647
|
+
return fallbackClient.executeMultiple(sql);
|
|
1648
|
+
},
|
|
1649
|
+
// migrate — delegate to fallback
|
|
1650
|
+
async migrate(stmts) {
|
|
1651
|
+
return fallbackClient.migrate(stmts);
|
|
1652
|
+
},
|
|
1653
|
+
// Sync mode — delegate to fallback
|
|
1654
|
+
sync() {
|
|
1655
|
+
return fallbackClient.sync();
|
|
1656
|
+
},
|
|
1657
|
+
close() {
|
|
1658
|
+
_useDaemon = false;
|
|
1659
|
+
},
|
|
1660
|
+
get closed() {
|
|
1661
|
+
return fallbackClient.closed;
|
|
1662
|
+
},
|
|
1663
|
+
get protocol() {
|
|
1664
|
+
return fallbackClient.protocol;
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
return {
|
|
1668
|
+
...client,
|
|
1669
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1670
|
+
_enableDaemon() {
|
|
1671
|
+
_useDaemon = true;
|
|
1672
|
+
},
|
|
1673
|
+
/** Check if daemon routing is active */
|
|
1674
|
+
_isDaemonActive() {
|
|
1675
|
+
return _useDaemon && isClientConnected();
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1680
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1681
|
+
const connected = await connectEmbedDaemon();
|
|
1682
|
+
if (!connected) {
|
|
1683
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1684
|
+
return null;
|
|
1685
|
+
}
|
|
1686
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1687
|
+
client._enableDaemon();
|
|
1688
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1689
|
+
return client;
|
|
1690
|
+
}
|
|
1691
|
+
var init_db_daemon_client = __esm({
|
|
1692
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1693
|
+
"use strict";
|
|
1694
|
+
init_exe_daemon_client();
|
|
1695
|
+
init_daemon_protocol();
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1494
1699
|
// src/lib/database.ts
|
|
1495
1700
|
var database_exports = {};
|
|
1496
1701
|
__export(database_exports, {
|
|
@@ -1499,6 +1704,7 @@ __export(database_exports, {
|
|
|
1499
1704
|
ensureSchema: () => ensureSchema,
|
|
1500
1705
|
getClient: () => getClient,
|
|
1501
1706
|
getRawClient: () => getRawClient,
|
|
1707
|
+
initDaemonClient: () => initDaemonClient,
|
|
1502
1708
|
initDatabase: () => initDatabase,
|
|
1503
1709
|
initTurso: () => initTurso,
|
|
1504
1710
|
isInitialized: () => isInitialized
|
|
@@ -1526,8 +1732,27 @@ function getClient() {
|
|
|
1526
1732
|
if (!_resilientClient) {
|
|
1527
1733
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1528
1734
|
}
|
|
1735
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1736
|
+
return _resilientClient;
|
|
1737
|
+
}
|
|
1738
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1739
|
+
return _daemonClient;
|
|
1740
|
+
}
|
|
1529
1741
|
return _resilientClient;
|
|
1530
1742
|
}
|
|
1743
|
+
async function initDaemonClient() {
|
|
1744
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1745
|
+
if (!_resilientClient) return;
|
|
1746
|
+
try {
|
|
1747
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1748
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1749
|
+
} catch (err) {
|
|
1750
|
+
process.stderr.write(
|
|
1751
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1752
|
+
`
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1531
1756
|
function getRawClient() {
|
|
1532
1757
|
if (!_client) {
|
|
1533
1758
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -2014,6 +2239,12 @@ async function ensureSchema() {
|
|
|
2014
2239
|
} catch {
|
|
2015
2240
|
}
|
|
2016
2241
|
}
|
|
2242
|
+
try {
|
|
2243
|
+
await client.execute(
|
|
2244
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
2245
|
+
);
|
|
2246
|
+
} catch {
|
|
2247
|
+
}
|
|
2017
2248
|
await client.executeMultiple(`
|
|
2018
2249
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2019
2250
|
id TEXT PRIMARY KEY,
|
|
@@ -2066,7 +2297,30 @@ async function ensureSchema() {
|
|
|
2066
2297
|
entity_id TEXT NOT NULL,
|
|
2067
2298
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
2068
2299
|
);
|
|
2300
|
+
|
|
2301
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
2302
|
+
name,
|
|
2303
|
+
content=entities,
|
|
2304
|
+
content_rowid=rowid
|
|
2305
|
+
);
|
|
2306
|
+
|
|
2307
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
2308
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
2309
|
+
END;
|
|
2310
|
+
|
|
2311
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
2312
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
2313
|
+
END;
|
|
2314
|
+
|
|
2315
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
2316
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
2317
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
2318
|
+
END;
|
|
2069
2319
|
`);
|
|
2320
|
+
try {
|
|
2321
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
2322
|
+
} catch {
|
|
2323
|
+
}
|
|
2070
2324
|
await client.executeMultiple(`
|
|
2071
2325
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
2072
2326
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -2247,6 +2501,33 @@ async function ensureSchema() {
|
|
|
2247
2501
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
2248
2502
|
ON conversations(channel_id);
|
|
2249
2503
|
`);
|
|
2504
|
+
await client.executeMultiple(`
|
|
2505
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
2506
|
+
session_uuid TEXT PRIMARY KEY,
|
|
2507
|
+
agent_id TEXT NOT NULL,
|
|
2508
|
+
session_name TEXT,
|
|
2509
|
+
task_id TEXT,
|
|
2510
|
+
project_name TEXT,
|
|
2511
|
+
started_at TEXT NOT NULL
|
|
2512
|
+
);
|
|
2513
|
+
|
|
2514
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
2515
|
+
ON session_agent_map(agent_id);
|
|
2516
|
+
`);
|
|
2517
|
+
try {
|
|
2518
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
2519
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
2520
|
+
await client.execute({
|
|
2521
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
2522
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
2523
|
+
FROM memories
|
|
2524
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
2525
|
+
GROUP BY session_id, agent_id`,
|
|
2526
|
+
args: []
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
} catch {
|
|
2530
|
+
}
|
|
2250
2531
|
try {
|
|
2251
2532
|
await client.execute({
|
|
2252
2533
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -2380,15 +2661,41 @@ async function ensureSchema() {
|
|
|
2380
2661
|
});
|
|
2381
2662
|
} catch {
|
|
2382
2663
|
}
|
|
2664
|
+
for (const col of [
|
|
2665
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2666
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2667
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2668
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2669
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2670
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2671
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2672
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2673
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2674
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2675
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2676
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2677
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2678
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2679
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2680
|
+
]) {
|
|
2681
|
+
try {
|
|
2682
|
+
await client.execute(col);
|
|
2683
|
+
} catch {
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2383
2686
|
}
|
|
2384
2687
|
async function disposeDatabase() {
|
|
2688
|
+
if (_daemonClient) {
|
|
2689
|
+
_daemonClient.close();
|
|
2690
|
+
_daemonClient = null;
|
|
2691
|
+
}
|
|
2385
2692
|
if (_client) {
|
|
2386
2693
|
_client.close();
|
|
2387
2694
|
_client = null;
|
|
2388
2695
|
_resilientClient = null;
|
|
2389
2696
|
}
|
|
2390
2697
|
}
|
|
2391
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
2698
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
2392
2699
|
var init_database = __esm({
|
|
2393
2700
|
"src/lib/database.ts"() {
|
|
2394
2701
|
"use strict";
|
|
@@ -2396,6 +2703,7 @@ var init_database = __esm({
|
|
|
2396
2703
|
init_employees();
|
|
2397
2704
|
_client = null;
|
|
2398
2705
|
_resilientClient = null;
|
|
2706
|
+
_daemonClient = null;
|
|
2399
2707
|
initTurso = initDatabase;
|
|
2400
2708
|
disposeTurso = disposeDatabase;
|
|
2401
2709
|
}
|
|
@@ -2421,6 +2729,232 @@ var init_compress = __esm({
|
|
|
2421
2729
|
}
|
|
2422
2730
|
});
|
|
2423
2731
|
|
|
2732
|
+
// src/lib/crdt-sync.ts
|
|
2733
|
+
var crdt_sync_exports = {};
|
|
2734
|
+
__export(crdt_sync_exports, {
|
|
2735
|
+
_setStatePath: () => _setStatePath,
|
|
2736
|
+
applyRemoteUpdate: () => applyRemoteUpdate,
|
|
2737
|
+
destroyCrdtDoc: () => destroyCrdtDoc,
|
|
2738
|
+
getDiffUpdate: () => getDiffUpdate,
|
|
2739
|
+
getFullState: () => getFullState,
|
|
2740
|
+
getStateVector: () => getStateVector,
|
|
2741
|
+
importExistingBehaviors: () => importExistingBehaviors,
|
|
2742
|
+
importExistingMemories: () => importExistingMemories,
|
|
2743
|
+
initCrdtDoc: () => initCrdtDoc,
|
|
2744
|
+
isCrdtSyncEnabled: () => isCrdtSyncEnabled,
|
|
2745
|
+
onUpdate: () => onUpdate,
|
|
2746
|
+
readAllBehaviors: () => readAllBehaviors,
|
|
2747
|
+
readAllMemories: () => readAllMemories,
|
|
2748
|
+
rebuildFromDb: () => rebuildFromDb
|
|
2749
|
+
});
|
|
2750
|
+
import * as Y from "yjs";
|
|
2751
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2752
|
+
import path7 from "path";
|
|
2753
|
+
import { homedir } from "os";
|
|
2754
|
+
function getStatePath() {
|
|
2755
|
+
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
2756
|
+
}
|
|
2757
|
+
function _setStatePath(p) {
|
|
2758
|
+
_statePathOverride = p;
|
|
2759
|
+
}
|
|
2760
|
+
function initCrdtDoc() {
|
|
2761
|
+
if (doc) return doc;
|
|
2762
|
+
doc = new Y.Doc();
|
|
2763
|
+
const sp = getStatePath();
|
|
2764
|
+
if (existsSync7(sp)) {
|
|
2765
|
+
try {
|
|
2766
|
+
const state = readFileSync5(sp);
|
|
2767
|
+
Y.applyUpdate(doc, new Uint8Array(state));
|
|
2768
|
+
} catch {
|
|
2769
|
+
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
2770
|
+
try {
|
|
2771
|
+
unlinkSync4(sp);
|
|
2772
|
+
} catch {
|
|
2773
|
+
}
|
|
2774
|
+
rebuildFromDb().catch((err) => {
|
|
2775
|
+
console.warn("[crdt-sync] rebuild from DB failed:", err);
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
doc.on("update", () => {
|
|
2780
|
+
persistState();
|
|
2781
|
+
});
|
|
2782
|
+
return doc;
|
|
2783
|
+
}
|
|
2784
|
+
function getMemoriesMap() {
|
|
2785
|
+
const d = initCrdtDoc();
|
|
2786
|
+
return d.getMap("memories");
|
|
2787
|
+
}
|
|
2788
|
+
function getBehaviorsMap() {
|
|
2789
|
+
const d = initCrdtDoc();
|
|
2790
|
+
return d.getMap("behaviors");
|
|
2791
|
+
}
|
|
2792
|
+
function applyRemoteUpdate(update) {
|
|
2793
|
+
const d = initCrdtDoc();
|
|
2794
|
+
Y.applyUpdate(d, update);
|
|
2795
|
+
}
|
|
2796
|
+
function getFullState() {
|
|
2797
|
+
const d = initCrdtDoc();
|
|
2798
|
+
return Y.encodeStateAsUpdate(d);
|
|
2799
|
+
}
|
|
2800
|
+
function getDiffUpdate(remoteStateVector) {
|
|
2801
|
+
const d = initCrdtDoc();
|
|
2802
|
+
return Y.encodeStateAsUpdate(d, remoteStateVector);
|
|
2803
|
+
}
|
|
2804
|
+
function getStateVector() {
|
|
2805
|
+
const d = initCrdtDoc();
|
|
2806
|
+
return Y.encodeStateVector(d);
|
|
2807
|
+
}
|
|
2808
|
+
function importExistingMemories(memories) {
|
|
2809
|
+
const map = getMemoriesMap();
|
|
2810
|
+
const d = initCrdtDoc();
|
|
2811
|
+
let imported = 0;
|
|
2812
|
+
d.transact(() => {
|
|
2813
|
+
for (const mem of memories) {
|
|
2814
|
+
if (!mem.id) continue;
|
|
2815
|
+
if (map.has(mem.id)) continue;
|
|
2816
|
+
const entry = new Y.Map();
|
|
2817
|
+
entry.set("id", mem.id);
|
|
2818
|
+
entry.set("agent_id", mem.agent_id ?? null);
|
|
2819
|
+
entry.set("agent_role", mem.agent_role ?? null);
|
|
2820
|
+
entry.set("session_id", mem.session_id ?? null);
|
|
2821
|
+
entry.set("timestamp", mem.timestamp ?? null);
|
|
2822
|
+
entry.set("tool_name", mem.tool_name ?? null);
|
|
2823
|
+
entry.set("project_name", mem.project_name ?? null);
|
|
2824
|
+
entry.set("has_error", mem.has_error ?? 0);
|
|
2825
|
+
entry.set("raw_text", mem.raw_text ?? "");
|
|
2826
|
+
entry.set("version", mem.version ?? 0);
|
|
2827
|
+
entry.set("author_device_id", mem.author_device_id ?? null);
|
|
2828
|
+
entry.set("scope", mem.scope ?? "business");
|
|
2829
|
+
map.set(mem.id, entry);
|
|
2830
|
+
imported++;
|
|
2831
|
+
}
|
|
2832
|
+
});
|
|
2833
|
+
return imported;
|
|
2834
|
+
}
|
|
2835
|
+
function importExistingBehaviors(behaviors) {
|
|
2836
|
+
const map = getBehaviorsMap();
|
|
2837
|
+
const d = initCrdtDoc();
|
|
2838
|
+
let imported = 0;
|
|
2839
|
+
d.transact(() => {
|
|
2840
|
+
for (const beh of behaviors) {
|
|
2841
|
+
if (!beh.id) continue;
|
|
2842
|
+
if (map.has(beh.id)) continue;
|
|
2843
|
+
const entry = new Y.Map();
|
|
2844
|
+
entry.set("id", beh.id);
|
|
2845
|
+
entry.set("agent_id", beh.agent_id ?? null);
|
|
2846
|
+
entry.set("project_name", beh.project_name ?? null);
|
|
2847
|
+
entry.set("domain", beh.domain ?? null);
|
|
2848
|
+
entry.set("content", beh.content ?? null);
|
|
2849
|
+
entry.set("active", beh.active ?? 1);
|
|
2850
|
+
entry.set("priority", beh.priority ?? "p1");
|
|
2851
|
+
entry.set("created_at", beh.created_at ?? null);
|
|
2852
|
+
entry.set("updated_at", beh.updated_at ?? null);
|
|
2853
|
+
map.set(beh.id, entry);
|
|
2854
|
+
imported++;
|
|
2855
|
+
}
|
|
2856
|
+
});
|
|
2857
|
+
return imported;
|
|
2858
|
+
}
|
|
2859
|
+
function readAllMemories() {
|
|
2860
|
+
const map = getMemoriesMap();
|
|
2861
|
+
const records = [];
|
|
2862
|
+
map.forEach((entry, id) => {
|
|
2863
|
+
records.push({
|
|
2864
|
+
id,
|
|
2865
|
+
agent_id: entry.get("agent_id"),
|
|
2866
|
+
agent_role: entry.get("agent_role"),
|
|
2867
|
+
session_id: entry.get("session_id"),
|
|
2868
|
+
timestamp: entry.get("timestamp"),
|
|
2869
|
+
tool_name: entry.get("tool_name"),
|
|
2870
|
+
project_name: entry.get("project_name"),
|
|
2871
|
+
has_error: entry.get("has_error"),
|
|
2872
|
+
raw_text: entry.get("raw_text"),
|
|
2873
|
+
version: entry.get("version"),
|
|
2874
|
+
author_device_id: entry.get("author_device_id"),
|
|
2875
|
+
scope: entry.get("scope")
|
|
2876
|
+
});
|
|
2877
|
+
});
|
|
2878
|
+
return records;
|
|
2879
|
+
}
|
|
2880
|
+
function readAllBehaviors() {
|
|
2881
|
+
const map = getBehaviorsMap();
|
|
2882
|
+
const records = [];
|
|
2883
|
+
map.forEach((entry, id) => {
|
|
2884
|
+
records.push({
|
|
2885
|
+
id,
|
|
2886
|
+
agent_id: entry.get("agent_id"),
|
|
2887
|
+
project_name: entry.get("project_name"),
|
|
2888
|
+
domain: entry.get("domain"),
|
|
2889
|
+
content: entry.get("content"),
|
|
2890
|
+
active: entry.get("active"),
|
|
2891
|
+
priority: entry.get("priority"),
|
|
2892
|
+
created_at: entry.get("created_at"),
|
|
2893
|
+
updated_at: entry.get("updated_at")
|
|
2894
|
+
});
|
|
2895
|
+
});
|
|
2896
|
+
return records;
|
|
2897
|
+
}
|
|
2898
|
+
function onUpdate(callback) {
|
|
2899
|
+
const d = initCrdtDoc();
|
|
2900
|
+
const handler = (update) => callback(update);
|
|
2901
|
+
d.on("update", handler);
|
|
2902
|
+
return () => d.off("update", handler);
|
|
2903
|
+
}
|
|
2904
|
+
function persistState() {
|
|
2905
|
+
if (!doc) return;
|
|
2906
|
+
try {
|
|
2907
|
+
const sp = getStatePath();
|
|
2908
|
+
const dir = path7.dirname(sp);
|
|
2909
|
+
if (!existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2910
|
+
const state = Y.encodeStateAsUpdate(doc);
|
|
2911
|
+
writeFileSync3(sp, Buffer.from(state));
|
|
2912
|
+
} catch {
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
function isCrdtSyncEnabled() {
|
|
2916
|
+
return process.env.EXE_CRDT_SYNC !== "0";
|
|
2917
|
+
}
|
|
2918
|
+
async function rebuildFromDb() {
|
|
2919
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
2920
|
+
const client = getClient2();
|
|
2921
|
+
const result = await client.execute(
|
|
2922
|
+
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
2923
|
+
);
|
|
2924
|
+
const memories = result.rows.map((row) => ({
|
|
2925
|
+
id: String(row.id),
|
|
2926
|
+
agent_id: row.agent_id,
|
|
2927
|
+
agent_role: row.agent_role,
|
|
2928
|
+
session_id: row.session_id,
|
|
2929
|
+
timestamp: row.timestamp,
|
|
2930
|
+
tool_name: row.tool_name,
|
|
2931
|
+
project_name: row.project_name,
|
|
2932
|
+
has_error: row.has_error,
|
|
2933
|
+
raw_text: row.raw_text,
|
|
2934
|
+
version: row.version,
|
|
2935
|
+
author_device_id: row.author_device_id,
|
|
2936
|
+
scope: row.scope
|
|
2937
|
+
}));
|
|
2938
|
+
const count = importExistingMemories(memories);
|
|
2939
|
+
persistState();
|
|
2940
|
+
return count;
|
|
2941
|
+
}
|
|
2942
|
+
function destroyCrdtDoc() {
|
|
2943
|
+
if (doc) {
|
|
2944
|
+
doc.destroy();
|
|
2945
|
+
doc = null;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
2949
|
+
var init_crdt_sync = __esm({
|
|
2950
|
+
"src/lib/crdt-sync.ts"() {
|
|
2951
|
+
"use strict";
|
|
2952
|
+
DEFAULT_STATE_PATH = path7.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
2953
|
+
_statePathOverride = null;
|
|
2954
|
+
doc = null;
|
|
2955
|
+
}
|
|
2956
|
+
});
|
|
2957
|
+
|
|
2424
2958
|
// src/lib/cloud-sync.ts
|
|
2425
2959
|
var cloud_sync_exports = {};
|
|
2426
2960
|
__export(cloud_sync_exports, {
|
|
@@ -2449,16 +2983,16 @@ __export(cloud_sync_exports, {
|
|
|
2449
2983
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
2450
2984
|
recordRosterDeletion: () => recordRosterDeletion
|
|
2451
2985
|
});
|
|
2452
|
-
import { readFileSync as
|
|
2986
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
2453
2987
|
import crypto2 from "crypto";
|
|
2454
|
-
import
|
|
2455
|
-
import { homedir } from "os";
|
|
2988
|
+
import path8 from "path";
|
|
2989
|
+
import { homedir as homedir2 } from "os";
|
|
2456
2990
|
function sqlSafe(v) {
|
|
2457
2991
|
return v === void 0 ? null : v;
|
|
2458
2992
|
}
|
|
2459
2993
|
function logError(msg) {
|
|
2460
2994
|
try {
|
|
2461
|
-
const logPath =
|
|
2995
|
+
const logPath = path8.join(homedir2(), ".exe-os", "workers.log");
|
|
2462
2996
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
2463
2997
|
`);
|
|
2464
2998
|
} catch {
|
|
@@ -2468,18 +3002,18 @@ async function withRosterLock(fn) {
|
|
|
2468
3002
|
try {
|
|
2469
3003
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
2470
3004
|
closeSync2(fd);
|
|
2471
|
-
|
|
3005
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
2472
3006
|
} catch (err) {
|
|
2473
3007
|
if (err.code === "EEXIST") {
|
|
2474
3008
|
try {
|
|
2475
|
-
const ts = parseInt(
|
|
3009
|
+
const ts = parseInt(readFileSync6(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
2476
3010
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
2477
3011
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
2478
3012
|
}
|
|
2479
|
-
|
|
3013
|
+
unlinkSync5(ROSTER_LOCK_PATH);
|
|
2480
3014
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
2481
3015
|
closeSync2(fd);
|
|
2482
|
-
|
|
3016
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
2483
3017
|
} catch (retryErr) {
|
|
2484
3018
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
2485
3019
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -2492,7 +3026,7 @@ async function withRosterLock(fn) {
|
|
|
2492
3026
|
return await fn();
|
|
2493
3027
|
} finally {
|
|
2494
3028
|
try {
|
|
2495
|
-
|
|
3029
|
+
unlinkSync5(ROSTER_LOCK_PATH);
|
|
2496
3030
|
} catch {
|
|
2497
3031
|
}
|
|
2498
3032
|
}
|
|
@@ -2637,29 +3171,75 @@ async function cloudSync(config) {
|
|
|
2637
3171
|
const pullResult = await cloudPull(lastPullVersion, config);
|
|
2638
3172
|
let pulled = 0;
|
|
2639
3173
|
if (pullResult.records.length > 0) {
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
3174
|
+
if (isCrdtSyncEnabled()) {
|
|
3175
|
+
const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
|
|
3176
|
+
initCrdtDoc2();
|
|
3177
|
+
importExistingMemories2(
|
|
3178
|
+
pullResult.records.map((rec) => ({
|
|
3179
|
+
id: String(rec.id ?? ""),
|
|
3180
|
+
agent_id: rec.agent_id,
|
|
3181
|
+
agent_role: rec.agent_role,
|
|
3182
|
+
session_id: rec.session_id,
|
|
3183
|
+
timestamp: rec.timestamp,
|
|
3184
|
+
tool_name: rec.tool_name,
|
|
3185
|
+
project_name: rec.project_name,
|
|
3186
|
+
has_error: rec.has_error ?? 0,
|
|
3187
|
+
raw_text: rec.raw_text ?? "",
|
|
3188
|
+
version: rec.version ?? 0,
|
|
3189
|
+
author_device_id: rec.author_device_id,
|
|
3190
|
+
scope: rec.scope ?? "business"
|
|
3191
|
+
}))
|
|
3192
|
+
);
|
|
3193
|
+
const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
|
|
3194
|
+
const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
|
|
3195
|
+
const stmts = merged.map((rec) => ({
|
|
3196
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
3197
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
3198
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
3199
|
+
author_device_id, scope)
|
|
3200
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3201
|
+
args: [
|
|
3202
|
+
sqlSafe(rec.id),
|
|
3203
|
+
sqlSafe(rec.agent_id),
|
|
3204
|
+
sqlSafe(rec.agent_role),
|
|
3205
|
+
sqlSafe(rec.session_id),
|
|
3206
|
+
sqlSafe(rec.timestamp),
|
|
3207
|
+
sqlSafe(rec.tool_name),
|
|
3208
|
+
sqlSafe(rec.project_name),
|
|
3209
|
+
sqlSafe(rec.has_error ?? 0),
|
|
3210
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
3211
|
+
sqlSafe(rec.version ?? 0),
|
|
3212
|
+
sqlSafe(rec.author_device_id),
|
|
3213
|
+
sqlSafe(rec.scope ?? "business")
|
|
3214
|
+
]
|
|
3215
|
+
}));
|
|
3216
|
+
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
3217
|
+
pulled = pullResult.records.length;
|
|
3218
|
+
} else {
|
|
3219
|
+
const stmts = pullResult.records.map((rec) => ({
|
|
3220
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
3221
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
3222
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
3223
|
+
author_device_id, scope)
|
|
3224
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3225
|
+
args: [
|
|
3226
|
+
sqlSafe(rec.id),
|
|
3227
|
+
sqlSafe(rec.agent_id),
|
|
3228
|
+
sqlSafe(rec.agent_role),
|
|
3229
|
+
sqlSafe(rec.session_id),
|
|
3230
|
+
sqlSafe(rec.timestamp),
|
|
3231
|
+
sqlSafe(rec.tool_name),
|
|
3232
|
+
sqlSafe(rec.project_name),
|
|
3233
|
+
sqlSafe(rec.has_error ?? 0),
|
|
3234
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
3235
|
+
sqlSafe(rec.version ?? 0),
|
|
3236
|
+
sqlSafe(rec.author_device_id),
|
|
3237
|
+
sqlSafe(rec.scope ?? "business")
|
|
3238
|
+
]
|
|
3239
|
+
}));
|
|
3240
|
+
await client.batch(stmts, "write");
|
|
3241
|
+
pulled = pullResult.records.length;
|
|
3242
|
+
}
|
|
2663
3243
|
}
|
|
2664
3244
|
if (pullResult.maxVersion > lastPullVersion) {
|
|
2665
3245
|
await client.execute({
|
|
@@ -2807,8 +3387,8 @@ async function cloudSync(config) {
|
|
|
2807
3387
|
try {
|
|
2808
3388
|
const employees = await loadEmployees();
|
|
2809
3389
|
rosterResult.employees = employees.length;
|
|
2810
|
-
const idDir =
|
|
2811
|
-
if (
|
|
3390
|
+
const idDir = path8.join(EXE_AI_DIR, "identity");
|
|
3391
|
+
if (existsSync8(idDir)) {
|
|
2812
3392
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
2813
3393
|
}
|
|
2814
3394
|
} catch {
|
|
@@ -2829,55 +3409,63 @@ async function cloudSync(config) {
|
|
|
2829
3409
|
function recordRosterDeletion(name) {
|
|
2830
3410
|
let deletions = [];
|
|
2831
3411
|
try {
|
|
2832
|
-
if (
|
|
2833
|
-
deletions = JSON.parse(
|
|
3412
|
+
if (existsSync8(ROSTER_DELETIONS_PATH)) {
|
|
3413
|
+
deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
2834
3414
|
}
|
|
2835
3415
|
} catch {
|
|
2836
3416
|
}
|
|
2837
3417
|
if (!deletions.includes(name)) deletions.push(name);
|
|
2838
|
-
|
|
3418
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
2839
3419
|
}
|
|
2840
3420
|
function consumeRosterDeletions() {
|
|
2841
3421
|
try {
|
|
2842
|
-
if (!
|
|
2843
|
-
const deletions = JSON.parse(
|
|
2844
|
-
|
|
3422
|
+
if (!existsSync8(ROSTER_DELETIONS_PATH)) return [];
|
|
3423
|
+
const deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
3424
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
|
|
2845
3425
|
return deletions;
|
|
2846
3426
|
} catch {
|
|
2847
3427
|
return [];
|
|
2848
3428
|
}
|
|
2849
3429
|
}
|
|
2850
3430
|
function buildRosterBlob(paths) {
|
|
2851
|
-
const rosterPath = paths?.rosterPath ??
|
|
2852
|
-
const identityDir = paths?.identityDir ??
|
|
2853
|
-
const configPath = paths?.configPath ??
|
|
3431
|
+
const rosterPath = paths?.rosterPath ?? path8.join(EXE_AI_DIR, "exe-employees.json");
|
|
3432
|
+
const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
|
|
3433
|
+
const configPath = paths?.configPath ?? path8.join(EXE_AI_DIR, "config.json");
|
|
2854
3434
|
let roster = [];
|
|
2855
|
-
if (
|
|
3435
|
+
if (existsSync8(rosterPath)) {
|
|
2856
3436
|
try {
|
|
2857
|
-
roster = JSON.parse(
|
|
3437
|
+
roster = JSON.parse(readFileSync6(rosterPath, "utf-8"));
|
|
2858
3438
|
} catch {
|
|
2859
3439
|
}
|
|
2860
3440
|
}
|
|
2861
3441
|
const identities = {};
|
|
2862
|
-
if (
|
|
3442
|
+
if (existsSync8(identityDir)) {
|
|
2863
3443
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
2864
3444
|
try {
|
|
2865
|
-
identities[file] =
|
|
3445
|
+
identities[file] = readFileSync6(path8.join(identityDir, file), "utf-8");
|
|
2866
3446
|
} catch {
|
|
2867
3447
|
}
|
|
2868
3448
|
}
|
|
2869
3449
|
}
|
|
2870
3450
|
let config;
|
|
2871
|
-
if (
|
|
3451
|
+
if (existsSync8(configPath)) {
|
|
2872
3452
|
try {
|
|
2873
|
-
config = JSON.parse(
|
|
3453
|
+
config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
3454
|
+
} catch {
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
let agentConfig;
|
|
3458
|
+
const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
3459
|
+
if (existsSync8(agentConfigPath)) {
|
|
3460
|
+
try {
|
|
3461
|
+
agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
|
|
2874
3462
|
} catch {
|
|
2875
3463
|
}
|
|
2876
3464
|
}
|
|
2877
3465
|
const deletedNames = consumeRosterDeletions();
|
|
2878
|
-
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
3466
|
+
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
2879
3467
|
const hash = crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
2880
|
-
return { roster, identities, config, deletedNames, version: hash };
|
|
3468
|
+
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
2881
3469
|
}
|
|
2882
3470
|
async function cloudPushRoster(config) {
|
|
2883
3471
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -2946,23 +3534,23 @@ async function cloudPullRoster(config) {
|
|
|
2946
3534
|
}
|
|
2947
3535
|
}
|
|
2948
3536
|
function mergeConfig(remoteConfig, configPath) {
|
|
2949
|
-
const cfgPath = configPath ??
|
|
3537
|
+
const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
|
|
2950
3538
|
let local = {};
|
|
2951
|
-
if (
|
|
3539
|
+
if (existsSync8(cfgPath)) {
|
|
2952
3540
|
try {
|
|
2953
|
-
local = JSON.parse(
|
|
3541
|
+
local = JSON.parse(readFileSync6(cfgPath, "utf-8"));
|
|
2954
3542
|
} catch {
|
|
2955
3543
|
}
|
|
2956
3544
|
}
|
|
2957
3545
|
const merged = { ...remoteConfig, ...local };
|
|
2958
|
-
const dir =
|
|
2959
|
-
if (!
|
|
2960
|
-
|
|
3546
|
+
const dir = path8.dirname(cfgPath);
|
|
3547
|
+
if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
|
|
3548
|
+
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
2961
3549
|
}
|
|
2962
3550
|
async function mergeRosterFromRemote(remote, paths) {
|
|
2963
3551
|
return withRosterLock(async () => {
|
|
2964
3552
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2965
|
-
const identityDir = paths?.identityDir ??
|
|
3553
|
+
const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
|
|
2966
3554
|
const localEmployees = await loadEmployees(rosterPath);
|
|
2967
3555
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
2968
3556
|
let added = 0;
|
|
@@ -2983,15 +3571,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2983
3571
|
) ?? lookupKey;
|
|
2984
3572
|
const remoteIdentity = remote.identities[matchedKey];
|
|
2985
3573
|
if (remoteIdentity) {
|
|
2986
|
-
if (!
|
|
2987
|
-
const idPath =
|
|
3574
|
+
if (!existsSync8(identityDir)) mkdirSync3(identityDir, { recursive: true });
|
|
3575
|
+
const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
|
|
2988
3576
|
let localIdentity = null;
|
|
2989
3577
|
try {
|
|
2990
|
-
localIdentity =
|
|
3578
|
+
localIdentity = existsSync8(idPath) ? readFileSync6(idPath, "utf-8") : null;
|
|
2991
3579
|
} catch {
|
|
2992
3580
|
}
|
|
2993
3581
|
if (localIdentity !== remoteIdentity) {
|
|
2994
|
-
|
|
3582
|
+
writeFileSync4(idPath, remoteIdentity, "utf-8");
|
|
2995
3583
|
identitiesUpdated++;
|
|
2996
3584
|
}
|
|
2997
3585
|
}
|
|
@@ -3015,6 +3603,21 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
3015
3603
|
} catch {
|
|
3016
3604
|
}
|
|
3017
3605
|
}
|
|
3606
|
+
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
3607
|
+
try {
|
|
3608
|
+
const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
3609
|
+
let local = {};
|
|
3610
|
+
if (existsSync8(agentConfigPath)) {
|
|
3611
|
+
try {
|
|
3612
|
+
local = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
|
|
3613
|
+
} catch {
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
const merged = { ...remote.agentConfig, ...local };
|
|
3617
|
+
writeFileSync4(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
3618
|
+
} catch {
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3018
3621
|
return { added, identitiesUpdated };
|
|
3019
3622
|
});
|
|
3020
3623
|
}
|
|
@@ -3444,13 +4047,14 @@ var init_cloud_sync = __esm({
|
|
|
3444
4047
|
init_compress();
|
|
3445
4048
|
init_license();
|
|
3446
4049
|
init_config();
|
|
4050
|
+
init_crdt_sync();
|
|
3447
4051
|
init_employees();
|
|
3448
4052
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
3449
4053
|
FETCH_TIMEOUT_MS = 3e4;
|
|
3450
4054
|
PUSH_BATCH_SIZE = 5e3;
|
|
3451
|
-
ROSTER_LOCK_PATH =
|
|
4055
|
+
ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
|
|
3452
4056
|
LOCK_STALE_MS = 3e4;
|
|
3453
|
-
ROSTER_DELETIONS_PATH =
|
|
4057
|
+
ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
|
|
3454
4058
|
}
|
|
3455
4059
|
});
|
|
3456
4060
|
|
|
@@ -4215,17 +4819,17 @@ __export(identity_exports, {
|
|
|
4215
4819
|
listIdentities: () => listIdentities,
|
|
4216
4820
|
updateIdentity: () => updateIdentity
|
|
4217
4821
|
});
|
|
4218
|
-
import { existsSync as
|
|
4822
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
4219
4823
|
import { readdirSync as readdirSync2 } from "fs";
|
|
4220
|
-
import
|
|
4824
|
+
import path9 from "path";
|
|
4221
4825
|
import { createHash as createHash2 } from "crypto";
|
|
4222
4826
|
function ensureDir() {
|
|
4223
|
-
if (!
|
|
4224
|
-
|
|
4827
|
+
if (!existsSync9(IDENTITY_DIR)) {
|
|
4828
|
+
mkdirSync4(IDENTITY_DIR, { recursive: true });
|
|
4225
4829
|
}
|
|
4226
4830
|
}
|
|
4227
4831
|
function identityPath(agentId) {
|
|
4228
|
-
return
|
|
4832
|
+
return path9.join(IDENTITY_DIR, `${agentId}.md`);
|
|
4229
4833
|
}
|
|
4230
4834
|
function parseFrontmatter(raw) {
|
|
4231
4835
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -4266,8 +4870,8 @@ function contentHash(content) {
|
|
|
4266
4870
|
}
|
|
4267
4871
|
function getIdentity(agentId) {
|
|
4268
4872
|
const filePath = identityPath(agentId);
|
|
4269
|
-
if (!
|
|
4270
|
-
const raw =
|
|
4873
|
+
if (!existsSync9(filePath)) return null;
|
|
4874
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
4271
4875
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
4272
4876
|
return {
|
|
4273
4877
|
agentId,
|
|
@@ -4281,7 +4885,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4281
4885
|
ensureDir();
|
|
4282
4886
|
const filePath = identityPath(agentId);
|
|
4283
4887
|
const hash = contentHash(content);
|
|
4284
|
-
|
|
4888
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
4285
4889
|
try {
|
|
4286
4890
|
const client = getClient();
|
|
4287
4891
|
await client.execute({
|
|
@@ -4337,7 +4941,7 @@ var init_identity = __esm({
|
|
|
4337
4941
|
"use strict";
|
|
4338
4942
|
init_config();
|
|
4339
4943
|
init_database();
|
|
4340
|
-
IDENTITY_DIR =
|
|
4944
|
+
IDENTITY_DIR = path9.join(EXE_AI_DIR, "identity");
|
|
4341
4945
|
}
|
|
4342
4946
|
});
|
|
4343
4947
|
|
|
@@ -4877,36 +5481,36 @@ __export(session_wrappers_exports, {
|
|
|
4877
5481
|
generateSessionWrappers: () => generateSessionWrappers
|
|
4878
5482
|
});
|
|
4879
5483
|
import {
|
|
4880
|
-
existsSync as
|
|
4881
|
-
readFileSync as
|
|
4882
|
-
writeFileSync as
|
|
4883
|
-
mkdirSync as
|
|
5484
|
+
existsSync as existsSync10,
|
|
5485
|
+
readFileSync as readFileSync8,
|
|
5486
|
+
writeFileSync as writeFileSync6,
|
|
5487
|
+
mkdirSync as mkdirSync5,
|
|
4884
5488
|
chmodSync,
|
|
4885
5489
|
readdirSync as readdirSync3,
|
|
4886
|
-
unlinkSync as
|
|
5490
|
+
unlinkSync as unlinkSync6
|
|
4887
5491
|
} from "fs";
|
|
4888
|
-
import
|
|
4889
|
-
import { homedir as
|
|
5492
|
+
import path10 from "path";
|
|
5493
|
+
import { homedir as homedir3 } from "os";
|
|
4890
5494
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
4891
|
-
const home = homeDir ??
|
|
4892
|
-
const binDir =
|
|
4893
|
-
const rosterPath =
|
|
4894
|
-
|
|
4895
|
-
const exeStartDst =
|
|
5495
|
+
const home = homeDir ?? homedir3();
|
|
5496
|
+
const binDir = path10.join(home, ".exe-os", "bin");
|
|
5497
|
+
const rosterPath = path10.join(home, ".exe-os", "exe-employees.json");
|
|
5498
|
+
mkdirSync5(binDir, { recursive: true });
|
|
5499
|
+
const exeStartDst = path10.join(binDir, "exe-start");
|
|
4896
5500
|
const candidates = [
|
|
4897
|
-
|
|
4898
|
-
|
|
5501
|
+
path10.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
5502
|
+
path10.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
4899
5503
|
];
|
|
4900
5504
|
for (const src of candidates) {
|
|
4901
|
-
if (
|
|
4902
|
-
|
|
5505
|
+
if (existsSync10(src)) {
|
|
5506
|
+
writeFileSync6(exeStartDst, readFileSync8(src));
|
|
4903
5507
|
chmodSync(exeStartDst, 493);
|
|
4904
5508
|
break;
|
|
4905
5509
|
}
|
|
4906
5510
|
}
|
|
4907
5511
|
let employees = [];
|
|
4908
5512
|
try {
|
|
4909
|
-
employees = JSON.parse(
|
|
5513
|
+
employees = JSON.parse(readFileSync8(rosterPath, "utf8"));
|
|
4910
5514
|
} catch {
|
|
4911
5515
|
return { created: 0, pathConfigured: false };
|
|
4912
5516
|
}
|
|
@@ -4916,11 +5520,11 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
4916
5520
|
try {
|
|
4917
5521
|
for (const f of readdirSync3(binDir)) {
|
|
4918
5522
|
if (f === "exe-start") continue;
|
|
4919
|
-
const fPath =
|
|
5523
|
+
const fPath = path10.join(binDir, f);
|
|
4920
5524
|
try {
|
|
4921
|
-
const content =
|
|
5525
|
+
const content = readFileSync8(fPath, "utf8");
|
|
4922
5526
|
if (content.includes("exe-start")) {
|
|
4923
|
-
|
|
5527
|
+
unlinkSync6(fPath);
|
|
4924
5528
|
}
|
|
4925
5529
|
} catch {
|
|
4926
5530
|
}
|
|
@@ -4933,8 +5537,30 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
4933
5537
|
`;
|
|
4934
5538
|
for (const emp of employees) {
|
|
4935
5539
|
for (let n = 1; n <= MAX_N; n++) {
|
|
4936
|
-
const wrapperPath =
|
|
4937
|
-
|
|
5540
|
+
const wrapperPath = path10.join(binDir, `${emp.name}${n}`);
|
|
5541
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
5542
|
+
chmodSync(wrapperPath, 493);
|
|
5543
|
+
created++;
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
const codexLauncherCandidates = [
|
|
5547
|
+
path10.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
5548
|
+
path10.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
5549
|
+
];
|
|
5550
|
+
let codexLauncher = null;
|
|
5551
|
+
for (const c of codexLauncherCandidates) {
|
|
5552
|
+
if (existsSync10(c)) {
|
|
5553
|
+
codexLauncher = c;
|
|
5554
|
+
break;
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
if (codexLauncher) {
|
|
5558
|
+
for (const emp of employees) {
|
|
5559
|
+
const wrapperPath = path10.join(binDir, `${emp.name}-codex`);
|
|
5560
|
+
const content = `#!/bin/bash
|
|
5561
|
+
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
5562
|
+
`;
|
|
5563
|
+
writeFileSync6(wrapperPath, content);
|
|
4938
5564
|
chmodSync(wrapperPath, 493);
|
|
4939
5565
|
created++;
|
|
4940
5566
|
}
|
|
@@ -4953,24 +5579,24 @@ export PATH="${binDir}:$PATH"
|
|
|
4953
5579
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
4954
5580
|
const profilePaths = [];
|
|
4955
5581
|
if (shell.includes("zsh")) {
|
|
4956
|
-
profilePaths.push(
|
|
5582
|
+
profilePaths.push(path10.join(home, ".zshrc"));
|
|
4957
5583
|
} else if (shell.includes("bash")) {
|
|
4958
|
-
profilePaths.push(
|
|
4959
|
-
profilePaths.push(
|
|
5584
|
+
profilePaths.push(path10.join(home, ".bashrc"));
|
|
5585
|
+
profilePaths.push(path10.join(home, ".bash_profile"));
|
|
4960
5586
|
} else {
|
|
4961
|
-
profilePaths.push(
|
|
5587
|
+
profilePaths.push(path10.join(home, ".profile"));
|
|
4962
5588
|
}
|
|
4963
5589
|
for (const profilePath of profilePaths) {
|
|
4964
5590
|
try {
|
|
4965
5591
|
let content = "";
|
|
4966
5592
|
try {
|
|
4967
|
-
content =
|
|
5593
|
+
content = readFileSync8(profilePath, "utf8");
|
|
4968
5594
|
} catch {
|
|
4969
5595
|
}
|
|
4970
5596
|
if (content.includes(".exe-os/bin")) {
|
|
4971
5597
|
return false;
|
|
4972
5598
|
}
|
|
4973
|
-
|
|
5599
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
4974
5600
|
return true;
|
|
4975
5601
|
} catch {
|
|
4976
5602
|
continue;
|
|
@@ -4990,9 +5616,9 @@ var init_session_wrappers = __esm({
|
|
|
4990
5616
|
init_config();
|
|
4991
5617
|
init_keychain();
|
|
4992
5618
|
import crypto3 from "crypto";
|
|
4993
|
-
import { existsSync as
|
|
5619
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync7 } from "fs";
|
|
4994
5620
|
import os4 from "os";
|
|
4995
|
-
import
|
|
5621
|
+
import path11 from "path";
|
|
4996
5622
|
import { createInterface } from "readline";
|
|
4997
5623
|
|
|
4998
5624
|
// src/lib/model-downloader.ts
|
|
@@ -5084,36 +5710,36 @@ async function fileHash(filePath) {
|
|
|
5084
5710
|
|
|
5085
5711
|
// src/lib/setup-wizard.ts
|
|
5086
5712
|
function findPackageRoot2() {
|
|
5087
|
-
let dir =
|
|
5088
|
-
const root =
|
|
5713
|
+
let dir = path11.dirname(new URL(import.meta.url).pathname);
|
|
5714
|
+
const root = path11.parse(dir).root;
|
|
5089
5715
|
while (dir !== root) {
|
|
5090
|
-
const pkgPath =
|
|
5091
|
-
if (
|
|
5716
|
+
const pkgPath = path11.join(dir, "package.json");
|
|
5717
|
+
if (existsSync11(pkgPath)) {
|
|
5092
5718
|
try {
|
|
5093
|
-
const pkg = JSON.parse(
|
|
5719
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
5094
5720
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
5095
5721
|
} catch {
|
|
5096
5722
|
}
|
|
5097
5723
|
}
|
|
5098
|
-
dir =
|
|
5724
|
+
dir = path11.dirname(dir);
|
|
5099
5725
|
}
|
|
5100
5726
|
return null;
|
|
5101
5727
|
}
|
|
5102
|
-
var SETUP_STATE_PATH =
|
|
5728
|
+
var SETUP_STATE_PATH = path11.join(os4.homedir(), ".exe-os", "setup-state.json");
|
|
5103
5729
|
function loadSetupState() {
|
|
5104
5730
|
try {
|
|
5105
|
-
return JSON.parse(
|
|
5731
|
+
return JSON.parse(readFileSync9(SETUP_STATE_PATH, "utf8"));
|
|
5106
5732
|
} catch {
|
|
5107
5733
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5108
5734
|
}
|
|
5109
5735
|
}
|
|
5110
5736
|
function saveSetupState(state) {
|
|
5111
|
-
|
|
5112
|
-
|
|
5737
|
+
mkdirSync6(path11.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
5738
|
+
writeFileSync7(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5113
5739
|
}
|
|
5114
5740
|
function clearSetupState() {
|
|
5115
5741
|
try {
|
|
5116
|
-
|
|
5742
|
+
unlinkSync7(SETUP_STATE_PATH);
|
|
5117
5743
|
} catch {
|
|
5118
5744
|
}
|
|
5119
5745
|
}
|
|
@@ -5207,7 +5833,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5207
5833
|
if (state.completedSteps.length > 0) {
|
|
5208
5834
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5209
5835
|
}
|
|
5210
|
-
if (
|
|
5836
|
+
if (existsSync11(LEGACY_LANCE_PATH)) {
|
|
5211
5837
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5212
5838
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5213
5839
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -5355,10 +5981,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
5355
5981
|
await saveConfig(config);
|
|
5356
5982
|
log("");
|
|
5357
5983
|
try {
|
|
5358
|
-
const claudeJsonPath =
|
|
5984
|
+
const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
|
|
5359
5985
|
let claudeJson = {};
|
|
5360
5986
|
try {
|
|
5361
|
-
claudeJson = JSON.parse(
|
|
5987
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
5362
5988
|
} catch {
|
|
5363
5989
|
}
|
|
5364
5990
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5367,7 +5993,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5367
5993
|
if (!projects[dir]) projects[dir] = {};
|
|
5368
5994
|
projects[dir].hasTrustDialogAccepted = true;
|
|
5369
5995
|
}
|
|
5370
|
-
|
|
5996
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5371
5997
|
} catch {
|
|
5372
5998
|
}
|
|
5373
5999
|
state.completedSteps.push(5);
|
|
@@ -5406,7 +6032,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5406
6032
|
let missingIdentities = [];
|
|
5407
6033
|
for (const emp of roster) {
|
|
5408
6034
|
const idPath = identityPath2(emp.name);
|
|
5409
|
-
if (!
|
|
6035
|
+
if (!existsSync11(idPath)) {
|
|
5410
6036
|
missingIdentities.push(emp.name);
|
|
5411
6037
|
}
|
|
5412
6038
|
}
|
|
@@ -5438,7 +6064,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5438
6064
|
}
|
|
5439
6065
|
missingIdentities = [];
|
|
5440
6066
|
for (const emp of roster) {
|
|
5441
|
-
if (!
|
|
6067
|
+
if (!existsSync11(identityPath2(emp.name))) {
|
|
5442
6068
|
missingIdentities.push(emp.name);
|
|
5443
6069
|
}
|
|
5444
6070
|
}
|
|
@@ -5469,11 +6095,12 @@ async function runSetupWizard(opts = {}) {
|
|
|
5469
6095
|
saveSetupState(state);
|
|
5470
6096
|
log("");
|
|
5471
6097
|
} else if (!state.completedSteps.includes(6)) {
|
|
5472
|
-
log("=== Your
|
|
6098
|
+
log("=== Your First Hire ===");
|
|
5473
6099
|
log("");
|
|
5474
|
-
log("
|
|
5475
|
-
log("
|
|
5476
|
-
log("
|
|
6100
|
+
log("Until now, you talked to one agent at a time.");
|
|
6101
|
+
log("Your COO changes that. You talk to them \u2014 they coordinate");
|
|
6102
|
+
log("your entire team. Engineers, marketers, specialists, all working");
|
|
6103
|
+
log("in parallel while you focus on what matters.");
|
|
5477
6104
|
log("");
|
|
5478
6105
|
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5479
6106
|
cooName = (cooNameInput || "exe").toLowerCase();
|
|
@@ -5494,9 +6121,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
5494
6121
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5495
6122
|
if (cooIdentityContent) {
|
|
5496
6123
|
const cooIdPath = identityPath2(cooName);
|
|
5497
|
-
|
|
6124
|
+
mkdirSync6(path11.dirname(cooIdPath), { recursive: true });
|
|
5498
6125
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5499
|
-
|
|
6126
|
+
writeFileSync7(cooIdPath, replaced, "utf-8");
|
|
5500
6127
|
}
|
|
5501
6128
|
registerBinSymlinks2(cooName);
|
|
5502
6129
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -5510,134 +6137,123 @@ async function runSetupWizard(opts = {}) {
|
|
|
5510
6137
|
}
|
|
5511
6138
|
log("");
|
|
5512
6139
|
if (!state.completedSteps.includes(7)) {
|
|
5513
|
-
log("=== Meet Your Specialists ===");
|
|
5514
|
-
log("");
|
|
5515
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5516
|
-
log("");
|
|
5517
|
-
log(" CTO (default: yoshi)");
|
|
5518
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5519
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5520
|
-
log("");
|
|
5521
|
-
log(" CMO (default: mari)");
|
|
5522
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5523
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5524
|
-
log("");
|
|
5525
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
5526
|
-
log("");
|
|
5527
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5528
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5529
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5530
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5531
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
5532
|
-
log("");
|
|
5533
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5534
|
-
log("instead of headcount.");
|
|
5535
|
-
log("");
|
|
5536
|
-
await ask(rl, "Press Enter to continue. ");
|
|
5537
|
-
state.completedSteps.push(7);
|
|
5538
|
-
saveSetupState(state);
|
|
5539
|
-
} else {
|
|
5540
|
-
log("Step 7 already complete \u2014 skipping.");
|
|
5541
|
-
}
|
|
5542
|
-
if (!state.completedSteps.includes(8)) {
|
|
5543
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5544
6140
|
let license;
|
|
5545
6141
|
try {
|
|
5546
6142
|
license = await checkLicense2();
|
|
5547
6143
|
} catch {
|
|
5548
6144
|
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5549
6145
|
}
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
if (licenseInput.startsWith("exe_sk_")) {
|
|
6146
|
+
let isLicensed = license.valid && license.plan !== "free";
|
|
6147
|
+
if (!isLicensed) {
|
|
6148
|
+
let licenseInput = await ask(rl, "Do you have an Exe OS license key? (press Enter to skip for free plan): ");
|
|
6149
|
+
if (licenseInput && !licenseInput.startsWith("exe_sk_")) {
|
|
6150
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
6151
|
+
licenseInput = await ask(rl, "Try again (or press Enter to skip): ");
|
|
6152
|
+
}
|
|
6153
|
+
if (licenseInput && licenseInput.startsWith("exe_sk_")) {
|
|
5559
6154
|
saveLicense2(licenseInput);
|
|
5560
6155
|
mirrorLicenseKey2(licenseInput);
|
|
5561
6156
|
try {
|
|
5562
|
-
|
|
6157
|
+
const result = await validateLicense2(licenseInput);
|
|
6158
|
+
if (result.valid && result.plan !== "free") {
|
|
6159
|
+
isLicensed = true;
|
|
6160
|
+
const planName = result.plan === "pro" ? "Solopreneur" : result.plan.charAt(0).toUpperCase() + result.plan.slice(1);
|
|
6161
|
+
log(`License activated. Plan: ${planName}`);
|
|
6162
|
+
} else if (!result.valid) {
|
|
6163
|
+
log("That license key is invalid or expired.");
|
|
6164
|
+
log("Get a new key at askexe.com.");
|
|
6165
|
+
} else {
|
|
6166
|
+
log("Key saved for cloud sync, but specialists require a paid plan.");
|
|
6167
|
+
log("Upgrade at askexe.com when you're ready.");
|
|
6168
|
+
}
|
|
5563
6169
|
} catch {
|
|
5564
|
-
|
|
5565
|
-
log("
|
|
6170
|
+
isLicensed = true;
|
|
6171
|
+
log("Couldn't reach the license server \u2014 key saved, specialists enabled.");
|
|
5566
6172
|
}
|
|
5567
|
-
} else if (!licenseInput) {
|
|
5568
|
-
log("You can activate anytime with: exe-os --activate <key>");
|
|
5569
|
-
} else {
|
|
5570
|
-
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
5571
|
-
log("You can activate anytime with: exe-os --activate <key>");
|
|
5572
6173
|
}
|
|
5573
6174
|
}
|
|
5574
|
-
if (
|
|
5575
|
-
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
5576
|
-
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
6175
|
+
if (!isLicensed) {
|
|
5577
6176
|
log("");
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
const ctoTemplate = getTemplateByRole2("CTO");
|
|
5581
|
-
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
5582
|
-
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
5583
|
-
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
5584
|
-
if (!employees.some((e) => e.name === ctoName)) {
|
|
5585
|
-
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5586
|
-
const ctoEmployee = {
|
|
5587
|
-
name: ctoName,
|
|
5588
|
-
role: "CTO",
|
|
5589
|
-
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
5590
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5591
|
-
};
|
|
5592
|
-
employees = addEmployee2(employees, ctoEmployee);
|
|
5593
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5594
|
-
}
|
|
5595
|
-
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
5596
|
-
if (ctoIdentityContent) {
|
|
5597
|
-
const ctoIdPath = identityPath2(ctoName);
|
|
5598
|
-
mkdirSync5(path10.dirname(ctoIdPath), { recursive: true });
|
|
5599
|
-
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
5600
|
-
writeFileSync6(ctoIdPath, replaced, "utf-8");
|
|
5601
|
-
}
|
|
5602
|
-
registerBinSymlinks2(ctoName);
|
|
5603
|
-
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
5604
|
-
log(`Created ${ctoName} (CTO)`);
|
|
5605
|
-
}
|
|
5606
|
-
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
5607
|
-
if (createCmo.toLowerCase() !== "n") {
|
|
5608
|
-
const cmoTemplate = getTemplateByRole2("CMO");
|
|
5609
|
-
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
5610
|
-
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
5611
|
-
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
5612
|
-
if (!employees.some((e) => e.name === cmoName)) {
|
|
5613
|
-
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5614
|
-
const cmoEmployee = {
|
|
5615
|
-
name: cmoName,
|
|
5616
|
-
role: "CMO",
|
|
5617
|
-
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
5618
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5619
|
-
};
|
|
5620
|
-
employees = addEmployee2(employees, cmoEmployee);
|
|
5621
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5622
|
-
}
|
|
5623
|
-
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
5624
|
-
if (cmoIdentityContent) {
|
|
5625
|
-
const cmoIdPath = identityPath2(cmoName);
|
|
5626
|
-
mkdirSync5(path10.dirname(cmoIdPath), { recursive: true });
|
|
5627
|
-
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
5628
|
-
writeFileSync6(cmoIdPath, replaced, "utf-8");
|
|
5629
|
-
}
|
|
5630
|
-
registerBinSymlinks2(cmoName);
|
|
5631
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5632
|
-
log(`Created ${cmoName} (CMO)`);
|
|
5633
|
-
}
|
|
6177
|
+
log("You're all set. Your COO handles everything from here.");
|
|
6178
|
+
log("When you're ready for specialists, add a license key at askexe.com.");
|
|
5634
6179
|
log("");
|
|
6180
|
+
log(`Type your COO's name to start: /${cooName}`);
|
|
6181
|
+
state.completedSteps.push(7, 8);
|
|
6182
|
+
saveSetupState(state);
|
|
6183
|
+
} else {
|
|
6184
|
+
state.completedSteps.push(7);
|
|
6185
|
+
saveSetupState(state);
|
|
6186
|
+
}
|
|
6187
|
+
} else {
|
|
6188
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
6189
|
+
}
|
|
6190
|
+
if (!state.completedSteps.includes(8)) {
|
|
6191
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
6192
|
+
log("=== Hire Your Team ===");
|
|
6193
|
+
log("");
|
|
6194
|
+
log("Your COO coordinates specialists. Each one stays focused on");
|
|
6195
|
+
log("their domain \u2014 no context switching, no competing priorities.");
|
|
6196
|
+
log("");
|
|
6197
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
6198
|
+
const ctoDefault = ctoTemplate?.name ?? "yoshi";
|
|
6199
|
+
log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
|
|
6200
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
6201
|
+
const cmoDefault = cmoTemplate?.name ?? "mari";
|
|
6202
|
+
log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
|
|
6203
|
+
log("");
|
|
6204
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
6205
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
6206
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
6207
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
6208
|
+
const ctoEmployee = {
|
|
6209
|
+
name: ctoName,
|
|
6210
|
+
role: "CTO",
|
|
6211
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
6212
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6213
|
+
};
|
|
6214
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
6215
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
6216
|
+
}
|
|
6217
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
6218
|
+
if (ctoIdentityContent) {
|
|
6219
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
6220
|
+
mkdirSync6(path11.dirname(ctoIdPath), { recursive: true });
|
|
6221
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
6222
|
+
writeFileSync7(ctoIdPath, replaced, "utf-8");
|
|
6223
|
+
}
|
|
6224
|
+
registerBinSymlinks2(ctoName);
|
|
6225
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
6226
|
+
log(`Created ${ctoName} (CTO)`);
|
|
6227
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
6228
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
6229
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
6230
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
6231
|
+
const cmoEmployee = {
|
|
6232
|
+
name: cmoName,
|
|
6233
|
+
role: "CMO",
|
|
6234
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
6235
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6236
|
+
};
|
|
6237
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
6238
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
6239
|
+
}
|
|
6240
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
6241
|
+
if (cmoIdentityContent) {
|
|
6242
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
6243
|
+
mkdirSync6(path11.dirname(cmoIdPath), { recursive: true });
|
|
6244
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
6245
|
+
writeFileSync7(cmoIdPath, replaced, "utf-8");
|
|
5635
6246
|
}
|
|
6247
|
+
registerBinSymlinks2(cmoName);
|
|
6248
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
6249
|
+
log(`Created ${cmoName} (CMO)`);
|
|
6250
|
+
log("");
|
|
5636
6251
|
state.completedSteps.push(8);
|
|
5637
6252
|
saveSetupState(state);
|
|
5638
6253
|
} else {
|
|
5639
6254
|
log("Step 8 already complete \u2014 skipping.");
|
|
5640
6255
|
}
|
|
6256
|
+
let pathJustConfigured = false;
|
|
5641
6257
|
if (!pairingRosterPulled) {
|
|
5642
6258
|
try {
|
|
5643
6259
|
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
@@ -5647,6 +6263,11 @@ async function runSetupWizard(opts = {}) {
|
|
|
5647
6263
|
if (wrapResult.created > 0) {
|
|
5648
6264
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
5649
6265
|
}
|
|
6266
|
+
if (wrapResult.pathConfigured) {
|
|
6267
|
+
const binDir = path11.join(os4.homedir(), ".exe-os", "bin");
|
|
6268
|
+
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
6269
|
+
pathJustConfigured = true;
|
|
6270
|
+
}
|
|
5650
6271
|
}
|
|
5651
6272
|
} catch {
|
|
5652
6273
|
}
|
|
@@ -5679,12 +6300,35 @@ async function runSetupWizard(opts = {}) {
|
|
|
5679
6300
|
if (createdEmployees.length > 0) {
|
|
5680
6301
|
log("Team: " + createdEmployees.map((e) => `${e.name} (${e.role})`).join(", "));
|
|
5681
6302
|
}
|
|
6303
|
+
let version = "";
|
|
6304
|
+
const pkgRoot2 = findPackageRoot2();
|
|
6305
|
+
if (pkgRoot2) {
|
|
6306
|
+
try {
|
|
6307
|
+
version = JSON.parse(readFileSync9(path11.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
6308
|
+
} catch {
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
const W = 27;
|
|
6312
|
+
const center = (s) => {
|
|
6313
|
+
const pad = W - s.length;
|
|
6314
|
+
return " ".repeat(Math.floor(pad / 2)) + s + " ".repeat(Math.ceil(pad / 2));
|
|
6315
|
+
};
|
|
6316
|
+
log("");
|
|
6317
|
+
log(` ${"\u2554" + "\u2550".repeat(W) + "\u2557"}`);
|
|
6318
|
+
log(` ${"\u2551" + center("e x e O S") + "\u2551"}`);
|
|
6319
|
+
if (version) log(` ${"\u2551" + center(`v${version}`) + "\u2551"}`);
|
|
6320
|
+
log(` ${"\u255A" + "\u2550".repeat(W) + "\u255D"}`);
|
|
5682
6321
|
log("");
|
|
5683
6322
|
log("=== Next Steps ===");
|
|
5684
6323
|
log("");
|
|
5685
|
-
log(
|
|
5686
|
-
log(`
|
|
5687
|
-
|
|
6324
|
+
log(` cd into a project folder: cd ~/my-project`);
|
|
6325
|
+
log(` Launch your COO: ${cooName}1`);
|
|
6326
|
+
if (pathJustConfigured) {
|
|
6327
|
+
const shell = process.env.SHELL ?? "/bin/zsh";
|
|
6328
|
+
const rcFile = shell.includes("bash") ? "~/.bashrc" : "~/.zshrc";
|
|
6329
|
+
log("");
|
|
6330
|
+
log(` (Run \`source ${rcFile}\` first, or open a new terminal)`);
|
|
6331
|
+
}
|
|
5688
6332
|
log("");
|
|
5689
6333
|
} finally {
|
|
5690
6334
|
rl.close();
|