@askexenow/exe-os 0.8.83 → 0.8.85
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 +97 -2
- package/dist/bin/cli.js +14350 -12518
- package/dist/bin/exe-agent.js +97 -88
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1257 -320
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +210 -34
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +97 -2
- package/dist/bin/exe-gateway.js +550 -171
- package/dist/bin/exe-healthcheck.js +1 -0
- package/dist/bin/exe-heartbeat.js +100 -5
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +902 -80
- package/dist/bin/exe-new-employee.js +38 -8
- package/dist/bin/exe-pending-messages.js +96 -2
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +98 -3
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +989 -226
- package/dist/bin/exe-session-cleanup.js +4806 -1665
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-status.js +97 -2
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +899 -207
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +38 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +904 -211
- package/dist/bin/setup.js +867 -268
- 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 +548 -166
- package/dist/hooks/bug-report-worker.js +208 -23
- package/dist/hooks/commit-complete.js +897 -205
- package/dist/hooks/error-recall.js +988 -226
- package/dist/hooks/ingest-worker.js +1638 -1194
- 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 +714 -104
- package/dist/hooks/pre-compact.js +897 -205
- package/dist/hooks/pre-tool-use.js +742 -123
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +995 -233
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +3941 -400
- package/dist/hooks/session-start.js +1001 -226
- package/dist/hooks/stop.js +725 -115
- package/dist/hooks/subagent-stop.js +714 -104
- package/dist/hooks/summary-worker.js +1964 -1330
- package/dist/index.js +1651 -1053
- package/dist/lib/cloud-sync.js +907 -86
- 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 +1955 -922
- package/dist/lib/hybrid-search.js +988 -226
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +8 -1
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +111 -22
- package/dist/lib/tmux-routing.js +120 -31
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5222 -475
- 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 +120 -22
- 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 +31 -1
- package/dist/mcp/tools/send-message.js +8 -1
- package/dist/mcp/tools/update-task.js +39 -10
- package/dist/runtime/index.js +911 -219
- package/dist/tui/App.js +997 -295
- package/package.json +6 -1
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,48 +3409,48 @@ 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"));
|
|
2874
3454
|
} catch {
|
|
2875
3455
|
}
|
|
2876
3456
|
}
|
|
@@ -2946,23 +3526,23 @@ async function cloudPullRoster(config) {
|
|
|
2946
3526
|
}
|
|
2947
3527
|
}
|
|
2948
3528
|
function mergeConfig(remoteConfig, configPath) {
|
|
2949
|
-
const cfgPath = configPath ??
|
|
3529
|
+
const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
|
|
2950
3530
|
let local = {};
|
|
2951
|
-
if (
|
|
3531
|
+
if (existsSync8(cfgPath)) {
|
|
2952
3532
|
try {
|
|
2953
|
-
local = JSON.parse(
|
|
3533
|
+
local = JSON.parse(readFileSync6(cfgPath, "utf-8"));
|
|
2954
3534
|
} catch {
|
|
2955
3535
|
}
|
|
2956
3536
|
}
|
|
2957
3537
|
const merged = { ...remoteConfig, ...local };
|
|
2958
|
-
const dir =
|
|
2959
|
-
if (!
|
|
2960
|
-
|
|
3538
|
+
const dir = path8.dirname(cfgPath);
|
|
3539
|
+
if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
|
|
3540
|
+
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
2961
3541
|
}
|
|
2962
3542
|
async function mergeRosterFromRemote(remote, paths) {
|
|
2963
3543
|
return withRosterLock(async () => {
|
|
2964
3544
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2965
|
-
const identityDir = paths?.identityDir ??
|
|
3545
|
+
const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
|
|
2966
3546
|
const localEmployees = await loadEmployees(rosterPath);
|
|
2967
3547
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
2968
3548
|
let added = 0;
|
|
@@ -2983,15 +3563,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2983
3563
|
) ?? lookupKey;
|
|
2984
3564
|
const remoteIdentity = remote.identities[matchedKey];
|
|
2985
3565
|
if (remoteIdentity) {
|
|
2986
|
-
if (!
|
|
2987
|
-
const idPath =
|
|
3566
|
+
if (!existsSync8(identityDir)) mkdirSync3(identityDir, { recursive: true });
|
|
3567
|
+
const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
|
|
2988
3568
|
let localIdentity = null;
|
|
2989
3569
|
try {
|
|
2990
|
-
localIdentity =
|
|
3570
|
+
localIdentity = existsSync8(idPath) ? readFileSync6(idPath, "utf-8") : null;
|
|
2991
3571
|
} catch {
|
|
2992
3572
|
}
|
|
2993
3573
|
if (localIdentity !== remoteIdentity) {
|
|
2994
|
-
|
|
3574
|
+
writeFileSync4(idPath, remoteIdentity, "utf-8");
|
|
2995
3575
|
identitiesUpdated++;
|
|
2996
3576
|
}
|
|
2997
3577
|
}
|
|
@@ -3444,13 +4024,14 @@ var init_cloud_sync = __esm({
|
|
|
3444
4024
|
init_compress();
|
|
3445
4025
|
init_license();
|
|
3446
4026
|
init_config();
|
|
4027
|
+
init_crdt_sync();
|
|
3447
4028
|
init_employees();
|
|
3448
4029
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
3449
4030
|
FETCH_TIMEOUT_MS = 3e4;
|
|
3450
4031
|
PUSH_BATCH_SIZE = 5e3;
|
|
3451
|
-
ROSTER_LOCK_PATH =
|
|
4032
|
+
ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
|
|
3452
4033
|
LOCK_STALE_MS = 3e4;
|
|
3453
|
-
ROSTER_DELETIONS_PATH =
|
|
4034
|
+
ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
|
|
3454
4035
|
}
|
|
3455
4036
|
});
|
|
3456
4037
|
|
|
@@ -4215,17 +4796,17 @@ __export(identity_exports, {
|
|
|
4215
4796
|
listIdentities: () => listIdentities,
|
|
4216
4797
|
updateIdentity: () => updateIdentity
|
|
4217
4798
|
});
|
|
4218
|
-
import { existsSync as
|
|
4799
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
4219
4800
|
import { readdirSync as readdirSync2 } from "fs";
|
|
4220
|
-
import
|
|
4801
|
+
import path9 from "path";
|
|
4221
4802
|
import { createHash as createHash2 } from "crypto";
|
|
4222
4803
|
function ensureDir() {
|
|
4223
|
-
if (!
|
|
4224
|
-
|
|
4804
|
+
if (!existsSync9(IDENTITY_DIR)) {
|
|
4805
|
+
mkdirSync4(IDENTITY_DIR, { recursive: true });
|
|
4225
4806
|
}
|
|
4226
4807
|
}
|
|
4227
4808
|
function identityPath(agentId) {
|
|
4228
|
-
return
|
|
4809
|
+
return path9.join(IDENTITY_DIR, `${agentId}.md`);
|
|
4229
4810
|
}
|
|
4230
4811
|
function parseFrontmatter(raw) {
|
|
4231
4812
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -4266,8 +4847,8 @@ function contentHash(content) {
|
|
|
4266
4847
|
}
|
|
4267
4848
|
function getIdentity(agentId) {
|
|
4268
4849
|
const filePath = identityPath(agentId);
|
|
4269
|
-
if (!
|
|
4270
|
-
const raw =
|
|
4850
|
+
if (!existsSync9(filePath)) return null;
|
|
4851
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
4271
4852
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
4272
4853
|
return {
|
|
4273
4854
|
agentId,
|
|
@@ -4281,7 +4862,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4281
4862
|
ensureDir();
|
|
4282
4863
|
const filePath = identityPath(agentId);
|
|
4283
4864
|
const hash = contentHash(content);
|
|
4284
|
-
|
|
4865
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
4285
4866
|
try {
|
|
4286
4867
|
const client = getClient();
|
|
4287
4868
|
await client.execute({
|
|
@@ -4337,7 +4918,7 @@ var init_identity = __esm({
|
|
|
4337
4918
|
"use strict";
|
|
4338
4919
|
init_config();
|
|
4339
4920
|
init_database();
|
|
4340
|
-
IDENTITY_DIR =
|
|
4921
|
+
IDENTITY_DIR = path9.join(EXE_AI_DIR, "identity");
|
|
4341
4922
|
}
|
|
4342
4923
|
});
|
|
4343
4924
|
|
|
@@ -4877,36 +5458,36 @@ __export(session_wrappers_exports, {
|
|
|
4877
5458
|
generateSessionWrappers: () => generateSessionWrappers
|
|
4878
5459
|
});
|
|
4879
5460
|
import {
|
|
4880
|
-
existsSync as
|
|
4881
|
-
readFileSync as
|
|
4882
|
-
writeFileSync as
|
|
4883
|
-
mkdirSync as
|
|
5461
|
+
existsSync as existsSync10,
|
|
5462
|
+
readFileSync as readFileSync8,
|
|
5463
|
+
writeFileSync as writeFileSync6,
|
|
5464
|
+
mkdirSync as mkdirSync5,
|
|
4884
5465
|
chmodSync,
|
|
4885
5466
|
readdirSync as readdirSync3,
|
|
4886
|
-
unlinkSync as
|
|
5467
|
+
unlinkSync as unlinkSync6
|
|
4887
5468
|
} from "fs";
|
|
4888
|
-
import
|
|
4889
|
-
import { homedir as
|
|
5469
|
+
import path10 from "path";
|
|
5470
|
+
import { homedir as homedir3 } from "os";
|
|
4890
5471
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
4891
|
-
const home = homeDir ??
|
|
4892
|
-
const binDir =
|
|
4893
|
-
const rosterPath =
|
|
4894
|
-
|
|
4895
|
-
const exeStartDst =
|
|
5472
|
+
const home = homeDir ?? homedir3();
|
|
5473
|
+
const binDir = path10.join(home, ".exe-os", "bin");
|
|
5474
|
+
const rosterPath = path10.join(home, ".exe-os", "exe-employees.json");
|
|
5475
|
+
mkdirSync5(binDir, { recursive: true });
|
|
5476
|
+
const exeStartDst = path10.join(binDir, "exe-start");
|
|
4896
5477
|
const candidates = [
|
|
4897
|
-
|
|
4898
|
-
|
|
5478
|
+
path10.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
5479
|
+
path10.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
4899
5480
|
];
|
|
4900
5481
|
for (const src of candidates) {
|
|
4901
|
-
if (
|
|
4902
|
-
|
|
5482
|
+
if (existsSync10(src)) {
|
|
5483
|
+
writeFileSync6(exeStartDst, readFileSync8(src));
|
|
4903
5484
|
chmodSync(exeStartDst, 493);
|
|
4904
5485
|
break;
|
|
4905
5486
|
}
|
|
4906
5487
|
}
|
|
4907
5488
|
let employees = [];
|
|
4908
5489
|
try {
|
|
4909
|
-
employees = JSON.parse(
|
|
5490
|
+
employees = JSON.parse(readFileSync8(rosterPath, "utf8"));
|
|
4910
5491
|
} catch {
|
|
4911
5492
|
return { created: 0, pathConfigured: false };
|
|
4912
5493
|
}
|
|
@@ -4916,11 +5497,11 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
4916
5497
|
try {
|
|
4917
5498
|
for (const f of readdirSync3(binDir)) {
|
|
4918
5499
|
if (f === "exe-start") continue;
|
|
4919
|
-
const fPath =
|
|
5500
|
+
const fPath = path10.join(binDir, f);
|
|
4920
5501
|
try {
|
|
4921
|
-
const content =
|
|
5502
|
+
const content = readFileSync8(fPath, "utf8");
|
|
4922
5503
|
if (content.includes("exe-start")) {
|
|
4923
|
-
|
|
5504
|
+
unlinkSync6(fPath);
|
|
4924
5505
|
}
|
|
4925
5506
|
} catch {
|
|
4926
5507
|
}
|
|
@@ -4933,8 +5514,8 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
4933
5514
|
`;
|
|
4934
5515
|
for (const emp of employees) {
|
|
4935
5516
|
for (let n = 1; n <= MAX_N; n++) {
|
|
4936
|
-
const wrapperPath =
|
|
4937
|
-
|
|
5517
|
+
const wrapperPath = path10.join(binDir, `${emp.name}${n}`);
|
|
5518
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
4938
5519
|
chmodSync(wrapperPath, 493);
|
|
4939
5520
|
created++;
|
|
4940
5521
|
}
|
|
@@ -4953,24 +5534,24 @@ export PATH="${binDir}:$PATH"
|
|
|
4953
5534
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
4954
5535
|
const profilePaths = [];
|
|
4955
5536
|
if (shell.includes("zsh")) {
|
|
4956
|
-
profilePaths.push(
|
|
5537
|
+
profilePaths.push(path10.join(home, ".zshrc"));
|
|
4957
5538
|
} else if (shell.includes("bash")) {
|
|
4958
|
-
profilePaths.push(
|
|
4959
|
-
profilePaths.push(
|
|
5539
|
+
profilePaths.push(path10.join(home, ".bashrc"));
|
|
5540
|
+
profilePaths.push(path10.join(home, ".bash_profile"));
|
|
4960
5541
|
} else {
|
|
4961
|
-
profilePaths.push(
|
|
5542
|
+
profilePaths.push(path10.join(home, ".profile"));
|
|
4962
5543
|
}
|
|
4963
5544
|
for (const profilePath of profilePaths) {
|
|
4964
5545
|
try {
|
|
4965
5546
|
let content = "";
|
|
4966
5547
|
try {
|
|
4967
|
-
content =
|
|
5548
|
+
content = readFileSync8(profilePath, "utf8");
|
|
4968
5549
|
} catch {
|
|
4969
5550
|
}
|
|
4970
5551
|
if (content.includes(".exe-os/bin")) {
|
|
4971
5552
|
return false;
|
|
4972
5553
|
}
|
|
4973
|
-
|
|
5554
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
4974
5555
|
return true;
|
|
4975
5556
|
} catch {
|
|
4976
5557
|
continue;
|
|
@@ -4990,9 +5571,9 @@ var init_session_wrappers = __esm({
|
|
|
4990
5571
|
init_config();
|
|
4991
5572
|
init_keychain();
|
|
4992
5573
|
import crypto3 from "crypto";
|
|
4993
|
-
import { existsSync as
|
|
5574
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync7 } from "fs";
|
|
4994
5575
|
import os4 from "os";
|
|
4995
|
-
import
|
|
5576
|
+
import path11 from "path";
|
|
4996
5577
|
import { createInterface } from "readline";
|
|
4997
5578
|
|
|
4998
5579
|
// src/lib/model-downloader.ts
|
|
@@ -5084,36 +5665,36 @@ async function fileHash(filePath) {
|
|
|
5084
5665
|
|
|
5085
5666
|
// src/lib/setup-wizard.ts
|
|
5086
5667
|
function findPackageRoot2() {
|
|
5087
|
-
let dir =
|
|
5088
|
-
const root =
|
|
5668
|
+
let dir = path11.dirname(new URL(import.meta.url).pathname);
|
|
5669
|
+
const root = path11.parse(dir).root;
|
|
5089
5670
|
while (dir !== root) {
|
|
5090
|
-
const pkgPath =
|
|
5091
|
-
if (
|
|
5671
|
+
const pkgPath = path11.join(dir, "package.json");
|
|
5672
|
+
if (existsSync11(pkgPath)) {
|
|
5092
5673
|
try {
|
|
5093
|
-
const pkg = JSON.parse(
|
|
5674
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
5094
5675
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
5095
5676
|
} catch {
|
|
5096
5677
|
}
|
|
5097
5678
|
}
|
|
5098
|
-
dir =
|
|
5679
|
+
dir = path11.dirname(dir);
|
|
5099
5680
|
}
|
|
5100
5681
|
return null;
|
|
5101
5682
|
}
|
|
5102
|
-
var SETUP_STATE_PATH =
|
|
5683
|
+
var SETUP_STATE_PATH = path11.join(os4.homedir(), ".exe-os", "setup-state.json");
|
|
5103
5684
|
function loadSetupState() {
|
|
5104
5685
|
try {
|
|
5105
|
-
return JSON.parse(
|
|
5686
|
+
return JSON.parse(readFileSync9(SETUP_STATE_PATH, "utf8"));
|
|
5106
5687
|
} catch {
|
|
5107
5688
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5108
5689
|
}
|
|
5109
5690
|
}
|
|
5110
5691
|
function saveSetupState(state) {
|
|
5111
|
-
|
|
5112
|
-
|
|
5692
|
+
mkdirSync6(path11.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
5693
|
+
writeFileSync7(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5113
5694
|
}
|
|
5114
5695
|
function clearSetupState() {
|
|
5115
5696
|
try {
|
|
5116
|
-
|
|
5697
|
+
unlinkSync7(SETUP_STATE_PATH);
|
|
5117
5698
|
} catch {
|
|
5118
5699
|
}
|
|
5119
5700
|
}
|
|
@@ -5207,7 +5788,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5207
5788
|
if (state.completedSteps.length > 0) {
|
|
5208
5789
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5209
5790
|
}
|
|
5210
|
-
if (
|
|
5791
|
+
if (existsSync11(LEGACY_LANCE_PATH)) {
|
|
5211
5792
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5212
5793
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5213
5794
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -5355,10 +5936,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
5355
5936
|
await saveConfig(config);
|
|
5356
5937
|
log("");
|
|
5357
5938
|
try {
|
|
5358
|
-
const claudeJsonPath =
|
|
5939
|
+
const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
|
|
5359
5940
|
let claudeJson = {};
|
|
5360
5941
|
try {
|
|
5361
|
-
claudeJson = JSON.parse(
|
|
5942
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
5362
5943
|
} catch {
|
|
5363
5944
|
}
|
|
5364
5945
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5367,7 +5948,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5367
5948
|
if (!projects[dir]) projects[dir] = {};
|
|
5368
5949
|
projects[dir].hasTrustDialogAccepted = true;
|
|
5369
5950
|
}
|
|
5370
|
-
|
|
5951
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5371
5952
|
} catch {
|
|
5372
5953
|
}
|
|
5373
5954
|
state.completedSteps.push(5);
|
|
@@ -5406,7 +5987,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5406
5987
|
let missingIdentities = [];
|
|
5407
5988
|
for (const emp of roster) {
|
|
5408
5989
|
const idPath = identityPath2(emp.name);
|
|
5409
|
-
if (!
|
|
5990
|
+
if (!existsSync11(idPath)) {
|
|
5410
5991
|
missingIdentities.push(emp.name);
|
|
5411
5992
|
}
|
|
5412
5993
|
}
|
|
@@ -5438,7 +6019,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5438
6019
|
}
|
|
5439
6020
|
missingIdentities = [];
|
|
5440
6021
|
for (const emp of roster) {
|
|
5441
|
-
if (!
|
|
6022
|
+
if (!existsSync11(identityPath2(emp.name))) {
|
|
5442
6023
|
missingIdentities.push(emp.name);
|
|
5443
6024
|
}
|
|
5444
6025
|
}
|
|
@@ -5469,11 +6050,12 @@ async function runSetupWizard(opts = {}) {
|
|
|
5469
6050
|
saveSetupState(state);
|
|
5470
6051
|
log("");
|
|
5471
6052
|
} else if (!state.completedSteps.includes(6)) {
|
|
5472
|
-
log("=== Your
|
|
6053
|
+
log("=== Your First Hire ===");
|
|
5473
6054
|
log("");
|
|
5474
|
-
log("
|
|
5475
|
-
log("
|
|
5476
|
-
log("
|
|
6055
|
+
log("Until now, you talked to one agent at a time.");
|
|
6056
|
+
log("Your COO changes that. You talk to them \u2014 they coordinate");
|
|
6057
|
+
log("your entire team. Engineers, marketers, specialists, all working");
|
|
6058
|
+
log("in parallel while you focus on what matters.");
|
|
5477
6059
|
log("");
|
|
5478
6060
|
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5479
6061
|
cooName = (cooNameInput || "exe").toLowerCase();
|
|
@@ -5494,9 +6076,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
5494
6076
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5495
6077
|
if (cooIdentityContent) {
|
|
5496
6078
|
const cooIdPath = identityPath2(cooName);
|
|
5497
|
-
|
|
6079
|
+
mkdirSync6(path11.dirname(cooIdPath), { recursive: true });
|
|
5498
6080
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5499
|
-
|
|
6081
|
+
writeFileSync7(cooIdPath, replaced, "utf-8");
|
|
5500
6082
|
}
|
|
5501
6083
|
registerBinSymlinks2(cooName);
|
|
5502
6084
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -5510,134 +6092,123 @@ async function runSetupWizard(opts = {}) {
|
|
|
5510
6092
|
}
|
|
5511
6093
|
log("");
|
|
5512
6094
|
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
6095
|
let license;
|
|
5545
6096
|
try {
|
|
5546
6097
|
license = await checkLicense2();
|
|
5547
6098
|
} catch {
|
|
5548
6099
|
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5549
6100
|
}
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
if (licenseInput.startsWith("exe_sk_")) {
|
|
6101
|
+
let isLicensed = license.valid && license.plan !== "free";
|
|
6102
|
+
if (!isLicensed) {
|
|
6103
|
+
let licenseInput = await ask(rl, "Do you have an Exe OS license key? (press Enter to skip for free plan): ");
|
|
6104
|
+
if (licenseInput && !licenseInput.startsWith("exe_sk_")) {
|
|
6105
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
6106
|
+
licenseInput = await ask(rl, "Try again (or press Enter to skip): ");
|
|
6107
|
+
}
|
|
6108
|
+
if (licenseInput && licenseInput.startsWith("exe_sk_")) {
|
|
5559
6109
|
saveLicense2(licenseInput);
|
|
5560
6110
|
mirrorLicenseKey2(licenseInput);
|
|
5561
6111
|
try {
|
|
5562
|
-
|
|
6112
|
+
const result = await validateLicense2(licenseInput);
|
|
6113
|
+
if (result.valid && result.plan !== "free") {
|
|
6114
|
+
isLicensed = true;
|
|
6115
|
+
const planName = result.plan === "pro" ? "Solopreneur" : result.plan.charAt(0).toUpperCase() + result.plan.slice(1);
|
|
6116
|
+
log(`License activated. Plan: ${planName}`);
|
|
6117
|
+
} else if (!result.valid) {
|
|
6118
|
+
log("That license key is invalid or expired.");
|
|
6119
|
+
log("Get a new key at askexe.com.");
|
|
6120
|
+
} else {
|
|
6121
|
+
log("Key saved for cloud sync, but specialists require a paid plan.");
|
|
6122
|
+
log("Upgrade at askexe.com when you're ready.");
|
|
6123
|
+
}
|
|
5563
6124
|
} catch {
|
|
5564
|
-
|
|
5565
|
-
log("
|
|
6125
|
+
isLicensed = true;
|
|
6126
|
+
log("Couldn't reach the license server \u2014 key saved, specialists enabled.");
|
|
5566
6127
|
}
|
|
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
6128
|
}
|
|
5573
6129
|
}
|
|
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)`);
|
|
6130
|
+
if (!isLicensed) {
|
|
5577
6131
|
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
|
-
}
|
|
6132
|
+
log("You're all set. Your COO handles everything from here.");
|
|
6133
|
+
log("When you're ready for specialists, add a license key at askexe.com.");
|
|
5634
6134
|
log("");
|
|
6135
|
+
log(`Type your COO's name to start: /${cooName}`);
|
|
6136
|
+
state.completedSteps.push(7, 8);
|
|
6137
|
+
saveSetupState(state);
|
|
6138
|
+
} else {
|
|
6139
|
+
state.completedSteps.push(7);
|
|
6140
|
+
saveSetupState(state);
|
|
6141
|
+
}
|
|
6142
|
+
} else {
|
|
6143
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
6144
|
+
}
|
|
6145
|
+
if (!state.completedSteps.includes(8)) {
|
|
6146
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
6147
|
+
log("=== Hire Your Team ===");
|
|
6148
|
+
log("");
|
|
6149
|
+
log("Your COO coordinates specialists. Each one stays focused on");
|
|
6150
|
+
log("their domain \u2014 no context switching, no competing priorities.");
|
|
6151
|
+
log("");
|
|
6152
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
6153
|
+
const ctoDefault = ctoTemplate?.name ?? "yoshi";
|
|
6154
|
+
log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
|
|
6155
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
6156
|
+
const cmoDefault = cmoTemplate?.name ?? "mari";
|
|
6157
|
+
log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
|
|
6158
|
+
log("");
|
|
6159
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
6160
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
6161
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
6162
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
6163
|
+
const ctoEmployee = {
|
|
6164
|
+
name: ctoName,
|
|
6165
|
+
role: "CTO",
|
|
6166
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
6167
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6168
|
+
};
|
|
6169
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
6170
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5635
6171
|
}
|
|
6172
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
6173
|
+
if (ctoIdentityContent) {
|
|
6174
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
6175
|
+
mkdirSync6(path11.dirname(ctoIdPath), { recursive: true });
|
|
6176
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
6177
|
+
writeFileSync7(ctoIdPath, replaced, "utf-8");
|
|
6178
|
+
}
|
|
6179
|
+
registerBinSymlinks2(ctoName);
|
|
6180
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
6181
|
+
log(`Created ${ctoName} (CTO)`);
|
|
6182
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
6183
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
6184
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
6185
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
6186
|
+
const cmoEmployee = {
|
|
6187
|
+
name: cmoName,
|
|
6188
|
+
role: "CMO",
|
|
6189
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
6190
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6191
|
+
};
|
|
6192
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
6193
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
6194
|
+
}
|
|
6195
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
6196
|
+
if (cmoIdentityContent) {
|
|
6197
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
6198
|
+
mkdirSync6(path11.dirname(cmoIdPath), { recursive: true });
|
|
6199
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
6200
|
+
writeFileSync7(cmoIdPath, replaced, "utf-8");
|
|
6201
|
+
}
|
|
6202
|
+
registerBinSymlinks2(cmoName);
|
|
6203
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
6204
|
+
log(`Created ${cmoName} (CMO)`);
|
|
6205
|
+
log("");
|
|
5636
6206
|
state.completedSteps.push(8);
|
|
5637
6207
|
saveSetupState(state);
|
|
5638
6208
|
} else {
|
|
5639
6209
|
log("Step 8 already complete \u2014 skipping.");
|
|
5640
6210
|
}
|
|
6211
|
+
let pathJustConfigured = false;
|
|
5641
6212
|
if (!pairingRosterPulled) {
|
|
5642
6213
|
try {
|
|
5643
6214
|
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
@@ -5647,6 +6218,11 @@ async function runSetupWizard(opts = {}) {
|
|
|
5647
6218
|
if (wrapResult.created > 0) {
|
|
5648
6219
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
5649
6220
|
}
|
|
6221
|
+
if (wrapResult.pathConfigured) {
|
|
6222
|
+
const binDir = path11.join(os4.homedir(), ".exe-os", "bin");
|
|
6223
|
+
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
6224
|
+
pathJustConfigured = true;
|
|
6225
|
+
}
|
|
5650
6226
|
}
|
|
5651
6227
|
} catch {
|
|
5652
6228
|
}
|
|
@@ -5679,12 +6255,35 @@ async function runSetupWizard(opts = {}) {
|
|
|
5679
6255
|
if (createdEmployees.length > 0) {
|
|
5680
6256
|
log("Team: " + createdEmployees.map((e) => `${e.name} (${e.role})`).join(", "));
|
|
5681
6257
|
}
|
|
6258
|
+
let version = "";
|
|
6259
|
+
const pkgRoot2 = findPackageRoot2();
|
|
6260
|
+
if (pkgRoot2) {
|
|
6261
|
+
try {
|
|
6262
|
+
version = JSON.parse(readFileSync9(path11.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
6263
|
+
} catch {
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
const W = 27;
|
|
6267
|
+
const center = (s) => {
|
|
6268
|
+
const pad = W - s.length;
|
|
6269
|
+
return " ".repeat(Math.floor(pad / 2)) + s + " ".repeat(Math.ceil(pad / 2));
|
|
6270
|
+
};
|
|
6271
|
+
log("");
|
|
6272
|
+
log(` ${"\u2554" + "\u2550".repeat(W) + "\u2557"}`);
|
|
6273
|
+
log(` ${"\u2551" + center("e x e O S") + "\u2551"}`);
|
|
6274
|
+
if (version) log(` ${"\u2551" + center(`v${version}`) + "\u2551"}`);
|
|
6275
|
+
log(` ${"\u255A" + "\u2550".repeat(W) + "\u255D"}`);
|
|
5682
6276
|
log("");
|
|
5683
6277
|
log("=== Next Steps ===");
|
|
5684
6278
|
log("");
|
|
5685
|
-
log(
|
|
5686
|
-
log(`
|
|
5687
|
-
|
|
6279
|
+
log(` cd into a project folder: cd ~/my-project`);
|
|
6280
|
+
log(` Launch your COO: ${cooName}1`);
|
|
6281
|
+
if (pathJustConfigured) {
|
|
6282
|
+
const shell = process.env.SHELL ?? "/bin/zsh";
|
|
6283
|
+
const rcFile = shell.includes("bash") ? "~/.bashrc" : "~/.zshrc";
|
|
6284
|
+
log("");
|
|
6285
|
+
log(` (Run \`source ${rcFile}\` first, or open a new terminal)`);
|
|
6286
|
+
}
|
|
5688
6287
|
log("");
|
|
5689
6288
|
} finally {
|
|
5690
6289
|
rl.close();
|