@askexenow/exe-os 0.9.30 → 0.9.32
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 +135 -7
- package/dist/bin/backfill-responses.js +135 -7
- package/dist/bin/backfill-vectors.js +135 -7
- package/dist/bin/cleanup-stale-review-tasks.js +139 -11
- package/dist/bin/cli.js +812 -486
- package/dist/bin/exe-assign.js +135 -7
- package/dist/bin/exe-boot.js +422 -113
- package/dist/bin/exe-cloud.js +160 -9
- package/dist/bin/exe-dispatch.js +136 -8
- package/dist/bin/exe-doctor.js +255 -13
- package/dist/bin/exe-export-behaviors.js +136 -8
- package/dist/bin/exe-forget.js +136 -8
- package/dist/bin/exe-gateway.js +171 -24
- package/dist/bin/exe-heartbeat.js +141 -13
- package/dist/bin/exe-kill.js +140 -12
- package/dist/bin/exe-launch-agent.js +143 -15
- package/dist/bin/exe-link.js +357 -48
- package/dist/bin/exe-pending-messages.js +136 -8
- package/dist/bin/exe-pending-notifications.js +136 -8
- package/dist/bin/exe-pending-reviews.js +138 -10
- package/dist/bin/exe-review.js +136 -8
- package/dist/bin/exe-search.js +155 -20
- package/dist/bin/exe-session-cleanup.js +166 -38
- package/dist/bin/exe-start-codex.js +142 -14
- package/dist/bin/exe-start-opencode.js +140 -12
- package/dist/bin/exe-status.js +148 -20
- package/dist/bin/exe-team.js +136 -8
- package/dist/bin/git-sweep.js +138 -10
- package/dist/bin/graph-backfill.js +135 -7
- package/dist/bin/graph-export.js +136 -8
- package/dist/bin/intercom-check.js +153 -25
- package/dist/bin/scan-tasks.js +138 -10
- package/dist/bin/setup.js +447 -121
- package/dist/bin/shard-migrate.js +135 -7
- package/dist/gateway/index.js +151 -23
- package/dist/hooks/bug-report-worker.js +151 -23
- package/dist/hooks/codex-stop-task-finalizer.js +145 -17
- package/dist/hooks/commit-complete.js +138 -10
- package/dist/hooks/error-recall.js +159 -24
- package/dist/hooks/ingest.js +142 -14
- package/dist/hooks/instructions-loaded.js +136 -8
- package/dist/hooks/notification.js +136 -8
- package/dist/hooks/post-compact.js +136 -8
- package/dist/hooks/post-tool-combined.js +159 -24
- package/dist/hooks/pre-compact.js +136 -8
- package/dist/hooks/pre-tool-use.js +144 -16
- package/dist/hooks/prompt-submit.js +195 -55
- package/dist/hooks/session-end.js +141 -13
- package/dist/hooks/session-start.js +165 -30
- package/dist/hooks/stop.js +136 -8
- package/dist/hooks/subagent-stop.js +136 -8
- package/dist/hooks/summary-worker.js +374 -65
- package/dist/index.js +136 -8
- package/dist/lib/cloud-sync.js +355 -46
- package/dist/lib/consolidation.js +1 -0
- package/dist/lib/exe-daemon.js +469 -127
- package/dist/lib/hybrid-search.js +155 -20
- package/dist/lib/keychain.js +191 -7
- package/dist/lib/schedules.js +138 -10
- package/dist/lib/store.js +135 -7
- package/dist/mcp/server.js +706 -213
- package/dist/runtime/index.js +136 -8
- package/dist/tui/App.js +208 -31
- package/package.json +1 -1
package/dist/bin/exe-boot.js
CHANGED
|
@@ -1172,8 +1172,8 @@ function findPackageRoot() {
|
|
|
1172
1172
|
function getAvailableMemoryGB() {
|
|
1173
1173
|
if (process.platform === "darwin") {
|
|
1174
1174
|
try {
|
|
1175
|
-
const { execSync:
|
|
1176
|
-
const vmstat =
|
|
1175
|
+
const { execSync: execSync11 } = __require("child_process");
|
|
1176
|
+
const vmstat = execSync11("vm_stat", { encoding: "utf8" });
|
|
1177
1177
|
const pageSize = 16384;
|
|
1178
1178
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1179
1179
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -3114,6 +3114,7 @@ __export(keychain_exports, {
|
|
|
3114
3114
|
});
|
|
3115
3115
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3116
3116
|
import { existsSync as existsSync6 } from "fs";
|
|
3117
|
+
import { execSync as execSync2 } from "child_process";
|
|
3117
3118
|
import path6 from "path";
|
|
3118
3119
|
import os5 from "os";
|
|
3119
3120
|
function getKeyDir() {
|
|
@@ -3122,6 +3123,83 @@ function getKeyDir() {
|
|
|
3122
3123
|
function getKeyPath() {
|
|
3123
3124
|
return path6.join(getKeyDir(), "master.key");
|
|
3124
3125
|
}
|
|
3126
|
+
function macKeychainGet() {
|
|
3127
|
+
if (process.platform !== "darwin") return null;
|
|
3128
|
+
try {
|
|
3129
|
+
return execSync2(
|
|
3130
|
+
`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3131
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3132
|
+
).trim();
|
|
3133
|
+
} catch {
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
function macKeychainSet(value) {
|
|
3138
|
+
if (process.platform !== "darwin") return false;
|
|
3139
|
+
try {
|
|
3140
|
+
try {
|
|
3141
|
+
execSync2(
|
|
3142
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3143
|
+
{ timeout: 5e3 }
|
|
3144
|
+
);
|
|
3145
|
+
} catch {
|
|
3146
|
+
}
|
|
3147
|
+
execSync2(
|
|
3148
|
+
`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3149
|
+
{ timeout: 5e3 }
|
|
3150
|
+
);
|
|
3151
|
+
return true;
|
|
3152
|
+
} catch {
|
|
3153
|
+
return false;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
function macKeychainDelete() {
|
|
3157
|
+
if (process.platform !== "darwin") return false;
|
|
3158
|
+
try {
|
|
3159
|
+
execSync2(
|
|
3160
|
+
`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3161
|
+
{ timeout: 5e3 }
|
|
3162
|
+
);
|
|
3163
|
+
return true;
|
|
3164
|
+
} catch {
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
function linuxSecretGet() {
|
|
3169
|
+
if (process.platform !== "linux") return null;
|
|
3170
|
+
try {
|
|
3171
|
+
return execSync2(
|
|
3172
|
+
`secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3173
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3174
|
+
).trim();
|
|
3175
|
+
} catch {
|
|
3176
|
+
return null;
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
function linuxSecretSet(value) {
|
|
3180
|
+
if (process.platform !== "linux") return false;
|
|
3181
|
+
try {
|
|
3182
|
+
execSync2(
|
|
3183
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
|
|
3184
|
+
{ timeout: 5e3 }
|
|
3185
|
+
);
|
|
3186
|
+
return true;
|
|
3187
|
+
} catch {
|
|
3188
|
+
return false;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
function linuxSecretDelete() {
|
|
3192
|
+
if (process.platform !== "linux") return false;
|
|
3193
|
+
try {
|
|
3194
|
+
execSync2(
|
|
3195
|
+
`secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3196
|
+
{ timeout: 5e3 }
|
|
3197
|
+
);
|
|
3198
|
+
return true;
|
|
3199
|
+
} catch {
|
|
3200
|
+
return false;
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3125
3203
|
async function tryKeytar() {
|
|
3126
3204
|
try {
|
|
3127
3205
|
return await import("keytar");
|
|
@@ -3129,13 +3207,72 @@ async function tryKeytar() {
|
|
|
3129
3207
|
return null;
|
|
3130
3208
|
}
|
|
3131
3209
|
}
|
|
3210
|
+
function deriveMachineKey() {
|
|
3211
|
+
try {
|
|
3212
|
+
const crypto10 = __require("crypto");
|
|
3213
|
+
const material = [
|
|
3214
|
+
os5.hostname(),
|
|
3215
|
+
os5.userInfo().username,
|
|
3216
|
+
os5.arch(),
|
|
3217
|
+
os5.platform(),
|
|
3218
|
+
// Machine ID on Linux (stable across reboots)
|
|
3219
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3220
|
+
].join("|");
|
|
3221
|
+
return crypto10.createHash("sha256").update(material).digest();
|
|
3222
|
+
} catch {
|
|
3223
|
+
return null;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
function readMachineId() {
|
|
3227
|
+
try {
|
|
3228
|
+
const { readFileSync: readFileSync17 } = __require("fs");
|
|
3229
|
+
return readFileSync17("/etc/machine-id", "utf-8").trim();
|
|
3230
|
+
} catch {
|
|
3231
|
+
return "";
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3235
|
+
const crypto10 = __require("crypto");
|
|
3236
|
+
const iv = crypto10.randomBytes(12);
|
|
3237
|
+
const cipher = crypto10.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3238
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3239
|
+
encrypted += cipher.final("base64");
|
|
3240
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3241
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3242
|
+
}
|
|
3243
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3244
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3245
|
+
try {
|
|
3246
|
+
const crypto10 = __require("crypto");
|
|
3247
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3248
|
+
if (parts.length !== 3) return null;
|
|
3249
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3250
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3251
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3252
|
+
const decipher = crypto10.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3253
|
+
decipher.setAuthTag(authTag);
|
|
3254
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3255
|
+
decrypted += decipher.final("utf-8");
|
|
3256
|
+
return decrypted;
|
|
3257
|
+
} catch {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3132
3261
|
async function getMasterKey() {
|
|
3262
|
+
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3263
|
+
if (nativeValue) {
|
|
3264
|
+
return Buffer.from(nativeValue, "base64");
|
|
3265
|
+
}
|
|
3133
3266
|
const keytar = await tryKeytar();
|
|
3134
3267
|
if (keytar) {
|
|
3135
3268
|
try {
|
|
3136
|
-
const
|
|
3137
|
-
if (
|
|
3138
|
-
|
|
3269
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3270
|
+
if (keytarValue) {
|
|
3271
|
+
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
3272
|
+
if (migrated) {
|
|
3273
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3274
|
+
}
|
|
3275
|
+
return Buffer.from(keytarValue, "base64");
|
|
3139
3276
|
}
|
|
3140
3277
|
} catch {
|
|
3141
3278
|
}
|
|
@@ -3149,8 +3286,31 @@ async function getMasterKey() {
|
|
|
3149
3286
|
return null;
|
|
3150
3287
|
}
|
|
3151
3288
|
try {
|
|
3152
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
3153
|
-
|
|
3289
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3290
|
+
let b64Value;
|
|
3291
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3292
|
+
const machineKey = deriveMachineKey();
|
|
3293
|
+
if (!machineKey) {
|
|
3294
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3295
|
+
return null;
|
|
3296
|
+
}
|
|
3297
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3298
|
+
if (!decrypted) {
|
|
3299
|
+
process.stderr.write(
|
|
3300
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
|
|
3301
|
+
);
|
|
3302
|
+
return null;
|
|
3303
|
+
}
|
|
3304
|
+
b64Value = decrypted;
|
|
3305
|
+
} else {
|
|
3306
|
+
b64Value = content;
|
|
3307
|
+
}
|
|
3308
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3309
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3310
|
+
if (migrated) {
|
|
3311
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3312
|
+
}
|
|
3313
|
+
return key;
|
|
3154
3314
|
} catch (err) {
|
|
3155
3315
|
process.stderr.write(
|
|
3156
3316
|
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3161,6 +3321,9 @@ async function getMasterKey() {
|
|
|
3161
3321
|
}
|
|
3162
3322
|
async function setMasterKey(key) {
|
|
3163
3323
|
const b64 = key.toString("base64");
|
|
3324
|
+
if (macKeychainSet(b64) || linuxSecretSet(b64)) {
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3164
3327
|
const keytar = await tryKeytar();
|
|
3165
3328
|
if (keytar) {
|
|
3166
3329
|
try {
|
|
@@ -3172,10 +3335,23 @@ async function setMasterKey(key) {
|
|
|
3172
3335
|
const dir = getKeyDir();
|
|
3173
3336
|
await mkdir3(dir, { recursive: true });
|
|
3174
3337
|
const keyPath = getKeyPath();
|
|
3175
|
-
|
|
3176
|
-
|
|
3338
|
+
const machineKey = deriveMachineKey();
|
|
3339
|
+
if (machineKey) {
|
|
3340
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3341
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3342
|
+
await chmod2(keyPath, 384);
|
|
3343
|
+
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
3344
|
+
} else {
|
|
3345
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3346
|
+
await chmod2(keyPath, 384);
|
|
3347
|
+
process.stderr.write(
|
|
3348
|
+
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3177
3351
|
}
|
|
3178
3352
|
async function deleteMasterKey() {
|
|
3353
|
+
macKeychainDelete();
|
|
3354
|
+
linuxSecretDelete();
|
|
3179
3355
|
const keytar = await tryKeytar();
|
|
3180
3356
|
if (keytar) {
|
|
3181
3357
|
try {
|
|
@@ -3217,12 +3393,13 @@ async function importMnemonic(mnemonic) {
|
|
|
3217
3393
|
const entropy = mnemonicToEntropy(trimmed);
|
|
3218
3394
|
return Buffer.from(entropy, "hex");
|
|
3219
3395
|
}
|
|
3220
|
-
var SERVICE, ACCOUNT;
|
|
3396
|
+
var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
|
|
3221
3397
|
var init_keychain = __esm({
|
|
3222
3398
|
"src/lib/keychain.ts"() {
|
|
3223
3399
|
"use strict";
|
|
3224
3400
|
SERVICE = "exe-mem";
|
|
3225
3401
|
ACCOUNT = "master-key";
|
|
3402
|
+
ENCRYPTED_PREFIX = "enc:";
|
|
3226
3403
|
}
|
|
3227
3404
|
});
|
|
3228
3405
|
|
|
@@ -3709,7 +3886,7 @@ __export(session_registry_exports, {
|
|
|
3709
3886
|
registerSession: () => registerSession
|
|
3710
3887
|
});
|
|
3711
3888
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
3712
|
-
import { execSync as
|
|
3889
|
+
import { execSync as execSync3 } from "child_process";
|
|
3713
3890
|
import path8 from "path";
|
|
3714
3891
|
import os6 from "os";
|
|
3715
3892
|
function registerSession(entry) {
|
|
@@ -3749,7 +3926,7 @@ function pruneStaleSessions() {
|
|
|
3749
3926
|
if (sessions.length === 0) return 0;
|
|
3750
3927
|
let liveSessions = [];
|
|
3751
3928
|
try {
|
|
3752
|
-
liveSessions =
|
|
3929
|
+
liveSessions = execSync3("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
3753
3930
|
encoding: "utf8"
|
|
3754
3931
|
}).trim().split("\n").filter(Boolean);
|
|
3755
3932
|
} catch {
|
|
@@ -3772,7 +3949,7 @@ var init_session_registry = __esm({
|
|
|
3772
3949
|
});
|
|
3773
3950
|
|
|
3774
3951
|
// src/lib/session-key.ts
|
|
3775
|
-
import { execSync as
|
|
3952
|
+
import { execSync as execSync4 } from "child_process";
|
|
3776
3953
|
function normalizeCommand(command) {
|
|
3777
3954
|
const trimmed = command.trim().toLowerCase();
|
|
3778
3955
|
const parts = trimmed.split(/[\\/]/);
|
|
@@ -3791,7 +3968,7 @@ function resolveRuntimeProcess() {
|
|
|
3791
3968
|
let pid = process.ppid;
|
|
3792
3969
|
for (let i = 0; i < 10; i++) {
|
|
3793
3970
|
try {
|
|
3794
|
-
const info =
|
|
3971
|
+
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
3795
3972
|
encoding: "utf8",
|
|
3796
3973
|
timeout: 2e3
|
|
3797
3974
|
}).trim();
|
|
@@ -3957,14 +4134,14 @@ var init_transport = __esm({
|
|
|
3957
4134
|
});
|
|
3958
4135
|
|
|
3959
4136
|
// src/lib/cc-agent-support.ts
|
|
3960
|
-
import { execSync as
|
|
4137
|
+
import { execSync as execSync5 } from "child_process";
|
|
3961
4138
|
function _resetCcAgentSupportCache() {
|
|
3962
4139
|
_cachedSupport = null;
|
|
3963
4140
|
}
|
|
3964
4141
|
function claudeSupportsAgentFlag() {
|
|
3965
4142
|
if (_cachedSupport !== null) return _cachedSupport;
|
|
3966
4143
|
try {
|
|
3967
|
-
const helpOutput =
|
|
4144
|
+
const helpOutput = execSync5("claude --help 2>&1", {
|
|
3968
4145
|
encoding: "utf-8",
|
|
3969
4146
|
timeout: 5e3
|
|
3970
4147
|
});
|
|
@@ -4478,8 +4655,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
4478
4655
|
}
|
|
4479
4656
|
function getCacheAgeMs() {
|
|
4480
4657
|
try {
|
|
4481
|
-
const { statSync:
|
|
4482
|
-
const s =
|
|
4658
|
+
const { statSync: statSync3 } = __require("fs");
|
|
4659
|
+
const s = statSync3(CACHE_PATH);
|
|
4483
4660
|
return Date.now() - s.mtimeMs;
|
|
4484
4661
|
} catch {
|
|
4485
4662
|
return Infinity;
|
|
@@ -5056,7 +5233,7 @@ __export(project_name_exports, {
|
|
|
5056
5233
|
_resetCache: () => _resetCache,
|
|
5057
5234
|
getProjectName: () => getProjectName
|
|
5058
5235
|
});
|
|
5059
|
-
import { execSync as
|
|
5236
|
+
import { execSync as execSync6 } from "child_process";
|
|
5060
5237
|
import path14 from "path";
|
|
5061
5238
|
function getProjectName(cwd) {
|
|
5062
5239
|
const dir = cwd ?? process.cwd();
|
|
@@ -5064,7 +5241,7 @@ function getProjectName(cwd) {
|
|
|
5064
5241
|
try {
|
|
5065
5242
|
let repoRoot;
|
|
5066
5243
|
try {
|
|
5067
|
-
const gitCommonDir =
|
|
5244
|
+
const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
|
|
5068
5245
|
cwd: dir,
|
|
5069
5246
|
encoding: "utf8",
|
|
5070
5247
|
timeout: 2e3,
|
|
@@ -5072,7 +5249,7 @@ function getProjectName(cwd) {
|
|
|
5072
5249
|
}).trim();
|
|
5073
5250
|
repoRoot = path14.dirname(gitCommonDir);
|
|
5074
5251
|
} catch {
|
|
5075
|
-
repoRoot =
|
|
5252
|
+
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
5076
5253
|
cwd: dir,
|
|
5077
5254
|
encoding: "utf8",
|
|
5078
5255
|
timeout: 2e3,
|
|
@@ -5167,7 +5344,7 @@ var init_session_scope = __esm({
|
|
|
5167
5344
|
import crypto4 from "crypto";
|
|
5168
5345
|
import path15 from "path";
|
|
5169
5346
|
import os10 from "os";
|
|
5170
|
-
import { execSync as
|
|
5347
|
+
import { execSync as execSync7 } from "child_process";
|
|
5171
5348
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5172
5349
|
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
5173
5350
|
async function writeCheckpoint(input) {
|
|
@@ -5512,14 +5689,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
5512
5689
|
if (!identifier || identifier === "unknown") return true;
|
|
5513
5690
|
try {
|
|
5514
5691
|
if (identifier.startsWith("%")) {
|
|
5515
|
-
const output =
|
|
5692
|
+
const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
|
|
5516
5693
|
timeout: 2e3,
|
|
5517
5694
|
encoding: "utf8",
|
|
5518
5695
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5519
5696
|
});
|
|
5520
5697
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
5521
5698
|
} else {
|
|
5522
|
-
|
|
5699
|
+
execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
5523
5700
|
timeout: 2e3,
|
|
5524
5701
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5525
5702
|
});
|
|
@@ -5528,7 +5705,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
5528
5705
|
} catch {
|
|
5529
5706
|
if (identifier.startsWith("%")) return true;
|
|
5530
5707
|
try {
|
|
5531
|
-
|
|
5708
|
+
execSync7("tmux list-sessions", {
|
|
5532
5709
|
timeout: 2e3,
|
|
5533
5710
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5534
5711
|
});
|
|
@@ -5543,12 +5720,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
5543
5720
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
5544
5721
|
try {
|
|
5545
5722
|
const since = new Date(taskCreatedAt).toISOString();
|
|
5546
|
-
const branch =
|
|
5723
|
+
const branch = execSync7(
|
|
5547
5724
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
5548
5725
|
{ encoding: "utf8", timeout: 3e3 }
|
|
5549
5726
|
).trim();
|
|
5550
5727
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
5551
|
-
const commitCount =
|
|
5728
|
+
const commitCount = execSync7(
|
|
5552
5729
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
5553
5730
|
{ encoding: "utf8", timeout: 5e3 }
|
|
5554
5731
|
).trim();
|
|
@@ -6286,10 +6463,10 @@ async function disposeEmbedder() {
|
|
|
6286
6463
|
async function embedDirect(text) {
|
|
6287
6464
|
const llamaCpp = await import("node-llama-cpp");
|
|
6288
6465
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6289
|
-
const { existsSync:
|
|
6290
|
-
const
|
|
6291
|
-
const modelPath =
|
|
6292
|
-
if (!
|
|
6466
|
+
const { existsSync: existsSync22 } = await import("fs");
|
|
6467
|
+
const path26 = await import("path");
|
|
6468
|
+
const modelPath = path26.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
6469
|
+
if (!existsSync22(modelPath)) {
|
|
6293
6470
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
6294
6471
|
}
|
|
6295
6472
|
const llama = await llamaCpp.getLlama();
|
|
@@ -7146,7 +7323,7 @@ __export(tmux_routing_exports, {
|
|
|
7146
7323
|
spawnEmployee: () => spawnEmployee,
|
|
7147
7324
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
7148
7325
|
});
|
|
7149
|
-
import { execFileSync as execFileSync2, execSync as
|
|
7326
|
+
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
7150
7327
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
7151
7328
|
import path19 from "path";
|
|
7152
7329
|
import os11 from "os";
|
|
@@ -7856,7 +8033,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7856
8033
|
let booted = false;
|
|
7857
8034
|
for (let i = 0; i < 30; i++) {
|
|
7858
8035
|
try {
|
|
7859
|
-
|
|
8036
|
+
execSync8("sleep 0.5");
|
|
7860
8037
|
} catch {
|
|
7861
8038
|
}
|
|
7862
8039
|
try {
|
|
@@ -8396,6 +8573,109 @@ var init_crdt_sync = __esm({
|
|
|
8396
8573
|
}
|
|
8397
8574
|
});
|
|
8398
8575
|
|
|
8576
|
+
// src/lib/db-backup.ts
|
|
8577
|
+
var db_backup_exports = {};
|
|
8578
|
+
__export(db_backup_exports, {
|
|
8579
|
+
createBackup: () => createBackup,
|
|
8580
|
+
findActiveDb: () => findActiveDb,
|
|
8581
|
+
getBackupDir: () => getBackupDir,
|
|
8582
|
+
getLatestBackup: () => getLatestBackup,
|
|
8583
|
+
hasBackupToday: () => hasBackupToday,
|
|
8584
|
+
listBackups: () => listBackups,
|
|
8585
|
+
rotateBackups: () => rotateBackups
|
|
8586
|
+
});
|
|
8587
|
+
import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync7, unlinkSync as unlinkSync10, statSync as statSync2 } from "fs";
|
|
8588
|
+
import path23 from "path";
|
|
8589
|
+
function findActiveDb() {
|
|
8590
|
+
for (const name of DB_NAMES) {
|
|
8591
|
+
const p = path23.join(EXE_AI_DIR, name);
|
|
8592
|
+
if (existsSync19(p)) return p;
|
|
8593
|
+
}
|
|
8594
|
+
return null;
|
|
8595
|
+
}
|
|
8596
|
+
function createBackup(reason = "manual") {
|
|
8597
|
+
const dbPath = findActiveDb();
|
|
8598
|
+
if (!dbPath) return null;
|
|
8599
|
+
mkdirSync11(BACKUP_DIR, { recursive: true });
|
|
8600
|
+
const dbName = path23.basename(dbPath, ".db");
|
|
8601
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
8602
|
+
const backupName = `${dbName}-${reason}-${timestamp}.db`;
|
|
8603
|
+
const backupPath = path23.join(BACKUP_DIR, backupName);
|
|
8604
|
+
copyFileSync(dbPath, backupPath);
|
|
8605
|
+
const walPath = dbPath + "-wal";
|
|
8606
|
+
if (existsSync19(walPath)) {
|
|
8607
|
+
try {
|
|
8608
|
+
copyFileSync(walPath, backupPath + "-wal");
|
|
8609
|
+
} catch {
|
|
8610
|
+
}
|
|
8611
|
+
}
|
|
8612
|
+
const shmPath = dbPath + "-shm";
|
|
8613
|
+
if (existsSync19(shmPath)) {
|
|
8614
|
+
try {
|
|
8615
|
+
copyFileSync(shmPath, backupPath + "-shm");
|
|
8616
|
+
} catch {
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
return backupPath;
|
|
8620
|
+
}
|
|
8621
|
+
function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
|
|
8622
|
+
if (!existsSync19(BACKUP_DIR)) return 0;
|
|
8623
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
|
|
8624
|
+
let deleted = 0;
|
|
8625
|
+
try {
|
|
8626
|
+
const files = readdirSync7(BACKUP_DIR);
|
|
8627
|
+
for (const file of files) {
|
|
8628
|
+
if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
|
|
8629
|
+
const filePath = path23.join(BACKUP_DIR, file);
|
|
8630
|
+
try {
|
|
8631
|
+
const stat = statSync2(filePath);
|
|
8632
|
+
if (stat.mtimeMs < cutoff) {
|
|
8633
|
+
unlinkSync10(filePath);
|
|
8634
|
+
deleted++;
|
|
8635
|
+
}
|
|
8636
|
+
} catch {
|
|
8637
|
+
}
|
|
8638
|
+
}
|
|
8639
|
+
} catch {
|
|
8640
|
+
}
|
|
8641
|
+
return deleted;
|
|
8642
|
+
}
|
|
8643
|
+
function listBackups() {
|
|
8644
|
+
if (!existsSync19(BACKUP_DIR)) return [];
|
|
8645
|
+
try {
|
|
8646
|
+
const files = readdirSync7(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
|
|
8647
|
+
return files.map((name) => {
|
|
8648
|
+
const p = path23.join(BACKUP_DIR, name);
|
|
8649
|
+
const stat = statSync2(p);
|
|
8650
|
+
return { path: p, name, size: stat.size, date: stat.mtime };
|
|
8651
|
+
}).sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
8652
|
+
} catch {
|
|
8653
|
+
return [];
|
|
8654
|
+
}
|
|
8655
|
+
}
|
|
8656
|
+
function hasBackupToday(reason) {
|
|
8657
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8658
|
+
const backups = listBackups();
|
|
8659
|
+
return backups.some((b) => b.name.includes(reason) && b.name.includes(today.replace(/-/g, "-")));
|
|
8660
|
+
}
|
|
8661
|
+
function getLatestBackup() {
|
|
8662
|
+
const backups = listBackups();
|
|
8663
|
+
return backups.length > 0 ? backups[0].path : null;
|
|
8664
|
+
}
|
|
8665
|
+
function getBackupDir() {
|
|
8666
|
+
return BACKUP_DIR;
|
|
8667
|
+
}
|
|
8668
|
+
var BACKUP_DIR, DEFAULT_KEEP_DAYS, DB_NAMES;
|
|
8669
|
+
var init_db_backup = __esm({
|
|
8670
|
+
"src/lib/db-backup.ts"() {
|
|
8671
|
+
"use strict";
|
|
8672
|
+
init_config();
|
|
8673
|
+
BACKUP_DIR = path23.join(EXE_AI_DIR, "backups");
|
|
8674
|
+
DEFAULT_KEEP_DAYS = 3;
|
|
8675
|
+
DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
|
|
8676
|
+
}
|
|
8677
|
+
});
|
|
8678
|
+
|
|
8399
8679
|
// src/lib/cloud-sync.ts
|
|
8400
8680
|
var cloud_sync_exports = {};
|
|
8401
8681
|
__export(cloud_sync_exports, {
|
|
@@ -8425,16 +8705,16 @@ __export(cloud_sync_exports, {
|
|
|
8425
8705
|
pushToPostgres: () => pushToPostgres,
|
|
8426
8706
|
recordRosterDeletion: () => recordRosterDeletion
|
|
8427
8707
|
});
|
|
8428
|
-
import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as
|
|
8708
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync20, readdirSync as readdirSync8, mkdirSync as mkdirSync12, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
8429
8709
|
import crypto8 from "crypto";
|
|
8430
|
-
import
|
|
8710
|
+
import path24 from "path";
|
|
8431
8711
|
import { homedir as homedir2 } from "os";
|
|
8432
8712
|
function sqlSafe(v) {
|
|
8433
8713
|
return v === void 0 ? null : v;
|
|
8434
8714
|
}
|
|
8435
8715
|
function logError(msg) {
|
|
8436
8716
|
try {
|
|
8437
|
-
const logPath =
|
|
8717
|
+
const logPath = path24.join(homedir2(), ".exe-os", "workers.log");
|
|
8438
8718
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
8439
8719
|
`);
|
|
8440
8720
|
} catch {
|
|
@@ -8443,10 +8723,10 @@ function logError(msg) {
|
|
|
8443
8723
|
function loadPgClient() {
|
|
8444
8724
|
if (_pgFailed) return null;
|
|
8445
8725
|
const postgresUrl = process.env.DATABASE_URL;
|
|
8446
|
-
const configPath =
|
|
8726
|
+
const configPath = path24.join(EXE_AI_DIR, "config.json");
|
|
8447
8727
|
let cloudPostgresUrl;
|
|
8448
8728
|
try {
|
|
8449
|
-
if (
|
|
8729
|
+
if (existsSync20(configPath)) {
|
|
8450
8730
|
const cfg = JSON.parse(readFileSync15(configPath, "utf8"));
|
|
8451
8731
|
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
8452
8732
|
if (cfg.cloud?.syncToPostgres === false) {
|
|
@@ -8465,8 +8745,8 @@ function loadPgClient() {
|
|
|
8465
8745
|
_pgPromise = (async () => {
|
|
8466
8746
|
const { createRequire: createRequire3 } = await import("module");
|
|
8467
8747
|
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
8468
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
8469
|
-
const req = createRequire3(
|
|
8748
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path24.join(homedir2(), "exe-db");
|
|
8749
|
+
const req = createRequire3(path24.join(exeDbRoot, "package.json"));
|
|
8470
8750
|
const entry = req.resolve("@prisma/client");
|
|
8471
8751
|
const mod = await import(pathToFileURL3(entry).href);
|
|
8472
8752
|
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
@@ -8519,7 +8799,7 @@ async function withRosterLock(fn) {
|
|
|
8519
8799
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
8520
8800
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
8521
8801
|
}
|
|
8522
|
-
|
|
8802
|
+
unlinkSync11(ROSTER_LOCK_PATH);
|
|
8523
8803
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
8524
8804
|
closeSync2(fd);
|
|
8525
8805
|
writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
|
|
@@ -8535,7 +8815,7 @@ async function withRosterLock(fn) {
|
|
|
8535
8815
|
return await fn();
|
|
8536
8816
|
} finally {
|
|
8537
8817
|
try {
|
|
8538
|
-
|
|
8818
|
+
unlinkSync11(ROSTER_LOCK_PATH);
|
|
8539
8819
|
} catch {
|
|
8540
8820
|
}
|
|
8541
8821
|
}
|
|
@@ -8906,13 +9186,42 @@ async function cloudSync(config) {
|
|
|
8906
9186
|
try {
|
|
8907
9187
|
const employees = await loadEmployees();
|
|
8908
9188
|
rosterResult.employees = employees.length;
|
|
8909
|
-
const idDir =
|
|
8910
|
-
if (
|
|
8911
|
-
rosterResult.identities =
|
|
9189
|
+
const idDir = path24.join(EXE_AI_DIR, "identity");
|
|
9190
|
+
if (existsSync20(idDir)) {
|
|
9191
|
+
rosterResult.identities = readdirSync8(idDir).filter((f) => f.endsWith(".md")).length;
|
|
8912
9192
|
}
|
|
8913
9193
|
} catch {
|
|
8914
9194
|
}
|
|
8915
9195
|
const totalMemories = await countRows("SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL");
|
|
9196
|
+
try {
|
|
9197
|
+
const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
|
|
9198
|
+
const { statSync: statFile } = await import("fs");
|
|
9199
|
+
const latestBackup = getLatestBackup2();
|
|
9200
|
+
if (latestBackup) {
|
|
9201
|
+
const backupSize = statFile(latestBackup).size;
|
|
9202
|
+
const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
|
|
9203
|
+
if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
|
|
9204
|
+
const backupData = readFileSync15(latestBackup);
|
|
9205
|
+
const deviceId = loadDeviceId() ?? "unknown";
|
|
9206
|
+
const encrypted = encryptSyncBlob(backupData);
|
|
9207
|
+
const backupRes = await fetchWithRetry(`${config.endpoint}/sync/push-db-backup`, {
|
|
9208
|
+
method: "POST",
|
|
9209
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}` },
|
|
9210
|
+
body: JSON.stringify({
|
|
9211
|
+
device_id: deviceId,
|
|
9212
|
+
filename: path24.basename(latestBackup),
|
|
9213
|
+
blob: encrypted,
|
|
9214
|
+
size: backupData.length
|
|
9215
|
+
})
|
|
9216
|
+
});
|
|
9217
|
+
if (backupRes && !backupRes.ok) {
|
|
9218
|
+
logError(`[cloud-sync] DB backup upload failed: ${backupRes.status}`);
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9221
|
+
}
|
|
9222
|
+
} catch (err) {
|
|
9223
|
+
logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
|
|
9224
|
+
}
|
|
8916
9225
|
return {
|
|
8917
9226
|
pushed,
|
|
8918
9227
|
pulled,
|
|
@@ -8928,7 +9237,7 @@ async function cloudSync(config) {
|
|
|
8928
9237
|
function recordRosterDeletion(name) {
|
|
8929
9238
|
let deletions = [];
|
|
8930
9239
|
try {
|
|
8931
|
-
if (
|
|
9240
|
+
if (existsSync20(ROSTER_DELETIONS_PATH)) {
|
|
8932
9241
|
deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
8933
9242
|
}
|
|
8934
9243
|
} catch {
|
|
@@ -8938,7 +9247,7 @@ function recordRosterDeletion(name) {
|
|
|
8938
9247
|
}
|
|
8939
9248
|
function consumeRosterDeletions() {
|
|
8940
9249
|
try {
|
|
8941
|
-
if (!
|
|
9250
|
+
if (!existsSync20(ROSTER_DELETIONS_PATH)) return [];
|
|
8942
9251
|
const deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
8943
9252
|
writeFileSync12(ROSTER_DELETIONS_PATH, "[]");
|
|
8944
9253
|
return deletions;
|
|
@@ -8947,35 +9256,35 @@ function consumeRosterDeletions() {
|
|
|
8947
9256
|
}
|
|
8948
9257
|
}
|
|
8949
9258
|
function buildRosterBlob(paths) {
|
|
8950
|
-
const rosterPath = paths?.rosterPath ??
|
|
8951
|
-
const identityDir = paths?.identityDir ??
|
|
8952
|
-
const configPath = paths?.configPath ??
|
|
9259
|
+
const rosterPath = paths?.rosterPath ?? path24.join(EXE_AI_DIR, "exe-employees.json");
|
|
9260
|
+
const identityDir = paths?.identityDir ?? path24.join(EXE_AI_DIR, "identity");
|
|
9261
|
+
const configPath = paths?.configPath ?? path24.join(EXE_AI_DIR, "config.json");
|
|
8953
9262
|
let roster = [];
|
|
8954
|
-
if (
|
|
9263
|
+
if (existsSync20(rosterPath)) {
|
|
8955
9264
|
try {
|
|
8956
9265
|
roster = JSON.parse(readFileSync15(rosterPath, "utf-8"));
|
|
8957
9266
|
} catch {
|
|
8958
9267
|
}
|
|
8959
9268
|
}
|
|
8960
9269
|
const identities = {};
|
|
8961
|
-
if (
|
|
8962
|
-
for (const file of
|
|
9270
|
+
if (existsSync20(identityDir)) {
|
|
9271
|
+
for (const file of readdirSync8(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
8963
9272
|
try {
|
|
8964
|
-
identities[file] = readFileSync15(
|
|
9273
|
+
identities[file] = readFileSync15(path24.join(identityDir, file), "utf-8");
|
|
8965
9274
|
} catch {
|
|
8966
9275
|
}
|
|
8967
9276
|
}
|
|
8968
9277
|
}
|
|
8969
9278
|
let config;
|
|
8970
|
-
if (
|
|
9279
|
+
if (existsSync20(configPath)) {
|
|
8971
9280
|
try {
|
|
8972
9281
|
config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
8973
9282
|
} catch {
|
|
8974
9283
|
}
|
|
8975
9284
|
}
|
|
8976
9285
|
let agentConfig;
|
|
8977
|
-
const agentConfigPath =
|
|
8978
|
-
if (
|
|
9286
|
+
const agentConfigPath = path24.join(EXE_AI_DIR, "agent-config.json");
|
|
9287
|
+
if (existsSync20(agentConfigPath)) {
|
|
8979
9288
|
try {
|
|
8980
9289
|
agentConfig = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
|
|
8981
9290
|
} catch {
|
|
@@ -9053,16 +9362,16 @@ async function cloudPullRoster(config) {
|
|
|
9053
9362
|
}
|
|
9054
9363
|
}
|
|
9055
9364
|
function mergeConfig(remoteConfig, configPath) {
|
|
9056
|
-
const cfgPath = configPath ??
|
|
9365
|
+
const cfgPath = configPath ?? path24.join(EXE_AI_DIR, "config.json");
|
|
9057
9366
|
let local = {};
|
|
9058
|
-
if (
|
|
9367
|
+
if (existsSync20(cfgPath)) {
|
|
9059
9368
|
try {
|
|
9060
9369
|
local = JSON.parse(readFileSync15(cfgPath, "utf-8"));
|
|
9061
9370
|
} catch {
|
|
9062
9371
|
}
|
|
9063
9372
|
}
|
|
9064
9373
|
const merged = { ...remoteConfig, ...local };
|
|
9065
|
-
const dir =
|
|
9374
|
+
const dir = path24.dirname(cfgPath);
|
|
9066
9375
|
ensurePrivateDirSync(dir);
|
|
9067
9376
|
writeFileSync12(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
9068
9377
|
enforcePrivateFileSync(cfgPath);
|
|
@@ -9070,7 +9379,7 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
9070
9379
|
async function mergeRosterFromRemote(remote, paths) {
|
|
9071
9380
|
return withRosterLock(async () => {
|
|
9072
9381
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
9073
|
-
const identityDir = paths?.identityDir ??
|
|
9382
|
+
const identityDir = paths?.identityDir ?? path24.join(EXE_AI_DIR, "identity");
|
|
9074
9383
|
const localEmployees = await loadEmployees(rosterPath);
|
|
9075
9384
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
9076
9385
|
let added = 0;
|
|
@@ -9091,11 +9400,11 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
9091
9400
|
) ?? lookupKey;
|
|
9092
9401
|
const remoteIdentity = remote.identities[matchedKey];
|
|
9093
9402
|
if (remoteIdentity) {
|
|
9094
|
-
if (!
|
|
9095
|
-
const idPath =
|
|
9403
|
+
if (!existsSync20(identityDir)) mkdirSync12(identityDir, { recursive: true });
|
|
9404
|
+
const idPath = path24.join(identityDir, `${remoteEmp.name}.md`);
|
|
9096
9405
|
let localIdentity = null;
|
|
9097
9406
|
try {
|
|
9098
|
-
localIdentity =
|
|
9407
|
+
localIdentity = existsSync20(idPath) ? readFileSync15(idPath, "utf-8") : null;
|
|
9099
9408
|
} catch {
|
|
9100
9409
|
}
|
|
9101
9410
|
if (localIdentity !== remoteIdentity) {
|
|
@@ -9125,16 +9434,16 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
9125
9434
|
}
|
|
9126
9435
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
9127
9436
|
try {
|
|
9128
|
-
const agentConfigPath =
|
|
9437
|
+
const agentConfigPath = path24.join(EXE_AI_DIR, "agent-config.json");
|
|
9129
9438
|
let local = {};
|
|
9130
|
-
if (
|
|
9439
|
+
if (existsSync20(agentConfigPath)) {
|
|
9131
9440
|
try {
|
|
9132
9441
|
local = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
|
|
9133
9442
|
} catch {
|
|
9134
9443
|
}
|
|
9135
9444
|
}
|
|
9136
9445
|
const merged = { ...remote.agentConfig, ...local };
|
|
9137
|
-
ensurePrivateDirSync(
|
|
9446
|
+
ensurePrivateDirSync(path24.dirname(agentConfigPath));
|
|
9138
9447
|
writeFileSync12(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
9139
9448
|
enforcePrivateFileSync(agentConfigPath);
|
|
9140
9449
|
} catch {
|
|
@@ -9575,11 +9884,11 @@ var init_cloud_sync = __esm({
|
|
|
9575
9884
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
9576
9885
|
FETCH_TIMEOUT_MS = 3e4;
|
|
9577
9886
|
PUSH_BATCH_SIZE = 5e3;
|
|
9578
|
-
ROSTER_LOCK_PATH =
|
|
9887
|
+
ROSTER_LOCK_PATH = path24.join(EXE_AI_DIR, "roster-merge.lock");
|
|
9579
9888
|
LOCK_STALE_MS = 3e4;
|
|
9580
9889
|
_pgPromise = null;
|
|
9581
9890
|
_pgFailed = false;
|
|
9582
|
-
ROSTER_DELETIONS_PATH =
|
|
9891
|
+
ROSTER_DELETIONS_PATH = path24.join(EXE_AI_DIR, "roster-deletions.json");
|
|
9583
9892
|
}
|
|
9584
9893
|
});
|
|
9585
9894
|
|
|
@@ -9592,7 +9901,7 @@ __export(schedules_exports, {
|
|
|
9592
9901
|
parseHumanCron: () => parseHumanCron
|
|
9593
9902
|
});
|
|
9594
9903
|
import crypto9 from "crypto";
|
|
9595
|
-
import { execSync as
|
|
9904
|
+
import { execSync as execSync10 } from "child_process";
|
|
9596
9905
|
function isValidCron(cron) {
|
|
9597
9906
|
const fields = cron.trim().split(/\s+/);
|
|
9598
9907
|
if (fields.length !== 5) return false;
|
|
@@ -9752,7 +10061,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
9752
10061
|
const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
|
|
9753
10062
|
const escapedPrompt = prompt.replace(/"/g, '\\"');
|
|
9754
10063
|
const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
|
|
9755
|
-
|
|
10064
|
+
execSync10(
|
|
9756
10065
|
`(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
|
|
9757
10066
|
{ timeout: 5e3, stdio: "ignore" }
|
|
9758
10067
|
);
|
|
@@ -9763,7 +10072,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
9763
10072
|
function removeFromCrontab(id) {
|
|
9764
10073
|
if (!isValidScheduleId(id)) return;
|
|
9765
10074
|
try {
|
|
9766
|
-
|
|
10075
|
+
execSync10(
|
|
9767
10076
|
`crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
|
|
9768
10077
|
{ timeout: 5e3, stdio: "ignore" }
|
|
9769
10078
|
);
|
|
@@ -9783,9 +10092,9 @@ var init_schedules = __esm({
|
|
|
9783
10092
|
|
|
9784
10093
|
// src/bin/exe-boot.ts
|
|
9785
10094
|
init_employees();
|
|
9786
|
-
import
|
|
10095
|
+
import path25 from "path";
|
|
9787
10096
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
9788
|
-
import { existsSync as
|
|
10097
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync9, unlinkSync as unlinkSync12 } from "fs";
|
|
9789
10098
|
import os12 from "os";
|
|
9790
10099
|
|
|
9791
10100
|
// src/lib/employee-templates.ts
|
|
@@ -10289,7 +10598,7 @@ init_config();
|
|
|
10289
10598
|
init_session_key();
|
|
10290
10599
|
init_employees();
|
|
10291
10600
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
|
|
10292
|
-
import { execSync as
|
|
10601
|
+
import { execSync as execSync9 } from "child_process";
|
|
10293
10602
|
import path20 from "path";
|
|
10294
10603
|
var CACHE_DIR = path20.join(EXE_AI_DIR, "session-cache");
|
|
10295
10604
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -10395,18 +10704,18 @@ async function boot(options) {
|
|
|
10395
10704
|
} catch {
|
|
10396
10705
|
}
|
|
10397
10706
|
try {
|
|
10398
|
-
const { readdirSync:
|
|
10707
|
+
const { readdirSync: readdirSync10, readFileSync: readFs } = await import("fs");
|
|
10399
10708
|
const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
|
|
10400
10709
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
10401
10710
|
const exeDir = "exe";
|
|
10402
|
-
const entries =
|
|
10711
|
+
const entries = readdirSync10(exeDir, { withFileTypes: true });
|
|
10403
10712
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
10404
10713
|
for (const dir of employeeDirs) {
|
|
10405
10714
|
const employee = dir.name;
|
|
10406
|
-
const taskDir =
|
|
10715
|
+
const taskDir = path25.join(exeDir, employee);
|
|
10407
10716
|
let files;
|
|
10408
10717
|
try {
|
|
10409
|
-
files =
|
|
10718
|
+
files = readdirSync10(taskDir).filter((f) => f.endsWith(".md"));
|
|
10410
10719
|
} catch {
|
|
10411
10720
|
continue;
|
|
10412
10721
|
}
|
|
@@ -10414,7 +10723,7 @@ async function boot(options) {
|
|
|
10414
10723
|
const taskFilePath = `exe/${employee}/${file}`;
|
|
10415
10724
|
let content;
|
|
10416
10725
|
try {
|
|
10417
|
-
content = readFs(
|
|
10726
|
+
content = readFs(path25.join(taskDir, file), "utf8");
|
|
10418
10727
|
} catch {
|
|
10419
10728
|
continue;
|
|
10420
10729
|
}
|
|
@@ -10500,12 +10809,12 @@ async function boot(options) {
|
|
|
10500
10809
|
}
|
|
10501
10810
|
try {
|
|
10502
10811
|
for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
|
|
10503
|
-
const reviewDir =
|
|
10504
|
-
if (
|
|
10505
|
-
for (const f of
|
|
10812
|
+
const reviewDir = path25.join(process.cwd(), "exe", reviewDirName);
|
|
10813
|
+
if (existsSync21(reviewDir)) {
|
|
10814
|
+
for (const f of readdirSync9(reviewDir)) {
|
|
10506
10815
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
10507
10816
|
try {
|
|
10508
|
-
|
|
10817
|
+
unlinkSync12(path25.join(reviewDir, f));
|
|
10509
10818
|
} catch {
|
|
10510
10819
|
}
|
|
10511
10820
|
}
|
|
@@ -10551,8 +10860,8 @@ async function boot(options) {
|
|
|
10551
10860
|
});
|
|
10552
10861
|
const taskFile = String(r.task_file);
|
|
10553
10862
|
try {
|
|
10554
|
-
const filePath =
|
|
10555
|
-
if (
|
|
10863
|
+
const filePath = path25.join(process.cwd(), taskFile);
|
|
10864
|
+
if (existsSync21(filePath)) {
|
|
10556
10865
|
let content = readFileSync16(filePath, "utf8");
|
|
10557
10866
|
content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
|
|
10558
10867
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
@@ -11026,8 +11335,8 @@ async function boot(options) {
|
|
|
11026
11335
|
updatedAt: String(row.updated_at)
|
|
11027
11336
|
}));
|
|
11028
11337
|
try {
|
|
11029
|
-
const { execSync:
|
|
11030
|
-
const gitLog =
|
|
11338
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
11339
|
+
const gitLog = execSync11(
|
|
11031
11340
|
"git log --oneline -10 --grep='Co-Authored-By: Claude' --grep='task(' --all-match 2>/dev/null || git log --oneline -5 --grep='Co-Authored-By: Claude' 2>/dev/null || git log --oneline -5 --grep='^task(' 2>/dev/null",
|
|
11032
11341
|
{
|
|
11033
11342
|
encoding: "utf8",
|
|
@@ -11050,19 +11359,19 @@ async function boot(options) {
|
|
|
11050
11359
|
})()
|
|
11051
11360
|
]);
|
|
11052
11361
|
try {
|
|
11053
|
-
const configPath =
|
|
11054
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
11362
|
+
const configPath = path25.join(
|
|
11363
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os12.homedir(), ".exe-os"),
|
|
11055
11364
|
"config.json"
|
|
11056
11365
|
);
|
|
11057
|
-
if (
|
|
11366
|
+
if (existsSync21(configPath)) {
|
|
11058
11367
|
const raw = JSON.parse(readFileSync16(configPath, "utf8"));
|
|
11059
11368
|
briefData.cloudConnected = !!(raw.cloud || raw.turso);
|
|
11060
11369
|
}
|
|
11061
11370
|
} catch {
|
|
11062
11371
|
}
|
|
11063
11372
|
try {
|
|
11064
|
-
const backfillFlagPath =
|
|
11065
|
-
const isBackfillNeeded = () =>
|
|
11373
|
+
const backfillFlagPath = path25.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
11374
|
+
const isBackfillNeeded = () => existsSync21(backfillFlagPath);
|
|
11066
11375
|
const coverageResult = await client.execute({
|
|
11067
11376
|
sql: `SELECT COUNT(*) as total,
|
|
11068
11377
|
SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
|
|
@@ -11084,8 +11393,8 @@ async function boot(options) {
|
|
|
11084
11393
|
let daemonRunning = false;
|
|
11085
11394
|
let daemonUptime;
|
|
11086
11395
|
let daemonRequestsServed;
|
|
11087
|
-
const socketPath =
|
|
11088
|
-
if (
|
|
11396
|
+
const socketPath = path25.join(EXE_AI_DIR, "exed.sock");
|
|
11397
|
+
if (existsSync21(socketPath)) {
|
|
11089
11398
|
try {
|
|
11090
11399
|
const net2 = await import("net");
|
|
11091
11400
|
const health = await new Promise((resolve) => {
|
|
@@ -11127,8 +11436,8 @@ async function boot(options) {
|
|
|
11127
11436
|
}
|
|
11128
11437
|
}
|
|
11129
11438
|
if (!daemonRunning) {
|
|
11130
|
-
const pidPath =
|
|
11131
|
-
if (
|
|
11439
|
+
const pidPath = path25.join(EXE_AI_DIR, "exed.pid");
|
|
11440
|
+
if (existsSync21(pidPath)) {
|
|
11132
11441
|
try {
|
|
11133
11442
|
const pid = parseInt(readFileSync16(pidPath, "utf8").trim(), 10);
|
|
11134
11443
|
if (pid > 0) {
|
|
@@ -11141,10 +11450,10 @@ async function boot(options) {
|
|
|
11141
11450
|
}
|
|
11142
11451
|
if (nullCount === 0) {
|
|
11143
11452
|
try {
|
|
11144
|
-
const flagPath =
|
|
11145
|
-
if (
|
|
11146
|
-
const { unlinkSync:
|
|
11147
|
-
|
|
11453
|
+
const flagPath = path25.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
11454
|
+
if (existsSync21(flagPath)) {
|
|
11455
|
+
const { unlinkSync: unlinkSync13 } = await import("fs");
|
|
11456
|
+
unlinkSync13(flagPath);
|
|
11148
11457
|
}
|
|
11149
11458
|
} catch {
|
|
11150
11459
|
}
|
|
@@ -11168,10 +11477,10 @@ async function boot(options) {
|
|
|
11168
11477
|
const { spawn: spawn2 } = await import("child_process");
|
|
11169
11478
|
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
11170
11479
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
11171
|
-
const backfillPath =
|
|
11172
|
-
if (
|
|
11480
|
+
const backfillPath = path25.resolve(path25.dirname(thisFile), "backfill-vectors.js");
|
|
11481
|
+
if (existsSync21(backfillPath)) {
|
|
11173
11482
|
const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
|
|
11174
|
-
const workerLogPath =
|
|
11483
|
+
const workerLogPath = path25.join(EXE_AI_DIR, "workers.log");
|
|
11175
11484
|
let stderrFd = "ignore";
|
|
11176
11485
|
try {
|
|
11177
11486
|
stderrFd = openSync3(workerLogPath, "a");
|
|
@@ -11201,8 +11510,8 @@ async function boot(options) {
|
|
|
11201
11510
|
const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
|
|
11202
11511
|
const missing = [];
|
|
11203
11512
|
for (const bin of criticalBinaries) {
|
|
11204
|
-
const binPath =
|
|
11205
|
-
if (!
|
|
11513
|
+
const binPath = path25.resolve(path25.dirname(thisFile), bin);
|
|
11514
|
+
if (!existsSync21(binPath)) {
|
|
11206
11515
|
missing.push(`dist/bin/${bin}`);
|
|
11207
11516
|
}
|
|
11208
11517
|
}
|
|
@@ -11231,7 +11540,7 @@ async function boot(options) {
|
|
|
11231
11540
|
console.log(brief);
|
|
11232
11541
|
return;
|
|
11233
11542
|
}
|
|
11234
|
-
const sessionDir =
|
|
11543
|
+
const sessionDir = path25.join(EXE_AI_DIR, "sessions", coordinatorName);
|
|
11235
11544
|
await mkdir5(sessionDir, { recursive: true });
|
|
11236
11545
|
const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
|
|
11237
11546
|
|
|
@@ -11240,7 +11549,7 @@ async function boot(options) {
|
|
|
11240
11549
|
# Status Brief
|
|
11241
11550
|
|
|
11242
11551
|
${brief}`;
|
|
11243
|
-
await writeFile6(
|
|
11552
|
+
await writeFile6(path25.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
|
|
11244
11553
|
const unread = await readUnreadNotifications();
|
|
11245
11554
|
if (unread.length > 0) {
|
|
11246
11555
|
console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
|
|
@@ -11249,11 +11558,11 @@ ${brief}`;
|
|
|
11249
11558
|
await cleanupOldNotifications();
|
|
11250
11559
|
console.log(brief);
|
|
11251
11560
|
try {
|
|
11252
|
-
const configPath2 =
|
|
11253
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
11561
|
+
const configPath2 = path25.join(
|
|
11562
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os12.homedir(), ".exe-os"),
|
|
11254
11563
|
"config.json"
|
|
11255
11564
|
);
|
|
11256
|
-
if (
|
|
11565
|
+
if (existsSync21(configPath2)) {
|
|
11257
11566
|
const rawCfg = JSON.parse(readFileSync16(configPath2, "utf8"));
|
|
11258
11567
|
const cloudCfg = rawCfg.cloud;
|
|
11259
11568
|
if (cloudCfg?.apiKey) {
|
|
@@ -11314,11 +11623,11 @@ async function updateIdleKillSuspectStreak(client, killsToday, liveSessions, tod
|
|
|
11314
11623
|
}
|
|
11315
11624
|
function runSplash() {
|
|
11316
11625
|
try {
|
|
11317
|
-
const { execSync:
|
|
11626
|
+
const { execSync: execSync11 } = __require("child_process");
|
|
11318
11627
|
const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
|
|
11319
11628
|
const config = loadConfigSync2();
|
|
11320
11629
|
if (!config.splashEffect) return;
|
|
11321
|
-
|
|
11630
|
+
execSync11(
|
|
11322
11631
|
'echo "EXE OS" | python3 -m terminaltexteffects decrypt --typing-speed 2 --ciphertext-colors 6B4C9A F5D76E --final-gradient-stops F5D76E F0EDE8 --final-gradient-direction vertical',
|
|
11323
11632
|
{ stdio: "inherit", timeout: 5e3 }
|
|
11324
11633
|
);
|