@askexenow/exe-os 0.9.8 → 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 +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
package/dist/bin/exe-call.js
CHANGED
|
@@ -9,9 +9,34 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/lib/secure-files.ts
|
|
13
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
14
|
+
import { chmod, mkdir } from "fs/promises";
|
|
15
|
+
function ensurePrivateDirSync(dirPath) {
|
|
16
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
17
|
+
try {
|
|
18
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function enforcePrivateFileSync(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
29
|
+
var init_secure_files = __esm({
|
|
30
|
+
"src/lib/secure-files.ts"() {
|
|
31
|
+
"use strict";
|
|
32
|
+
PRIVATE_DIR_MODE = 448;
|
|
33
|
+
PRIVATE_FILE_MODE = 384;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
12
37
|
// src/lib/config.ts
|
|
13
|
-
import { readFile, writeFile
|
|
14
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
38
|
+
import { readFile, writeFile } from "fs/promises";
|
|
39
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
40
|
import path from "path";
|
|
16
41
|
import os from "os";
|
|
17
42
|
function resolveDataDir() {
|
|
@@ -19,7 +44,7 @@ function resolveDataDir() {
|
|
|
19
44
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
45
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
21
46
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
22
|
-
if (!
|
|
47
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
48
|
try {
|
|
24
49
|
renameSync(legacyDir, newDir);
|
|
25
50
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -34,6 +59,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
34
59
|
var init_config = __esm({
|
|
35
60
|
"src/lib/config.ts"() {
|
|
36
61
|
"use strict";
|
|
62
|
+
init_secure_files();
|
|
37
63
|
EXE_AI_DIR = resolveDataDir();
|
|
38
64
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
39
65
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -140,10 +166,10 @@ __export(agent_config_exports, {
|
|
|
140
166
|
saveAgentConfig: () => saveAgentConfig,
|
|
141
167
|
setAgentRuntime: () => setAgentRuntime
|
|
142
168
|
});
|
|
143
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as
|
|
169
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
144
170
|
import path2 from "path";
|
|
145
171
|
function loadAgentConfig() {
|
|
146
|
-
if (!
|
|
172
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
147
173
|
try {
|
|
148
174
|
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
149
175
|
} catch {
|
|
@@ -152,8 +178,9 @@ function loadAgentConfig() {
|
|
|
152
178
|
}
|
|
153
179
|
function saveAgentConfig(config) {
|
|
154
180
|
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
155
|
-
|
|
181
|
+
ensurePrivateDirSync(dir);
|
|
156
182
|
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
183
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
157
184
|
}
|
|
158
185
|
function getAgentRuntime(agentId) {
|
|
159
186
|
const config = loadAgentConfig();
|
|
@@ -193,6 +220,7 @@ var init_agent_config = __esm({
|
|
|
193
220
|
"use strict";
|
|
194
221
|
init_config();
|
|
195
222
|
init_runtime_table();
|
|
223
|
+
init_secure_files();
|
|
196
224
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
197
225
|
KNOWN_RUNTIMES = {
|
|
198
226
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -240,7 +268,7 @@ __export(employees_exports, {
|
|
|
240
268
|
validateEmployeeName: () => validateEmployeeName
|
|
241
269
|
});
|
|
242
270
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
243
|
-
import { existsSync as
|
|
271
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
244
272
|
import { execSync } from "child_process";
|
|
245
273
|
import path3 from "path";
|
|
246
274
|
import os2 from "os";
|
|
@@ -279,7 +307,7 @@ function validateEmployeeName(name) {
|
|
|
279
307
|
return { valid: true };
|
|
280
308
|
}
|
|
281
309
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
282
|
-
if (!
|
|
310
|
+
if (!existsSync4(employeesPath)) {
|
|
283
311
|
return [];
|
|
284
312
|
}
|
|
285
313
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -294,7 +322,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
294
322
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
295
323
|
}
|
|
296
324
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
297
|
-
if (!
|
|
325
|
+
if (!existsSync4(employeesPath)) return [];
|
|
298
326
|
try {
|
|
299
327
|
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
300
328
|
} catch {
|
|
@@ -342,7 +370,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
342
370
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
343
371
|
if (!coordinator) return;
|
|
344
372
|
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
345
|
-
if (!
|
|
373
|
+
if (!existsSync4(idPath)) return;
|
|
346
374
|
const content = readFileSync3(idPath, "utf-8");
|
|
347
375
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
348
376
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -396,9 +424,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
396
424
|
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
397
425
|
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
398
426
|
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
399
|
-
if (
|
|
427
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
400
428
|
renameSync2(oldPath, newPath);
|
|
401
|
-
} else if (
|
|
429
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
402
430
|
const content = readFileSync3(oldPath, "utf-8");
|
|
403
431
|
writeFileSync2(newPath, content, "utf-8");
|
|
404
432
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -441,7 +469,7 @@ function registerBinSymlinks(name) {
|
|
|
441
469
|
for (const suffix of ["", "-opencode"]) {
|
|
442
470
|
const linkName = `${name}${suffix}`;
|
|
443
471
|
const linkPath = path3.join(binDir, linkName);
|
|
444
|
-
if (
|
|
472
|
+
if (existsSync4(linkPath)) {
|
|
445
473
|
skipped.push(linkName);
|
|
446
474
|
continue;
|
|
447
475
|
}
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -117,9 +117,34 @@ var init_keychain = __esm({
|
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
// src/lib/secure-files.ts
|
|
121
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
122
|
+
import { chmod as chmod2, mkdir as mkdir2 } from "fs/promises";
|
|
123
|
+
async function ensurePrivateDir(dirPath) {
|
|
124
|
+
await mkdir2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
125
|
+
try {
|
|
126
|
+
await chmod2(dirPath, PRIVATE_DIR_MODE);
|
|
127
|
+
} catch {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function enforcePrivateFile(filePath) {
|
|
131
|
+
try {
|
|
132
|
+
await chmod2(filePath, PRIVATE_FILE_MODE);
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
137
|
+
var init_secure_files = __esm({
|
|
138
|
+
"src/lib/secure-files.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
PRIVATE_DIR_MODE = 448;
|
|
141
|
+
PRIVATE_FILE_MODE = 384;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
120
145
|
// src/lib/config.ts
|
|
121
|
-
import { readFile as readFile2, writeFile as writeFile2
|
|
122
|
-
import { readFileSync, existsSync as
|
|
146
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
147
|
+
import { readFileSync, existsSync as existsSync3, renameSync } from "fs";
|
|
123
148
|
import path2 from "path";
|
|
124
149
|
import os2 from "os";
|
|
125
150
|
function resolveDataDir() {
|
|
@@ -127,7 +152,7 @@ function resolveDataDir() {
|
|
|
127
152
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
128
153
|
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
129
154
|
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
130
|
-
if (!
|
|
155
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
131
156
|
try {
|
|
132
157
|
renameSync(legacyDir, newDir);
|
|
133
158
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -190,9 +215,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
190
215
|
}
|
|
191
216
|
async function loadConfig() {
|
|
192
217
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
193
|
-
await
|
|
218
|
+
await ensurePrivateDir(dir);
|
|
194
219
|
const configPath = path2.join(dir, "config.json");
|
|
195
|
-
if (!
|
|
220
|
+
if (!existsSync3(configPath)) {
|
|
196
221
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
197
222
|
}
|
|
198
223
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -205,6 +230,7 @@ async function loadConfig() {
|
|
|
205
230
|
`);
|
|
206
231
|
try {
|
|
207
232
|
await writeFile2(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
233
|
+
await enforcePrivateFile(configPath);
|
|
208
234
|
} catch {
|
|
209
235
|
}
|
|
210
236
|
}
|
|
@@ -222,17 +248,16 @@ async function loadConfig() {
|
|
|
222
248
|
}
|
|
223
249
|
async function saveConfig(config) {
|
|
224
250
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
225
|
-
await
|
|
251
|
+
await ensurePrivateDir(dir);
|
|
226
252
|
const configPath = path2.join(dir, "config.json");
|
|
227
253
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
228
|
-
|
|
229
|
-
await chmod2(configPath, 384);
|
|
230
|
-
}
|
|
254
|
+
await enforcePrivateFile(configPath);
|
|
231
255
|
}
|
|
232
256
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
233
257
|
var init_config = __esm({
|
|
234
258
|
"src/lib/config.ts"() {
|
|
235
259
|
"use strict";
|
|
260
|
+
init_secure_files();
|
|
236
261
|
EXE_AI_DIR = resolveDataDir();
|
|
237
262
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
238
263
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -318,7 +343,7 @@ var init_db_retry = __esm({
|
|
|
318
343
|
|
|
319
344
|
// src/lib/employees.ts
|
|
320
345
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
321
|
-
import { existsSync as
|
|
346
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
322
347
|
import { execSync } from "child_process";
|
|
323
348
|
import path3 from "path";
|
|
324
349
|
import os3 from "os";
|
|
@@ -381,8 +406,11 @@ __export(license_exports, {
|
|
|
381
406
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
382
407
|
validateLicense: () => validateLicense
|
|
383
408
|
});
|
|
384
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as
|
|
409
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
385
410
|
import { randomUUID } from "crypto";
|
|
411
|
+
import { createRequire as createRequire2 } from "module";
|
|
412
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
413
|
+
import os5 from "os";
|
|
386
414
|
import path5 from "path";
|
|
387
415
|
import { jwtVerify, importSPKI } from "jose";
|
|
388
416
|
async function fetchRetry(url, init) {
|
|
@@ -396,34 +424,34 @@ async function fetchRetry(url, init) {
|
|
|
396
424
|
function loadDeviceId() {
|
|
397
425
|
const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
|
|
398
426
|
try {
|
|
399
|
-
if (
|
|
427
|
+
if (existsSync5(deviceJsonPath)) {
|
|
400
428
|
const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
|
|
401
429
|
if (data.deviceId) return data.deviceId;
|
|
402
430
|
}
|
|
403
431
|
} catch {
|
|
404
432
|
}
|
|
405
433
|
try {
|
|
406
|
-
if (
|
|
434
|
+
if (existsSync5(DEVICE_ID_PATH)) {
|
|
407
435
|
const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
|
|
408
436
|
if (id2) return id2;
|
|
409
437
|
}
|
|
410
438
|
} catch {
|
|
411
439
|
}
|
|
412
440
|
const id = randomUUID();
|
|
413
|
-
|
|
441
|
+
mkdirSync2(EXE_AI_DIR, { recursive: true });
|
|
414
442
|
writeFileSync2(DEVICE_ID_PATH, id, "utf8");
|
|
415
443
|
return id;
|
|
416
444
|
}
|
|
417
445
|
function loadLicense() {
|
|
418
446
|
try {
|
|
419
|
-
if (!
|
|
447
|
+
if (!existsSync5(LICENSE_PATH)) return null;
|
|
420
448
|
return readFileSync3(LICENSE_PATH, "utf8").trim();
|
|
421
449
|
} catch {
|
|
422
450
|
return null;
|
|
423
451
|
}
|
|
424
452
|
}
|
|
425
453
|
function saveLicense(apiKey) {
|
|
426
|
-
|
|
454
|
+
mkdirSync2(EXE_AI_DIR, { recursive: true });
|
|
427
455
|
writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
428
456
|
}
|
|
429
457
|
async function verifyLicenseJwt(token) {
|
|
@@ -450,7 +478,7 @@ async function verifyLicenseJwt(token) {
|
|
|
450
478
|
}
|
|
451
479
|
async function getCachedLicense() {
|
|
452
480
|
try {
|
|
453
|
-
if (!
|
|
481
|
+
if (!existsSync5(CACHE_PATH)) return null;
|
|
454
482
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
455
483
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
456
484
|
return await verifyLicenseJwt(raw.token);
|
|
@@ -460,7 +488,7 @@ async function getCachedLicense() {
|
|
|
460
488
|
}
|
|
461
489
|
function readCachedToken() {
|
|
462
490
|
try {
|
|
463
|
-
if (!
|
|
491
|
+
if (!existsSync5(CACHE_PATH)) return null;
|
|
464
492
|
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
465
493
|
return typeof raw.token === "string" ? raw.token : null;
|
|
466
494
|
} catch {
|
|
@@ -499,52 +527,126 @@ function cacheResponse(token) {
|
|
|
499
527
|
} catch {
|
|
500
528
|
}
|
|
501
529
|
}
|
|
502
|
-
|
|
503
|
-
|
|
530
|
+
function loadPrismaForLicense() {
|
|
531
|
+
if (_prismaFailed) return null;
|
|
532
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
533
|
+
if (!dbUrl) {
|
|
534
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path5.join(os5.homedir(), "exe-db");
|
|
535
|
+
if (!existsSync5(path5.join(exeDbRoot, "package.json"))) {
|
|
536
|
+
_prismaFailed = true;
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (!_prismaPromise) {
|
|
541
|
+
_prismaPromise = (async () => {
|
|
542
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
543
|
+
if (explicitPath) {
|
|
544
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
545
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
546
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
547
|
+
return new Ctor2();
|
|
548
|
+
}
|
|
549
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path5.join(os5.homedir(), "exe-db");
|
|
550
|
+
const req = createRequire2(path5.join(exeDbRoot, "package.json"));
|
|
551
|
+
const entry = req.resolve("@prisma/client");
|
|
552
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
553
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
554
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
555
|
+
return new Ctor();
|
|
556
|
+
})().catch((err) => {
|
|
557
|
+
_prismaFailed = true;
|
|
558
|
+
_prismaPromise = null;
|
|
559
|
+
throw err;
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
return _prismaPromise;
|
|
563
|
+
}
|
|
564
|
+
async function validateViaPostgres(apiKey) {
|
|
565
|
+
const loader = loadPrismaForLicense();
|
|
566
|
+
if (!loader) return null;
|
|
567
|
+
try {
|
|
568
|
+
const prisma = await loader;
|
|
569
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
570
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
571
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
572
|
+
apiKey
|
|
573
|
+
);
|
|
574
|
+
if (!rows || rows.length === 0) return null;
|
|
575
|
+
const row = rows[0];
|
|
576
|
+
if (row.status !== "active") return null;
|
|
577
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
578
|
+
const plan = row.plan;
|
|
579
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
580
|
+
return {
|
|
581
|
+
valid: true,
|
|
582
|
+
plan,
|
|
583
|
+
email: row.email,
|
|
584
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
585
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
586
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
587
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
588
|
+
};
|
|
589
|
+
} catch {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
504
594
|
try {
|
|
505
595
|
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
506
596
|
method: "POST",
|
|
507
597
|
headers: { "Content-Type": "application/json" },
|
|
508
|
-
body: JSON.stringify({ apiKey, deviceId
|
|
598
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
509
599
|
signal: AbortSignal.timeout(1e4)
|
|
510
600
|
});
|
|
511
|
-
if (res.ok)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
601
|
+
if (!res.ok) return null;
|
|
602
|
+
const data = await res.json();
|
|
603
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
604
|
+
if (!data.valid) return null;
|
|
605
|
+
if (data.token) {
|
|
606
|
+
cacheResponse(data.token);
|
|
607
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
608
|
+
if (verified) return verified;
|
|
609
|
+
}
|
|
610
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
611
|
+
return {
|
|
612
|
+
valid: data.valid,
|
|
613
|
+
plan: data.plan,
|
|
614
|
+
email: data.email,
|
|
615
|
+
expiresAt: data.expiresAt,
|
|
616
|
+
deviceLimit: limits.devices,
|
|
617
|
+
employeeLimit: limits.employees,
|
|
618
|
+
memoryLimit: limits.memories
|
|
619
|
+
};
|
|
620
|
+
} catch {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function validateLicense(apiKey, deviceId) {
|
|
625
|
+
const did = deviceId ?? loadDeviceId();
|
|
626
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
627
|
+
if (pgResult) {
|
|
628
|
+
try {
|
|
629
|
+
writeFileSync2(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
return pgResult;
|
|
633
|
+
}
|
|
634
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
635
|
+
if (cfResult) return cfResult;
|
|
636
|
+
const cached = await getCachedLicense();
|
|
637
|
+
if (cached) return cached;
|
|
638
|
+
try {
|
|
639
|
+
if (existsSync5(CACHE_PATH)) {
|
|
640
|
+
const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
|
|
641
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
642
|
+
return raw.pgLicense;
|
|
524
643
|
}
|
|
525
|
-
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
526
|
-
return {
|
|
527
|
-
valid: data.valid,
|
|
528
|
-
plan: data.plan,
|
|
529
|
-
email: data.email,
|
|
530
|
-
expiresAt: data.expiresAt,
|
|
531
|
-
deviceLimit: limits.devices,
|
|
532
|
-
employeeLimit: limits.employees,
|
|
533
|
-
memoryLimit: limits.memories
|
|
534
|
-
};
|
|
535
644
|
}
|
|
536
|
-
const cached = await getCachedLicense();
|
|
537
|
-
if (cached) return cached;
|
|
538
|
-
const raw = getRawCachedPlan();
|
|
539
|
-
if (raw) return raw;
|
|
540
|
-
return { ...FREE_LICENSE, valid: false, plan: "free" };
|
|
541
645
|
} catch {
|
|
542
|
-
const cached = await getCachedLicense();
|
|
543
|
-
if (cached) return cached;
|
|
544
|
-
const rawFallback = getRawCachedPlan();
|
|
545
|
-
if (rawFallback) return rawFallback;
|
|
546
|
-
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
547
646
|
}
|
|
647
|
+
const rawFallback = getRawCachedPlan();
|
|
648
|
+
if (rawFallback) return rawFallback;
|
|
649
|
+
return { ...FREE_LICENSE, valid: false };
|
|
548
650
|
}
|
|
549
651
|
function getCacheAgeMs() {
|
|
550
652
|
try {
|
|
@@ -560,7 +662,7 @@ async function checkLicense() {
|
|
|
560
662
|
if (!key) {
|
|
561
663
|
try {
|
|
562
664
|
const configPath = path5.join(EXE_AI_DIR, "config.json");
|
|
563
|
-
if (
|
|
665
|
+
if (existsSync5(configPath)) {
|
|
564
666
|
const raw = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
565
667
|
const cloud = raw.cloud;
|
|
566
668
|
if (cloud?.apiKey) {
|
|
@@ -715,7 +817,7 @@ function stopLicenseRevalidation() {
|
|
|
715
817
|
_revalTimer = null;
|
|
716
818
|
}
|
|
717
819
|
}
|
|
718
|
-
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
820
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
|
|
719
821
|
var init_license = __esm({
|
|
720
822
|
"src/lib/license.ts"() {
|
|
721
823
|
"use strict";
|
|
@@ -746,6 +848,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
746
848
|
employeeLimit: 1,
|
|
747
849
|
memoryLimit: 5e3
|
|
748
850
|
};
|
|
851
|
+
_prismaPromise = null;
|
|
852
|
+
_prismaFailed = false;
|
|
749
853
|
CACHE_MAX_AGE_MS = 36e5;
|
|
750
854
|
_revalTimer = null;
|
|
751
855
|
}
|
|
@@ -753,7 +857,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
753
857
|
|
|
754
858
|
// src/lib/crdt-sync.ts
|
|
755
859
|
import * as Y from "yjs";
|
|
756
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as
|
|
860
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
757
861
|
import path6 from "path";
|
|
758
862
|
import { homedir } from "os";
|
|
759
863
|
var DEFAULT_STATE_PATH;
|
|
@@ -801,7 +905,7 @@ function isMainModule(importMetaUrl) {
|
|
|
801
905
|
|
|
802
906
|
// src/lib/cloud-sync.ts
|
|
803
907
|
init_database();
|
|
804
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as
|
|
908
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync3, openSync, closeSync } from "fs";
|
|
805
909
|
import crypto3 from "crypto";
|
|
806
910
|
import path7 from "path";
|
|
807
911
|
import { homedir as homedir2 } from "os";
|
|
@@ -817,6 +921,7 @@ init_license();
|
|
|
817
921
|
init_config();
|
|
818
922
|
init_crdt_sync();
|
|
819
923
|
init_employees();
|
|
924
|
+
init_secure_files();
|
|
820
925
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
821
926
|
var ROSTER_LOCK_PATH = path7.join(EXE_AI_DIR, "roster-merge.lock");
|
|
822
927
|
function assertSecureEndpoint(endpoint) {
|