@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/lib/config.js
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
// src/lib/config.ts
|
|
2
|
-
import { readFile, writeFile
|
|
3
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
2
|
+
import { readFile, writeFile } from "fs/promises";
|
|
3
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import os from "os";
|
|
6
|
+
|
|
7
|
+
// src/lib/secure-files.ts
|
|
8
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
9
|
+
import { chmod, mkdir } from "fs/promises";
|
|
10
|
+
var PRIVATE_DIR_MODE = 448;
|
|
11
|
+
var PRIVATE_FILE_MODE = 384;
|
|
12
|
+
async function ensurePrivateDir(dirPath) {
|
|
13
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
14
|
+
try {
|
|
15
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function enforcePrivateFile(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/lib/config.ts
|
|
6
27
|
function resolveDataDir() {
|
|
7
28
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
8
29
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
9
30
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
10
31
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
11
|
-
if (!
|
|
32
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
12
33
|
try {
|
|
13
34
|
renameSync(legacyDir, newDir);
|
|
14
35
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -144,9 +165,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
144
165
|
}
|
|
145
166
|
async function loadConfig() {
|
|
146
167
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
147
|
-
await
|
|
168
|
+
await ensurePrivateDir(dir);
|
|
148
169
|
const configPath = path.join(dir, "config.json");
|
|
149
|
-
if (!
|
|
170
|
+
if (!existsSync2(configPath)) {
|
|
150
171
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
151
172
|
}
|
|
152
173
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -159,6 +180,7 @@ async function loadConfig() {
|
|
|
159
180
|
`);
|
|
160
181
|
try {
|
|
161
182
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
183
|
+
await enforcePrivateFile(configPath);
|
|
162
184
|
} catch {
|
|
163
185
|
}
|
|
164
186
|
}
|
|
@@ -177,7 +199,7 @@ async function loadConfig() {
|
|
|
177
199
|
function loadConfigSync() {
|
|
178
200
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
179
201
|
const configPath = path.join(dir, "config.json");
|
|
180
|
-
if (!
|
|
202
|
+
if (!existsSync2(configPath)) {
|
|
181
203
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
182
204
|
}
|
|
183
205
|
try {
|
|
@@ -195,12 +217,10 @@ function loadConfigSync() {
|
|
|
195
217
|
}
|
|
196
218
|
async function saveConfig(config) {
|
|
197
219
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
198
|
-
await
|
|
220
|
+
await ensurePrivateDir(dir);
|
|
199
221
|
const configPath = path.join(dir, "config.json");
|
|
200
222
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
201
|
-
|
|
202
|
-
await chmod(configPath, 384);
|
|
203
|
-
}
|
|
223
|
+
await enforcePrivateFile(configPath);
|
|
204
224
|
}
|
|
205
225
|
async function loadConfigFrom(configPath) {
|
|
206
226
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -10,9 +10,18 @@ var init_db_retry = __esm({
|
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
// src/lib/secure-files.ts
|
|
14
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
15
|
+
import { chmod, mkdir } from "fs/promises";
|
|
16
|
+
var init_secure_files = __esm({
|
|
17
|
+
"src/lib/secure-files.ts"() {
|
|
18
|
+
"use strict";
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
13
22
|
// src/lib/config.ts
|
|
14
|
-
import { readFile, writeFile
|
|
15
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
23
|
+
import { readFile, writeFile } from "fs/promises";
|
|
24
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
16
25
|
import path from "path";
|
|
17
26
|
import os from "os";
|
|
18
27
|
function resolveDataDir() {
|
|
@@ -20,7 +29,7 @@ function resolveDataDir() {
|
|
|
20
29
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
21
30
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
22
31
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
23
|
-
if (!
|
|
32
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
24
33
|
try {
|
|
25
34
|
renameSync(legacyDir, newDir);
|
|
26
35
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -35,6 +44,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
35
44
|
var init_config = __esm({
|
|
36
45
|
"src/lib/config.ts"() {
|
|
37
46
|
"use strict";
|
|
47
|
+
init_secure_files();
|
|
38
48
|
EXE_AI_DIR = resolveDataDir();
|
|
39
49
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
40
50
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -103,7 +113,7 @@ var init_config = __esm({
|
|
|
103
113
|
|
|
104
114
|
// src/lib/employees.ts
|
|
105
115
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
106
|
-
import { existsSync as
|
|
116
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
107
117
|
import { execSync } from "child_process";
|
|
108
118
|
import path2 from "path";
|
|
109
119
|
import os2 from "os";
|
|
@@ -124,7 +134,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
124
134
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
125
135
|
}
|
|
126
136
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
127
|
-
if (!
|
|
137
|
+
if (!existsSync3(employeesPath)) return [];
|
|
128
138
|
try {
|
|
129
139
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
130
140
|
} catch {
|
|
@@ -184,7 +194,7 @@ init_database();
|
|
|
184
194
|
|
|
185
195
|
// src/lib/keychain.ts
|
|
186
196
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
187
|
-
import { existsSync as
|
|
197
|
+
import { existsSync as existsSync4 } from "fs";
|
|
188
198
|
import path4 from "path";
|
|
189
199
|
import os4 from "os";
|
|
190
200
|
|
package/dist/lib/database.js
CHANGED
|
@@ -8,9 +8,34 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// src/lib/secure-files.ts
|
|
12
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
13
|
+
import { chmod, mkdir } from "fs/promises";
|
|
14
|
+
function ensurePrivateDirSync(dirPath) {
|
|
15
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
16
|
+
try {
|
|
17
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function enforcePrivateFileSync(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
28
|
+
var init_secure_files = __esm({
|
|
29
|
+
"src/lib/secure-files.ts"() {
|
|
30
|
+
"use strict";
|
|
31
|
+
PRIVATE_DIR_MODE = 448;
|
|
32
|
+
PRIVATE_FILE_MODE = 384;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
11
36
|
// src/lib/config.ts
|
|
12
|
-
import { readFile, writeFile
|
|
13
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
37
|
+
import { readFile, writeFile } from "fs/promises";
|
|
38
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
14
39
|
import path from "path";
|
|
15
40
|
import os from "os";
|
|
16
41
|
function resolveDataDir() {
|
|
@@ -18,7 +43,7 @@ function resolveDataDir() {
|
|
|
18
43
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
19
44
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
20
45
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
21
|
-
if (!
|
|
46
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
22
47
|
try {
|
|
23
48
|
renameSync(legacyDir, newDir);
|
|
24
49
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -33,6 +58,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
33
58
|
var init_config = __esm({
|
|
34
59
|
"src/lib/config.ts"() {
|
|
35
60
|
"use strict";
|
|
61
|
+
init_secure_files();
|
|
36
62
|
EXE_AI_DIR = resolveDataDir();
|
|
37
63
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
38
64
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -99,13 +125,50 @@ var init_config = __esm({
|
|
|
99
125
|
}
|
|
100
126
|
});
|
|
101
127
|
|
|
128
|
+
// src/lib/daemon-auth.ts
|
|
129
|
+
import crypto from "crypto";
|
|
130
|
+
import path4 from "path";
|
|
131
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
132
|
+
function normalizeToken(token) {
|
|
133
|
+
if (!token) return null;
|
|
134
|
+
const trimmed = token.trim();
|
|
135
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
136
|
+
}
|
|
137
|
+
function readDaemonToken() {
|
|
138
|
+
try {
|
|
139
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
140
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function ensureDaemonToken(seed) {
|
|
146
|
+
const existing = readDaemonToken();
|
|
147
|
+
if (existing) return existing;
|
|
148
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
149
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
150
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
151
|
+
`, "utf8");
|
|
152
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
153
|
+
return token;
|
|
154
|
+
}
|
|
155
|
+
var DAEMON_TOKEN_PATH;
|
|
156
|
+
var init_daemon_auth = __esm({
|
|
157
|
+
"src/lib/daemon-auth.ts"() {
|
|
158
|
+
"use strict";
|
|
159
|
+
init_config();
|
|
160
|
+
init_secure_files();
|
|
161
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
102
165
|
// src/lib/exe-daemon-client.ts
|
|
103
166
|
import net from "net";
|
|
104
167
|
import os4 from "os";
|
|
105
168
|
import { spawn } from "child_process";
|
|
106
169
|
import { randomUUID } from "crypto";
|
|
107
|
-
import { existsSync as
|
|
108
|
-
import
|
|
170
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
171
|
+
import path5 from "path";
|
|
109
172
|
import { fileURLToPath } from "url";
|
|
110
173
|
function handleData(chunk) {
|
|
111
174
|
_buffer += chunk.toString();
|
|
@@ -133,9 +196,9 @@ function handleData(chunk) {
|
|
|
133
196
|
}
|
|
134
197
|
}
|
|
135
198
|
function cleanupStaleFiles() {
|
|
136
|
-
if (
|
|
199
|
+
if (existsSync5(PID_PATH)) {
|
|
137
200
|
try {
|
|
138
|
-
const pid = parseInt(
|
|
201
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
139
202
|
if (pid > 0) {
|
|
140
203
|
try {
|
|
141
204
|
process.kill(pid, 0);
|
|
@@ -156,11 +219,11 @@ function cleanupStaleFiles() {
|
|
|
156
219
|
}
|
|
157
220
|
}
|
|
158
221
|
function findPackageRoot() {
|
|
159
|
-
let dir =
|
|
160
|
-
const { root } =
|
|
222
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
223
|
+
const { root } = path5.parse(dir);
|
|
161
224
|
while (dir !== root) {
|
|
162
|
-
if (
|
|
163
|
-
dir =
|
|
225
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
226
|
+
dir = path5.dirname(dir);
|
|
164
227
|
}
|
|
165
228
|
return null;
|
|
166
229
|
}
|
|
@@ -186,16 +249,17 @@ function spawnDaemon() {
|
|
|
186
249
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
187
250
|
return;
|
|
188
251
|
}
|
|
189
|
-
const daemonPath =
|
|
190
|
-
if (!
|
|
252
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
253
|
+
if (!existsSync5(daemonPath)) {
|
|
191
254
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
192
255
|
`);
|
|
193
256
|
return;
|
|
194
257
|
}
|
|
195
258
|
const resolvedPath = daemonPath;
|
|
259
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
196
260
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
197
261
|
`);
|
|
198
|
-
const logPath =
|
|
262
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
199
263
|
let stderrFd = "ignore";
|
|
200
264
|
try {
|
|
201
265
|
stderrFd = openSync(logPath, "a");
|
|
@@ -213,7 +277,8 @@ function spawnDaemon() {
|
|
|
213
277
|
TMUX_PANE: void 0,
|
|
214
278
|
// Prevents resolveExeSession() from scoping to one session
|
|
215
279
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
216
|
-
EXE_DAEMON_PID: PID_PATH
|
|
280
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
281
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
217
282
|
}
|
|
218
283
|
});
|
|
219
284
|
child.unref();
|
|
@@ -320,13 +385,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
320
385
|
return;
|
|
321
386
|
}
|
|
322
387
|
const id = randomUUID();
|
|
388
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
323
389
|
const timer = setTimeout(() => {
|
|
324
390
|
_pending.delete(id);
|
|
325
391
|
resolve({ error: "Request timeout" });
|
|
326
392
|
}, timeoutMs);
|
|
327
393
|
_pending.set(id, { resolve, timer });
|
|
328
394
|
try {
|
|
329
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
395
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
330
396
|
} catch {
|
|
331
397
|
clearTimeout(timer);
|
|
332
398
|
_pending.delete(id);
|
|
@@ -337,17 +403,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
337
403
|
function isClientConnected() {
|
|
338
404
|
return _connected;
|
|
339
405
|
}
|
|
340
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
406
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
341
407
|
var init_exe_daemon_client = __esm({
|
|
342
408
|
"src/lib/exe-daemon-client.ts"() {
|
|
343
409
|
"use strict";
|
|
344
410
|
init_config();
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
411
|
+
init_daemon_auth();
|
|
412
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
413
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
414
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
348
415
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
349
416
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
350
417
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
418
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
351
419
|
_socket = null;
|
|
352
420
|
_connected = false;
|
|
353
421
|
_buffer = "";
|
|
@@ -610,7 +678,7 @@ function wrapWithRetry(client) {
|
|
|
610
678
|
// src/lib/employees.ts
|
|
611
679
|
init_config();
|
|
612
680
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
613
|
-
import { existsSync as
|
|
681
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
614
682
|
import { execSync } from "child_process";
|
|
615
683
|
import path2 from "path";
|
|
616
684
|
import os2 from "os";
|
|
@@ -630,7 +698,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
|
630
698
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
631
699
|
}
|
|
632
700
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
633
|
-
if (!
|
|
701
|
+
if (!existsSync3(employeesPath)) return [];
|
|
634
702
|
try {
|
|
635
703
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
636
704
|
} catch {
|
|
@@ -1581,6 +1649,7 @@ async function ensureSchema() {
|
|
|
1581
1649
|
project TEXT NOT NULL,
|
|
1582
1650
|
summary TEXT NOT NULL,
|
|
1583
1651
|
task_file TEXT,
|
|
1652
|
+
session_scope TEXT,
|
|
1584
1653
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1585
1654
|
created_at TEXT NOT NULL
|
|
1586
1655
|
);
|
|
@@ -1589,7 +1658,7 @@ async function ensureSchema() {
|
|
|
1589
1658
|
ON notifications(read);
|
|
1590
1659
|
|
|
1591
1660
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1592
|
-
ON notifications(agent_id);
|
|
1661
|
+
ON notifications(agent_id, session_scope);
|
|
1593
1662
|
|
|
1594
1663
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1595
1664
|
ON notifications(task_file);
|
|
@@ -1627,6 +1696,7 @@ async function ensureSchema() {
|
|
|
1627
1696
|
target_agent TEXT NOT NULL,
|
|
1628
1697
|
target_project TEXT,
|
|
1629
1698
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1699
|
+
session_scope TEXT,
|
|
1630
1700
|
content TEXT NOT NULL,
|
|
1631
1701
|
priority TEXT DEFAULT 'normal',
|
|
1632
1702
|
status TEXT DEFAULT 'pending',
|
|
@@ -1640,10 +1710,31 @@ async function ensureSchema() {
|
|
|
1640
1710
|
);
|
|
1641
1711
|
|
|
1642
1712
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1643
|
-
ON messages(target_agent, status);
|
|
1713
|
+
ON messages(target_agent, session_scope, status);
|
|
1644
1714
|
|
|
1645
1715
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1646
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1716
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1717
|
+
`);
|
|
1718
|
+
try {
|
|
1719
|
+
await client.execute({
|
|
1720
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1721
|
+
args: []
|
|
1722
|
+
});
|
|
1723
|
+
} catch {
|
|
1724
|
+
}
|
|
1725
|
+
try {
|
|
1726
|
+
await client.execute({
|
|
1727
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1728
|
+
args: []
|
|
1729
|
+
});
|
|
1730
|
+
} catch {
|
|
1731
|
+
}
|
|
1732
|
+
await client.executeMultiple(`
|
|
1733
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1734
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1735
|
+
|
|
1736
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1737
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1647
1738
|
`);
|
|
1648
1739
|
try {
|
|
1649
1740
|
await client.execute({
|
|
@@ -2227,6 +2318,13 @@ async function ensureSchema() {
|
|
|
2227
2318
|
} catch {
|
|
2228
2319
|
}
|
|
2229
2320
|
}
|
|
2321
|
+
try {
|
|
2322
|
+
await client.execute({
|
|
2323
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2324
|
+
args: []
|
|
2325
|
+
});
|
|
2326
|
+
} catch {
|
|
2327
|
+
}
|
|
2230
2328
|
}
|
|
2231
2329
|
var disposeTurso = disposeDatabase;
|
|
2232
2330
|
async function disposeDatabase() {
|
|
@@ -3,21 +3,42 @@ import net from "net";
|
|
|
3
3
|
import os2 from "os";
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
|
-
import { existsSync as
|
|
7
|
-
import
|
|
6
|
+
import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
7
|
+
import path3 from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// src/lib/config.ts
|
|
11
|
-
import { readFile, writeFile
|
|
12
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
11
|
+
import { readFile, writeFile } from "fs/promises";
|
|
12
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
13
13
|
import path from "path";
|
|
14
14
|
import os from "os";
|
|
15
|
+
|
|
16
|
+
// src/lib/secure-files.ts
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
18
|
+
import { chmod, mkdir } from "fs/promises";
|
|
19
|
+
var PRIVATE_DIR_MODE = 448;
|
|
20
|
+
var PRIVATE_FILE_MODE = 384;
|
|
21
|
+
function ensurePrivateDirSync(dirPath) {
|
|
22
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
23
|
+
try {
|
|
24
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function enforcePrivateFileSync(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/lib/config.ts
|
|
15
36
|
function resolveDataDir() {
|
|
16
37
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
17
38
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
18
39
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
19
40
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
20
|
-
if (!
|
|
41
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
21
42
|
try {
|
|
22
43
|
renameSync(legacyDir, newDir);
|
|
23
44
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -92,13 +113,43 @@ var DEFAULT_CONFIG = {
|
|
|
92
113
|
}
|
|
93
114
|
};
|
|
94
115
|
|
|
116
|
+
// src/lib/daemon-auth.ts
|
|
117
|
+
import crypto from "crypto";
|
|
118
|
+
import path2 from "path";
|
|
119
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
120
|
+
var DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
|
|
121
|
+
function normalizeToken(token) {
|
|
122
|
+
if (!token) return null;
|
|
123
|
+
const trimmed = token.trim();
|
|
124
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
125
|
+
}
|
|
126
|
+
function readDaemonToken() {
|
|
127
|
+
try {
|
|
128
|
+
if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
|
|
129
|
+
return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function ensureDaemonToken(seed) {
|
|
135
|
+
const existing = readDaemonToken();
|
|
136
|
+
if (existing) return existing;
|
|
137
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
138
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
139
|
+
writeFileSync(DAEMON_TOKEN_PATH, `${token}
|
|
140
|
+
`, "utf8");
|
|
141
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
142
|
+
return token;
|
|
143
|
+
}
|
|
144
|
+
|
|
95
145
|
// src/lib/exe-daemon-client.ts
|
|
96
|
-
var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
97
|
-
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
98
|
-
var SPAWN_LOCK_PATH =
|
|
146
|
+
var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
147
|
+
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
148
|
+
var SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
99
149
|
var SPAWN_LOCK_STALE_MS = 3e4;
|
|
100
150
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
101
151
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
152
|
+
var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
102
153
|
var _socket = null;
|
|
103
154
|
var _connected = false;
|
|
104
155
|
var _buffer = "";
|
|
@@ -130,9 +181,9 @@ function handleData(chunk) {
|
|
|
130
181
|
}
|
|
131
182
|
}
|
|
132
183
|
function cleanupStaleFiles() {
|
|
133
|
-
if (
|
|
184
|
+
if (existsSync4(PID_PATH)) {
|
|
134
185
|
try {
|
|
135
|
-
const pid = parseInt(
|
|
186
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
136
187
|
if (pid > 0) {
|
|
137
188
|
try {
|
|
138
189
|
process.kill(pid, 0);
|
|
@@ -153,11 +204,11 @@ function cleanupStaleFiles() {
|
|
|
153
204
|
}
|
|
154
205
|
}
|
|
155
206
|
function findPackageRoot() {
|
|
156
|
-
let dir =
|
|
157
|
-
const { root } =
|
|
207
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
208
|
+
const { root } = path3.parse(dir);
|
|
158
209
|
while (dir !== root) {
|
|
159
|
-
if (
|
|
160
|
-
dir =
|
|
210
|
+
if (existsSync4(path3.join(dir, "package.json"))) return dir;
|
|
211
|
+
dir = path3.dirname(dir);
|
|
161
212
|
}
|
|
162
213
|
return null;
|
|
163
214
|
}
|
|
@@ -183,16 +234,17 @@ function spawnDaemon() {
|
|
|
183
234
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
184
235
|
return;
|
|
185
236
|
}
|
|
186
|
-
const daemonPath =
|
|
187
|
-
if (!
|
|
237
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
238
|
+
if (!existsSync4(daemonPath)) {
|
|
188
239
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
189
240
|
`);
|
|
190
241
|
return;
|
|
191
242
|
}
|
|
192
243
|
const resolvedPath = daemonPath;
|
|
244
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
193
245
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
194
246
|
`);
|
|
195
|
-
const logPath =
|
|
247
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
196
248
|
let stderrFd = "ignore";
|
|
197
249
|
try {
|
|
198
250
|
stderrFd = openSync(logPath, "a");
|
|
@@ -210,7 +262,8 @@ function spawnDaemon() {
|
|
|
210
262
|
TMUX_PANE: void 0,
|
|
211
263
|
// Prevents resolveExeSession() from scoping to one session
|
|
212
264
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
213
|
-
EXE_DAEMON_PID: PID_PATH
|
|
265
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
266
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
214
267
|
}
|
|
215
268
|
});
|
|
216
269
|
child.unref();
|
|
@@ -317,13 +370,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
317
370
|
return;
|
|
318
371
|
}
|
|
319
372
|
const id = randomUUID();
|
|
373
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
320
374
|
const timer = setTimeout(() => {
|
|
321
375
|
_pending.delete(id);
|
|
322
376
|
resolve({ error: "Request timeout" });
|
|
323
377
|
}, timeoutMs);
|
|
324
378
|
_pending.set(id, { resolve, timer });
|
|
325
379
|
try {
|
|
326
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
380
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
327
381
|
} catch {
|
|
328
382
|
clearTimeout(timer);
|
|
329
383
|
_pending.delete(id);
|