@askexenow/exe-os 0.9.7 → 0.9.9
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 +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
|
@@ -15,9 +15,34 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/lib/secure-files.ts
|
|
19
|
+
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
20
|
+
import { chmod, mkdir } from "fs/promises";
|
|
21
|
+
function ensurePrivateDirSync(dirPath) {
|
|
22
|
+
mkdirSync2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
23
|
+
try {
|
|
24
|
+
chmodSync2(dirPath, PRIVATE_DIR_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function enforcePrivateFileSync(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync2(filePath)) chmodSync2(filePath, PRIVATE_FILE_MODE);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
35
|
+
var init_secure_files = __esm({
|
|
36
|
+
"src/lib/secure-files.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
PRIVATE_DIR_MODE = 448;
|
|
39
|
+
PRIVATE_FILE_MODE = 384;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
18
43
|
// src/lib/config.ts
|
|
19
|
-
import { readFile, writeFile
|
|
20
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
44
|
+
import { readFile, writeFile } from "fs/promises";
|
|
45
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, renameSync } from "fs";
|
|
21
46
|
import path2 from "path";
|
|
22
47
|
import os from "os";
|
|
23
48
|
function resolveDataDir() {
|
|
@@ -25,7 +50,7 @@ function resolveDataDir() {
|
|
|
25
50
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
26
51
|
const newDir = path2.join(os.homedir(), ".exe-os");
|
|
27
52
|
const legacyDir = path2.join(os.homedir(), ".exe-mem");
|
|
28
|
-
if (!
|
|
53
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
29
54
|
try {
|
|
30
55
|
renameSync(legacyDir, newDir);
|
|
31
56
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -40,6 +65,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
40
65
|
var init_config = __esm({
|
|
41
66
|
"src/lib/config.ts"() {
|
|
42
67
|
"use strict";
|
|
68
|
+
init_secure_files();
|
|
43
69
|
EXE_AI_DIR = resolveDataDir();
|
|
44
70
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
45
71
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -106,12 +132,135 @@ var init_config = __esm({
|
|
|
106
132
|
}
|
|
107
133
|
});
|
|
108
134
|
|
|
135
|
+
// src/lib/runtime-table.ts
|
|
136
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
137
|
+
var init_runtime_table = __esm({
|
|
138
|
+
"src/lib/runtime-table.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
RUNTIME_TABLE = {
|
|
141
|
+
codex: {
|
|
142
|
+
binary: "codex",
|
|
143
|
+
launchMode: "interactive",
|
|
144
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
145
|
+
inlineFlag: "--no-alt-screen",
|
|
146
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
147
|
+
defaultModel: "gpt-5.4"
|
|
148
|
+
},
|
|
149
|
+
opencode: {
|
|
150
|
+
binary: "opencode",
|
|
151
|
+
launchMode: "exec",
|
|
152
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
153
|
+
inlineFlag: "",
|
|
154
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
155
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
DEFAULT_RUNTIME = "claude";
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// src/lib/agent-config.ts
|
|
163
|
+
var agent_config_exports = {};
|
|
164
|
+
__export(agent_config_exports, {
|
|
165
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
166
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
167
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
168
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
169
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
170
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
171
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
172
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
173
|
+
setAgentRuntime: () => setAgentRuntime
|
|
174
|
+
});
|
|
175
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4 } from "fs";
|
|
176
|
+
import path3 from "path";
|
|
177
|
+
function loadAgentConfig() {
|
|
178
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
179
|
+
try {
|
|
180
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
181
|
+
} catch {
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function saveAgentConfig(config) {
|
|
186
|
+
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
187
|
+
ensurePrivateDirSync(dir);
|
|
188
|
+
writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
189
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
190
|
+
}
|
|
191
|
+
function getAgentRuntime(agentId) {
|
|
192
|
+
const config = loadAgentConfig();
|
|
193
|
+
const entry = config[agentId];
|
|
194
|
+
if (entry) return entry;
|
|
195
|
+
const orgDefault = config["default"];
|
|
196
|
+
if (orgDefault) return orgDefault;
|
|
197
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
198
|
+
}
|
|
199
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
200
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
201
|
+
if (!knownModels) {
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (!knownModels.includes(model)) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const config = loadAgentConfig();
|
|
214
|
+
config[agentId] = { runtime, model };
|
|
215
|
+
saveAgentConfig(config);
|
|
216
|
+
return { ok: true };
|
|
217
|
+
}
|
|
218
|
+
function clearAgentRuntime(agentId) {
|
|
219
|
+
const config = loadAgentConfig();
|
|
220
|
+
delete config[agentId];
|
|
221
|
+
saveAgentConfig(config);
|
|
222
|
+
}
|
|
223
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
224
|
+
var init_agent_config = __esm({
|
|
225
|
+
"src/lib/agent-config.ts"() {
|
|
226
|
+
"use strict";
|
|
227
|
+
init_config();
|
|
228
|
+
init_runtime_table();
|
|
229
|
+
init_secure_files();
|
|
230
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
231
|
+
KNOWN_RUNTIMES = {
|
|
232
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
233
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
234
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
235
|
+
};
|
|
236
|
+
RUNTIME_LABELS = {
|
|
237
|
+
claude: "Claude Code (Anthropic)",
|
|
238
|
+
codex: "Codex (OpenAI)",
|
|
239
|
+
opencode: "OpenCode (open source)"
|
|
240
|
+
};
|
|
241
|
+
DEFAULT_MODELS = {
|
|
242
|
+
claude: "claude-opus-4",
|
|
243
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
244
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
109
249
|
// src/lib/employees.ts
|
|
110
250
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
111
|
-
import { existsSync as
|
|
251
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
112
252
|
import { execSync } from "child_process";
|
|
113
|
-
import
|
|
253
|
+
import path4 from "path";
|
|
114
254
|
import os2 from "os";
|
|
255
|
+
function normalizeRole(role) {
|
|
256
|
+
return (role ?? "").trim().toLowerCase();
|
|
257
|
+
}
|
|
258
|
+
function isCoordinatorRole(role) {
|
|
259
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
260
|
+
}
|
|
261
|
+
function getCoordinatorEmployee(employees) {
|
|
262
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
263
|
+
}
|
|
115
264
|
function validateEmployeeName(name) {
|
|
116
265
|
if (!name) {
|
|
117
266
|
return { valid: false, error: "Name is required" };
|
|
@@ -128,7 +277,7 @@ function validateEmployeeName(name) {
|
|
|
128
277
|
return { valid: true };
|
|
129
278
|
}
|
|
130
279
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
131
|
-
if (!
|
|
280
|
+
if (!existsSync5(employeesPath)) {
|
|
132
281
|
return [];
|
|
133
282
|
}
|
|
134
283
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -139,9 +288,17 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
139
288
|
}
|
|
140
289
|
}
|
|
141
290
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
142
|
-
await mkdir2(
|
|
291
|
+
await mkdir2(path4.dirname(employeesPath), { recursive: true });
|
|
143
292
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
144
293
|
}
|
|
294
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
295
|
+
if (!existsSync5(employeesPath)) return [];
|
|
296
|
+
try {
|
|
297
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
298
|
+
} catch {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
145
302
|
function addEmployee(employees, employee) {
|
|
146
303
|
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
147
304
|
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
@@ -149,6 +306,52 @@ function addEmployee(employees, employee) {
|
|
|
149
306
|
}
|
|
150
307
|
return [...employees, normalized];
|
|
151
308
|
}
|
|
309
|
+
function appendToCoordinatorTeam(employee) {
|
|
310
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
311
|
+
if (!coordinator) return;
|
|
312
|
+
const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
313
|
+
if (!existsSync5(idPath)) return;
|
|
314
|
+
const content = readFileSync4(idPath, "utf-8");
|
|
315
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
316
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
317
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
318
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
319
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
320
|
+
const entry = `
|
|
321
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
322
|
+
`;
|
|
323
|
+
let updated;
|
|
324
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
325
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
326
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
327
|
+
} else {
|
|
328
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
329
|
+
}
|
|
330
|
+
writeFileSync3(idPath, updated, "utf-8");
|
|
331
|
+
}
|
|
332
|
+
function capitalize(s) {
|
|
333
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
334
|
+
}
|
|
335
|
+
async function hireEmployee(employee) {
|
|
336
|
+
const employees = await loadEmployees();
|
|
337
|
+
const updated = addEmployee(employees, employee);
|
|
338
|
+
await saveEmployees(updated);
|
|
339
|
+
try {
|
|
340
|
+
appendToCoordinatorTeam(employee);
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
345
|
+
const config = loadAgentConfig2();
|
|
346
|
+
const name = employee.name.toLowerCase();
|
|
347
|
+
if (!config[name] && config["default"]) {
|
|
348
|
+
config[name] = { ...config["default"] };
|
|
349
|
+
saveAgentConfig2(config);
|
|
350
|
+
}
|
|
351
|
+
} catch {
|
|
352
|
+
}
|
|
353
|
+
return updated;
|
|
354
|
+
}
|
|
152
355
|
function findExeBin() {
|
|
153
356
|
try {
|
|
154
357
|
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
@@ -165,7 +368,7 @@ function registerBinSymlinks(name) {
|
|
|
165
368
|
errors.push("Could not find 'exe-os' in PATH");
|
|
166
369
|
return { created, skipped, errors };
|
|
167
370
|
}
|
|
168
|
-
const binDir =
|
|
371
|
+
const binDir = path4.dirname(exeBinPath);
|
|
169
372
|
let target;
|
|
170
373
|
try {
|
|
171
374
|
target = readlinkSync(exeBinPath);
|
|
@@ -175,8 +378,8 @@ function registerBinSymlinks(name) {
|
|
|
175
378
|
}
|
|
176
379
|
for (const suffix of ["", "-opencode"]) {
|
|
177
380
|
const linkName = `${name}${suffix}`;
|
|
178
|
-
const linkPath =
|
|
179
|
-
if (
|
|
381
|
+
const linkPath = path4.join(binDir, linkName);
|
|
382
|
+
if (existsSync5(linkPath)) {
|
|
180
383
|
skipped.push(linkName);
|
|
181
384
|
continue;
|
|
182
385
|
}
|
|
@@ -189,12 +392,15 @@ function registerBinSymlinks(name) {
|
|
|
189
392
|
}
|
|
190
393
|
return { created, skipped, errors };
|
|
191
394
|
}
|
|
192
|
-
var EMPLOYEES_PATH;
|
|
395
|
+
var EMPLOYEES_PATH, COORDINATOR_ROLE, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
193
396
|
var init_employees = __esm({
|
|
194
397
|
"src/lib/employees.ts"() {
|
|
195
398
|
"use strict";
|
|
196
399
|
init_config();
|
|
197
|
-
EMPLOYEES_PATH =
|
|
400
|
+
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
401
|
+
COORDINATOR_ROLE = "COO";
|
|
402
|
+
IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
|
|
403
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
198
404
|
}
|
|
199
405
|
});
|
|
200
406
|
|
|
@@ -205,12 +411,36 @@ var init_db_retry = __esm({
|
|
|
205
411
|
}
|
|
206
412
|
});
|
|
207
413
|
|
|
414
|
+
// src/lib/database-adapter.ts
|
|
415
|
+
import os3 from "os";
|
|
416
|
+
import path5 from "path";
|
|
417
|
+
import { createRequire } from "module";
|
|
418
|
+
import { pathToFileURL } from "url";
|
|
419
|
+
var BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES;
|
|
420
|
+
var init_database_adapter = __esm({
|
|
421
|
+
"src/lib/database-adapter.ts"() {
|
|
422
|
+
"use strict";
|
|
423
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
424
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
425
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
426
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
427
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
428
|
+
};
|
|
429
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
430
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
208
435
|
// src/lib/database.ts
|
|
209
436
|
import { createClient } from "@libsql/client";
|
|
210
437
|
function getClient() {
|
|
211
|
-
if (!
|
|
438
|
+
if (!_adapterClient) {
|
|
212
439
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
213
440
|
}
|
|
441
|
+
if (process.env.DATABASE_URL) {
|
|
442
|
+
return _adapterClient;
|
|
443
|
+
}
|
|
214
444
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
215
445
|
return _resilientClient;
|
|
216
446
|
}
|
|
@@ -219,14 +449,16 @@ function getClient() {
|
|
|
219
449
|
}
|
|
220
450
|
return _resilientClient;
|
|
221
451
|
}
|
|
222
|
-
var _resilientClient, _daemonClient;
|
|
452
|
+
var _resilientClient, _daemonClient, _adapterClient;
|
|
223
453
|
var init_database = __esm({
|
|
224
454
|
"src/lib/database.ts"() {
|
|
225
455
|
"use strict";
|
|
226
456
|
init_db_retry();
|
|
227
457
|
init_employees();
|
|
458
|
+
init_database_adapter();
|
|
228
459
|
_resilientClient = null;
|
|
229
460
|
_daemonClient = null;
|
|
461
|
+
_adapterClient = null;
|
|
230
462
|
}
|
|
231
463
|
});
|
|
232
464
|
|
|
@@ -780,17 +1012,17 @@ __export(identity_exports, {
|
|
|
780
1012
|
listIdentities: () => listIdentities,
|
|
781
1013
|
updateIdentity: () => updateIdentity
|
|
782
1014
|
});
|
|
783
|
-
import { existsSync as
|
|
1015
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
784
1016
|
import { readdirSync as readdirSync2 } from "fs";
|
|
785
|
-
import
|
|
1017
|
+
import path8 from "path";
|
|
786
1018
|
import { createHash } from "crypto";
|
|
787
1019
|
function ensureDir() {
|
|
788
|
-
if (!
|
|
789
|
-
|
|
1020
|
+
if (!existsSync8(IDENTITY_DIR2)) {
|
|
1021
|
+
mkdirSync4(IDENTITY_DIR2, { recursive: true });
|
|
790
1022
|
}
|
|
791
1023
|
}
|
|
792
1024
|
function identityPath(agentId) {
|
|
793
|
-
return
|
|
1025
|
+
return path8.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
794
1026
|
}
|
|
795
1027
|
function parseFrontmatter(raw) {
|
|
796
1028
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -831,8 +1063,8 @@ function contentHash(content) {
|
|
|
831
1063
|
}
|
|
832
1064
|
function getIdentity(agentId) {
|
|
833
1065
|
const filePath = identityPath(agentId);
|
|
834
|
-
if (!
|
|
835
|
-
const raw =
|
|
1066
|
+
if (!existsSync8(filePath)) return null;
|
|
1067
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
836
1068
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
837
1069
|
return {
|
|
838
1070
|
agentId,
|
|
@@ -846,7 +1078,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
846
1078
|
ensureDir();
|
|
847
1079
|
const filePath = identityPath(agentId);
|
|
848
1080
|
const hash = contentHash(content);
|
|
849
|
-
|
|
1081
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
850
1082
|
try {
|
|
851
1083
|
const client = getClient();
|
|
852
1084
|
await client.execute({
|
|
@@ -863,7 +1095,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
863
1095
|
}
|
|
864
1096
|
function listIdentities() {
|
|
865
1097
|
ensureDir();
|
|
866
|
-
const files = readdirSync2(
|
|
1098
|
+
const files = readdirSync2(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
867
1099
|
const results = [];
|
|
868
1100
|
for (const file of files) {
|
|
869
1101
|
const agentId = file.replace(".md", "");
|
|
@@ -896,40 +1128,40 @@ ${teamLines.join("\n")}`);
|
|
|
896
1128
|
}
|
|
897
1129
|
return parts.join("\n\n");
|
|
898
1130
|
}
|
|
899
|
-
var
|
|
1131
|
+
var IDENTITY_DIR2;
|
|
900
1132
|
var init_identity = __esm({
|
|
901
1133
|
"src/lib/identity.ts"() {
|
|
902
1134
|
"use strict";
|
|
903
1135
|
init_config();
|
|
904
1136
|
init_database();
|
|
905
|
-
|
|
1137
|
+
IDENTITY_DIR2 = path8.join(EXE_AI_DIR, "identity");
|
|
906
1138
|
}
|
|
907
1139
|
});
|
|
908
1140
|
|
|
909
1141
|
// src/lib/agent-symlinks.ts
|
|
910
|
-
import
|
|
911
|
-
import
|
|
1142
|
+
import os5 from "os";
|
|
1143
|
+
import path9 from "path";
|
|
912
1144
|
import {
|
|
913
|
-
existsSync as
|
|
1145
|
+
existsSync as existsSync9,
|
|
914
1146
|
lstatSync,
|
|
915
|
-
mkdirSync as
|
|
1147
|
+
mkdirSync as mkdirSync5,
|
|
916
1148
|
readlinkSync as readlinkSync2,
|
|
917
1149
|
symlinkSync as symlinkSync2
|
|
918
1150
|
} from "fs";
|
|
919
1151
|
function claudeAgentsDir(homeDir) {
|
|
920
|
-
return
|
|
1152
|
+
return path9.join(homeDir, ".claude", "agents");
|
|
921
1153
|
}
|
|
922
1154
|
function identitySourcePath(homeDir, agentId) {
|
|
923
|
-
return
|
|
1155
|
+
return path9.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
|
|
924
1156
|
}
|
|
925
1157
|
function claudeAgentLinkPath(homeDir, agentId) {
|
|
926
|
-
return
|
|
1158
|
+
return path9.join(claudeAgentsDir(homeDir), `${agentId}.md`);
|
|
927
1159
|
}
|
|
928
|
-
function ensureAgentSymlink(agentId, homeDir =
|
|
1160
|
+
function ensureAgentSymlink(agentId, homeDir = os5.homedir()) {
|
|
929
1161
|
const target = identitySourcePath(homeDir, agentId);
|
|
930
1162
|
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
931
|
-
|
|
932
|
-
if (
|
|
1163
|
+
mkdirSync5(claudeAgentsDir(homeDir), { recursive: true });
|
|
1164
|
+
if (existsSync9(link)) {
|
|
933
1165
|
let stat;
|
|
934
1166
|
try {
|
|
935
1167
|
stat = lstatSync(link);
|
|
@@ -963,7 +1195,7 @@ function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
|
963
1195
|
}
|
|
964
1196
|
return { agentId, action: "created", target, link };
|
|
965
1197
|
}
|
|
966
|
-
async function ensureAllAgentSymlinks(homeDir =
|
|
1198
|
+
async function ensureAllAgentSymlinks(homeDir = os5.homedir()) {
|
|
967
1199
|
const employees = await loadEmployees();
|
|
968
1200
|
return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
|
|
969
1201
|
}
|
|
@@ -998,14 +1230,14 @@ var init_mcp_prefix = __esm({
|
|
|
998
1230
|
});
|
|
999
1231
|
|
|
1000
1232
|
// src/lib/preferences.ts
|
|
1001
|
-
import { existsSync as
|
|
1002
|
-
import
|
|
1003
|
-
import
|
|
1004
|
-
function loadPreferences(homeDir =
|
|
1005
|
-
const configPath =
|
|
1006
|
-
if (!
|
|
1233
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
1234
|
+
import path10 from "path";
|
|
1235
|
+
import os6 from "os";
|
|
1236
|
+
function loadPreferences(homeDir = os6.homedir()) {
|
|
1237
|
+
const configPath = path10.join(homeDir, ".exe-os", "config.json");
|
|
1238
|
+
if (!existsSync10(configPath)) return {};
|
|
1007
1239
|
try {
|
|
1008
|
-
const config = JSON.parse(
|
|
1240
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
1009
1241
|
return config.preferences ?? {};
|
|
1010
1242
|
} catch {
|
|
1011
1243
|
return {};
|
|
@@ -1014,6 +1246,7 @@ function loadPreferences(homeDir = os4.homedir()) {
|
|
|
1014
1246
|
var init_preferences = __esm({
|
|
1015
1247
|
"src/lib/preferences.ts"() {
|
|
1016
1248
|
"use strict";
|
|
1249
|
+
init_secure_files();
|
|
1017
1250
|
}
|
|
1018
1251
|
});
|
|
1019
1252
|
|
|
@@ -1031,52 +1264,52 @@ __export(installer_exports, {
|
|
|
1031
1264
|
setupTmux: () => setupTmux
|
|
1032
1265
|
});
|
|
1033
1266
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
1034
|
-
import { existsSync as
|
|
1035
|
-
import
|
|
1036
|
-
import
|
|
1267
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync7, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
1268
|
+
import path11 from "path";
|
|
1269
|
+
import os7 from "os";
|
|
1037
1270
|
import { execSync as execSync2 } from "child_process";
|
|
1038
1271
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1039
1272
|
function resolvePackageRoot() {
|
|
1040
1273
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
1041
|
-
let dir =
|
|
1042
|
-
const root =
|
|
1274
|
+
let dir = path11.dirname(thisFile);
|
|
1275
|
+
const root = path11.parse(dir).root;
|
|
1043
1276
|
while (dir !== root) {
|
|
1044
|
-
const pkgPath =
|
|
1045
|
-
if (
|
|
1277
|
+
const pkgPath = path11.join(dir, "package.json");
|
|
1278
|
+
if (existsSync11(pkgPath)) {
|
|
1046
1279
|
try {
|
|
1047
|
-
const pkg = JSON.parse(
|
|
1280
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
1048
1281
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
1049
1282
|
} catch {
|
|
1050
1283
|
}
|
|
1051
1284
|
}
|
|
1052
|
-
dir =
|
|
1285
|
+
dir = path11.dirname(dir);
|
|
1053
1286
|
}
|
|
1054
|
-
return
|
|
1287
|
+
return path11.resolve(path11.dirname(thisFile), "..", "..", "..");
|
|
1055
1288
|
}
|
|
1056
|
-
async function copySlashCommands(packageRoot, homeDir =
|
|
1289
|
+
async function copySlashCommands(packageRoot, homeDir = os7.homedir()) {
|
|
1057
1290
|
let copied = 0;
|
|
1058
1291
|
let skipped = 0;
|
|
1059
|
-
const skillsBase =
|
|
1060
|
-
const exeDir =
|
|
1061
|
-
if (
|
|
1292
|
+
const skillsBase = path11.join(homeDir, ".claude", "skills");
|
|
1293
|
+
const exeDir = path11.join(packageRoot, "src", "commands", "exe");
|
|
1294
|
+
if (existsSync11(exeDir)) {
|
|
1062
1295
|
const entries = await readdir(exeDir);
|
|
1063
1296
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
1064
1297
|
for (const file of mdFiles) {
|
|
1065
1298
|
const name = file.replace(".md", "");
|
|
1066
|
-
const destDir =
|
|
1299
|
+
const destDir = path11.join(skillsBase, `exe-${name}`);
|
|
1067
1300
|
await mkdir3(destDir, { recursive: true });
|
|
1068
|
-
const srcPath =
|
|
1069
|
-
const destPath =
|
|
1301
|
+
const srcPath = path11.join(exeDir, file);
|
|
1302
|
+
const destPath = path11.join(destDir, "SKILL.md");
|
|
1070
1303
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
1071
1304
|
if (result) copied++;
|
|
1072
1305
|
else skipped++;
|
|
1073
1306
|
}
|
|
1074
1307
|
}
|
|
1075
|
-
const topLevelSrc =
|
|
1076
|
-
if (
|
|
1077
|
-
const destDir =
|
|
1308
|
+
const topLevelSrc = path11.join(packageRoot, "src", "commands", "exe.md");
|
|
1309
|
+
if (existsSync11(topLevelSrc)) {
|
|
1310
|
+
const destDir = path11.join(skillsBase, "exe");
|
|
1078
1311
|
await mkdir3(destDir, { recursive: true });
|
|
1079
|
-
const destPath =
|
|
1312
|
+
const destPath = path11.join(destDir, "SKILL.md");
|
|
1080
1313
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
1081
1314
|
if (result) copied++;
|
|
1082
1315
|
else skipped++;
|
|
@@ -1099,17 +1332,17 @@ name: ${skillName}
|
|
|
1099
1332
|
`);
|
|
1100
1333
|
}
|
|
1101
1334
|
}
|
|
1102
|
-
if (
|
|
1335
|
+
if (existsSync11(destPath)) {
|
|
1103
1336
|
const existing = await readFile3(destPath, "utf-8");
|
|
1104
1337
|
if (existing === content) return false;
|
|
1105
1338
|
}
|
|
1106
1339
|
await writeFile3(destPath, content);
|
|
1107
1340
|
return true;
|
|
1108
1341
|
}
|
|
1109
|
-
async function registerMcpServer(packageRoot, homeDir =
|
|
1110
|
-
const claudeJsonPath =
|
|
1342
|
+
async function registerMcpServer(packageRoot, homeDir = os7.homedir()) {
|
|
1343
|
+
const claudeJsonPath = path11.join(homeDir, ".claude.json");
|
|
1111
1344
|
let claudeJson = {};
|
|
1112
|
-
if (
|
|
1345
|
+
if (existsSync11(claudeJsonPath)) {
|
|
1113
1346
|
try {
|
|
1114
1347
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
1115
1348
|
} catch {
|
|
@@ -1122,25 +1355,25 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
1122
1355
|
const newEntry = {
|
|
1123
1356
|
type: "stdio",
|
|
1124
1357
|
command: "node",
|
|
1125
|
-
args: [
|
|
1358
|
+
args: [path11.join(packageRoot, "dist", "mcp", "server.js")],
|
|
1126
1359
|
env: {}
|
|
1127
1360
|
};
|
|
1128
1361
|
const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
1129
|
-
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
1130
1362
|
const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1363
|
+
if (claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
1364
|
+
delete claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
1365
|
+
}
|
|
1366
|
+
if (memMatches && !claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
1367
|
+
await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
|
|
1134
1368
|
return false;
|
|
1135
1369
|
}
|
|
1136
1370
|
claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
|
|
1137
|
-
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
1138
1371
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
1139
|
-
await cleanSettingsJsonMcp(
|
|
1372
|
+
await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
|
|
1140
1373
|
return true;
|
|
1141
1374
|
}
|
|
1142
1375
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
1143
|
-
if (!
|
|
1376
|
+
if (!existsSync11(settingsPath)) return;
|
|
1144
1377
|
try {
|
|
1145
1378
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1146
1379
|
const servers = settings.mcpServers;
|
|
@@ -1160,14 +1393,14 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
1160
1393
|
} catch {
|
|
1161
1394
|
}
|
|
1162
1395
|
}
|
|
1163
|
-
async function mergeHooks(packageRoot, homeDir =
|
|
1164
|
-
const settingsPath =
|
|
1165
|
-
const logsDir =
|
|
1166
|
-
const hookLogPath =
|
|
1396
|
+
async function mergeHooks(packageRoot, homeDir = os7.homedir()) {
|
|
1397
|
+
const settingsPath = path11.join(homeDir, ".claude", "settings.json");
|
|
1398
|
+
const logsDir = path11.join(homeDir, ".exe-os", "logs");
|
|
1399
|
+
const hookLogPath = path11.join(logsDir, "hooks.log");
|
|
1167
1400
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1168
1401
|
await mkdir3(logsDir, { recursive: true });
|
|
1169
1402
|
let settings = {};
|
|
1170
|
-
if (
|
|
1403
|
+
if (existsSync11(settingsPath)) {
|
|
1171
1404
|
try {
|
|
1172
1405
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1173
1406
|
} catch {
|
|
@@ -1189,11 +1422,11 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1189
1422
|
hooks: [
|
|
1190
1423
|
{
|
|
1191
1424
|
type: "command",
|
|
1192
|
-
command: `node "${
|
|
1425
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
1193
1426
|
},
|
|
1194
1427
|
{
|
|
1195
1428
|
type: "command",
|
|
1196
|
-
command: `node "${
|
|
1429
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
1197
1430
|
}
|
|
1198
1431
|
]
|
|
1199
1432
|
},
|
|
@@ -1205,7 +1438,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1205
1438
|
hooks: [
|
|
1206
1439
|
{
|
|
1207
1440
|
type: "command",
|
|
1208
|
-
command: `node "${
|
|
1441
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1209
1442
|
timeout: 1e4
|
|
1210
1443
|
}
|
|
1211
1444
|
]
|
|
@@ -1218,7 +1451,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1218
1451
|
hooks: [
|
|
1219
1452
|
{
|
|
1220
1453
|
type: "command",
|
|
1221
|
-
command: `node "${
|
|
1454
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1222
1455
|
}
|
|
1223
1456
|
]
|
|
1224
1457
|
},
|
|
@@ -1230,7 +1463,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1230
1463
|
hooks: [
|
|
1231
1464
|
{
|
|
1232
1465
|
type: "command",
|
|
1233
|
-
command: `node "${
|
|
1466
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1234
1467
|
timeout: 5e3
|
|
1235
1468
|
}
|
|
1236
1469
|
]
|
|
@@ -1243,7 +1476,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1243
1476
|
hooks: [
|
|
1244
1477
|
{
|
|
1245
1478
|
type: "command",
|
|
1246
|
-
command: `node "${
|
|
1479
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1247
1480
|
}
|
|
1248
1481
|
]
|
|
1249
1482
|
},
|
|
@@ -1256,7 +1489,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1256
1489
|
hooks: [
|
|
1257
1490
|
{
|
|
1258
1491
|
type: "command",
|
|
1259
|
-
command: `node "${
|
|
1492
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1260
1493
|
}
|
|
1261
1494
|
]
|
|
1262
1495
|
},
|
|
@@ -1268,7 +1501,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1268
1501
|
hooks: [
|
|
1269
1502
|
{
|
|
1270
1503
|
type: "command",
|
|
1271
|
-
command: `node "${
|
|
1504
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
1272
1505
|
}
|
|
1273
1506
|
]
|
|
1274
1507
|
},
|
|
@@ -1280,7 +1513,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1280
1513
|
hooks: [
|
|
1281
1514
|
{
|
|
1282
1515
|
type: "command",
|
|
1283
|
-
command: `node "${
|
|
1516
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
1284
1517
|
timeout: 1e4
|
|
1285
1518
|
}
|
|
1286
1519
|
]
|
|
@@ -1293,7 +1526,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1293
1526
|
hooks: [
|
|
1294
1527
|
{
|
|
1295
1528
|
type: "command",
|
|
1296
|
-
command: `node "${
|
|
1529
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
1297
1530
|
}
|
|
1298
1531
|
]
|
|
1299
1532
|
},
|
|
@@ -1305,7 +1538,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1305
1538
|
hooks: [
|
|
1306
1539
|
{
|
|
1307
1540
|
type: "command",
|
|
1308
|
-
command: `node "${
|
|
1541
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
1309
1542
|
}
|
|
1310
1543
|
]
|
|
1311
1544
|
},
|
|
@@ -1317,7 +1550,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1317
1550
|
hooks: [
|
|
1318
1551
|
{
|
|
1319
1552
|
type: "command",
|
|
1320
|
-
command: `node "${
|
|
1553
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
1321
1554
|
timeout: 1e4
|
|
1322
1555
|
}
|
|
1323
1556
|
]
|
|
@@ -1330,7 +1563,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1330
1563
|
hooks: [
|
|
1331
1564
|
{
|
|
1332
1565
|
type: "command",
|
|
1333
|
-
command: `node "${
|
|
1566
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
1334
1567
|
}
|
|
1335
1568
|
]
|
|
1336
1569
|
},
|
|
@@ -1429,13 +1662,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1429
1662
|
allowList.push(tool);
|
|
1430
1663
|
}
|
|
1431
1664
|
}
|
|
1432
|
-
await mkdir3(
|
|
1665
|
+
await mkdir3(path11.dirname(settingsPath), { recursive: true });
|
|
1433
1666
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1434
1667
|
return { added, skipped };
|
|
1435
1668
|
}
|
|
1436
|
-
async function cleanOldShellFunctions(homeDir =
|
|
1437
|
-
const rosterPath =
|
|
1438
|
-
if (!
|
|
1669
|
+
async function cleanOldShellFunctions(homeDir = os7.homedir()) {
|
|
1670
|
+
const rosterPath = path11.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1671
|
+
if (!existsSync11(rosterPath)) return 0;
|
|
1439
1672
|
let employees;
|
|
1440
1673
|
try {
|
|
1441
1674
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -1450,13 +1683,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
1450
1683
|
return { name: n, funcDef, forLoop };
|
|
1451
1684
|
});
|
|
1452
1685
|
const rcFiles = [
|
|
1453
|
-
|
|
1454
|
-
|
|
1686
|
+
path11.join(homeDir, ".zshrc"),
|
|
1687
|
+
path11.join(homeDir, ".bashrc")
|
|
1455
1688
|
];
|
|
1456
1689
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1457
1690
|
let totalRemoved = 0;
|
|
1458
1691
|
for (const rcPath of rcFiles) {
|
|
1459
|
-
if (!
|
|
1692
|
+
if (!existsSync11(rcPath)) continue;
|
|
1460
1693
|
let content;
|
|
1461
1694
|
try {
|
|
1462
1695
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -1560,8 +1793,8 @@ function escapeRegExp(s) {
|
|
|
1560
1793
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1561
1794
|
}
|
|
1562
1795
|
async function injectOrchestrationRules(homeDir) {
|
|
1563
|
-
const claudeDir =
|
|
1564
|
-
const claudeMdPath =
|
|
1796
|
+
const claudeDir = path11.join(homeDir, ".claude");
|
|
1797
|
+
const claudeMdPath = path11.join(claudeDir, "CLAUDE.md");
|
|
1565
1798
|
await mkdir3(claudeDir, { recursive: true });
|
|
1566
1799
|
let existing = "";
|
|
1567
1800
|
try {
|
|
@@ -1583,19 +1816,19 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
1583
1816
|
await writeFile3(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
1584
1817
|
return "injected";
|
|
1585
1818
|
}
|
|
1586
|
-
async function installStatusLine(packageRoot, homeDir =
|
|
1819
|
+
async function installStatusLine(packageRoot, homeDir = os7.homedir()) {
|
|
1587
1820
|
const prefs = loadPreferences(homeDir);
|
|
1588
1821
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
1589
|
-
const claudeDir =
|
|
1822
|
+
const claudeDir = path11.join(homeDir, ".claude");
|
|
1590
1823
|
await mkdir3(claudeDir, { recursive: true });
|
|
1591
|
-
const assetPath =
|
|
1592
|
-
if (!
|
|
1593
|
-
const destScript =
|
|
1824
|
+
const assetPath = path11.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1825
|
+
if (!existsSync11(assetPath)) return "asset-missing";
|
|
1826
|
+
const destScript = path11.join(claudeDir, "statusline-command.sh");
|
|
1594
1827
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1595
1828
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1596
|
-
const settingsPath =
|
|
1829
|
+
const settingsPath = path11.join(claudeDir, "settings.json");
|
|
1597
1830
|
let settings = {};
|
|
1598
|
-
if (
|
|
1831
|
+
if (existsSync11(settingsPath)) {
|
|
1599
1832
|
try {
|
|
1600
1833
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1601
1834
|
} catch {
|
|
@@ -1632,13 +1865,13 @@ async function runInstaller(homeDir) {
|
|
|
1632
1865
|
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
1633
1866
|
`
|
|
1634
1867
|
);
|
|
1635
|
-
const resolvedHome = homeDir ??
|
|
1636
|
-
const exeWorkspace =
|
|
1637
|
-
if (!
|
|
1868
|
+
const resolvedHome = homeDir ?? os7.homedir();
|
|
1869
|
+
const exeWorkspace = path11.join(resolvedHome, "exe");
|
|
1870
|
+
if (!existsSync11(exeWorkspace)) {
|
|
1638
1871
|
try {
|
|
1639
|
-
await mkdir3(
|
|
1640
|
-
await mkdir3(
|
|
1641
|
-
await mkdir3(
|
|
1872
|
+
await mkdir3(path11.join(exeWorkspace, "content"), { recursive: true });
|
|
1873
|
+
await mkdir3(path11.join(exeWorkspace, "operations"), { recursive: true });
|
|
1874
|
+
await mkdir3(path11.join(exeWorkspace, "output"), { recursive: true });
|
|
1642
1875
|
process.stderr.write(
|
|
1643
1876
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1644
1877
|
`
|
|
@@ -1673,34 +1906,34 @@ exe-os installed successfully.
|
|
|
1673
1906
|
`);
|
|
1674
1907
|
}
|
|
1675
1908
|
function setupTmux(home) {
|
|
1676
|
-
const homeDir = home ??
|
|
1677
|
-
const exeDir =
|
|
1678
|
-
const exeTmuxConf =
|
|
1679
|
-
const userTmuxConf =
|
|
1680
|
-
const backupPath =
|
|
1909
|
+
const homeDir = home ?? os7.homedir();
|
|
1910
|
+
const exeDir = path11.join(homeDir, ".exe-os");
|
|
1911
|
+
const exeTmuxConf = path11.join(exeDir, "tmux.conf");
|
|
1912
|
+
const userTmuxConf = path11.join(homeDir, ".tmux.conf");
|
|
1913
|
+
const backupPath = path11.join(homeDir, ".tmux.conf.backup");
|
|
1681
1914
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1682
1915
|
const pkgRoot = resolvePackageRoot();
|
|
1683
|
-
const assetPath =
|
|
1684
|
-
if (!
|
|
1916
|
+
const assetPath = path11.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1917
|
+
if (!existsSync11(assetPath)) {
|
|
1685
1918
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1686
1919
|
`);
|
|
1687
1920
|
return;
|
|
1688
1921
|
}
|
|
1689
1922
|
mkdirSync6(exeDir, { recursive: true });
|
|
1690
1923
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1691
|
-
if (
|
|
1692
|
-
const existing =
|
|
1924
|
+
if (existsSync11(userTmuxConf)) {
|
|
1925
|
+
const existing = readFileSync9(userTmuxConf, "utf8");
|
|
1693
1926
|
if (!existing.includes(sourceLine)) {
|
|
1694
|
-
if (!
|
|
1927
|
+
if (!existsSync11(backupPath)) {
|
|
1695
1928
|
copyFileSync(userTmuxConf, backupPath);
|
|
1696
1929
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1697
1930
|
`);
|
|
1698
1931
|
}
|
|
1699
|
-
|
|
1932
|
+
writeFileSync7(userTmuxConf, `${sourceLine}
|
|
1700
1933
|
${existing}`);
|
|
1701
1934
|
}
|
|
1702
1935
|
} else {
|
|
1703
|
-
|
|
1936
|
+
writeFileSync7(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1704
1937
|
${sourceLine}
|
|
1705
1938
|
`);
|
|
1706
1939
|
}
|
|
@@ -1711,10 +1944,10 @@ ${sourceLine}
|
|
|
1711
1944
|
process.stderr.write("exe-os: tmux config installed\n");
|
|
1712
1945
|
}
|
|
1713
1946
|
function setupGhostty(home) {
|
|
1714
|
-
const homeDir = home ??
|
|
1715
|
-
const xdgConfig =
|
|
1716
|
-
const macConfig =
|
|
1717
|
-
const ghosttyInstalled =
|
|
1947
|
+
const homeDir = home ?? os7.homedir();
|
|
1948
|
+
const xdgConfig = path11.join(homeDir, ".config", "ghostty");
|
|
1949
|
+
const macConfig = path11.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1950
|
+
const ghosttyInstalled = existsSync11(xdgConfig) || existsSync11(macConfig) || (() => {
|
|
1718
1951
|
try {
|
|
1719
1952
|
execSync2("which ghostty 2>/dev/null");
|
|
1720
1953
|
return true;
|
|
@@ -1726,47 +1959,47 @@ function setupGhostty(home) {
|
|
|
1726
1959
|
return;
|
|
1727
1960
|
}
|
|
1728
1961
|
const pkgRoot = resolvePackageRoot();
|
|
1729
|
-
const assetPath =
|
|
1730
|
-
if (!
|
|
1962
|
+
const assetPath = path11.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1963
|
+
if (!existsSync11(assetPath)) {
|
|
1731
1964
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1732
1965
|
return;
|
|
1733
1966
|
}
|
|
1734
1967
|
const configDir = xdgConfig;
|
|
1735
|
-
const configPath =
|
|
1736
|
-
const backupPath =
|
|
1968
|
+
const configPath = path11.join(configDir, "config");
|
|
1969
|
+
const backupPath = path11.join(configDir, "config.backup");
|
|
1737
1970
|
mkdirSync6(configDir, { recursive: true });
|
|
1738
1971
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1739
1972
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1740
|
-
const assetContent =
|
|
1973
|
+
const assetContent = readFileSync9(assetPath, "utf8").trim();
|
|
1741
1974
|
const markedSection = `${START_MARKER}
|
|
1742
1975
|
${assetContent}
|
|
1743
1976
|
${END_MARKER}`;
|
|
1744
|
-
if (
|
|
1745
|
-
const existing =
|
|
1977
|
+
if (existsSync11(configPath)) {
|
|
1978
|
+
const existing = readFileSync9(configPath, "utf8");
|
|
1746
1979
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1747
1980
|
const before = existing.slice(0, existing.indexOf(START_MARKER));
|
|
1748
1981
|
const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
|
|
1749
|
-
|
|
1982
|
+
writeFileSync7(configPath, `${before}${markedSection}${after}`);
|
|
1750
1983
|
} else if (existing.includes("Exe OS")) {
|
|
1751
|
-
if (!
|
|
1984
|
+
if (!existsSync11(backupPath)) {
|
|
1752
1985
|
copyFileSync(configPath, backupPath);
|
|
1753
1986
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1754
1987
|
`);
|
|
1755
1988
|
}
|
|
1756
|
-
|
|
1989
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1757
1990
|
`);
|
|
1758
1991
|
} else {
|
|
1759
|
-
if (!
|
|
1992
|
+
if (!existsSync11(backupPath)) {
|
|
1760
1993
|
copyFileSync(configPath, backupPath);
|
|
1761
1994
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1762
1995
|
`);
|
|
1763
1996
|
}
|
|
1764
|
-
|
|
1997
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1765
1998
|
|
|
1766
1999
|
${existing}`);
|
|
1767
2000
|
}
|
|
1768
2001
|
} else {
|
|
1769
|
-
|
|
2002
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1770
2003
|
`);
|
|
1771
2004
|
}
|
|
1772
2005
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1807,8 +2040,8 @@ ${EXE_SECTION_END}`;
|
|
|
1807
2040
|
});
|
|
1808
2041
|
|
|
1809
2042
|
// src/bin/exe-new-employee.ts
|
|
1810
|
-
import { existsSync as
|
|
1811
|
-
import
|
|
2043
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7 } from "fs";
|
|
2044
|
+
import path12 from "path";
|
|
1812
2045
|
|
|
1813
2046
|
// src/lib/session-wrappers.ts
|
|
1814
2047
|
import {
|
|
@@ -2400,18 +2633,21 @@ function isMainModule(importMetaUrl) {
|
|
|
2400
2633
|
// src/lib/plan-limits.ts
|
|
2401
2634
|
init_database();
|
|
2402
2635
|
init_employees();
|
|
2403
|
-
import { readFileSync as
|
|
2404
|
-
import
|
|
2636
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
2637
|
+
import path7 from "path";
|
|
2405
2638
|
|
|
2406
2639
|
// src/lib/license.ts
|
|
2407
2640
|
init_config();
|
|
2408
|
-
import { readFileSync as
|
|
2641
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
2409
2642
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2410
|
-
import
|
|
2643
|
+
import { createRequire as createRequire2 } from "module";
|
|
2644
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2645
|
+
import os4 from "os";
|
|
2646
|
+
import path6 from "path";
|
|
2411
2647
|
import { jwtVerify, importSPKI } from "jose";
|
|
2412
|
-
var LICENSE_PATH =
|
|
2413
|
-
var CACHE_PATH =
|
|
2414
|
-
var DEVICE_ID_PATH =
|
|
2648
|
+
var LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
|
|
2649
|
+
var CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
|
|
2650
|
+
var DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
|
|
2415
2651
|
var API_BASE = "https://askexe.com/cloud";
|
|
2416
2652
|
var RETRY_DELAY_MS = 500;
|
|
2417
2653
|
async function fetchRetry(url, init) {
|
|
@@ -2444,37 +2680,37 @@ var FREE_LICENSE = {
|
|
|
2444
2680
|
memoryLimit: 5e3
|
|
2445
2681
|
};
|
|
2446
2682
|
function loadDeviceId() {
|
|
2447
|
-
const deviceJsonPath =
|
|
2683
|
+
const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
|
|
2448
2684
|
try {
|
|
2449
|
-
if (
|
|
2450
|
-
const data = JSON.parse(
|
|
2685
|
+
if (existsSync6(deviceJsonPath)) {
|
|
2686
|
+
const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
|
|
2451
2687
|
if (data.deviceId) return data.deviceId;
|
|
2452
2688
|
}
|
|
2453
2689
|
} catch {
|
|
2454
2690
|
}
|
|
2455
2691
|
try {
|
|
2456
|
-
if (
|
|
2457
|
-
const id2 =
|
|
2692
|
+
if (existsSync6(DEVICE_ID_PATH)) {
|
|
2693
|
+
const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
|
|
2458
2694
|
if (id2) return id2;
|
|
2459
2695
|
}
|
|
2460
2696
|
} catch {
|
|
2461
2697
|
}
|
|
2462
2698
|
const id = randomUUID2();
|
|
2463
|
-
|
|
2464
|
-
|
|
2699
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
2700
|
+
writeFileSync4(DEVICE_ID_PATH, id, "utf8");
|
|
2465
2701
|
return id;
|
|
2466
2702
|
}
|
|
2467
2703
|
function loadLicense() {
|
|
2468
2704
|
try {
|
|
2469
|
-
if (!
|
|
2470
|
-
return
|
|
2705
|
+
if (!existsSync6(LICENSE_PATH)) return null;
|
|
2706
|
+
return readFileSync5(LICENSE_PATH, "utf8").trim();
|
|
2471
2707
|
} catch {
|
|
2472
2708
|
return null;
|
|
2473
2709
|
}
|
|
2474
2710
|
}
|
|
2475
2711
|
function saveLicense(apiKey) {
|
|
2476
|
-
|
|
2477
|
-
|
|
2712
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
2713
|
+
writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2478
2714
|
}
|
|
2479
2715
|
async function verifyLicenseJwt(token) {
|
|
2480
2716
|
try {
|
|
@@ -2500,8 +2736,8 @@ async function verifyLicenseJwt(token) {
|
|
|
2500
2736
|
}
|
|
2501
2737
|
async function getCachedLicense() {
|
|
2502
2738
|
try {
|
|
2503
|
-
if (!
|
|
2504
|
-
const raw = JSON.parse(
|
|
2739
|
+
if (!existsSync6(CACHE_PATH)) return null;
|
|
2740
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
2505
2741
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
2506
2742
|
return await verifyLicenseJwt(raw.token);
|
|
2507
2743
|
} catch {
|
|
@@ -2510,8 +2746,8 @@ async function getCachedLicense() {
|
|
|
2510
2746
|
}
|
|
2511
2747
|
function readCachedToken() {
|
|
2512
2748
|
try {
|
|
2513
|
-
if (!
|
|
2514
|
-
const raw = JSON.parse(
|
|
2749
|
+
if (!existsSync6(CACHE_PATH)) return null;
|
|
2750
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
2515
2751
|
return typeof raw.token === "string" ? raw.token : null;
|
|
2516
2752
|
} catch {
|
|
2517
2753
|
return null;
|
|
@@ -2545,56 +2781,132 @@ function getRawCachedPlan() {
|
|
|
2545
2781
|
}
|
|
2546
2782
|
function cacheResponse(token) {
|
|
2547
2783
|
try {
|
|
2548
|
-
|
|
2784
|
+
writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
2549
2785
|
} catch {
|
|
2550
2786
|
}
|
|
2551
2787
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2788
|
+
var _prismaPromise = null;
|
|
2789
|
+
var _prismaFailed = false;
|
|
2790
|
+
function loadPrismaForLicense() {
|
|
2791
|
+
if (_prismaFailed) return null;
|
|
2792
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
2793
|
+
if (!dbUrl) {
|
|
2794
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
|
|
2795
|
+
if (!existsSync6(path6.join(exeDbRoot, "package.json"))) {
|
|
2796
|
+
_prismaFailed = true;
|
|
2797
|
+
return null;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if (!_prismaPromise) {
|
|
2801
|
+
_prismaPromise = (async () => {
|
|
2802
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
2803
|
+
if (explicitPath) {
|
|
2804
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
2805
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
2806
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
2807
|
+
return new Ctor2();
|
|
2808
|
+
}
|
|
2809
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
|
|
2810
|
+
const req = createRequire2(path6.join(exeDbRoot, "package.json"));
|
|
2811
|
+
const entry = req.resolve("@prisma/client");
|
|
2812
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
2813
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
2814
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
2815
|
+
return new Ctor();
|
|
2816
|
+
})().catch((err) => {
|
|
2817
|
+
_prismaFailed = true;
|
|
2818
|
+
_prismaPromise = null;
|
|
2819
|
+
throw err;
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
return _prismaPromise;
|
|
2823
|
+
}
|
|
2824
|
+
async function validateViaPostgres(apiKey) {
|
|
2825
|
+
const loader = loadPrismaForLicense();
|
|
2826
|
+
if (!loader) return null;
|
|
2827
|
+
try {
|
|
2828
|
+
const prisma = await loader;
|
|
2829
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
2830
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
2831
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
2832
|
+
apiKey
|
|
2833
|
+
);
|
|
2834
|
+
if (!rows || rows.length === 0) return null;
|
|
2835
|
+
const row = rows[0];
|
|
2836
|
+
if (row.status !== "active") return null;
|
|
2837
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
2838
|
+
const plan = row.plan;
|
|
2839
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
2840
|
+
return {
|
|
2841
|
+
valid: true,
|
|
2842
|
+
plan,
|
|
2843
|
+
email: row.email,
|
|
2844
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
2845
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
2846
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
2847
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
2848
|
+
};
|
|
2849
|
+
} catch {
|
|
2850
|
+
return null;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
2554
2854
|
try {
|
|
2555
2855
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
2556
2856
|
method: "POST",
|
|
2557
2857
|
headers: { "Content-Type": "application/json" },
|
|
2558
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
2858
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
2559
2859
|
signal: AbortSignal.timeout(1e4)
|
|
2560
2860
|
});
|
|
2561
|
-
if (res.ok)
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2861
|
+
if (!res.ok) return null;
|
|
2862
|
+
const data = await res.json();
|
|
2863
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
2864
|
+
if (!data.valid) return null;
|
|
2865
|
+
if (data.token) {
|
|
2866
|
+
cacheResponse(data.token);
|
|
2867
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
2868
|
+
if (verified) return verified;
|
|
2869
|
+
}
|
|
2870
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
2871
|
+
return {
|
|
2872
|
+
valid: data.valid,
|
|
2873
|
+
plan: data.plan,
|
|
2874
|
+
email: data.email,
|
|
2875
|
+
expiresAt: data.expiresAt,
|
|
2876
|
+
deviceLimit: limits.devices,
|
|
2877
|
+
employeeLimit: limits.employees,
|
|
2878
|
+
memoryLimit: limits.memories
|
|
2879
|
+
};
|
|
2880
|
+
} catch {
|
|
2881
|
+
return null;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
async function validateLicense(apiKey, deviceId) {
|
|
2885
|
+
const did = deviceId ?? loadDeviceId();
|
|
2886
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
2887
|
+
if (pgResult) {
|
|
2888
|
+
try {
|
|
2889
|
+
writeFileSync4(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
2890
|
+
} catch {
|
|
2891
|
+
}
|
|
2892
|
+
return pgResult;
|
|
2893
|
+
}
|
|
2894
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
2895
|
+
if (cfResult) return cfResult;
|
|
2896
|
+
const cached = await getCachedLicense();
|
|
2897
|
+
if (cached) return cached;
|
|
2898
|
+
try {
|
|
2899
|
+
if (existsSync6(CACHE_PATH)) {
|
|
2900
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
2901
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
2902
|
+
return raw.pgLicense;
|
|
2574
2903
|
}
|
|
2575
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
2576
|
-
return {
|
|
2577
|
-
valid: data.valid,
|
|
2578
|
-
plan: data.plan,
|
|
2579
|
-
email: data.email,
|
|
2580
|
-
expiresAt: data.expiresAt,
|
|
2581
|
-
deviceLimit: limits.devices,
|
|
2582
|
-
employeeLimit: limits.employees,
|
|
2583
|
-
memoryLimit: limits.memories
|
|
2584
|
-
};
|
|
2585
2904
|
}
|
|
2586
|
-
const cached = await getCachedLicense();
|
|
2587
|
-
if (cached) return cached;
|
|
2588
|
-
const raw = getRawCachedPlan();
|
|
2589
|
-
if (raw) return raw;
|
|
2590
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
2591
2905
|
} catch {
|
|
2592
|
-
const cached = await getCachedLicense();
|
|
2593
|
-
if (cached) return cached;
|
|
2594
|
-
const rawFallback = getRawCachedPlan();
|
|
2595
|
-
if (rawFallback) return rawFallback;
|
|
2596
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
2597
2906
|
}
|
|
2907
|
+
const rawFallback = getRawCachedPlan();
|
|
2908
|
+
if (rawFallback) return rawFallback;
|
|
2909
|
+
return { ...FREE_LICENSE, valid: false };
|
|
2598
2910
|
}
|
|
2599
2911
|
var CACHE_MAX_AGE_MS = 36e5;
|
|
2600
2912
|
function getCacheAgeMs() {
|
|
@@ -2610,9 +2922,9 @@ async function checkLicense() {
|
|
|
2610
2922
|
let key = loadLicense();
|
|
2611
2923
|
if (!key) {
|
|
2612
2924
|
try {
|
|
2613
|
-
const configPath =
|
|
2614
|
-
if (
|
|
2615
|
-
const raw = JSON.parse(
|
|
2925
|
+
const configPath = path6.join(EXE_AI_DIR, "config.json");
|
|
2926
|
+
if (existsSync6(configPath)) {
|
|
2927
|
+
const raw = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
2616
2928
|
const cloud = raw.cloud;
|
|
2617
2929
|
if (cloud?.apiKey) {
|
|
2618
2930
|
key = cloud.apiKey;
|
|
@@ -2637,7 +2949,7 @@ var PlanLimitError = class extends Error {
|
|
|
2637
2949
|
this.name = "PlanLimitError";
|
|
2638
2950
|
}
|
|
2639
2951
|
};
|
|
2640
|
-
var CACHE_PATH2 =
|
|
2952
|
+
var CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2641
2953
|
async function assertEmployeeLimit(license, rosterPath) {
|
|
2642
2954
|
const lic = license ?? await checkLicense();
|
|
2643
2955
|
if (lic.employeeLimit < 0) return;
|
|
@@ -2671,7 +2983,6 @@ async function main() {
|
|
|
2671
2983
|
console.error(`Invalid name: ${validation.error}`);
|
|
2672
2984
|
process.exit(1);
|
|
2673
2985
|
}
|
|
2674
|
-
const employees = await loadEmployees();
|
|
2675
2986
|
let newEmployee;
|
|
2676
2987
|
const effectiveTemplate = templateName ?? name;
|
|
2677
2988
|
const template = getTemplate(effectiveTemplate);
|
|
@@ -2709,14 +3020,12 @@ async function main() {
|
|
|
2709
3020
|
}
|
|
2710
3021
|
throw err;
|
|
2711
3022
|
}
|
|
2712
|
-
let updated;
|
|
2713
3023
|
try {
|
|
2714
|
-
|
|
3024
|
+
await hireEmployee(newEmployee);
|
|
2715
3025
|
} catch (err) {
|
|
2716
3026
|
console.error(err instanceof Error ? err.message : String(err));
|
|
2717
3027
|
process.exit(1);
|
|
2718
3028
|
}
|
|
2719
|
-
await saveEmployees(updated);
|
|
2720
3029
|
try {
|
|
2721
3030
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
2722
3031
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
@@ -2733,7 +3042,7 @@ async function main() {
|
|
|
2733
3042
|
const identityTemplate = templateKey ? getIdentityTemplate(templateKey) : null;
|
|
2734
3043
|
if (identityTemplate) {
|
|
2735
3044
|
const idPath = identityPath2(name);
|
|
2736
|
-
const dir =
|
|
3045
|
+
const dir = path12.dirname(idPath);
|
|
2737
3046
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
2738
3047
|
const content = identityTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`);
|
|
2739
3048
|
fs.writeFileSync(idPath, content, "utf-8");
|
|
@@ -2741,8 +3050,8 @@ async function main() {
|
|
|
2741
3050
|
}
|
|
2742
3051
|
} catch {
|
|
2743
3052
|
}
|
|
2744
|
-
const taskDir =
|
|
2745
|
-
if (!
|
|
3053
|
+
const taskDir = path12.join(process.cwd(), "exe", name);
|
|
3054
|
+
if (!existsSync12(taskDir)) {
|
|
2746
3055
|
mkdirSync7(taskDir, { recursive: true });
|
|
2747
3056
|
}
|
|
2748
3057
|
const bins = registerBinSymlinks(newEmployee.name);
|