@askexenow/exe-os 0.8.61 → 0.8.63
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 +2 -3
- package/dist/bin/backfill-responses.js +3 -4
- package/dist/bin/backfill-vectors.js +0 -1
- package/dist/bin/cleanup-stale-review-tasks.js +0 -1
- package/dist/bin/cli.js +1654 -397
- package/dist/bin/exe-assign.js +0 -1
- package/dist/bin/exe-boot.js +61 -84
- package/dist/bin/exe-cloud.js +28 -60
- package/dist/bin/exe-dispatch.js +0 -1
- package/dist/bin/exe-doctor.js +0 -1
- package/dist/bin/exe-export-behaviors.js +1 -2
- package/dist/bin/exe-forget.js +0 -1
- package/dist/bin/exe-gateway.js +16 -17
- package/dist/bin/exe-heartbeat.js +1 -2
- package/dist/bin/exe-kill.js +2 -3
- package/dist/bin/exe-launch-agent.js +1 -2
- package/dist/bin/exe-link.js +49 -78
- package/dist/bin/exe-pending-messages.js +1 -2
- package/dist/bin/exe-pending-notifications.js +0 -1
- package/dist/bin/exe-pending-reviews.js +1 -2
- package/dist/bin/exe-review.js +0 -1
- package/dist/bin/exe-search.js +2 -3
- package/dist/bin/exe-session-cleanup.js +4 -5
- package/dist/bin/exe-status.js +0 -1
- package/dist/bin/exe-team.js +0 -1
- package/dist/bin/git-sweep.js +0 -1
- package/dist/bin/graph-backfill.js +5 -6
- package/dist/bin/graph-export.js +0 -1
- package/dist/bin/scan-tasks.js +0 -1
- package/dist/bin/setup.js +1460 -115
- package/dist/bin/shard-migrate.js +0 -1
- package/dist/bin/wiki-sync.js +0 -1
- package/dist/gateway/index.js +16 -17
- package/dist/hooks/bug-report-worker.js +11 -12
- package/dist/hooks/commit-complete.js +0 -1
- package/dist/hooks/error-recall.js +2 -3
- package/dist/hooks/ingest-worker.js +14 -15
- package/dist/hooks/instructions-loaded.js +0 -1
- package/dist/hooks/notification.js +0 -1
- package/dist/hooks/post-compact.js +0 -1
- package/dist/hooks/pre-compact.js +2 -3
- package/dist/hooks/pre-tool-use.js +0 -1
- package/dist/hooks/prompt-ingest-worker.js +2 -3
- package/dist/hooks/prompt-submit.js +6 -7
- package/dist/hooks/response-ingest-worker.js +2 -3
- package/dist/hooks/session-end.js +3 -4
- package/dist/hooks/session-start.js +2 -3
- package/dist/hooks/stop.js +2 -3
- package/dist/hooks/subagent-stop.js +0 -1
- package/dist/hooks/summary-worker.js +51 -74
- package/dist/index.js +7 -8
- package/dist/lib/cloud-sync.js +21 -12
- package/dist/lib/consolidation.js +0 -1
- package/dist/lib/exe-daemon.js +33 -65
- package/dist/lib/hybrid-search.js +2 -3
- package/dist/lib/keychain.js +19 -58
- package/dist/lib/schedules.js +2 -3
- package/dist/lib/store.js +0 -1
- package/dist/mcp/server.js +29 -30
- package/dist/runtime/index.js +2 -3
- package/dist/tui/App.js +24 -56
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1986,7 +1986,6 @@ import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4
|
|
|
1986
1986
|
import { existsSync as existsSync5 } from "fs";
|
|
1987
1987
|
import path5 from "path";
|
|
1988
1988
|
import os4 from "os";
|
|
1989
|
-
import crypto from "crypto";
|
|
1990
1989
|
function getKeyDir() {
|
|
1991
1990
|
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
|
|
1992
1991
|
}
|
|
@@ -2051,65 +2050,34 @@ async function deleteMasterKey() {
|
|
|
2051
2050
|
await unlink(keyPath);
|
|
2052
2051
|
}
|
|
2053
2052
|
}
|
|
2054
|
-
function
|
|
2055
|
-
if (key.length !== 32) {
|
|
2056
|
-
throw new Error(`Key must be 32 bytes, got ${key.length}`);
|
|
2057
|
-
}
|
|
2058
|
-
const hash = crypto.createHash("sha256").update(key).digest();
|
|
2059
|
-
const checksumByte = hash[0];
|
|
2060
|
-
let bits = "";
|
|
2061
|
-
for (const byte of key) {
|
|
2062
|
-
bits += byte.toString(2).padStart(8, "0");
|
|
2063
|
-
}
|
|
2064
|
-
bits += checksumByte.toString(2).padStart(8, "0");
|
|
2065
|
-
const words = [];
|
|
2066
|
-
let wordlist;
|
|
2053
|
+
async function loadBip39() {
|
|
2067
2054
|
try {
|
|
2068
|
-
|
|
2069
|
-
wordlist = bip39.wordlists?.english ?? bip39.default?.wordlists?.english;
|
|
2070
|
-
if (!wordlist) throw new Error("no wordlist");
|
|
2055
|
+
return await import("bip39");
|
|
2071
2056
|
} catch {
|
|
2072
|
-
throw new Error(
|
|
2057
|
+
throw new Error(
|
|
2058
|
+
"bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
|
|
2059
|
+
);
|
|
2073
2060
|
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2061
|
+
}
|
|
2062
|
+
async function exportMnemonic(key) {
|
|
2063
|
+
if (key.length !== 32) {
|
|
2064
|
+
throw new Error(`Key must be 32 bytes, got ${key.length}`);
|
|
2077
2065
|
}
|
|
2078
|
-
|
|
2066
|
+
const { entropyToMnemonic } = await loadBip39();
|
|
2067
|
+
return entropyToMnemonic(key.toString("hex"));
|
|
2079
2068
|
}
|
|
2080
|
-
function importMnemonic(mnemonic) {
|
|
2081
|
-
const
|
|
2069
|
+
async function importMnemonic(mnemonic) {
|
|
2070
|
+
const trimmed = mnemonic.trim();
|
|
2071
|
+
const words = trimmed.split(/\s+/);
|
|
2082
2072
|
if (words.length !== 24) {
|
|
2083
2073
|
throw new Error(`Expected 24 words, got ${words.length}`);
|
|
2084
2074
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
wordlist = bip39.wordlists?.english ?? bip39.default?.wordlists?.english;
|
|
2089
|
-
if (!wordlist) throw new Error("no wordlist");
|
|
2090
|
-
} catch {
|
|
2091
|
-
throw new Error("bip39 package required. Install with: npm install bip39");
|
|
2075
|
+
const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
|
|
2076
|
+
if (!validateMnemonic(trimmed)) {
|
|
2077
|
+
throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
|
|
2092
2078
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
const index = wordlist.indexOf(word.toLowerCase());
|
|
2096
|
-
if (index === -1) {
|
|
2097
|
-
throw new Error(`Invalid BIP39 word: "${word}"`);
|
|
2098
|
-
}
|
|
2099
|
-
bits += index.toString(2).padStart(11, "0");
|
|
2100
|
-
}
|
|
2101
|
-
const entropyBits = bits.slice(0, 256);
|
|
2102
|
-
const checksumBits = bits.slice(256, 264);
|
|
2103
|
-
const key = Buffer.alloc(32);
|
|
2104
|
-
for (let i = 0; i < 32; i++) {
|
|
2105
|
-
key[i] = parseInt(entropyBits.slice(i * 8, (i + 1) * 8), 2);
|
|
2106
|
-
}
|
|
2107
|
-
const hash = crypto.createHash("sha256").update(key).digest();
|
|
2108
|
-
const expectedChecksum = hash[0].toString(2).padStart(8, "0");
|
|
2109
|
-
if (checksumBits !== expectedChecksum) {
|
|
2110
|
-
throw new Error("Invalid mnemonic checksum");
|
|
2111
|
-
}
|
|
2112
|
-
return key;
|
|
2079
|
+
const entropy = mnemonicToEntropy(trimmed);
|
|
2080
|
+
return Buffer.from(entropy, "hex");
|
|
2113
2081
|
}
|
|
2114
2082
|
var SERVICE, ACCOUNT;
|
|
2115
2083
|
var init_keychain = __esm({
|
|
@@ -3450,7 +3418,7 @@ var backfill_conversations_exports = {};
|
|
|
3450
3418
|
__export(backfill_conversations_exports, {
|
|
3451
3419
|
backfillConversations: () => backfillConversations
|
|
3452
3420
|
});
|
|
3453
|
-
import
|
|
3421
|
+
import crypto from "crypto";
|
|
3454
3422
|
import { createReadStream } from "fs";
|
|
3455
3423
|
import { readdir as readdir2, stat } from "fs/promises";
|
|
3456
3424
|
import path8 from "path";
|
|
@@ -3755,7 +3723,7 @@ async function backfillConversations(options) {
|
|
|
3755
3723
|
}
|
|
3756
3724
|
}
|
|
3757
3725
|
await writeMemory({
|
|
3758
|
-
id:
|
|
3726
|
+
id: crypto.randomUUID(),
|
|
3759
3727
|
agent_id: conv.agentId,
|
|
3760
3728
|
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3761
3729
|
session_id: conv.sessionId,
|
|
@@ -4770,10 +4738,10 @@ async function disposeEmbedder() {
|
|
|
4770
4738
|
async function embedDirect(text) {
|
|
4771
4739
|
const llamaCpp = await import("node-llama-cpp");
|
|
4772
4740
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4773
|
-
const { existsSync:
|
|
4774
|
-
const
|
|
4775
|
-
const modelPath =
|
|
4776
|
-
if (!
|
|
4741
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
4742
|
+
const path36 = await import("path");
|
|
4743
|
+
const modelPath = path36.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4744
|
+
if (!existsSync24(modelPath)) {
|
|
4777
4745
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4778
4746
|
}
|
|
4779
4747
|
const llama = await llamaCpp.getLlama();
|
|
@@ -5131,60 +5099,1125 @@ async function assertVpsLicense(opts) {
|
|
|
5131
5099
|
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
5132
5100
|
);
|
|
5133
5101
|
}
|
|
5134
|
-
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
5135
|
-
if (_revalTimer) return;
|
|
5136
|
-
_revalTimer = setInterval(async () => {
|
|
5137
|
-
try {
|
|
5138
|
-
const license = await checkLicense();
|
|
5139
|
-
if (!license.valid) {
|
|
5140
|
-
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
5141
|
-
}
|
|
5142
|
-
} catch {
|
|
5143
|
-
}
|
|
5144
|
-
}, intervalMs);
|
|
5145
|
-
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
5146
|
-
_revalTimer.unref();
|
|
5102
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
5103
|
+
if (_revalTimer) return;
|
|
5104
|
+
_revalTimer = setInterval(async () => {
|
|
5105
|
+
try {
|
|
5106
|
+
const license = await checkLicense();
|
|
5107
|
+
if (!license.valid) {
|
|
5108
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
5109
|
+
}
|
|
5110
|
+
} catch {
|
|
5111
|
+
}
|
|
5112
|
+
}, intervalMs);
|
|
5113
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
5114
|
+
_revalTimer.unref();
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
function stopLicenseRevalidation() {
|
|
5118
|
+
if (_revalTimer) {
|
|
5119
|
+
clearInterval(_revalTimer);
|
|
5120
|
+
_revalTimer = null;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
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;
|
|
5124
|
+
var init_license = __esm({
|
|
5125
|
+
"src/lib/license.ts"() {
|
|
5126
|
+
"use strict";
|
|
5127
|
+
init_config();
|
|
5128
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
5129
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
5130
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
5131
|
+
API_BASE = "https://askexe.com/cloud";
|
|
5132
|
+
RETRY_DELAY_MS = 500;
|
|
5133
|
+
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
5134
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
5135
|
+
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
5136
|
+
-----END PUBLIC KEY-----`;
|
|
5137
|
+
LICENSE_JWT_ALG = "ES256";
|
|
5138
|
+
PLAN_LIMITS = {
|
|
5139
|
+
free: { devices: 1, employees: 1, memories: 5e4 },
|
|
5140
|
+
pro: { devices: 2, employees: 5, memories: 25e4 },
|
|
5141
|
+
team: { devices: 10, employees: 20, memories: 1e6 },
|
|
5142
|
+
agency: { devices: 50, employees: 100, memories: 1e7 },
|
|
5143
|
+
enterprise: { devices: -1, employees: -1, memories: -1 }
|
|
5144
|
+
};
|
|
5145
|
+
FREE_LICENSE = {
|
|
5146
|
+
valid: true,
|
|
5147
|
+
plan: "free",
|
|
5148
|
+
email: "",
|
|
5149
|
+
expiresAt: null,
|
|
5150
|
+
deviceLimit: 1,
|
|
5151
|
+
employeeLimit: 1,
|
|
5152
|
+
memoryLimit: 5e4
|
|
5153
|
+
};
|
|
5154
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
5155
|
+
_revalTimer = null;
|
|
5156
|
+
}
|
|
5157
|
+
});
|
|
5158
|
+
|
|
5159
|
+
// src/lib/crypto.ts
|
|
5160
|
+
var crypto_exports = {};
|
|
5161
|
+
__export(crypto_exports, {
|
|
5162
|
+
decryptSyncBlob: () => decryptSyncBlob,
|
|
5163
|
+
encryptSyncBlob: () => encryptSyncBlob,
|
|
5164
|
+
initSyncCrypto: () => initSyncCrypto,
|
|
5165
|
+
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
5166
|
+
});
|
|
5167
|
+
import crypto2 from "crypto";
|
|
5168
|
+
function initSyncCrypto(masterKey) {
|
|
5169
|
+
if (masterKey.length !== 32) {
|
|
5170
|
+
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
5171
|
+
}
|
|
5172
|
+
_syncKey = Buffer.from(
|
|
5173
|
+
crypto2.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
5174
|
+
);
|
|
5175
|
+
}
|
|
5176
|
+
function isSyncCryptoInitialized() {
|
|
5177
|
+
return _syncKey !== null;
|
|
5178
|
+
}
|
|
5179
|
+
function requireSyncKey() {
|
|
5180
|
+
if (!_syncKey) {
|
|
5181
|
+
throw new Error("Sync crypto not initialized. Call initSyncCrypto(masterKey) first.");
|
|
5182
|
+
}
|
|
5183
|
+
return _syncKey;
|
|
5184
|
+
}
|
|
5185
|
+
function encryptSyncBlob(data) {
|
|
5186
|
+
const key = requireSyncKey();
|
|
5187
|
+
const iv = crypto2.randomBytes(IV_LENGTH);
|
|
5188
|
+
const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
|
|
5189
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
5190
|
+
const tag = cipher.getAuthTag();
|
|
5191
|
+
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
5192
|
+
}
|
|
5193
|
+
function decryptSyncBlob(ciphertext) {
|
|
5194
|
+
const key = requireSyncKey();
|
|
5195
|
+
const combined = Buffer.from(ciphertext, "base64");
|
|
5196
|
+
if (combined.length < IV_LENGTH + TAG_LENGTH) {
|
|
5197
|
+
throw new Error("Sync blob too short to contain IV + tag");
|
|
5198
|
+
}
|
|
5199
|
+
const iv = combined.subarray(0, IV_LENGTH);
|
|
5200
|
+
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
5201
|
+
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
5202
|
+
const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
|
|
5203
|
+
decipher.setAuthTag(tag);
|
|
5204
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
5205
|
+
}
|
|
5206
|
+
var ALGORITHM, IV_LENGTH, TAG_LENGTH, SYNC_HKDF_INFO, _syncKey;
|
|
5207
|
+
var init_crypto = __esm({
|
|
5208
|
+
"src/lib/crypto.ts"() {
|
|
5209
|
+
"use strict";
|
|
5210
|
+
ALGORITHM = "aes-256-gcm";
|
|
5211
|
+
IV_LENGTH = 12;
|
|
5212
|
+
TAG_LENGTH = 16;
|
|
5213
|
+
SYNC_HKDF_INFO = "exe-mem-sync-v2";
|
|
5214
|
+
_syncKey = null;
|
|
5215
|
+
}
|
|
5216
|
+
});
|
|
5217
|
+
|
|
5218
|
+
// src/lib/compress.ts
|
|
5219
|
+
import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
|
|
5220
|
+
function compress(input) {
|
|
5221
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
5222
|
+
return brotliCompressSync(input, {
|
|
5223
|
+
params: {
|
|
5224
|
+
[constants.BROTLI_PARAM_QUALITY]: 4
|
|
5225
|
+
}
|
|
5226
|
+
});
|
|
5227
|
+
}
|
|
5228
|
+
function decompress(input) {
|
|
5229
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
5230
|
+
return brotliDecompressSync(input);
|
|
5231
|
+
}
|
|
5232
|
+
var init_compress = __esm({
|
|
5233
|
+
"src/lib/compress.ts"() {
|
|
5234
|
+
"use strict";
|
|
5235
|
+
}
|
|
5236
|
+
});
|
|
5237
|
+
|
|
5238
|
+
// src/lib/cloud-sync.ts
|
|
5239
|
+
var cloud_sync_exports = {};
|
|
5240
|
+
__export(cloud_sync_exports, {
|
|
5241
|
+
assertSecureEndpoint: () => assertSecureEndpoint,
|
|
5242
|
+
buildRosterBlob: () => buildRosterBlob,
|
|
5243
|
+
cloudPull: () => cloudPull,
|
|
5244
|
+
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
5245
|
+
cloudPullBlob: () => cloudPullBlob,
|
|
5246
|
+
cloudPullConversations: () => cloudPullConversations,
|
|
5247
|
+
cloudPullDocuments: () => cloudPullDocuments,
|
|
5248
|
+
cloudPullGlobalProcedures: () => cloudPullGlobalProcedures,
|
|
5249
|
+
cloudPullGraphRAG: () => cloudPullGraphRAG,
|
|
5250
|
+
cloudPullRoster: () => cloudPullRoster,
|
|
5251
|
+
cloudPullTasks: () => cloudPullTasks,
|
|
5252
|
+
cloudPush: () => cloudPush,
|
|
5253
|
+
cloudPushBehaviors: () => cloudPushBehaviors,
|
|
5254
|
+
cloudPushBlob: () => cloudPushBlob,
|
|
5255
|
+
cloudPushConversations: () => cloudPushConversations,
|
|
5256
|
+
cloudPushDocuments: () => cloudPushDocuments,
|
|
5257
|
+
cloudPushGlobalProcedures: () => cloudPushGlobalProcedures,
|
|
5258
|
+
cloudPushGraphRAG: () => cloudPushGraphRAG,
|
|
5259
|
+
cloudPushRoster: () => cloudPushRoster,
|
|
5260
|
+
cloudPushTasks: () => cloudPushTasks,
|
|
5261
|
+
cloudSync: () => cloudSync,
|
|
5262
|
+
mergeConfig: () => mergeConfig,
|
|
5263
|
+
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
5264
|
+
recordRosterDeletion: () => recordRosterDeletion
|
|
5265
|
+
});
|
|
5266
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync11, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
5267
|
+
import crypto3 from "crypto";
|
|
5268
|
+
import path12 from "path";
|
|
5269
|
+
import { homedir as homedir3 } from "os";
|
|
5270
|
+
function sqlSafe(v) {
|
|
5271
|
+
return v === void 0 ? null : v;
|
|
5272
|
+
}
|
|
5273
|
+
function logError(msg) {
|
|
5274
|
+
try {
|
|
5275
|
+
const logPath = path12.join(homedir3(), ".exe-os", "workers.log");
|
|
5276
|
+
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
5277
|
+
`);
|
|
5278
|
+
} catch {
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
async function withRosterLock(fn) {
|
|
5282
|
+
try {
|
|
5283
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
5284
|
+
closeSync2(fd);
|
|
5285
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5286
|
+
} catch (err) {
|
|
5287
|
+
if (err.code === "EEXIST") {
|
|
5288
|
+
try {
|
|
5289
|
+
const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
5290
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
5291
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
5292
|
+
}
|
|
5293
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
5294
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
5295
|
+
closeSync2(fd);
|
|
5296
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5297
|
+
} catch (retryErr) {
|
|
5298
|
+
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
5299
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
5300
|
+
}
|
|
5301
|
+
} else {
|
|
5302
|
+
throw err;
|
|
5303
|
+
}
|
|
5304
|
+
}
|
|
5305
|
+
try {
|
|
5306
|
+
return await fn();
|
|
5307
|
+
} finally {
|
|
5308
|
+
try {
|
|
5309
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
5310
|
+
} catch {
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
async function fetchWithRetry(url, init) {
|
|
5315
|
+
const MAX_RETRIES2 = 3;
|
|
5316
|
+
const BASE_DELAY_MS2 = 200;
|
|
5317
|
+
let lastError;
|
|
5318
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
5319
|
+
try {
|
|
5320
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
5321
|
+
const resp = await fetch(url, { ...init, signal });
|
|
5322
|
+
if (resp && resp.status >= 500 && attempt < MAX_RETRIES2) {
|
|
5323
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
5324
|
+
continue;
|
|
5325
|
+
}
|
|
5326
|
+
return resp;
|
|
5327
|
+
} catch (err) {
|
|
5328
|
+
lastError = err;
|
|
5329
|
+
if (attempt === MAX_RETRIES2) throw err;
|
|
5330
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
throw lastError;
|
|
5334
|
+
}
|
|
5335
|
+
function assertSecureEndpoint(endpoint) {
|
|
5336
|
+
if (endpoint.startsWith("https://")) return;
|
|
5337
|
+
if (endpoint.startsWith("http://")) {
|
|
5338
|
+
try {
|
|
5339
|
+
const parsed = new URL(endpoint);
|
|
5340
|
+
if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
|
|
5341
|
+
} catch {
|
|
5342
|
+
return;
|
|
5343
|
+
}
|
|
5344
|
+
throw new Error(
|
|
5345
|
+
`Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
|
|
5346
|
+
);
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
async function cloudPush(records, maxVersion, config) {
|
|
5350
|
+
if (records.length === 0) return true;
|
|
5351
|
+
assertSecureEndpoint(config.endpoint);
|
|
5352
|
+
try {
|
|
5353
|
+
const json = JSON.stringify(records);
|
|
5354
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5355
|
+
const blob = encryptSyncBlob(compressed);
|
|
5356
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
|
|
5357
|
+
method: "POST",
|
|
5358
|
+
headers: {
|
|
5359
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5360
|
+
"Content-Type": "application/json",
|
|
5361
|
+
"X-Device-Id": loadDeviceId(),
|
|
5362
|
+
"X-Expected-Version": String(maxVersion)
|
|
5363
|
+
},
|
|
5364
|
+
body: JSON.stringify({ version: maxVersion, blob })
|
|
5365
|
+
});
|
|
5366
|
+
if (resp == null) {
|
|
5367
|
+
logError("[cloud-sync] PUSH FAILED: no response from server");
|
|
5368
|
+
return false;
|
|
5369
|
+
}
|
|
5370
|
+
if (resp.status === 409) {
|
|
5371
|
+
logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
|
|
5372
|
+
return false;
|
|
5373
|
+
}
|
|
5374
|
+
return resp.ok;
|
|
5375
|
+
} catch (err) {
|
|
5376
|
+
logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
5377
|
+
return false;
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
async function cloudPull(sinceVersion, config) {
|
|
5381
|
+
assertSecureEndpoint(config.endpoint);
|
|
5382
|
+
try {
|
|
5383
|
+
const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
|
|
5384
|
+
method: "POST",
|
|
5385
|
+
headers: {
|
|
5386
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5387
|
+
"Content-Type": "application/json",
|
|
5388
|
+
"X-Device-Id": loadDeviceId()
|
|
5389
|
+
},
|
|
5390
|
+
body: JSON.stringify({ since_version: sinceVersion })
|
|
5391
|
+
});
|
|
5392
|
+
if (response == null) {
|
|
5393
|
+
logError("[cloud-sync] PULL FAILED: no response from server");
|
|
5394
|
+
return { records: [], maxVersion: sinceVersion };
|
|
5395
|
+
}
|
|
5396
|
+
if (!response.ok) return { records: [], maxVersion: sinceVersion };
|
|
5397
|
+
const data = await response.json();
|
|
5398
|
+
const allRecords = [];
|
|
5399
|
+
for (const { blob } of data.blobs ?? []) {
|
|
5400
|
+
try {
|
|
5401
|
+
const compressed = decryptSyncBlob(blob);
|
|
5402
|
+
const json = decompress(compressed).toString("utf8");
|
|
5403
|
+
const records = JSON.parse(json);
|
|
5404
|
+
allRecords.push(...records);
|
|
5405
|
+
} catch {
|
|
5406
|
+
continue;
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
|
|
5410
|
+
} catch (err) {
|
|
5411
|
+
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
5412
|
+
return { records: [], maxVersion: sinceVersion };
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
async function cloudSync(config) {
|
|
5416
|
+
let client;
|
|
5417
|
+
try {
|
|
5418
|
+
client = getClient();
|
|
5419
|
+
} catch {
|
|
5420
|
+
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
5421
|
+
}
|
|
5422
|
+
try {
|
|
5423
|
+
await client.execute(
|
|
5424
|
+
"CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
|
|
5425
|
+
);
|
|
5426
|
+
} catch (e) {
|
|
5427
|
+
logError(`[cloud-sync] sync_meta CREATE failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5428
|
+
}
|
|
5429
|
+
const pullMeta = await client.execute(
|
|
5430
|
+
"SELECT value FROM sync_meta WHERE key = 'last_cloud_pull_version'"
|
|
5431
|
+
);
|
|
5432
|
+
const lastPullVersion = pullMeta.rows.length > 0 ? Number(pullMeta.rows[0].value) : 0;
|
|
5433
|
+
const pullResult = await cloudPull(lastPullVersion, config);
|
|
5434
|
+
let pulled = 0;
|
|
5435
|
+
if (pullResult.records.length > 0) {
|
|
5436
|
+
const stmts = pullResult.records.map((rec) => ({
|
|
5437
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
5438
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
5439
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
5440
|
+
author_device_id, scope)
|
|
5441
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5442
|
+
args: [
|
|
5443
|
+
sqlSafe(rec.id),
|
|
5444
|
+
sqlSafe(rec.agent_id),
|
|
5445
|
+
sqlSafe(rec.agent_role),
|
|
5446
|
+
sqlSafe(rec.session_id),
|
|
5447
|
+
sqlSafe(rec.timestamp),
|
|
5448
|
+
sqlSafe(rec.tool_name),
|
|
5449
|
+
sqlSafe(rec.project_name),
|
|
5450
|
+
sqlSafe(rec.has_error ?? 0),
|
|
5451
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
5452
|
+
sqlSafe(rec.version ?? 0),
|
|
5453
|
+
sqlSafe(rec.author_device_id),
|
|
5454
|
+
sqlSafe(rec.scope ?? "business")
|
|
5455
|
+
]
|
|
5456
|
+
}));
|
|
5457
|
+
await client.batch(stmts, "write");
|
|
5458
|
+
pulled = pullResult.records.length;
|
|
5459
|
+
}
|
|
5460
|
+
if (pullResult.maxVersion > lastPullVersion) {
|
|
5461
|
+
await client.execute({
|
|
5462
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?)",
|
|
5463
|
+
args: [String(pullResult.maxVersion)]
|
|
5464
|
+
});
|
|
5465
|
+
}
|
|
5466
|
+
const pushMeta = await client.execute(
|
|
5467
|
+
"SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
|
|
5468
|
+
);
|
|
5469
|
+
const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
|
|
5470
|
+
let pushed = 0;
|
|
5471
|
+
let batchCursor = lastPushVersion;
|
|
5472
|
+
while (true) {
|
|
5473
|
+
const recordsResult = await client.execute({
|
|
5474
|
+
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
5475
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
5476
|
+
author_device_id, scope
|
|
5477
|
+
FROM memories
|
|
5478
|
+
WHERE version > ?
|
|
5479
|
+
AND (scope IS NULL OR scope != 'personal')
|
|
5480
|
+
ORDER BY version ASC
|
|
5481
|
+
LIMIT ?`,
|
|
5482
|
+
args: [batchCursor, PUSH_BATCH_SIZE]
|
|
5483
|
+
});
|
|
5484
|
+
if (recordsResult.rows.length === 0) break;
|
|
5485
|
+
const records = recordsResult.rows.map((row) => ({
|
|
5486
|
+
id: row.id,
|
|
5487
|
+
agent_id: row.agent_id,
|
|
5488
|
+
agent_role: row.agent_role,
|
|
5489
|
+
session_id: row.session_id,
|
|
5490
|
+
timestamp: row.timestamp,
|
|
5491
|
+
tool_name: row.tool_name,
|
|
5492
|
+
project_name: row.project_name,
|
|
5493
|
+
has_error: row.has_error,
|
|
5494
|
+
raw_text: row.raw_text,
|
|
5495
|
+
version: row.version,
|
|
5496
|
+
author_device_id: row.author_device_id,
|
|
5497
|
+
scope: row.scope
|
|
5498
|
+
}));
|
|
5499
|
+
const maxVersion = Number(records[records.length - 1].version);
|
|
5500
|
+
const pushOk = await cloudPush(records, maxVersion, config);
|
|
5501
|
+
if (!pushOk) break;
|
|
5502
|
+
await client.execute({
|
|
5503
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
5504
|
+
args: [String(maxVersion)]
|
|
5505
|
+
});
|
|
5506
|
+
pushed += records.length;
|
|
5507
|
+
batchCursor = maxVersion;
|
|
5508
|
+
if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
|
|
5509
|
+
}
|
|
5510
|
+
try {
|
|
5511
|
+
await cloudPushRoster(config);
|
|
5512
|
+
} catch (err) {
|
|
5513
|
+
logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5514
|
+
}
|
|
5515
|
+
try {
|
|
5516
|
+
await cloudPullRoster(config);
|
|
5517
|
+
} catch (err) {
|
|
5518
|
+
logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5519
|
+
}
|
|
5520
|
+
try {
|
|
5521
|
+
await cloudPushGlobalProcedures(config);
|
|
5522
|
+
} catch (err) {
|
|
5523
|
+
logError(`[cloud-sync] Global procedures push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5524
|
+
}
|
|
5525
|
+
try {
|
|
5526
|
+
await cloudPullGlobalProcedures(config);
|
|
5527
|
+
} catch (err) {
|
|
5528
|
+
logError(`[cloud-sync] Global procedures pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5529
|
+
}
|
|
5530
|
+
let behaviorsResult = { pushed: false, pulled: 0 };
|
|
5531
|
+
try {
|
|
5532
|
+
behaviorsResult.pushed = await cloudPushBehaviors(config);
|
|
5533
|
+
} catch (err) {
|
|
5534
|
+
logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5535
|
+
}
|
|
5536
|
+
try {
|
|
5537
|
+
const pullResult2 = await cloudPullBehaviors(config);
|
|
5538
|
+
behaviorsResult.pulled = pullResult2.pulled;
|
|
5539
|
+
} catch (err) {
|
|
5540
|
+
logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5541
|
+
}
|
|
5542
|
+
let graphragResult = { pushed: false, pulled: 0 };
|
|
5543
|
+
try {
|
|
5544
|
+
graphragResult.pushed = await cloudPushGraphRAG(config);
|
|
5545
|
+
} catch (err) {
|
|
5546
|
+
logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5547
|
+
}
|
|
5548
|
+
try {
|
|
5549
|
+
const pullResult2 = await cloudPullGraphRAG(config);
|
|
5550
|
+
graphragResult.pulled = pullResult2.pulled;
|
|
5551
|
+
} catch (err) {
|
|
5552
|
+
logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5553
|
+
}
|
|
5554
|
+
let tasksResult = { pushed: false, pulled: 0 };
|
|
5555
|
+
try {
|
|
5556
|
+
tasksResult.pushed = await cloudPushTasks(config);
|
|
5557
|
+
} catch (err) {
|
|
5558
|
+
logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5559
|
+
}
|
|
5560
|
+
try {
|
|
5561
|
+
const pullResult2 = await cloudPullTasks(config);
|
|
5562
|
+
tasksResult.pulled = pullResult2.pulled;
|
|
5563
|
+
} catch (err) {
|
|
5564
|
+
logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5565
|
+
}
|
|
5566
|
+
let conversationsResult = { pushed: false, pulled: 0 };
|
|
5567
|
+
try {
|
|
5568
|
+
conversationsResult.pushed = await cloudPushConversations(config);
|
|
5569
|
+
} catch (err) {
|
|
5570
|
+
logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5571
|
+
}
|
|
5572
|
+
try {
|
|
5573
|
+
const pullResult2 = await cloudPullConversations(config);
|
|
5574
|
+
conversationsResult.pulled = pullResult2.pulled;
|
|
5575
|
+
} catch (err) {
|
|
5576
|
+
logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5577
|
+
}
|
|
5578
|
+
let documentsResult = { pushed: false, pulled: 0 };
|
|
5579
|
+
try {
|
|
5580
|
+
documentsResult.pushed = await cloudPushDocuments(config);
|
|
5581
|
+
} catch (err) {
|
|
5582
|
+
logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5583
|
+
}
|
|
5584
|
+
try {
|
|
5585
|
+
const pullResult2 = await cloudPullDocuments(config);
|
|
5586
|
+
documentsResult.pulled = pullResult2.pulled;
|
|
5587
|
+
} catch (err) {
|
|
5588
|
+
logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5589
|
+
}
|
|
5590
|
+
return {
|
|
5591
|
+
pushed,
|
|
5592
|
+
pulled,
|
|
5593
|
+
behaviors: behaviorsResult,
|
|
5594
|
+
graphrag: graphragResult,
|
|
5595
|
+
tasks: tasksResult,
|
|
5596
|
+
conversations: conversationsResult,
|
|
5597
|
+
documents: documentsResult
|
|
5598
|
+
};
|
|
5599
|
+
}
|
|
5600
|
+
function recordRosterDeletion(name) {
|
|
5601
|
+
let deletions = [];
|
|
5602
|
+
try {
|
|
5603
|
+
if (existsSync11(ROSTER_DELETIONS_PATH)) {
|
|
5604
|
+
deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5605
|
+
}
|
|
5606
|
+
} catch {
|
|
5607
|
+
}
|
|
5608
|
+
if (!deletions.includes(name)) deletions.push(name);
|
|
5609
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
5610
|
+
}
|
|
5611
|
+
function consumeRosterDeletions() {
|
|
5612
|
+
try {
|
|
5613
|
+
if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
|
|
5614
|
+
const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5615
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
|
|
5616
|
+
return deletions;
|
|
5617
|
+
} catch {
|
|
5618
|
+
return [];
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
function buildRosterBlob(paths) {
|
|
5622
|
+
const rosterPath = paths?.rosterPath ?? path12.join(EXE_AI_DIR, "exe-employees.json");
|
|
5623
|
+
const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
|
|
5624
|
+
const configPath = paths?.configPath ?? path12.join(EXE_AI_DIR, "config.json");
|
|
5625
|
+
let roster = [];
|
|
5626
|
+
if (existsSync11(rosterPath)) {
|
|
5627
|
+
try {
|
|
5628
|
+
roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
|
|
5629
|
+
} catch {
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
const identities = {};
|
|
5633
|
+
if (existsSync11(identityDir)) {
|
|
5634
|
+
for (const file of readdirSync2(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
5635
|
+
try {
|
|
5636
|
+
identities[file] = readFileSync7(path12.join(identityDir, file), "utf-8");
|
|
5637
|
+
} catch {
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
let config;
|
|
5642
|
+
if (existsSync11(configPath)) {
|
|
5643
|
+
try {
|
|
5644
|
+
config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
5645
|
+
} catch {
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
const deletedNames = consumeRosterDeletions();
|
|
5649
|
+
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
5650
|
+
const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5651
|
+
return { roster, identities, config, deletedNames, version: hash };
|
|
5652
|
+
}
|
|
5653
|
+
async function cloudPushRoster(config) {
|
|
5654
|
+
assertSecureEndpoint(config.endpoint);
|
|
5655
|
+
const blob = buildRosterBlob();
|
|
5656
|
+
if (blob.roster.length === 0) return true;
|
|
5657
|
+
try {
|
|
5658
|
+
const client = getClient();
|
|
5659
|
+
const meta = await client.execute(
|
|
5660
|
+
"SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
|
|
5661
|
+
);
|
|
5662
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
5663
|
+
if (blob.version === lastVersion) return true;
|
|
5664
|
+
} catch {
|
|
5665
|
+
}
|
|
5666
|
+
try {
|
|
5667
|
+
const json = JSON.stringify(blob);
|
|
5668
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5669
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
5670
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
|
|
5671
|
+
method: "POST",
|
|
5672
|
+
headers: {
|
|
5673
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5674
|
+
"Content-Type": "application/json",
|
|
5675
|
+
"X-Device-Id": loadDeviceId()
|
|
5676
|
+
},
|
|
5677
|
+
body: JSON.stringify({ blob: encrypted })
|
|
5678
|
+
});
|
|
5679
|
+
if (resp.ok) {
|
|
5680
|
+
try {
|
|
5681
|
+
const client = getClient();
|
|
5682
|
+
await client.execute({
|
|
5683
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?)",
|
|
5684
|
+
args: [String(blob.version)]
|
|
5685
|
+
});
|
|
5686
|
+
} catch {
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
return resp.ok;
|
|
5690
|
+
} catch (err) {
|
|
5691
|
+
process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
5692
|
+
`);
|
|
5693
|
+
return false;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
async function cloudPullRoster(config) {
|
|
5697
|
+
assertSecureEndpoint(config.endpoint);
|
|
5698
|
+
try {
|
|
5699
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
|
|
5700
|
+
method: "GET",
|
|
5701
|
+
headers: {
|
|
5702
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5703
|
+
"X-Device-Id": loadDeviceId()
|
|
5704
|
+
}
|
|
5705
|
+
});
|
|
5706
|
+
if (!resp.ok) return { added: 0 };
|
|
5707
|
+
const data = await resp.json();
|
|
5708
|
+
if (!data.blob) return { added: 0 };
|
|
5709
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
5710
|
+
const json = decompress(compressed).toString("utf8");
|
|
5711
|
+
const remote = JSON.parse(json);
|
|
5712
|
+
return mergeRosterFromRemote(remote);
|
|
5713
|
+
} catch (err) {
|
|
5714
|
+
process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
5715
|
+
`);
|
|
5716
|
+
return { added: 0 };
|
|
5717
|
+
}
|
|
5718
|
+
}
|
|
5719
|
+
function mergeConfig(remoteConfig, configPath) {
|
|
5720
|
+
const cfgPath = configPath ?? path12.join(EXE_AI_DIR, "config.json");
|
|
5721
|
+
let local = {};
|
|
5722
|
+
if (existsSync11(cfgPath)) {
|
|
5723
|
+
try {
|
|
5724
|
+
local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
|
|
5725
|
+
} catch {
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
const merged = { ...remoteConfig, ...local };
|
|
5729
|
+
const dir = path12.dirname(cfgPath);
|
|
5730
|
+
if (!existsSync11(dir)) mkdirSync5(dir, { recursive: true });
|
|
5731
|
+
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
5732
|
+
}
|
|
5733
|
+
async function mergeRosterFromRemote(remote, paths) {
|
|
5734
|
+
return withRosterLock(async () => {
|
|
5735
|
+
const rosterPath = paths?.rosterPath ?? void 0;
|
|
5736
|
+
const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
|
|
5737
|
+
const localEmployees = await loadEmployees(rosterPath);
|
|
5738
|
+
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
5739
|
+
let added = 0;
|
|
5740
|
+
let identitiesUpdated = 0;
|
|
5741
|
+
for (const remoteEmp of remote.roster) {
|
|
5742
|
+
if (!localNames.has(remoteEmp.name)) {
|
|
5743
|
+
localEmployees.push(remoteEmp);
|
|
5744
|
+
localNames.add(remoteEmp.name);
|
|
5745
|
+
added++;
|
|
5746
|
+
try {
|
|
5747
|
+
registerBinSymlinks(remoteEmp.name);
|
|
5748
|
+
} catch {
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
const remoteIdentity = remote.identities[`${remoteEmp.name}.md`];
|
|
5752
|
+
if (remoteIdentity) {
|
|
5753
|
+
if (!existsSync11(identityDir)) mkdirSync5(identityDir, { recursive: true });
|
|
5754
|
+
const idPath = path12.join(identityDir, `${remoteEmp.name}.md`);
|
|
5755
|
+
let localIdentity = null;
|
|
5756
|
+
try {
|
|
5757
|
+
localIdentity = existsSync11(idPath) ? readFileSync7(idPath, "utf-8") : null;
|
|
5758
|
+
} catch {
|
|
5759
|
+
}
|
|
5760
|
+
if (localIdentity !== remoteIdentity) {
|
|
5761
|
+
writeFileSync4(idPath, remoteIdentity, "utf-8");
|
|
5762
|
+
identitiesUpdated++;
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
let removed = 0;
|
|
5767
|
+
if (remote.deletedNames && remote.deletedNames.length > 0) {
|
|
5768
|
+
const toRemove = new Set(remote.deletedNames);
|
|
5769
|
+
const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
|
|
5770
|
+
removed = localEmployees.length - filtered.length;
|
|
5771
|
+
if (removed > 0) {
|
|
5772
|
+
localEmployees.length = 0;
|
|
5773
|
+
localEmployees.push(...filtered);
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
if (added > 0 || removed > 0) {
|
|
5777
|
+
await saveEmployees(localEmployees, rosterPath);
|
|
5778
|
+
}
|
|
5779
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
5780
|
+
try {
|
|
5781
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
5782
|
+
} catch {
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5785
|
+
return { added, identitiesUpdated };
|
|
5786
|
+
});
|
|
5787
|
+
}
|
|
5788
|
+
async function cloudPushBlob(route, data, metaKey, config) {
|
|
5789
|
+
if (data.length === 0) return { ok: true };
|
|
5790
|
+
assertSecureEndpoint(config.endpoint);
|
|
5791
|
+
const json = JSON.stringify(data);
|
|
5792
|
+
const version = Buffer.from(json).length;
|
|
5793
|
+
try {
|
|
5794
|
+
const client = getClient();
|
|
5795
|
+
const meta = await client.execute({
|
|
5796
|
+
sql: "SELECT value FROM sync_meta WHERE key = ?",
|
|
5797
|
+
args: [metaKey]
|
|
5798
|
+
});
|
|
5799
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
5800
|
+
if (version === lastVersion) return { ok: true };
|
|
5801
|
+
} catch {
|
|
5802
|
+
}
|
|
5803
|
+
try {
|
|
5804
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5805
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
5806
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
5807
|
+
method: "POST",
|
|
5808
|
+
headers: {
|
|
5809
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5810
|
+
"Content-Type": "application/json",
|
|
5811
|
+
"X-Device-Id": loadDeviceId()
|
|
5812
|
+
},
|
|
5813
|
+
body: JSON.stringify({ blob: encrypted })
|
|
5814
|
+
});
|
|
5815
|
+
if (resp.ok) {
|
|
5816
|
+
try {
|
|
5817
|
+
const client = getClient();
|
|
5818
|
+
await client.execute({
|
|
5819
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
|
|
5820
|
+
args: [metaKey, String(version)]
|
|
5821
|
+
});
|
|
5822
|
+
} catch {
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5825
|
+
return { ok: resp.ok };
|
|
5826
|
+
} catch (err) {
|
|
5827
|
+
logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5828
|
+
return { ok: false };
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5831
|
+
async function cloudPullBlob(route, config) {
|
|
5832
|
+
assertSecureEndpoint(config.endpoint);
|
|
5833
|
+
try {
|
|
5834
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
5835
|
+
method: "GET",
|
|
5836
|
+
headers: {
|
|
5837
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5838
|
+
"X-Device-Id": loadDeviceId()
|
|
5839
|
+
}
|
|
5840
|
+
});
|
|
5841
|
+
if (!resp.ok) return null;
|
|
5842
|
+
const data = await resp.json();
|
|
5843
|
+
if (!data.blob) return null;
|
|
5844
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
5845
|
+
const json = decompress(compressed).toString("utf8");
|
|
5846
|
+
return JSON.parse(json);
|
|
5847
|
+
} catch (err) {
|
|
5848
|
+
logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5849
|
+
return null;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
async function cloudPushGlobalProcedures(config) {
|
|
5853
|
+
const client = getClient();
|
|
5854
|
+
const result = await client.execute("SELECT * FROM global_procedures LIMIT 1000");
|
|
5855
|
+
const rows = result.rows;
|
|
5856
|
+
const { ok } = await cloudPushBlob(
|
|
5857
|
+
"/sync/push-global-procedures",
|
|
5858
|
+
rows,
|
|
5859
|
+
"last_global_procedures_push_version",
|
|
5860
|
+
config
|
|
5861
|
+
);
|
|
5862
|
+
return ok;
|
|
5863
|
+
}
|
|
5864
|
+
async function cloudPullGlobalProcedures(config) {
|
|
5865
|
+
const remoteProcs = await cloudPullBlob(
|
|
5866
|
+
"/sync/pull-global-procedures",
|
|
5867
|
+
config
|
|
5868
|
+
);
|
|
5869
|
+
if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
|
|
5870
|
+
const client = getClient();
|
|
5871
|
+
const stmts = remoteProcs.map((p) => ({
|
|
5872
|
+
sql: `INSERT INTO global_procedures
|
|
5873
|
+
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
5874
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
5875
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
5876
|
+
title = excluded.title,
|
|
5877
|
+
content = excluded.content,
|
|
5878
|
+
priority = excluded.priority,
|
|
5879
|
+
domain = excluded.domain,
|
|
5880
|
+
active = excluded.active,
|
|
5881
|
+
updated_at = excluded.updated_at
|
|
5882
|
+
WHERE excluded.updated_at > global_procedures.updated_at`,
|
|
5883
|
+
args: [
|
|
5884
|
+
sqlSafe(p.id),
|
|
5885
|
+
sqlSafe(p.title),
|
|
5886
|
+
sqlSafe(p.content),
|
|
5887
|
+
sqlSafe(p.priority ?? "p0"),
|
|
5888
|
+
sqlSafe(p.domain),
|
|
5889
|
+
sqlSafe(p.active ?? 1),
|
|
5890
|
+
sqlSafe(p.created_at),
|
|
5891
|
+
sqlSafe(p.updated_at)
|
|
5892
|
+
]
|
|
5893
|
+
}));
|
|
5894
|
+
await client.batch(stmts, "write");
|
|
5895
|
+
return { pulled: remoteProcs.length };
|
|
5896
|
+
}
|
|
5897
|
+
async function cloudPushBehaviors(config) {
|
|
5898
|
+
const client = getClient();
|
|
5899
|
+
const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
5900
|
+
const rows = result.rows;
|
|
5901
|
+
const { ok } = await cloudPushBlob(
|
|
5902
|
+
"/sync/push-behaviors",
|
|
5903
|
+
rows,
|
|
5904
|
+
"last_behaviors_push_version",
|
|
5905
|
+
config
|
|
5906
|
+
);
|
|
5907
|
+
return ok;
|
|
5908
|
+
}
|
|
5909
|
+
async function cloudPullBehaviors(config) {
|
|
5910
|
+
const remoteBehaviors = await cloudPullBlob(
|
|
5911
|
+
"/sync/pull-behaviors",
|
|
5912
|
+
config
|
|
5913
|
+
);
|
|
5914
|
+
if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
|
|
5915
|
+
const client = getClient();
|
|
5916
|
+
let pulled = 0;
|
|
5917
|
+
for (const behavior of remoteBehaviors) {
|
|
5918
|
+
const existing = await client.execute({
|
|
5919
|
+
sql: `SELECT COUNT(*) as cnt FROM behaviors
|
|
5920
|
+
WHERE agent_id = ? AND content = ?`,
|
|
5921
|
+
args: [sqlSafe(behavior.agent_id), sqlSafe(behavior.content)]
|
|
5922
|
+
});
|
|
5923
|
+
if (Number(existing.rows[0]?.cnt) > 0) continue;
|
|
5924
|
+
await client.execute({
|
|
5925
|
+
sql: `INSERT OR IGNORE INTO behaviors
|
|
5926
|
+
(id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
|
|
5927
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5928
|
+
args: [
|
|
5929
|
+
sqlSafe(behavior.id),
|
|
5930
|
+
sqlSafe(behavior.agent_id),
|
|
5931
|
+
sqlSafe(behavior.project_name),
|
|
5932
|
+
sqlSafe(behavior.domain),
|
|
5933
|
+
sqlSafe(behavior.content),
|
|
5934
|
+
sqlSafe(behavior.active ?? 1),
|
|
5935
|
+
sqlSafe(behavior.priority ?? "p1"),
|
|
5936
|
+
sqlSafe(behavior.created_at),
|
|
5937
|
+
sqlSafe(behavior.updated_at)
|
|
5938
|
+
]
|
|
5939
|
+
});
|
|
5940
|
+
pulled++;
|
|
5941
|
+
}
|
|
5942
|
+
return { pulled };
|
|
5943
|
+
}
|
|
5944
|
+
async function cloudPushGraphRAG(config) {
|
|
5945
|
+
const client = getClient();
|
|
5946
|
+
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
5947
|
+
client.execute("SELECT * FROM entities LIMIT 50000"),
|
|
5948
|
+
client.execute("SELECT * FROM relationships LIMIT 50000"),
|
|
5949
|
+
client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
|
|
5950
|
+
client.execute("SELECT * FROM entity_memories LIMIT 50000"),
|
|
5951
|
+
client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
|
|
5952
|
+
client.execute("SELECT * FROM hyperedges LIMIT 50000"),
|
|
5953
|
+
client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
|
|
5954
|
+
]);
|
|
5955
|
+
const blob = {
|
|
5956
|
+
entities: entities.rows,
|
|
5957
|
+
relationships: relationships.rows,
|
|
5958
|
+
entity_aliases: aliases.rows,
|
|
5959
|
+
entity_memories: entityMems.rows,
|
|
5960
|
+
relationship_memories: relMems.rows,
|
|
5961
|
+
hyperedges: hyperedges.rows,
|
|
5962
|
+
hyperedge_nodes: hyperedgeNodes.rows
|
|
5963
|
+
};
|
|
5964
|
+
const { ok } = await cloudPushBlob(
|
|
5965
|
+
"/sync/push-graphrag",
|
|
5966
|
+
[blob],
|
|
5967
|
+
"last_graphrag_push_version",
|
|
5968
|
+
config
|
|
5969
|
+
);
|
|
5970
|
+
return ok;
|
|
5971
|
+
}
|
|
5972
|
+
async function cloudPullGraphRAG(config) {
|
|
5973
|
+
const data = await cloudPullBlob(
|
|
5974
|
+
"/sync/pull-graphrag",
|
|
5975
|
+
config
|
|
5976
|
+
);
|
|
5977
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
5978
|
+
const blob = data[0];
|
|
5979
|
+
const client = getClient();
|
|
5980
|
+
let pulled = 0;
|
|
5981
|
+
if (blob.entities.length > 0) {
|
|
5982
|
+
const stmts = blob.entities.map((e) => ({
|
|
5983
|
+
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
|
|
5984
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
5985
|
+
args: [sqlSafe(e.id), sqlSafe(e.name), sqlSafe(e.type), sqlSafe(e.first_seen), sqlSafe(e.last_seen), sqlSafe(e.properties ?? "{}")]
|
|
5986
|
+
}));
|
|
5987
|
+
await client.batch(stmts, "write");
|
|
5988
|
+
pulled += stmts.length;
|
|
5989
|
+
}
|
|
5990
|
+
if (blob.relationships.length > 0) {
|
|
5991
|
+
const stmts = blob.relationships.map((r) => ({
|
|
5992
|
+
sql: `INSERT OR IGNORE INTO relationships
|
|
5993
|
+
(id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
|
|
5994
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5995
|
+
args: [
|
|
5996
|
+
sqlSafe(r.id),
|
|
5997
|
+
sqlSafe(r.source_entity_id),
|
|
5998
|
+
sqlSafe(r.target_entity_id),
|
|
5999
|
+
sqlSafe(r.type),
|
|
6000
|
+
sqlSafe(r.weight ?? 1),
|
|
6001
|
+
sqlSafe(r.timestamp),
|
|
6002
|
+
sqlSafe(r.properties ?? "{}"),
|
|
6003
|
+
sqlSafe(r.confidence ?? 1),
|
|
6004
|
+
sqlSafe(r.confidence_label ?? "extracted")
|
|
6005
|
+
]
|
|
6006
|
+
}));
|
|
6007
|
+
await client.batch(stmts, "write");
|
|
6008
|
+
pulled += stmts.length;
|
|
6009
|
+
}
|
|
6010
|
+
if (blob.entity_aliases.length > 0) {
|
|
6011
|
+
const stmts = blob.entity_aliases.map((a) => ({
|
|
6012
|
+
sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
|
|
6013
|
+
args: [sqlSafe(a.alias), sqlSafe(a.canonical_entity_id)]
|
|
6014
|
+
}));
|
|
6015
|
+
await client.batch(stmts, "write");
|
|
6016
|
+
pulled += stmts.length;
|
|
5147
6017
|
}
|
|
6018
|
+
if (blob.entity_memories.length > 0) {
|
|
6019
|
+
const stmts = blob.entity_memories.map((em) => ({
|
|
6020
|
+
sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
|
|
6021
|
+
args: [sqlSafe(em.entity_id), sqlSafe(em.memory_id)]
|
|
6022
|
+
}));
|
|
6023
|
+
await client.batch(stmts, "write");
|
|
6024
|
+
pulled += stmts.length;
|
|
6025
|
+
}
|
|
6026
|
+
if (blob.relationship_memories.length > 0) {
|
|
6027
|
+
const stmts = blob.relationship_memories.map((rm) => ({
|
|
6028
|
+
sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
|
|
6029
|
+
args: [sqlSafe(rm.relationship_id), sqlSafe(rm.memory_id)]
|
|
6030
|
+
}));
|
|
6031
|
+
await client.batch(stmts, "write");
|
|
6032
|
+
pulled += stmts.length;
|
|
6033
|
+
}
|
|
6034
|
+
if (blob.hyperedges.length > 0) {
|
|
6035
|
+
const stmts = blob.hyperedges.map((h) => ({
|
|
6036
|
+
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
6037
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
6038
|
+
args: [sqlSafe(h.id), sqlSafe(h.label), sqlSafe(h.relation), sqlSafe(h.confidence ?? 1), sqlSafe(h.timestamp)]
|
|
6039
|
+
}));
|
|
6040
|
+
await client.batch(stmts, "write");
|
|
6041
|
+
pulled += stmts.length;
|
|
6042
|
+
}
|
|
6043
|
+
if (blob.hyperedge_nodes.length > 0) {
|
|
6044
|
+
const stmts = blob.hyperedge_nodes.map((hn) => ({
|
|
6045
|
+
sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
|
|
6046
|
+
args: [sqlSafe(hn.hyperedge_id), sqlSafe(hn.entity_id)]
|
|
6047
|
+
}));
|
|
6048
|
+
await client.batch(stmts, "write");
|
|
6049
|
+
pulled += stmts.length;
|
|
6050
|
+
}
|
|
6051
|
+
return { pulled };
|
|
5148
6052
|
}
|
|
5149
|
-
function
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
6053
|
+
async function cloudPushTasks(config) {
|
|
6054
|
+
const client = getClient();
|
|
6055
|
+
const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
6056
|
+
const rows = result.rows;
|
|
6057
|
+
const { ok } = await cloudPushBlob(
|
|
6058
|
+
"/sync/push-tasks",
|
|
6059
|
+
rows,
|
|
6060
|
+
"last_tasks_push_version",
|
|
6061
|
+
config
|
|
6062
|
+
);
|
|
6063
|
+
return ok;
|
|
6064
|
+
}
|
|
6065
|
+
async function cloudPullTasks(config) {
|
|
6066
|
+
const remoteTasks = await cloudPullBlob(
|
|
6067
|
+
"/sync/pull-tasks",
|
|
6068
|
+
config
|
|
6069
|
+
);
|
|
6070
|
+
if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
|
|
6071
|
+
const client = getClient();
|
|
6072
|
+
const stmts = remoteTasks.map((t) => ({
|
|
6073
|
+
sql: `INSERT OR IGNORE INTO tasks
|
|
6074
|
+
(id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
|
|
6075
|
+
blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
|
|
6076
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6077
|
+
args: [
|
|
6078
|
+
sqlSafe(t.id),
|
|
6079
|
+
sqlSafe(t.title),
|
|
6080
|
+
sqlSafe(t.assigned_to),
|
|
6081
|
+
sqlSafe(t.assigned_by),
|
|
6082
|
+
sqlSafe(t.project_name),
|
|
6083
|
+
sqlSafe(t.priority ?? "p1"),
|
|
6084
|
+
sqlSafe(t.status ?? "open"),
|
|
6085
|
+
sqlSafe(t.task_file),
|
|
6086
|
+
sqlSafe(t.created_at),
|
|
6087
|
+
sqlSafe(t.updated_at),
|
|
6088
|
+
sqlSafe(t.blocked_by),
|
|
6089
|
+
sqlSafe(t.parent_task_id),
|
|
6090
|
+
sqlSafe(t.budget_tokens),
|
|
6091
|
+
sqlSafe(t.budget_fallback_model),
|
|
6092
|
+
sqlSafe(t.tokens_used ?? 0),
|
|
6093
|
+
sqlSafe(t.tokens_warned_at)
|
|
6094
|
+
]
|
|
6095
|
+
}));
|
|
6096
|
+
await client.batch(stmts, "write");
|
|
6097
|
+
return { pulled: remoteTasks.length };
|
|
6098
|
+
}
|
|
6099
|
+
async function cloudPushConversations(config) {
|
|
6100
|
+
const client = getClient();
|
|
6101
|
+
const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
6102
|
+
const rows = result.rows;
|
|
6103
|
+
const { ok } = await cloudPushBlob(
|
|
6104
|
+
"/sync/push-conversations",
|
|
6105
|
+
rows,
|
|
6106
|
+
"last_conversations_push_version",
|
|
6107
|
+
config
|
|
6108
|
+
);
|
|
6109
|
+
return ok;
|
|
6110
|
+
}
|
|
6111
|
+
async function cloudPullConversations(config) {
|
|
6112
|
+
const remoteConvos = await cloudPullBlob(
|
|
6113
|
+
"/sync/pull-conversations",
|
|
6114
|
+
config
|
|
6115
|
+
);
|
|
6116
|
+
if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
|
|
6117
|
+
const client = getClient();
|
|
6118
|
+
const stmts = remoteConvos.map((c) => ({
|
|
6119
|
+
sql: `INSERT OR IGNORE INTO conversations
|
|
6120
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
6121
|
+
recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
|
|
6122
|
+
content_metadata, agent_response, agent_name, timestamp, ingested_at)
|
|
6123
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6124
|
+
args: [
|
|
6125
|
+
sqlSafe(c.id),
|
|
6126
|
+
sqlSafe(c.platform),
|
|
6127
|
+
sqlSafe(c.external_id),
|
|
6128
|
+
sqlSafe(c.sender_id),
|
|
6129
|
+
sqlSafe(c.sender_name),
|
|
6130
|
+
sqlSafe(c.sender_phone),
|
|
6131
|
+
sqlSafe(c.sender_email),
|
|
6132
|
+
sqlSafe(c.recipient_id),
|
|
6133
|
+
sqlSafe(c.channel_id),
|
|
6134
|
+
sqlSafe(c.thread_id),
|
|
6135
|
+
sqlSafe(c.reply_to_id),
|
|
6136
|
+
sqlSafe(c.content_text),
|
|
6137
|
+
sqlSafe(c.content_media),
|
|
6138
|
+
sqlSafe(c.content_metadata),
|
|
6139
|
+
sqlSafe(c.agent_response),
|
|
6140
|
+
sqlSafe(c.agent_name),
|
|
6141
|
+
sqlSafe(c.timestamp),
|
|
6142
|
+
sqlSafe(c.ingested_at)
|
|
6143
|
+
]
|
|
6144
|
+
}));
|
|
6145
|
+
await client.batch(stmts, "write");
|
|
6146
|
+
return { pulled: remoteConvos.length };
|
|
6147
|
+
}
|
|
6148
|
+
async function cloudPushDocuments(config) {
|
|
6149
|
+
const client = getClient();
|
|
6150
|
+
const [workspaces, documents] = await Promise.all([
|
|
6151
|
+
client.execute("SELECT * FROM workspaces LIMIT 1000"),
|
|
6152
|
+
client.execute("SELECT * FROM documents LIMIT 10000")
|
|
6153
|
+
]);
|
|
6154
|
+
const blob = {
|
|
6155
|
+
workspaces: workspaces.rows,
|
|
6156
|
+
documents: documents.rows
|
|
6157
|
+
};
|
|
6158
|
+
const { ok } = await cloudPushBlob(
|
|
6159
|
+
"/sync/push-documents",
|
|
6160
|
+
[blob],
|
|
6161
|
+
"last_documents_push_version",
|
|
6162
|
+
config
|
|
6163
|
+
);
|
|
6164
|
+
return ok;
|
|
6165
|
+
}
|
|
6166
|
+
async function cloudPullDocuments(config) {
|
|
6167
|
+
const data = await cloudPullBlob(
|
|
6168
|
+
"/sync/pull-documents",
|
|
6169
|
+
config
|
|
6170
|
+
);
|
|
6171
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
6172
|
+
const blob = data[0];
|
|
6173
|
+
const client = getClient();
|
|
6174
|
+
let pulled = 0;
|
|
6175
|
+
if (blob.workspaces.length > 0) {
|
|
6176
|
+
const stmts = blob.workspaces.map((w) => ({
|
|
6177
|
+
sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
|
|
6178
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
6179
|
+
args: [sqlSafe(w.id), sqlSafe(w.slug), sqlSafe(w.name), sqlSafe(w.owner_agent_id), sqlSafe(w.created_at), sqlSafe(w.metadata)]
|
|
6180
|
+
}));
|
|
6181
|
+
await client.batch(stmts, "write");
|
|
6182
|
+
pulled += stmts.length;
|
|
6183
|
+
}
|
|
6184
|
+
if (blob.documents.length > 0) {
|
|
6185
|
+
const stmts = blob.documents.map((d) => ({
|
|
6186
|
+
sql: `INSERT OR IGNORE INTO documents
|
|
6187
|
+
(id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
|
|
6188
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6189
|
+
args: [
|
|
6190
|
+
sqlSafe(d.id),
|
|
6191
|
+
sqlSafe(d.workspace_id),
|
|
6192
|
+
sqlSafe(d.filename),
|
|
6193
|
+
sqlSafe(d.mime),
|
|
6194
|
+
sqlSafe(d.source_type),
|
|
6195
|
+
sqlSafe(d.user_id),
|
|
6196
|
+
sqlSafe(d.uploaded_at),
|
|
6197
|
+
sqlSafe(d.metadata)
|
|
6198
|
+
]
|
|
6199
|
+
}));
|
|
6200
|
+
await client.batch(stmts, "write");
|
|
6201
|
+
pulled += stmts.length;
|
|
5153
6202
|
}
|
|
6203
|
+
return { pulled };
|
|
5154
6204
|
}
|
|
5155
|
-
var
|
|
5156
|
-
var
|
|
5157
|
-
"src/lib/
|
|
6205
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
6206
|
+
var init_cloud_sync = __esm({
|
|
6207
|
+
"src/lib/cloud-sync.ts"() {
|
|
5158
6208
|
"use strict";
|
|
6209
|
+
init_database();
|
|
6210
|
+
init_crypto();
|
|
6211
|
+
init_compress();
|
|
6212
|
+
init_license();
|
|
5159
6213
|
init_config();
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
5168
|
-
-----END PUBLIC KEY-----`;
|
|
5169
|
-
LICENSE_JWT_ALG = "ES256";
|
|
5170
|
-
PLAN_LIMITS = {
|
|
5171
|
-
free: { devices: 1, employees: 1, memories: 5e4 },
|
|
5172
|
-
pro: { devices: 2, employees: 5, memories: 25e4 },
|
|
5173
|
-
team: { devices: 10, employees: 20, memories: 1e6 },
|
|
5174
|
-
agency: { devices: 50, employees: 100, memories: 1e7 },
|
|
5175
|
-
enterprise: { devices: -1, employees: -1, memories: -1 }
|
|
5176
|
-
};
|
|
5177
|
-
FREE_LICENSE = {
|
|
5178
|
-
valid: true,
|
|
5179
|
-
plan: "free",
|
|
5180
|
-
email: "",
|
|
5181
|
-
expiresAt: null,
|
|
5182
|
-
deviceLimit: 1,
|
|
5183
|
-
employeeLimit: 1,
|
|
5184
|
-
memoryLimit: 5e4
|
|
5185
|
-
};
|
|
5186
|
-
CACHE_MAX_AGE_MS = 36e5;
|
|
5187
|
-
_revalTimer = null;
|
|
6214
|
+
init_employees();
|
|
6215
|
+
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
6216
|
+
FETCH_TIMEOUT_MS = 3e4;
|
|
6217
|
+
PUSH_BATCH_SIZE = 5e3;
|
|
6218
|
+
ROSTER_LOCK_PATH = path12.join(EXE_AI_DIR, "roster-merge.lock");
|
|
6219
|
+
LOCK_STALE_MS = 3e4;
|
|
6220
|
+
ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5188
6221
|
}
|
|
5189
6222
|
});
|
|
5190
6223
|
|
|
@@ -5197,17 +6230,17 @@ __export(identity_exports, {
|
|
|
5197
6230
|
listIdentities: () => listIdentities,
|
|
5198
6231
|
updateIdentity: () => updateIdentity
|
|
5199
6232
|
});
|
|
5200
|
-
import { existsSync as
|
|
5201
|
-
import { readdirSync as
|
|
5202
|
-
import
|
|
6233
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
6234
|
+
import { readdirSync as readdirSync3 } from "fs";
|
|
6235
|
+
import path13 from "path";
|
|
5203
6236
|
import { createHash as createHash2 } from "crypto";
|
|
5204
6237
|
function ensureDir() {
|
|
5205
|
-
if (!
|
|
5206
|
-
|
|
6238
|
+
if (!existsSync12(IDENTITY_DIR)) {
|
|
6239
|
+
mkdirSync6(IDENTITY_DIR, { recursive: true });
|
|
5207
6240
|
}
|
|
5208
6241
|
}
|
|
5209
6242
|
function identityPath(agentId) {
|
|
5210
|
-
return
|
|
6243
|
+
return path13.join(IDENTITY_DIR, `${agentId}.md`);
|
|
5211
6244
|
}
|
|
5212
6245
|
function parseFrontmatter(raw) {
|
|
5213
6246
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -5248,8 +6281,8 @@ function contentHash(content) {
|
|
|
5248
6281
|
}
|
|
5249
6282
|
function getIdentity(agentId) {
|
|
5250
6283
|
const filePath = identityPath(agentId);
|
|
5251
|
-
if (!
|
|
5252
|
-
const raw =
|
|
6284
|
+
if (!existsSync12(filePath)) return null;
|
|
6285
|
+
const raw = readFileSync8(filePath, "utf-8");
|
|
5253
6286
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
5254
6287
|
return {
|
|
5255
6288
|
agentId,
|
|
@@ -5263,7 +6296,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
5263
6296
|
ensureDir();
|
|
5264
6297
|
const filePath = identityPath(agentId);
|
|
5265
6298
|
const hash = contentHash(content);
|
|
5266
|
-
|
|
6299
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
5267
6300
|
try {
|
|
5268
6301
|
const client = getClient();
|
|
5269
6302
|
await client.execute({
|
|
@@ -5280,7 +6313,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
5280
6313
|
}
|
|
5281
6314
|
function listIdentities() {
|
|
5282
6315
|
ensureDir();
|
|
5283
|
-
const files =
|
|
6316
|
+
const files = readdirSync3(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
5284
6317
|
const results = [];
|
|
5285
6318
|
for (const file of files) {
|
|
5286
6319
|
const agentId = file.replace(".md", "");
|
|
@@ -5319,7 +6352,7 @@ var init_identity = __esm({
|
|
|
5319
6352
|
"use strict";
|
|
5320
6353
|
init_config();
|
|
5321
6354
|
init_database();
|
|
5322
|
-
IDENTITY_DIR =
|
|
6355
|
+
IDENTITY_DIR = path13.join(EXE_AI_DIR, "identity");
|
|
5323
6356
|
}
|
|
5324
6357
|
});
|
|
5325
6358
|
|
|
@@ -5853,31 +6886,162 @@ ${PLAN_MODE_COMPAT}
|
|
|
5853
6886
|
}
|
|
5854
6887
|
});
|
|
5855
6888
|
|
|
6889
|
+
// src/lib/session-wrappers.ts
|
|
6890
|
+
var session_wrappers_exports = {};
|
|
6891
|
+
__export(session_wrappers_exports, {
|
|
6892
|
+
generateSessionWrappers: () => generateSessionWrappers
|
|
6893
|
+
});
|
|
6894
|
+
import {
|
|
6895
|
+
existsSync as existsSync13,
|
|
6896
|
+
readFileSync as readFileSync9,
|
|
6897
|
+
writeFileSync as writeFileSync6,
|
|
6898
|
+
mkdirSync as mkdirSync7,
|
|
6899
|
+
chmodSync,
|
|
6900
|
+
readdirSync as readdirSync4,
|
|
6901
|
+
unlinkSync as unlinkSync5
|
|
6902
|
+
} from "fs";
|
|
6903
|
+
import path14 from "path";
|
|
6904
|
+
import { homedir as homedir4 } from "os";
|
|
6905
|
+
function generateSessionWrappers(packageRoot, homeDir) {
|
|
6906
|
+
const home = homeDir ?? homedir4();
|
|
6907
|
+
const binDir = path14.join(home, ".exe-os", "bin");
|
|
6908
|
+
const rosterPath = path14.join(home, ".exe-os", "exe-employees.json");
|
|
6909
|
+
mkdirSync7(binDir, { recursive: true });
|
|
6910
|
+
const exeStartDst = path14.join(binDir, "exe-start");
|
|
6911
|
+
const candidates = [
|
|
6912
|
+
path14.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
6913
|
+
path14.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
6914
|
+
];
|
|
6915
|
+
for (const src of candidates) {
|
|
6916
|
+
if (existsSync13(src)) {
|
|
6917
|
+
writeFileSync6(exeStartDst, readFileSync9(src));
|
|
6918
|
+
chmodSync(exeStartDst, 493);
|
|
6919
|
+
break;
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
let employees = [];
|
|
6923
|
+
try {
|
|
6924
|
+
employees = JSON.parse(readFileSync9(rosterPath, "utf8"));
|
|
6925
|
+
} catch {
|
|
6926
|
+
return { created: 0, pathConfigured: false };
|
|
6927
|
+
}
|
|
6928
|
+
if (employees.length === 0) {
|
|
6929
|
+
return { created: 0, pathConfigured: false };
|
|
6930
|
+
}
|
|
6931
|
+
try {
|
|
6932
|
+
for (const f of readdirSync4(binDir)) {
|
|
6933
|
+
if (f === "exe-start") continue;
|
|
6934
|
+
const fPath = path14.join(binDir, f);
|
|
6935
|
+
try {
|
|
6936
|
+
const content = readFileSync9(fPath, "utf8");
|
|
6937
|
+
if (content.includes("exe-start")) {
|
|
6938
|
+
unlinkSync5(fPath);
|
|
6939
|
+
}
|
|
6940
|
+
} catch {
|
|
6941
|
+
}
|
|
6942
|
+
}
|
|
6943
|
+
} catch {
|
|
6944
|
+
}
|
|
6945
|
+
let created = 0;
|
|
6946
|
+
const wrapperContent = `#!/bin/bash
|
|
6947
|
+
exec "${exeStartDst}" "$0" "$@"
|
|
6948
|
+
`;
|
|
6949
|
+
for (const emp of employees) {
|
|
6950
|
+
for (let n = 1; n <= MAX_N; n++) {
|
|
6951
|
+
const wrapperPath = path14.join(binDir, `${emp.name}${n}`);
|
|
6952
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
6953
|
+
chmodSync(wrapperPath, 493);
|
|
6954
|
+
created++;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
const pathConfigured = ensurePath(home, binDir);
|
|
6958
|
+
return { created, pathConfigured };
|
|
6959
|
+
}
|
|
6960
|
+
function ensurePath(home, binDir) {
|
|
6961
|
+
if (process.env.PATH?.split(":").includes(binDir)) {
|
|
6962
|
+
return false;
|
|
6963
|
+
}
|
|
6964
|
+
const exportLine = `
|
|
6965
|
+
# exe-os session commands
|
|
6966
|
+
export PATH="${binDir}:$PATH"
|
|
6967
|
+
`;
|
|
6968
|
+
const shell = process.env.SHELL ?? "/bin/bash";
|
|
6969
|
+
const profilePaths = [];
|
|
6970
|
+
if (shell.includes("zsh")) {
|
|
6971
|
+
profilePaths.push(path14.join(home, ".zshrc"));
|
|
6972
|
+
} else if (shell.includes("bash")) {
|
|
6973
|
+
profilePaths.push(path14.join(home, ".bashrc"));
|
|
6974
|
+
profilePaths.push(path14.join(home, ".bash_profile"));
|
|
6975
|
+
} else {
|
|
6976
|
+
profilePaths.push(path14.join(home, ".profile"));
|
|
6977
|
+
}
|
|
6978
|
+
for (const profilePath of profilePaths) {
|
|
6979
|
+
try {
|
|
6980
|
+
let content = "";
|
|
6981
|
+
try {
|
|
6982
|
+
content = readFileSync9(profilePath, "utf8");
|
|
6983
|
+
} catch {
|
|
6984
|
+
}
|
|
6985
|
+
if (content.includes(".exe-os/bin")) {
|
|
6986
|
+
return false;
|
|
6987
|
+
}
|
|
6988
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
6989
|
+
return true;
|
|
6990
|
+
} catch {
|
|
6991
|
+
continue;
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
return false;
|
|
6995
|
+
}
|
|
6996
|
+
var MAX_N;
|
|
6997
|
+
var init_session_wrappers = __esm({
|
|
6998
|
+
"src/lib/session-wrappers.ts"() {
|
|
6999
|
+
"use strict";
|
|
7000
|
+
MAX_N = 9;
|
|
7001
|
+
}
|
|
7002
|
+
});
|
|
7003
|
+
|
|
5856
7004
|
// src/lib/setup-wizard.ts
|
|
5857
7005
|
var setup_wizard_exports = {};
|
|
5858
7006
|
__export(setup_wizard_exports, {
|
|
5859
7007
|
runSetupWizard: () => runSetupWizard,
|
|
5860
7008
|
validateModel: () => validateModel
|
|
5861
7009
|
});
|
|
5862
|
-
import
|
|
5863
|
-
import { existsSync as
|
|
7010
|
+
import crypto4 from "crypto";
|
|
7011
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
5864
7012
|
import os5 from "os";
|
|
5865
|
-
import
|
|
7013
|
+
import path15 from "path";
|
|
5866
7014
|
import { createInterface as createInterface2 } from "readline";
|
|
7015
|
+
function findPackageRoot2() {
|
|
7016
|
+
let dir = path15.dirname(new URL(import.meta.url).pathname);
|
|
7017
|
+
const root = path15.parse(dir).root;
|
|
7018
|
+
while (dir !== root) {
|
|
7019
|
+
const pkgPath = path15.join(dir, "package.json");
|
|
7020
|
+
if (existsSync14(pkgPath)) {
|
|
7021
|
+
try {
|
|
7022
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
7023
|
+
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
7024
|
+
} catch {
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
dir = path15.dirname(dir);
|
|
7028
|
+
}
|
|
7029
|
+
return null;
|
|
7030
|
+
}
|
|
5867
7031
|
function loadSetupState() {
|
|
5868
7032
|
try {
|
|
5869
|
-
return JSON.parse(
|
|
7033
|
+
return JSON.parse(readFileSync10(SETUP_STATE_PATH, "utf8"));
|
|
5870
7034
|
} catch {
|
|
5871
7035
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5872
7036
|
}
|
|
5873
7037
|
}
|
|
5874
7038
|
function saveSetupState(state) {
|
|
5875
|
-
|
|
5876
|
-
|
|
7039
|
+
mkdirSync8(path15.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
7040
|
+
writeFileSync7(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5877
7041
|
}
|
|
5878
7042
|
function clearSetupState() {
|
|
5879
7043
|
try {
|
|
5880
|
-
|
|
7044
|
+
unlinkSync6(SETUP_STATE_PATH);
|
|
5881
7045
|
} catch {
|
|
5882
7046
|
}
|
|
5883
7047
|
}
|
|
@@ -5911,20 +7075,67 @@ async function runSetupWizard(opts = {}) {
|
|
|
5911
7075
|
log("");
|
|
5912
7076
|
log("=== exe-os Setup ===");
|
|
5913
7077
|
log("");
|
|
5914
|
-
log("
|
|
5915
|
-
log(" Run `exe-link` to sync your encryption key and skip setup.");
|
|
7078
|
+
log("Is this a new installation or pairing with an existing device?");
|
|
5916
7079
|
log("");
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
7080
|
+
log(" [1] New installation (first device)");
|
|
7081
|
+
log(" [2] Pair with existing device (I have a 24-word phrase)");
|
|
7082
|
+
log("");
|
|
7083
|
+
const installType = await ask(rl, "Choice (1/2): ");
|
|
7084
|
+
let isPairing = false;
|
|
7085
|
+
let pairingRosterPulled = false;
|
|
7086
|
+
if (installType === "2") {
|
|
7087
|
+
isPairing = true;
|
|
7088
|
+
log("");
|
|
7089
|
+
const { importMnemonic: importMnemonic2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|
|
7090
|
+
const mnemonic = await ask(rl, "Paste your 24-word recovery phrase: ");
|
|
7091
|
+
try {
|
|
7092
|
+
const key = await importMnemonic2(mnemonic);
|
|
7093
|
+
await setMasterKey(key);
|
|
7094
|
+
log("Master key imported and stored securely.");
|
|
7095
|
+
log("");
|
|
7096
|
+
log("Enter the API key from your existing device.");
|
|
7097
|
+
log("(Find it in ~/.exe-os/config.json under cloud.apiKey, or ask your team lead.)");
|
|
7098
|
+
log("");
|
|
7099
|
+
const apiKey = await ask(rl, "API key (exe_sk_...): ");
|
|
7100
|
+
if (apiKey && apiKey.startsWith("exe_sk_")) {
|
|
7101
|
+
const cloudEndpoint = "https://askexe.com/cloud";
|
|
7102
|
+
const cloudCfg = { apiKey, endpoint: cloudEndpoint };
|
|
7103
|
+
const earlyConfig = await loadConfig();
|
|
7104
|
+
earlyConfig.cloud = cloudCfg;
|
|
7105
|
+
await saveConfig(earlyConfig);
|
|
7106
|
+
const { saveLicense: saveLic, mirrorLicenseKey: mirrorLic } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
7107
|
+
saveLic(apiKey);
|
|
7108
|
+
mirrorLic(apiKey);
|
|
7109
|
+
log("Cloud sync configured.");
|
|
7110
|
+
try {
|
|
7111
|
+
const { initSyncCrypto: initSyncCrypto2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
7112
|
+
const { cloudPullRoster: cloudPullRoster2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
|
|
7113
|
+
initSyncCrypto2(key);
|
|
7114
|
+
const result = await cloudPullRoster2({ apiKey, endpoint: cloudEndpoint });
|
|
7115
|
+
if (result.added > 0) {
|
|
7116
|
+
log(`Pulled ${result.added} employee(s) from Exe Cloud.`);
|
|
7117
|
+
pairingRosterPulled = true;
|
|
7118
|
+
}
|
|
7119
|
+
} catch {
|
|
7120
|
+
log("Could not pull roster from cloud \u2014 you can set up employees manually.");
|
|
7121
|
+
}
|
|
7122
|
+
} else {
|
|
7123
|
+
log("No API key provided \u2014 cloud sync will need to be configured later.");
|
|
7124
|
+
log("Run /exe-cloud after setup to connect.");
|
|
7125
|
+
}
|
|
7126
|
+
log("");
|
|
7127
|
+
} catch (err) {
|
|
7128
|
+
log(`Key import failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7129
|
+
log("Check your phrase and try again, or choose option 1 for a fresh install.");
|
|
7130
|
+
rl.close();
|
|
7131
|
+
return;
|
|
7132
|
+
}
|
|
5922
7133
|
}
|
|
5923
7134
|
const state = loadSetupState();
|
|
5924
7135
|
if (state.completedSteps.length > 0) {
|
|
5925
7136
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5926
7137
|
}
|
|
5927
|
-
if (
|
|
7138
|
+
if (existsSync14(LEGACY_LANCE_PATH)) {
|
|
5928
7139
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5929
7140
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5930
7141
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -5936,7 +7147,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5936
7147
|
log("Encryption key already exists \u2014 skipping generation.");
|
|
5937
7148
|
} else {
|
|
5938
7149
|
log("Generating 256-bit encryption key...");
|
|
5939
|
-
const key =
|
|
7150
|
+
const key = crypto4.randomBytes(32);
|
|
5940
7151
|
await setMasterKey(key);
|
|
5941
7152
|
log("Encryption key generated and stored securely.");
|
|
5942
7153
|
}
|
|
@@ -5947,7 +7158,15 @@ async function runSetupWizard(opts = {}) {
|
|
|
5947
7158
|
}
|
|
5948
7159
|
log("");
|
|
5949
7160
|
let cloudConfig;
|
|
5950
|
-
if (
|
|
7161
|
+
if (isPairing) {
|
|
7162
|
+
const pairingConfig = await loadConfig();
|
|
7163
|
+
if (pairingConfig.cloud?.apiKey) {
|
|
7164
|
+
cloudConfig = pairingConfig.cloud;
|
|
7165
|
+
log("Cloud sync: using shared API key from pairing.");
|
|
7166
|
+
}
|
|
7167
|
+
state.completedSteps.push(2);
|
|
7168
|
+
saveSetupState(state);
|
|
7169
|
+
} else if (!state.completedSteps.includes(2)) {
|
|
5951
7170
|
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5952
7171
|
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5953
7172
|
log("only your encryption key can decrypt it.");
|
|
@@ -6028,10 +7247,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
6028
7247
|
await saveConfig(config);
|
|
6029
7248
|
log("");
|
|
6030
7249
|
try {
|
|
6031
|
-
const claudeJsonPath =
|
|
7250
|
+
const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
|
|
6032
7251
|
let claudeJson = {};
|
|
6033
7252
|
try {
|
|
6034
|
-
claudeJson = JSON.parse(
|
|
7253
|
+
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
6035
7254
|
} catch {
|
|
6036
7255
|
}
|
|
6037
7256
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6040,7 +7259,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6040
7259
|
if (!projects[dir]) projects[dir] = {};
|
|
6041
7260
|
projects[dir].hasTrustDialogAccepted = true;
|
|
6042
7261
|
}
|
|
6043
|
-
|
|
7262
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6044
7263
|
} catch {
|
|
6045
7264
|
}
|
|
6046
7265
|
state.completedSteps.push(5);
|
|
@@ -6066,7 +7285,34 @@ async function runSetupWizard(opts = {}) {
|
|
|
6066
7285
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
6067
7286
|
const createdEmployees = [];
|
|
6068
7287
|
let cooName = "exe";
|
|
6069
|
-
if (
|
|
7288
|
+
if (pairingRosterPulled) {
|
|
7289
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
7290
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
7291
|
+
if (existingCoo) {
|
|
7292
|
+
cooName = existingCoo.name;
|
|
7293
|
+
log(`Team synced from cloud. COO: ${cooName}`);
|
|
7294
|
+
const teamList = roster.map((e) => `${e.name} (${e.role})`).join(", ");
|
|
7295
|
+
log(`Team: ${teamList}`);
|
|
7296
|
+
createdEmployees.push(...roster.map((e) => ({ name: e.name, role: e.role })));
|
|
7297
|
+
}
|
|
7298
|
+
for (const emp of roster) {
|
|
7299
|
+
registerBinSymlinks2(emp.name);
|
|
7300
|
+
}
|
|
7301
|
+
try {
|
|
7302
|
+
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
7303
|
+
const pkgRoot = findPackageRoot2();
|
|
7304
|
+
if (pkgRoot) {
|
|
7305
|
+
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
7306
|
+
if (wrapResult.created > 0) {
|
|
7307
|
+
log(`Session shortcuts generated: ${roster.map((e) => `${e.name}1`).join(", ")}, ...`);
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
} catch {
|
|
7311
|
+
}
|
|
7312
|
+
state.completedSteps.push(6, 7, 8);
|
|
7313
|
+
saveSetupState(state);
|
|
7314
|
+
log("");
|
|
7315
|
+
} else if (!state.completedSteps.includes(6)) {
|
|
6070
7316
|
log("=== Your Team ===");
|
|
6071
7317
|
log("");
|
|
6072
7318
|
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
@@ -6092,9 +7338,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6092
7338
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
6093
7339
|
if (cooIdentityContent) {
|
|
6094
7340
|
const cooIdPath = identityPath2(cooName);
|
|
6095
|
-
|
|
7341
|
+
mkdirSync8(path15.dirname(cooIdPath), { recursive: true });
|
|
6096
7342
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
6097
|
-
|
|
7343
|
+
writeFileSync7(cooIdPath, replaced, "utf-8");
|
|
6098
7344
|
}
|
|
6099
7345
|
registerBinSymlinks2(cooName);
|
|
6100
7346
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -6193,9 +7439,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6193
7439
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
6194
7440
|
if (ctoIdentityContent) {
|
|
6195
7441
|
const ctoIdPath = identityPath2(ctoName);
|
|
6196
|
-
|
|
7442
|
+
mkdirSync8(path15.dirname(ctoIdPath), { recursive: true });
|
|
6197
7443
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
6198
|
-
|
|
7444
|
+
writeFileSync7(ctoIdPath, replaced, "utf-8");
|
|
6199
7445
|
}
|
|
6200
7446
|
registerBinSymlinks2(ctoName);
|
|
6201
7447
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -6221,9 +7467,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6221
7467
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
6222
7468
|
if (cmoIdentityContent) {
|
|
6223
7469
|
const cmoIdPath = identityPath2(cmoName);
|
|
6224
|
-
|
|
7470
|
+
mkdirSync8(path15.dirname(cmoIdPath), { recursive: true });
|
|
6225
7471
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
6226
|
-
|
|
7472
|
+
writeFileSync7(cmoIdPath, replaced, "utf-8");
|
|
6227
7473
|
}
|
|
6228
7474
|
registerBinSymlinks2(cmoName);
|
|
6229
7475
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -6236,11 +7482,24 @@ async function runSetupWizard(opts = {}) {
|
|
|
6236
7482
|
} else {
|
|
6237
7483
|
log("Step 8 already complete \u2014 skipping.");
|
|
6238
7484
|
}
|
|
7485
|
+
if (!pairingRosterPulled) {
|
|
7486
|
+
try {
|
|
7487
|
+
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
7488
|
+
const pkgRoot = findPackageRoot2();
|
|
7489
|
+
if (pkgRoot) {
|
|
7490
|
+
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
7491
|
+
if (wrapResult.created > 0) {
|
|
7492
|
+
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
} catch {
|
|
7496
|
+
}
|
|
7497
|
+
}
|
|
6239
7498
|
clearSetupState();
|
|
6240
7499
|
log("=== Two Ways to Work ===");
|
|
6241
7500
|
log("");
|
|
6242
7501
|
log(" 1. Claude Code mode");
|
|
6243
|
-
log(` Type \`${cooName}\` in your
|
|
7502
|
+
log(` Type \`${cooName}1\` in your project folder. Works with your Claude Code subscription.`);
|
|
6244
7503
|
log(" Best for developers who live in the terminal.");
|
|
6245
7504
|
log("");
|
|
6246
7505
|
log(" 2. Dashboard mode");
|
|
@@ -6250,8 +7509,6 @@ async function runSetupWizard(opts = {}) {
|
|
|
6250
7509
|
log(" Both modes share the same memory, employees, and data.");
|
|
6251
7510
|
log(" You can switch anytime.");
|
|
6252
7511
|
log("");
|
|
6253
|
-
log(" For Claude Code mode, run: exe-os claude");
|
|
6254
|
-
log("");
|
|
6255
7512
|
log("=== Setup Complete ===");
|
|
6256
7513
|
log("Database: " + config.dbPath);
|
|
6257
7514
|
if (cloudConfig) {
|
|
@@ -6267,7 +7524,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6267
7524
|
log("Team: " + createdEmployees.map((e) => `${e.name} (${e.role})`).join(", "));
|
|
6268
7525
|
}
|
|
6269
7526
|
log("");
|
|
6270
|
-
log(`Type \`${cooName}\` to start (Claude Code) or \`exe-os\` for dashboard.`);
|
|
7527
|
+
log(`Type \`${cooName}1\` to start (Claude Code) or \`exe-os\` for dashboard.`);
|
|
6271
7528
|
log("");
|
|
6272
7529
|
} finally {
|
|
6273
7530
|
rl.close();
|
|
@@ -6280,17 +7537,17 @@ var init_setup_wizard = __esm({
|
|
|
6280
7537
|
init_config();
|
|
6281
7538
|
init_keychain();
|
|
6282
7539
|
init_model_downloader();
|
|
6283
|
-
SETUP_STATE_PATH =
|
|
7540
|
+
SETUP_STATE_PATH = path15.join(os5.homedir(), ".exe-os", "setup-state.json");
|
|
6284
7541
|
}
|
|
6285
7542
|
});
|
|
6286
7543
|
|
|
6287
7544
|
// src/lib/update-check.ts
|
|
6288
7545
|
import { execSync as execSync4 } from "child_process";
|
|
6289
|
-
import { readFileSync as
|
|
6290
|
-
import
|
|
7546
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7547
|
+
import path16 from "path";
|
|
6291
7548
|
function getLocalVersion(packageRoot) {
|
|
6292
|
-
const pkgPath =
|
|
6293
|
-
const pkg = JSON.parse(
|
|
7549
|
+
const pkgPath = path16.join(packageRoot, "package.json");
|
|
7550
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
6294
7551
|
return pkg.version;
|
|
6295
7552
|
}
|
|
6296
7553
|
function getRemoteVersion() {
|
|
@@ -7925,7 +9182,7 @@ var init_yoga_wasm_base64_esm = __esm({
|
|
|
7925
9182
|
});
|
|
7926
9183
|
|
|
7927
9184
|
// node_modules/yoga-layout/dist/src/generated/YGEnums.js
|
|
7928
|
-
var Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap,
|
|
9185
|
+
var Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, constants2, YGEnums_default;
|
|
7929
9186
|
var init_YGEnums = __esm({
|
|
7930
9187
|
"node_modules/yoga-layout/dist/src/generated/YGEnums.js"() {
|
|
7931
9188
|
"use strict";
|
|
@@ -8055,7 +9312,7 @@ var init_YGEnums = __esm({
|
|
|
8055
9312
|
Wrap2[Wrap2["WrapReverse"] = 2] = "WrapReverse";
|
|
8056
9313
|
return Wrap2;
|
|
8057
9314
|
})({});
|
|
8058
|
-
|
|
9315
|
+
constants2 = {
|
|
8059
9316
|
ALIGN_AUTO: Align.Auto,
|
|
8060
9317
|
ALIGN_FLEX_START: Align.FlexStart,
|
|
8061
9318
|
ALIGN_CENTER: Align.Center,
|
|
@@ -8129,7 +9386,7 @@ var init_YGEnums = __esm({
|
|
|
8129
9386
|
WRAP_WRAP: Wrap.Wrap,
|
|
8130
9387
|
WRAP_WRAP_REVERSE: Wrap.WrapReverse
|
|
8131
9388
|
};
|
|
8132
|
-
YGEnums_default =
|
|
9389
|
+
YGEnums_default = constants2;
|
|
8133
9390
|
}
|
|
8134
9391
|
});
|
|
8135
9392
|
|
|
@@ -10813,8 +12070,8 @@ var init_ErrorOverview = __esm({
|
|
|
10813
12070
|
"use strict";
|
|
10814
12071
|
init_Box();
|
|
10815
12072
|
init_Text();
|
|
10816
|
-
cleanupPath = (
|
|
10817
|
-
return
|
|
12073
|
+
cleanupPath = (path36) => {
|
|
12074
|
+
return path36?.replace(`file://${cwd()}/`, "");
|
|
10818
12075
|
};
|
|
10819
12076
|
stackUtils = new StackUtils({
|
|
10820
12077
|
cwd: cwd(),
|
|
@@ -12843,14 +14100,14 @@ __export(session_registry_exports, {
|
|
|
12843
14100
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
12844
14101
|
registerSession: () => registerSession
|
|
12845
14102
|
});
|
|
12846
|
-
import { readFileSync as
|
|
14103
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, existsSync as existsSync16 } from "fs";
|
|
12847
14104
|
import { execSync as execSync6 } from "child_process";
|
|
12848
|
-
import
|
|
14105
|
+
import path17 from "path";
|
|
12849
14106
|
import os6 from "os";
|
|
12850
14107
|
function registerSession(entry) {
|
|
12851
|
-
const dir =
|
|
12852
|
-
if (!
|
|
12853
|
-
|
|
14108
|
+
const dir = path17.dirname(REGISTRY_PATH);
|
|
14109
|
+
if (!existsSync16(dir)) {
|
|
14110
|
+
mkdirSync9(dir, { recursive: true });
|
|
12854
14111
|
}
|
|
12855
14112
|
const sessions = listSessions();
|
|
12856
14113
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -12859,11 +14116,11 @@ function registerSession(entry) {
|
|
|
12859
14116
|
} else {
|
|
12860
14117
|
sessions.push(entry);
|
|
12861
14118
|
}
|
|
12862
|
-
|
|
14119
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
12863
14120
|
}
|
|
12864
14121
|
function listSessions() {
|
|
12865
14122
|
try {
|
|
12866
|
-
const raw =
|
|
14123
|
+
const raw = readFileSync13(REGISTRY_PATH, "utf8");
|
|
12867
14124
|
return JSON.parse(raw);
|
|
12868
14125
|
} catch {
|
|
12869
14126
|
return [];
|
|
@@ -12884,7 +14141,7 @@ function pruneStaleSessions() {
|
|
|
12884
14141
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
12885
14142
|
const pruned = sessions.length - alive.length;
|
|
12886
14143
|
if (pruned > 0) {
|
|
12887
|
-
|
|
14144
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
12888
14145
|
}
|
|
12889
14146
|
return pruned;
|
|
12890
14147
|
}
|
|
@@ -12892,7 +14149,7 @@ var REGISTRY_PATH;
|
|
|
12892
14149
|
var init_session_registry = __esm({
|
|
12893
14150
|
"src/lib/session-registry.ts"() {
|
|
12894
14151
|
"use strict";
|
|
12895
|
-
REGISTRY_PATH =
|
|
14152
|
+
REGISTRY_PATH = path17.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
12896
14153
|
}
|
|
12897
14154
|
});
|
|
12898
14155
|
|
|
@@ -13097,17 +14354,17 @@ var init_provider_table = __esm({
|
|
|
13097
14354
|
});
|
|
13098
14355
|
|
|
13099
14356
|
// src/lib/intercom-queue.ts
|
|
13100
|
-
import { readFileSync as
|
|
13101
|
-
import
|
|
14357
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, renameSync as renameSync4, existsSync as existsSync17, mkdirSync as mkdirSync10 } from "fs";
|
|
14358
|
+
import path18 from "path";
|
|
13102
14359
|
import os7 from "os";
|
|
13103
14360
|
function ensureDir2() {
|
|
13104
|
-
const dir =
|
|
13105
|
-
if (!
|
|
14361
|
+
const dir = path18.dirname(QUEUE_PATH);
|
|
14362
|
+
if (!existsSync17(dir)) mkdirSync10(dir, { recursive: true });
|
|
13106
14363
|
}
|
|
13107
14364
|
function readQueue() {
|
|
13108
14365
|
try {
|
|
13109
|
-
if (!
|
|
13110
|
-
return JSON.parse(
|
|
14366
|
+
if (!existsSync17(QUEUE_PATH)) return [];
|
|
14367
|
+
return JSON.parse(readFileSync14(QUEUE_PATH, "utf8"));
|
|
13111
14368
|
} catch {
|
|
13112
14369
|
return [];
|
|
13113
14370
|
}
|
|
@@ -13115,7 +14372,7 @@ function readQueue() {
|
|
|
13115
14372
|
function writeQueue(queue) {
|
|
13116
14373
|
ensureDir2();
|
|
13117
14374
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
13118
|
-
|
|
14375
|
+
writeFileSync9(tmp, JSON.stringify(queue, null, 2));
|
|
13119
14376
|
renameSync4(tmp, QUEUE_PATH);
|
|
13120
14377
|
}
|
|
13121
14378
|
function queueIntercom(targetSession, reason) {
|
|
@@ -13139,19 +14396,19 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
13139
14396
|
var init_intercom_queue = __esm({
|
|
13140
14397
|
"src/lib/intercom-queue.ts"() {
|
|
13141
14398
|
"use strict";
|
|
13142
|
-
QUEUE_PATH =
|
|
14399
|
+
QUEUE_PATH = path18.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
13143
14400
|
TTL_MS = 60 * 60 * 1e3;
|
|
13144
|
-
INTERCOM_LOG =
|
|
14401
|
+
INTERCOM_LOG = path18.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
13145
14402
|
}
|
|
13146
14403
|
});
|
|
13147
14404
|
|
|
13148
14405
|
// src/lib/plan-limits.ts
|
|
13149
|
-
import { readFileSync as
|
|
13150
|
-
import
|
|
14406
|
+
import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
|
|
14407
|
+
import path19 from "path";
|
|
13151
14408
|
function getLicenseSync() {
|
|
13152
14409
|
try {
|
|
13153
|
-
if (!
|
|
13154
|
-
const raw = JSON.parse(
|
|
14410
|
+
if (!existsSync18(CACHE_PATH2)) return freeLicense();
|
|
14411
|
+
const raw = JSON.parse(readFileSync15(CACHE_PATH2, "utf8"));
|
|
13155
14412
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
13156
14413
|
const parts = raw.token.split(".");
|
|
13157
14414
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -13189,8 +14446,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
13189
14446
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
13190
14447
|
let count = 0;
|
|
13191
14448
|
try {
|
|
13192
|
-
if (
|
|
13193
|
-
const raw =
|
|
14449
|
+
if (existsSync18(filePath)) {
|
|
14450
|
+
const raw = readFileSync15(filePath, "utf8");
|
|
13194
14451
|
const employees = JSON.parse(raw);
|
|
13195
14452
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
13196
14453
|
}
|
|
@@ -13219,25 +14476,25 @@ var init_plan_limits = __esm({
|
|
|
13219
14476
|
this.name = "PlanLimitError";
|
|
13220
14477
|
}
|
|
13221
14478
|
};
|
|
13222
|
-
CACHE_PATH2 =
|
|
14479
|
+
CACHE_PATH2 = path19.join(EXE_AI_DIR, "license-cache.json");
|
|
13223
14480
|
}
|
|
13224
14481
|
});
|
|
13225
14482
|
|
|
13226
14483
|
// src/lib/notifications.ts
|
|
13227
|
-
import
|
|
13228
|
-
import
|
|
14484
|
+
import crypto5 from "crypto";
|
|
14485
|
+
import path20 from "path";
|
|
13229
14486
|
import os8 from "os";
|
|
13230
14487
|
import {
|
|
13231
|
-
readFileSync as
|
|
13232
|
-
readdirSync as
|
|
13233
|
-
unlinkSync as
|
|
13234
|
-
existsSync as
|
|
14488
|
+
readFileSync as readFileSync16,
|
|
14489
|
+
readdirSync as readdirSync5,
|
|
14490
|
+
unlinkSync as unlinkSync7,
|
|
14491
|
+
existsSync as existsSync19,
|
|
13235
14492
|
rmdirSync
|
|
13236
14493
|
} from "fs";
|
|
13237
14494
|
async function writeNotification(notification) {
|
|
13238
14495
|
try {
|
|
13239
14496
|
const client = getClient();
|
|
13240
|
-
const id =
|
|
14497
|
+
const id = crypto5.randomUUID();
|
|
13241
14498
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13242
14499
|
await client.execute({
|
|
13243
14500
|
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
@@ -13276,7 +14533,7 @@ var init_notifications = __esm({
|
|
|
13276
14533
|
});
|
|
13277
14534
|
|
|
13278
14535
|
// src/lib/session-kill-telemetry.ts
|
|
13279
|
-
import
|
|
14536
|
+
import crypto6 from "crypto";
|
|
13280
14537
|
async function recordSessionKill(input) {
|
|
13281
14538
|
try {
|
|
13282
14539
|
const client = getClient();
|
|
@@ -13286,7 +14543,7 @@ async function recordSessionKill(input) {
|
|
|
13286
14543
|
ticks_idle, estimated_tokens_saved)
|
|
13287
14544
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
13288
14545
|
args: [
|
|
13289
|
-
|
|
14546
|
+
crypto6.randomUUID(),
|
|
13290
14547
|
input.sessionName,
|
|
13291
14548
|
input.agentId,
|
|
13292
14549
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13325,11 +14582,11 @@ __export(tasks_crud_exports, {
|
|
|
13325
14582
|
updateTaskStatus: () => updateTaskStatus,
|
|
13326
14583
|
writeCheckpoint: () => writeCheckpoint
|
|
13327
14584
|
});
|
|
13328
|
-
import
|
|
13329
|
-
import
|
|
14585
|
+
import crypto7 from "crypto";
|
|
14586
|
+
import path21 from "path";
|
|
13330
14587
|
import { execSync as execSync9 } from "child_process";
|
|
13331
14588
|
import { mkdir as mkdir6, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
13332
|
-
import { existsSync as
|
|
14589
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
13333
14590
|
async function writeCheckpoint(input) {
|
|
13334
14591
|
const client = getClient();
|
|
13335
14592
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -13416,7 +14673,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
13416
14673
|
}
|
|
13417
14674
|
async function createTaskCore(input) {
|
|
13418
14675
|
const client = getClient();
|
|
13419
|
-
const id =
|
|
14676
|
+
const id = crypto7.randomUUID();
|
|
13420
14677
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13421
14678
|
const slug = slugify(input.title);
|
|
13422
14679
|
const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
|
|
@@ -13461,8 +14718,8 @@ async function createTaskCore(input) {
|
|
|
13461
14718
|
}
|
|
13462
14719
|
if (input.baseDir) {
|
|
13463
14720
|
try {
|
|
13464
|
-
await mkdir6(
|
|
13465
|
-
await mkdir6(
|
|
14721
|
+
await mkdir6(path21.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
14722
|
+
await mkdir6(path21.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
13466
14723
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
13467
14724
|
await ensureGitignoreExe(input.baseDir);
|
|
13468
14725
|
} catch {
|
|
@@ -13682,9 +14939,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
13682
14939
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
13683
14940
|
}
|
|
13684
14941
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
13685
|
-
const archPath =
|
|
14942
|
+
const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
13686
14943
|
try {
|
|
13687
|
-
if (
|
|
14944
|
+
if (existsSync20(archPath)) return;
|
|
13688
14945
|
const template = [
|
|
13689
14946
|
`# ${projectName} \u2014 System Architecture`,
|
|
13690
14947
|
"",
|
|
@@ -13717,10 +14974,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
13717
14974
|
}
|
|
13718
14975
|
}
|
|
13719
14976
|
async function ensureGitignoreExe(baseDir) {
|
|
13720
|
-
const gitignorePath =
|
|
14977
|
+
const gitignorePath = path21.join(baseDir, ".gitignore");
|
|
13721
14978
|
try {
|
|
13722
|
-
if (
|
|
13723
|
-
const content =
|
|
14979
|
+
if (existsSync20(gitignorePath)) {
|
|
14980
|
+
const content = readFileSync17(gitignorePath, "utf-8");
|
|
13724
14981
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
13725
14982
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
13726
14983
|
} else {
|
|
@@ -13741,8 +14998,8 @@ var init_tasks_crud = __esm({
|
|
|
13741
14998
|
});
|
|
13742
14999
|
|
|
13743
15000
|
// src/lib/tasks-review.ts
|
|
13744
|
-
import
|
|
13745
|
-
import { existsSync as
|
|
15001
|
+
import path22 from "path";
|
|
15002
|
+
import { existsSync as existsSync21, readdirSync as readdirSync6, unlinkSync as unlinkSync8 } from "fs";
|
|
13746
15003
|
async function countPendingReviews(sessionScope) {
|
|
13747
15004
|
const client = getClient();
|
|
13748
15005
|
if (sessionScope) {
|
|
@@ -13923,11 +15180,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
13923
15180
|
);
|
|
13924
15181
|
}
|
|
13925
15182
|
try {
|
|
13926
|
-
const cacheDir =
|
|
13927
|
-
if (
|
|
13928
|
-
for (const f of
|
|
15183
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
15184
|
+
if (existsSync21(cacheDir)) {
|
|
15185
|
+
for (const f of readdirSync6(cacheDir)) {
|
|
13929
15186
|
if (f.startsWith("review-notified-")) {
|
|
13930
|
-
|
|
15187
|
+
unlinkSync8(path22.join(cacheDir, f));
|
|
13931
15188
|
}
|
|
13932
15189
|
}
|
|
13933
15190
|
}
|
|
@@ -13948,7 +15205,7 @@ var init_tasks_review = __esm({
|
|
|
13948
15205
|
});
|
|
13949
15206
|
|
|
13950
15207
|
// src/lib/tasks-chain.ts
|
|
13951
|
-
import
|
|
15208
|
+
import path23 from "path";
|
|
13952
15209
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
13953
15210
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
13954
15211
|
const client = getClient();
|
|
@@ -13965,7 +15222,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
13965
15222
|
});
|
|
13966
15223
|
for (const ur of unblockedRows.rows) {
|
|
13967
15224
|
try {
|
|
13968
|
-
const ubFile =
|
|
15225
|
+
const ubFile = path23.join(baseDir, String(ur.task_file));
|
|
13969
15226
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
13970
15227
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
13971
15228
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -14034,7 +15291,7 @@ var init_tasks_chain = __esm({
|
|
|
14034
15291
|
|
|
14035
15292
|
// src/lib/project-name.ts
|
|
14036
15293
|
import { execSync as execSync10 } from "child_process";
|
|
14037
|
-
import
|
|
15294
|
+
import path24 from "path";
|
|
14038
15295
|
function getProjectName(cwd2) {
|
|
14039
15296
|
const dir = cwd2 ?? process.cwd();
|
|
14040
15297
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -14047,7 +15304,7 @@ function getProjectName(cwd2) {
|
|
|
14047
15304
|
timeout: 2e3,
|
|
14048
15305
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14049
15306
|
}).trim();
|
|
14050
|
-
repoRoot =
|
|
15307
|
+
repoRoot = path24.dirname(gitCommonDir);
|
|
14051
15308
|
} catch {
|
|
14052
15309
|
repoRoot = execSync10("git rev-parse --show-toplevel", {
|
|
14053
15310
|
cwd: dir,
|
|
@@ -14056,11 +15313,11 @@ function getProjectName(cwd2) {
|
|
|
14056
15313
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14057
15314
|
}).trim();
|
|
14058
15315
|
}
|
|
14059
|
-
_cached2 =
|
|
15316
|
+
_cached2 = path24.basename(repoRoot);
|
|
14060
15317
|
_cachedCwd = dir;
|
|
14061
15318
|
return _cached2;
|
|
14062
15319
|
} catch {
|
|
14063
|
-
_cached2 =
|
|
15320
|
+
_cached2 = path24.basename(dir);
|
|
14064
15321
|
_cachedCwd = dir;
|
|
14065
15322
|
return _cached2;
|
|
14066
15323
|
}
|
|
@@ -14202,10 +15459,10 @@ var init_tasks_notify = __esm({
|
|
|
14202
15459
|
});
|
|
14203
15460
|
|
|
14204
15461
|
// src/lib/behaviors.ts
|
|
14205
|
-
import
|
|
15462
|
+
import crypto8 from "crypto";
|
|
14206
15463
|
async function storeBehavior(opts) {
|
|
14207
15464
|
const client = getClient();
|
|
14208
|
-
const id =
|
|
15465
|
+
const id = crypto8.randomUUID();
|
|
14209
15466
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14210
15467
|
await client.execute({
|
|
14211
15468
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -14234,7 +15491,7 @@ __export(skill_learning_exports, {
|
|
|
14234
15491
|
storeTrajectory: () => storeTrajectory,
|
|
14235
15492
|
sweepTrajectories: () => sweepTrajectories
|
|
14236
15493
|
});
|
|
14237
|
-
import
|
|
15494
|
+
import crypto9 from "crypto";
|
|
14238
15495
|
async function extractTrajectory(taskId, agentId) {
|
|
14239
15496
|
const client = getClient();
|
|
14240
15497
|
const result = await client.execute({
|
|
@@ -14263,11 +15520,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
14263
15520
|
return signature;
|
|
14264
15521
|
}
|
|
14265
15522
|
function hashSignature(signature) {
|
|
14266
|
-
return
|
|
15523
|
+
return crypto9.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
14267
15524
|
}
|
|
14268
15525
|
async function storeTrajectory(opts) {
|
|
14269
15526
|
const client = getClient();
|
|
14270
|
-
const id =
|
|
15527
|
+
const id = crypto9.randomUUID();
|
|
14271
15528
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14272
15529
|
const signatureHash = hashSignature(opts.signature);
|
|
14273
15530
|
await client.execute({
|
|
@@ -14532,8 +15789,8 @@ __export(tasks_exports, {
|
|
|
14532
15789
|
updateTaskStatus: () => updateTaskStatus,
|
|
14533
15790
|
writeCheckpoint: () => writeCheckpoint
|
|
14534
15791
|
});
|
|
14535
|
-
import
|
|
14536
|
-
import { writeFileSync as
|
|
15792
|
+
import path25 from "path";
|
|
15793
|
+
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync11, unlinkSync as unlinkSync9 } from "fs";
|
|
14537
15794
|
async function createTask(input) {
|
|
14538
15795
|
const result = await createTaskCore(input);
|
|
14539
15796
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -14552,14 +15809,14 @@ async function updateTask(input) {
|
|
|
14552
15809
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
14553
15810
|
try {
|
|
14554
15811
|
const agent = String(row.assigned_to);
|
|
14555
|
-
const cacheDir =
|
|
14556
|
-
const cachePath =
|
|
15812
|
+
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
15813
|
+
const cachePath = path25.join(cacheDir, `current-task-${agent}.json`);
|
|
14557
15814
|
if (input.status === "in_progress") {
|
|
14558
|
-
|
|
14559
|
-
|
|
15815
|
+
mkdirSync11(cacheDir, { recursive: true });
|
|
15816
|
+
writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
14560
15817
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
14561
15818
|
try {
|
|
14562
|
-
|
|
15819
|
+
unlinkSync9(cachePath);
|
|
14563
15820
|
} catch {
|
|
14564
15821
|
}
|
|
14565
15822
|
}
|
|
@@ -15001,13 +16258,13 @@ __export(tmux_routing_exports, {
|
|
|
15001
16258
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
15002
16259
|
});
|
|
15003
16260
|
import { execFileSync as execFileSync3, execSync as execSync11 } from "child_process";
|
|
15004
|
-
import { readFileSync as
|
|
15005
|
-
import
|
|
16261
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, existsSync as existsSync22, appendFileSync as appendFileSync2 } from "fs";
|
|
16262
|
+
import path26 from "path";
|
|
15006
16263
|
import os9 from "os";
|
|
15007
16264
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15008
|
-
import { unlinkSync as
|
|
16265
|
+
import { unlinkSync as unlinkSync10 } from "fs";
|
|
15009
16266
|
function spawnLockPath(sessionName) {
|
|
15010
|
-
return
|
|
16267
|
+
return path26.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
15011
16268
|
}
|
|
15012
16269
|
function isProcessAlive(pid) {
|
|
15013
16270
|
try {
|
|
@@ -15018,13 +16275,13 @@ function isProcessAlive(pid) {
|
|
|
15018
16275
|
}
|
|
15019
16276
|
}
|
|
15020
16277
|
function acquireSpawnLock2(sessionName) {
|
|
15021
|
-
if (!
|
|
15022
|
-
|
|
16278
|
+
if (!existsSync22(SPAWN_LOCK_DIR)) {
|
|
16279
|
+
mkdirSync12(SPAWN_LOCK_DIR, { recursive: true });
|
|
15023
16280
|
}
|
|
15024
16281
|
const lockFile = spawnLockPath(sessionName);
|
|
15025
|
-
if (
|
|
16282
|
+
if (existsSync22(lockFile)) {
|
|
15026
16283
|
try {
|
|
15027
|
-
const lock = JSON.parse(
|
|
16284
|
+
const lock = JSON.parse(readFileSync18(lockFile, "utf8"));
|
|
15028
16285
|
const age = Date.now() - lock.timestamp;
|
|
15029
16286
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
15030
16287
|
return false;
|
|
@@ -15032,25 +16289,25 @@ function acquireSpawnLock2(sessionName) {
|
|
|
15032
16289
|
} catch {
|
|
15033
16290
|
}
|
|
15034
16291
|
}
|
|
15035
|
-
|
|
16292
|
+
writeFileSync11(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
15036
16293
|
return true;
|
|
15037
16294
|
}
|
|
15038
16295
|
function releaseSpawnLock2(sessionName) {
|
|
15039
16296
|
try {
|
|
15040
|
-
|
|
16297
|
+
unlinkSync10(spawnLockPath(sessionName));
|
|
15041
16298
|
} catch {
|
|
15042
16299
|
}
|
|
15043
16300
|
}
|
|
15044
16301
|
function resolveBehaviorsExporterScript() {
|
|
15045
16302
|
try {
|
|
15046
16303
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
15047
|
-
const scriptPath =
|
|
15048
|
-
|
|
16304
|
+
const scriptPath = path26.join(
|
|
16305
|
+
path26.dirname(thisFile),
|
|
15049
16306
|
"..",
|
|
15050
16307
|
"bin",
|
|
15051
16308
|
"exe-export-behaviors.js"
|
|
15052
16309
|
);
|
|
15053
|
-
return
|
|
16310
|
+
return existsSync22(scriptPath) ? scriptPath : null;
|
|
15054
16311
|
} catch {
|
|
15055
16312
|
return null;
|
|
15056
16313
|
}
|
|
@@ -15114,12 +16371,12 @@ function extractRootExe(name) {
|
|
|
15114
16371
|
return match?.[1] ?? null;
|
|
15115
16372
|
}
|
|
15116
16373
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
15117
|
-
if (!
|
|
15118
|
-
|
|
16374
|
+
if (!existsSync22(SESSION_CACHE)) {
|
|
16375
|
+
mkdirSync12(SESSION_CACHE, { recursive: true });
|
|
15119
16376
|
}
|
|
15120
16377
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
15121
|
-
const filePath =
|
|
15122
|
-
|
|
16378
|
+
const filePath = path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
16379
|
+
writeFileSync11(filePath, JSON.stringify({
|
|
15123
16380
|
parentExe: rootExe,
|
|
15124
16381
|
dispatchedBy: dispatchedBy || rootExe,
|
|
15125
16382
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15127,7 +16384,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
15127
16384
|
}
|
|
15128
16385
|
function getParentExe(sessionKey) {
|
|
15129
16386
|
try {
|
|
15130
|
-
const data = JSON.parse(
|
|
16387
|
+
const data = JSON.parse(readFileSync18(path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
15131
16388
|
return data.parentExe || null;
|
|
15132
16389
|
} catch {
|
|
15133
16390
|
return null;
|
|
@@ -15135,8 +16392,8 @@ function getParentExe(sessionKey) {
|
|
|
15135
16392
|
}
|
|
15136
16393
|
function getDispatchedBy(sessionKey) {
|
|
15137
16394
|
try {
|
|
15138
|
-
const data = JSON.parse(
|
|
15139
|
-
|
|
16395
|
+
const data = JSON.parse(readFileSync18(
|
|
16396
|
+
path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
15140
16397
|
"utf8"
|
|
15141
16398
|
));
|
|
15142
16399
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -15197,16 +16454,16 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
15197
16454
|
}
|
|
15198
16455
|
function readDebounceState() {
|
|
15199
16456
|
try {
|
|
15200
|
-
if (!
|
|
15201
|
-
return JSON.parse(
|
|
16457
|
+
if (!existsSync22(DEBOUNCE_FILE)) return {};
|
|
16458
|
+
return JSON.parse(readFileSync18(DEBOUNCE_FILE, "utf8"));
|
|
15202
16459
|
} catch {
|
|
15203
16460
|
return {};
|
|
15204
16461
|
}
|
|
15205
16462
|
}
|
|
15206
16463
|
function writeDebounceState(state) {
|
|
15207
16464
|
try {
|
|
15208
|
-
if (!
|
|
15209
|
-
|
|
16465
|
+
if (!existsSync22(SESSION_CACHE)) mkdirSync12(SESSION_CACHE, { recursive: true });
|
|
16466
|
+
writeFileSync11(DEBOUNCE_FILE, JSON.stringify(state));
|
|
15210
16467
|
} catch {
|
|
15211
16468
|
}
|
|
15212
16469
|
}
|
|
@@ -15230,7 +16487,7 @@ function logIntercom(msg) {
|
|
|
15230
16487
|
process.stderr.write(`[intercom] ${msg}
|
|
15231
16488
|
`);
|
|
15232
16489
|
try {
|
|
15233
|
-
|
|
16490
|
+
appendFileSync2(INTERCOM_LOG2, line);
|
|
15234
16491
|
} catch {
|
|
15235
16492
|
}
|
|
15236
16493
|
}
|
|
@@ -15395,26 +16652,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15395
16652
|
const transport = getTransport();
|
|
15396
16653
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
15397
16654
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
15398
|
-
const logDir =
|
|
15399
|
-
const logFile =
|
|
15400
|
-
if (!
|
|
15401
|
-
|
|
16655
|
+
const logDir = path26.join(os9.homedir(), ".exe-os", "session-logs");
|
|
16656
|
+
const logFile = path26.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
16657
|
+
if (!existsSync22(logDir)) {
|
|
16658
|
+
mkdirSync12(logDir, { recursive: true });
|
|
15402
16659
|
}
|
|
15403
16660
|
transport.kill(sessionName);
|
|
15404
16661
|
let cleanupSuffix = "";
|
|
15405
16662
|
try {
|
|
15406
16663
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
15407
|
-
const cleanupScript =
|
|
15408
|
-
if (
|
|
16664
|
+
const cleanupScript = path26.join(path26.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
16665
|
+
if (existsSync22(cleanupScript)) {
|
|
15409
16666
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
15410
16667
|
}
|
|
15411
16668
|
} catch {
|
|
15412
16669
|
}
|
|
15413
16670
|
try {
|
|
15414
|
-
const claudeJsonPath =
|
|
16671
|
+
const claudeJsonPath = path26.join(os9.homedir(), ".claude.json");
|
|
15415
16672
|
let claudeJson = {};
|
|
15416
16673
|
try {
|
|
15417
|
-
claudeJson = JSON.parse(
|
|
16674
|
+
claudeJson = JSON.parse(readFileSync18(claudeJsonPath, "utf8"));
|
|
15418
16675
|
} catch {
|
|
15419
16676
|
}
|
|
15420
16677
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -15422,17 +16679,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15422
16679
|
const trustDir = opts?.cwd ?? projectDir;
|
|
15423
16680
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
15424
16681
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
15425
|
-
|
|
16682
|
+
writeFileSync11(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
15426
16683
|
} catch {
|
|
15427
16684
|
}
|
|
15428
16685
|
try {
|
|
15429
|
-
const settingsDir =
|
|
16686
|
+
const settingsDir = path26.join(os9.homedir(), ".claude", "projects");
|
|
15430
16687
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
15431
|
-
const projSettingsDir =
|
|
15432
|
-
const settingsPath =
|
|
16688
|
+
const projSettingsDir = path26.join(settingsDir, normalizedKey);
|
|
16689
|
+
const settingsPath = path26.join(projSettingsDir, "settings.json");
|
|
15433
16690
|
let settings = {};
|
|
15434
16691
|
try {
|
|
15435
|
-
settings = JSON.parse(
|
|
16692
|
+
settings = JSON.parse(readFileSync18(settingsPath, "utf8"));
|
|
15436
16693
|
} catch {
|
|
15437
16694
|
}
|
|
15438
16695
|
const perms = settings.permissions ?? {};
|
|
@@ -15460,8 +16717,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15460
16717
|
if (changed) {
|
|
15461
16718
|
perms.allow = allow;
|
|
15462
16719
|
settings.permissions = perms;
|
|
15463
|
-
|
|
15464
|
-
|
|
16720
|
+
mkdirSync12(projSettingsDir, { recursive: true });
|
|
16721
|
+
writeFileSync11(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
15465
16722
|
}
|
|
15466
16723
|
} catch {
|
|
15467
16724
|
}
|
|
@@ -15473,7 +16730,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15473
16730
|
let behaviorsFlag = "";
|
|
15474
16731
|
let legacyFallbackWarned = false;
|
|
15475
16732
|
if (!useExeAgent && !useBinSymlink) {
|
|
15476
|
-
const identityPath2 =
|
|
16733
|
+
const identityPath2 = path26.join(
|
|
15477
16734
|
os9.homedir(),
|
|
15478
16735
|
".exe-os",
|
|
15479
16736
|
"identity",
|
|
@@ -15483,13 +16740,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15483
16740
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
15484
16741
|
if (hasAgentFlag) {
|
|
15485
16742
|
identityFlag = ` --agent ${employeeName}`;
|
|
15486
|
-
} else if (
|
|
16743
|
+
} else if (existsSync22(identityPath2)) {
|
|
15487
16744
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
15488
16745
|
legacyFallbackWarned = true;
|
|
15489
16746
|
}
|
|
15490
16747
|
const behaviorsFile = exportBehaviorsSync(
|
|
15491
16748
|
employeeName,
|
|
15492
|
-
|
|
16749
|
+
path26.basename(spawnCwd),
|
|
15493
16750
|
sessionName
|
|
15494
16751
|
);
|
|
15495
16752
|
if (behaviorsFile) {
|
|
@@ -15504,16 +16761,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15504
16761
|
}
|
|
15505
16762
|
let sessionContextFlag = "";
|
|
15506
16763
|
try {
|
|
15507
|
-
const ctxDir =
|
|
15508
|
-
|
|
15509
|
-
const ctxFile =
|
|
16764
|
+
const ctxDir = path26.join(os9.homedir(), ".exe-os", "session-cache");
|
|
16765
|
+
mkdirSync12(ctxDir, { recursive: true });
|
|
16766
|
+
const ctxFile = path26.join(ctxDir, `session-context-${sessionName}.md`);
|
|
15510
16767
|
const ctxContent = [
|
|
15511
16768
|
`## Session Context`,
|
|
15512
16769
|
`You are running in tmux session: ${sessionName}.`,
|
|
15513
16770
|
`Your parent exe session is ${exeSession}.`,
|
|
15514
16771
|
`Your employees (if any) use the -${exeSession} suffix (e.g., tom-${exeSession}).`
|
|
15515
16772
|
].join("\n");
|
|
15516
|
-
|
|
16773
|
+
writeFileSync11(ctxFile, ctxContent);
|
|
15517
16774
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
15518
16775
|
} catch {
|
|
15519
16776
|
}
|
|
@@ -15551,8 +16808,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15551
16808
|
transport.pipeLog(sessionName, logFile);
|
|
15552
16809
|
try {
|
|
15553
16810
|
const mySession = getMySession();
|
|
15554
|
-
const dispatchInfo =
|
|
15555
|
-
|
|
16811
|
+
const dispatchInfo = path26.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
16812
|
+
writeFileSync11(dispatchInfo, JSON.stringify({
|
|
15556
16813
|
dispatchedBy: mySession,
|
|
15557
16814
|
rootExe: exeSession,
|
|
15558
16815
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
@@ -15615,14 +16872,14 @@ var init_tmux_routing = __esm({
|
|
|
15615
16872
|
init_provider_table();
|
|
15616
16873
|
init_intercom_queue();
|
|
15617
16874
|
init_plan_limits();
|
|
15618
|
-
SPAWN_LOCK_DIR =
|
|
15619
|
-
SESSION_CACHE =
|
|
16875
|
+
SPAWN_LOCK_DIR = path26.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
16876
|
+
SESSION_CACHE = path26.join(os9.homedir(), ".exe-os", "session-cache");
|
|
15620
16877
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
15621
16878
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
15622
16879
|
VERIFY_PANE_LINES = 200;
|
|
15623
16880
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
15624
|
-
INTERCOM_LOG2 =
|
|
15625
|
-
DEBOUNCE_FILE =
|
|
16881
|
+
INTERCOM_LOG2 = path26.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
16882
|
+
DEBOUNCE_FILE = path26.join(SESSION_CACHE, "intercom-debounce.json");
|
|
15626
16883
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
15627
16884
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
15628
16885
|
}
|
|
@@ -16043,11 +17300,11 @@ function Footer() {
|
|
|
16043
17300
|
} catch {
|
|
16044
17301
|
}
|
|
16045
17302
|
try {
|
|
16046
|
-
const { existsSync:
|
|
17303
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
16047
17304
|
const { join } = await import("path");
|
|
16048
17305
|
const home = process.env.HOME ?? "";
|
|
16049
17306
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
16050
|
-
setDaemon(
|
|
17307
|
+
setDaemon(existsSync24(pidPath) ? "running" : "stopped");
|
|
16051
17308
|
} catch {
|
|
16052
17309
|
setDaemon("unknown");
|
|
16053
17310
|
}
|
|
@@ -18078,10 +19335,10 @@ var init_hooks = __esm({
|
|
|
18078
19335
|
});
|
|
18079
19336
|
|
|
18080
19337
|
// src/runtime/safety-checks.ts
|
|
18081
|
-
import
|
|
19338
|
+
import path27 from "path";
|
|
18082
19339
|
import os10 from "os";
|
|
18083
19340
|
function checkPathSafety(filePath) {
|
|
18084
|
-
const resolved =
|
|
19341
|
+
const resolved = path27.resolve(filePath);
|
|
18085
19342
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
18086
19343
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
18087
19344
|
if (matches) {
|
|
@@ -18091,7 +19348,7 @@ function checkPathSafety(filePath) {
|
|
|
18091
19348
|
return { safe: true, bypassImmune: true };
|
|
18092
19349
|
}
|
|
18093
19350
|
function checkReadPathSafety(filePath) {
|
|
18094
|
-
const resolved =
|
|
19351
|
+
const resolved = path27.resolve(filePath);
|
|
18095
19352
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
18096
19353
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
18097
19354
|
);
|
|
@@ -18117,11 +19374,11 @@ var init_safety_checks = __esm({
|
|
|
18117
19374
|
reason: "Git config can set hooks and command execution"
|
|
18118
19375
|
},
|
|
18119
19376
|
{
|
|
18120
|
-
pattern: (p) => p.startsWith(
|
|
19377
|
+
pattern: (p) => p.startsWith(path27.join(HOME, ".claude")),
|
|
18121
19378
|
reason: "Claude configuration files are protected"
|
|
18122
19379
|
},
|
|
18123
19380
|
{
|
|
18124
|
-
pattern: (p) => p.startsWith(
|
|
19381
|
+
pattern: (p) => p.startsWith(path27.join(HOME, ".exe-os")),
|
|
18125
19382
|
reason: "exe-os configuration files are protected"
|
|
18126
19383
|
},
|
|
18127
19384
|
{
|
|
@@ -18138,7 +19395,7 @@ var init_safety_checks = __esm({
|
|
|
18138
19395
|
},
|
|
18139
19396
|
{
|
|
18140
19397
|
pattern: (p) => {
|
|
18141
|
-
const name =
|
|
19398
|
+
const name = path27.basename(p);
|
|
18142
19399
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
18143
19400
|
},
|
|
18144
19401
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -18165,7 +19422,7 @@ __export(file_read_exports, {
|
|
|
18165
19422
|
FileReadTool: () => FileReadTool
|
|
18166
19423
|
});
|
|
18167
19424
|
import fs3 from "fs/promises";
|
|
18168
|
-
import
|
|
19425
|
+
import path28 from "path";
|
|
18169
19426
|
import { z } from "zod";
|
|
18170
19427
|
function isBinary(buf) {
|
|
18171
19428
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -18201,7 +19458,7 @@ var init_file_read = __esm({
|
|
|
18201
19458
|
return { behavior: "allow" };
|
|
18202
19459
|
},
|
|
18203
19460
|
async call(input, context) {
|
|
18204
|
-
const filePath =
|
|
19461
|
+
const filePath = path28.isAbsolute(input.file_path) ? input.file_path : path28.resolve(context.cwd, input.file_path);
|
|
18205
19462
|
let stat2;
|
|
18206
19463
|
try {
|
|
18207
19464
|
stat2 = await fs3.stat(filePath);
|
|
@@ -18241,7 +19498,7 @@ __export(glob_exports, {
|
|
|
18241
19498
|
GlobTool: () => GlobTool
|
|
18242
19499
|
});
|
|
18243
19500
|
import fs4 from "fs/promises";
|
|
18244
|
-
import
|
|
19501
|
+
import path29 from "path";
|
|
18245
19502
|
import { z as z2 } from "zod";
|
|
18246
19503
|
async function walkDir(dir, maxDepth = 10) {
|
|
18247
19504
|
const results = [];
|
|
@@ -18257,7 +19514,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
18257
19514
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
18258
19515
|
continue;
|
|
18259
19516
|
}
|
|
18260
|
-
const fullPath =
|
|
19517
|
+
const fullPath = path29.join(current, entry.name);
|
|
18261
19518
|
if (entry.isDirectory()) {
|
|
18262
19519
|
await walk(fullPath, depth + 1);
|
|
18263
19520
|
} else {
|
|
@@ -18291,11 +19548,11 @@ var init_glob = __esm({
|
|
|
18291
19548
|
inputSchema: inputSchema2,
|
|
18292
19549
|
isReadOnly: true,
|
|
18293
19550
|
async call(input, context) {
|
|
18294
|
-
const baseDir = input.path ?
|
|
19551
|
+
const baseDir = input.path ? path29.isAbsolute(input.path) ? input.path : path29.resolve(context.cwd, input.path) : context.cwd;
|
|
18295
19552
|
try {
|
|
18296
19553
|
const entries = await walkDir(baseDir);
|
|
18297
19554
|
const matched = entries.filter(
|
|
18298
|
-
(e) => simpleGlobMatch(
|
|
19555
|
+
(e) => simpleGlobMatch(path29.relative(baseDir, e.path), input.pattern)
|
|
18299
19556
|
);
|
|
18300
19557
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
18301
19558
|
if (matched.length === 0) {
|
|
@@ -18321,7 +19578,7 @@ __export(grep_exports, {
|
|
|
18321
19578
|
});
|
|
18322
19579
|
import { spawn as spawn2 } from "child_process";
|
|
18323
19580
|
import fs5 from "fs/promises";
|
|
18324
|
-
import
|
|
19581
|
+
import path30 from "path";
|
|
18325
19582
|
import { z as z3 } from "zod";
|
|
18326
19583
|
function runRipgrep(input, searchPath, context) {
|
|
18327
19584
|
return new Promise((resolve, reject) => {
|
|
@@ -18375,7 +19632,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
18375
19632
|
}
|
|
18376
19633
|
for (const entry of entries) {
|
|
18377
19634
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
18378
|
-
const fullPath =
|
|
19635
|
+
const fullPath = path30.join(dir, entry.name);
|
|
18379
19636
|
if (entry.isDirectory()) {
|
|
18380
19637
|
await walk(fullPath);
|
|
18381
19638
|
} else {
|
|
@@ -18421,7 +19678,7 @@ var init_grep = __esm({
|
|
|
18421
19678
|
inputSchema: inputSchema3,
|
|
18422
19679
|
isReadOnly: true,
|
|
18423
19680
|
async call(input, context) {
|
|
18424
|
-
const searchPath = input.path ?
|
|
19681
|
+
const searchPath = input.path ? path30.isAbsolute(input.path) ? input.path : path30.resolve(context.cwd, input.path) : context.cwd;
|
|
18425
19682
|
try {
|
|
18426
19683
|
const result = await runRipgrep(input, searchPath, context);
|
|
18427
19684
|
return result;
|
|
@@ -18446,7 +19703,7 @@ __export(file_write_exports, {
|
|
|
18446
19703
|
FileWriteTool: () => FileWriteTool
|
|
18447
19704
|
});
|
|
18448
19705
|
import fs6 from "fs/promises";
|
|
18449
|
-
import
|
|
19706
|
+
import path31 from "path";
|
|
18450
19707
|
import { z as z4 } from "zod";
|
|
18451
19708
|
var inputSchema4, FileWriteTool;
|
|
18452
19709
|
var init_file_write = __esm({
|
|
@@ -18474,8 +19731,8 @@ var init_file_write = __esm({
|
|
|
18474
19731
|
return { behavior: "allow" };
|
|
18475
19732
|
},
|
|
18476
19733
|
async call(input, context) {
|
|
18477
|
-
const filePath =
|
|
18478
|
-
const dir =
|
|
19734
|
+
const filePath = path31.isAbsolute(input.file_path) ? input.file_path : path31.resolve(context.cwd, input.file_path);
|
|
19735
|
+
const dir = path31.dirname(filePath);
|
|
18479
19736
|
await fs6.mkdir(dir, { recursive: true });
|
|
18480
19737
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
18481
19738
|
return {
|
|
@@ -18493,7 +19750,7 @@ __export(file_edit_exports, {
|
|
|
18493
19750
|
FileEditTool: () => FileEditTool
|
|
18494
19751
|
});
|
|
18495
19752
|
import fs7 from "fs/promises";
|
|
18496
|
-
import
|
|
19753
|
+
import path32 from "path";
|
|
18497
19754
|
import { z as z5 } from "zod";
|
|
18498
19755
|
function countOccurrences(haystack, needle) {
|
|
18499
19756
|
let count = 0;
|
|
@@ -18534,7 +19791,7 @@ var init_file_edit = __esm({
|
|
|
18534
19791
|
return { behavior: "allow" };
|
|
18535
19792
|
},
|
|
18536
19793
|
async call(input, context) {
|
|
18537
|
-
const filePath =
|
|
19794
|
+
const filePath = path32.isAbsolute(input.file_path) ? input.file_path : path32.resolve(context.cwd, input.file_path);
|
|
18538
19795
|
let content;
|
|
18539
19796
|
try {
|
|
18540
19797
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -18776,8 +20033,8 @@ var init_bash = __esm({
|
|
|
18776
20033
|
// src/tui/views/CommandCenter.tsx
|
|
18777
20034
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
18778
20035
|
import TextInput from "ink-text-input";
|
|
18779
|
-
import
|
|
18780
|
-
import { homedir as
|
|
20036
|
+
import path33 from "path";
|
|
20037
|
+
import { homedir as homedir5 } from "os";
|
|
18781
20038
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
18782
20039
|
function CommandCenterView({
|
|
18783
20040
|
onSelectProject,
|
|
@@ -18811,15 +20068,15 @@ function CommandCenterView({
|
|
|
18811
20068
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
18812
20069
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
18813
20070
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
18814
|
-
const { readFileSync:
|
|
20071
|
+
const { readFileSync: readFileSync20, existsSync: existsSync24 } = await import("fs");
|
|
18815
20072
|
const { join } = await import("path");
|
|
18816
|
-
const { homedir:
|
|
18817
|
-
const configPath = join(
|
|
20073
|
+
const { homedir: homedir7 } = await import("os");
|
|
20074
|
+
const configPath = join(homedir7(), ".exe-os", "config.json");
|
|
18818
20075
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
18819
20076
|
let providerConfigs = {};
|
|
18820
|
-
if (
|
|
20077
|
+
if (existsSync24(configPath)) {
|
|
18821
20078
|
try {
|
|
18822
|
-
const raw = JSON.parse(
|
|
20079
|
+
const raw = JSON.parse(readFileSync20(configPath, "utf8"));
|
|
18823
20080
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
18824
20081
|
if (raw.providers && typeof raw.providers === "object") {
|
|
18825
20082
|
providerConfigs = raw.providers;
|
|
@@ -18877,10 +20134,10 @@ function CommandCenterView({
|
|
|
18877
20134
|
registry.register(BashTool2);
|
|
18878
20135
|
let agentRole = "CTO";
|
|
18879
20136
|
try {
|
|
18880
|
-
const markerDir = join(
|
|
20137
|
+
const markerDir = join(homedir7(), ".exe-os", "session-cache");
|
|
18881
20138
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
18882
20139
|
for (const f of agentFiles) {
|
|
18883
|
-
const data = JSON.parse(
|
|
20140
|
+
const data = JSON.parse(readFileSync20(join(markerDir, f), "utf8"));
|
|
18884
20141
|
if (data.agentRole) {
|
|
18885
20142
|
agentRole = data.agentRole;
|
|
18886
20143
|
break;
|
|
@@ -19017,7 +20274,7 @@ function CommandCenterView({
|
|
|
19017
20274
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
19018
20275
|
projectName: p.projectName,
|
|
19019
20276
|
exeSession: p.exeSession,
|
|
19020
|
-
projectDir:
|
|
20277
|
+
projectDir: path33.join(homedir5(), p.projectName),
|
|
19021
20278
|
employeeCount: p.employees.length,
|
|
19022
20279
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
19023
20280
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -19055,7 +20312,7 @@ function CommandCenterView({
|
|
|
19055
20312
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
19056
20313
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
19057
20314
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
19058
|
-
const { existsSync:
|
|
20315
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
19059
20316
|
const { join } = await import("path");
|
|
19060
20317
|
const client = getClient2();
|
|
19061
20318
|
if (!client) {
|
|
@@ -19124,7 +20381,7 @@ function CommandCenterView({
|
|
|
19124
20381
|
}
|
|
19125
20382
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
19126
20383
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
19127
|
-
const hasGit = projectDir ?
|
|
20384
|
+
const hasGit = projectDir ? existsSync24(join(projectDir, ".git")) : false;
|
|
19128
20385
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
19129
20386
|
projectList.push({
|
|
19130
20387
|
projectName: name,
|
|
@@ -19149,7 +20406,7 @@ function CommandCenterView({
|
|
|
19149
20406
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
19150
20407
|
try {
|
|
19151
20408
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
19152
|
-
setHealth((h) => ({ ...h, daemon:
|
|
20409
|
+
setHealth((h) => ({ ...h, daemon: existsSync24(pidPath) ? "running" : "stopped" }));
|
|
19153
20410
|
} catch {
|
|
19154
20411
|
}
|
|
19155
20412
|
const activityResult = await client.execute(
|
|
@@ -20010,8 +21267,8 @@ var init_useOrchestrator = __esm({
|
|
|
20010
21267
|
|
|
20011
21268
|
// src/tui/views/Sessions.tsx
|
|
20012
21269
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
20013
|
-
import
|
|
20014
|
-
import { homedir as
|
|
21270
|
+
import path34 from "path";
|
|
21271
|
+
import { homedir as homedir6 } from "os";
|
|
20015
21272
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
20016
21273
|
function SessionsView({
|
|
20017
21274
|
initialProject,
|
|
@@ -20045,7 +21302,7 @@ function SessionsView({
|
|
|
20045
21302
|
if (demo) {
|
|
20046
21303
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
20047
21304
|
...p,
|
|
20048
|
-
projectDir:
|
|
21305
|
+
projectDir: path34.join(homedir6(), p.projectName),
|
|
20049
21306
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
20050
21307
|
})));
|
|
20051
21308
|
return;
|
|
@@ -20804,16 +22061,16 @@ __export(ws_auth_exports, {
|
|
|
20804
22061
|
deriveWsAuthToken: () => deriveWsAuthToken,
|
|
20805
22062
|
hashAuthToken: () => hashAuthToken
|
|
20806
22063
|
});
|
|
20807
|
-
import
|
|
22064
|
+
import crypto10 from "crypto";
|
|
20808
22065
|
function deriveWsAuthToken(masterKey) {
|
|
20809
|
-
return Buffer.from(
|
|
22066
|
+
return Buffer.from(crypto10.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
|
|
20810
22067
|
}
|
|
20811
22068
|
function deriveOrgId(masterKey) {
|
|
20812
|
-
const raw = Buffer.from(
|
|
20813
|
-
return
|
|
22069
|
+
const raw = Buffer.from(crypto10.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
|
|
22070
|
+
return crypto10.createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
20814
22071
|
}
|
|
20815
22072
|
function hashAuthToken(token) {
|
|
20816
|
-
return
|
|
22073
|
+
return crypto10.createHash("sha256").update(token).digest("hex");
|
|
20817
22074
|
}
|
|
20818
22075
|
var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
|
|
20819
22076
|
var init_ws_auth = __esm({
|
|
@@ -21017,7 +22274,7 @@ function assertSecureWsUrl(url) {
|
|
|
21017
22274
|
`Malformed WebSocket URL rejected: "${url}".`
|
|
21018
22275
|
);
|
|
21019
22276
|
}
|
|
21020
|
-
if (
|
|
22277
|
+
if (LOCALHOST_PATTERNS2.test(parsed.hostname)) return;
|
|
21021
22278
|
throw new Error(
|
|
21022
22279
|
`Insecure WebSocket URL rejected: "${url}". Use wss:// for remote hosts. Plain ws:// is only allowed for localhost.`
|
|
21023
22280
|
);
|
|
@@ -21026,7 +22283,7 @@ function assertSecureWsUrl(url) {
|
|
|
21026
22283
|
function isGatewayEvent(msg) {
|
|
21027
22284
|
return typeof msg.type === "string" && GATEWAY_EVENT_TYPES.has(msg.type);
|
|
21028
22285
|
}
|
|
21029
|
-
var MIN_RECONNECT_MS, MAX_RECONNECT_MS, AUTH_RESPONSE_TIMEOUT_MS, DEFAULT_CHANNELS, GATEWAY_EVENT_TYPES,
|
|
22286
|
+
var MIN_RECONNECT_MS, MAX_RECONNECT_MS, AUTH_RESPONSE_TIMEOUT_MS, DEFAULT_CHANNELS, GATEWAY_EVENT_TYPES, LOCALHOST_PATTERNS2;
|
|
21030
22287
|
var init_gateway_client = __esm({
|
|
21031
22288
|
"src/lib/gateway-client.ts"() {
|
|
21032
22289
|
"use strict";
|
|
@@ -21042,7 +22299,7 @@ var init_gateway_client = __esm({
|
|
|
21042
22299
|
"health",
|
|
21043
22300
|
"escalation"
|
|
21044
22301
|
]);
|
|
21045
|
-
|
|
22302
|
+
LOCALHOST_PATTERNS2 = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
21046
22303
|
}
|
|
21047
22304
|
});
|
|
21048
22305
|
|
|
@@ -21173,12 +22430,12 @@ async function loadGatewayConfig() {
|
|
|
21173
22430
|
state.running = false;
|
|
21174
22431
|
}
|
|
21175
22432
|
try {
|
|
21176
|
-
const { existsSync:
|
|
22433
|
+
const { existsSync: existsSync24, readFileSync: readFileSync20 } = await import("fs");
|
|
21177
22434
|
const { join } = await import("path");
|
|
21178
22435
|
const home = process.env.HOME ?? "";
|
|
21179
22436
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
21180
|
-
if (
|
|
21181
|
-
const raw = JSON.parse(
|
|
22437
|
+
if (existsSync24(configPath)) {
|
|
22438
|
+
const raw = JSON.parse(readFileSync20(configPath, "utf8"));
|
|
21182
22439
|
state.port = raw.port ?? 3100;
|
|
21183
22440
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
21184
22441
|
if (raw.adapters) {
|
|
@@ -21801,12 +23058,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
21801
23058
|
setMembers(teamData);
|
|
21802
23059
|
setDbError(null);
|
|
21803
23060
|
try {
|
|
21804
|
-
const { existsSync:
|
|
23061
|
+
const { existsSync: existsSync24, readFileSync: readFileSync20 } = await import("fs");
|
|
21805
23062
|
const { join } = await import("path");
|
|
21806
23063
|
const home = process.env.HOME ?? "";
|
|
21807
23064
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
21808
|
-
if (
|
|
21809
|
-
const raw = JSON.parse(
|
|
23065
|
+
if (existsSync24(gatewayConfig)) {
|
|
23066
|
+
const raw = JSON.parse(readFileSync20(gatewayConfig, "utf8"));
|
|
21810
23067
|
if (raw.agents && raw.agents.length > 0) {
|
|
21811
23068
|
setExternals(raw.agents.map((a) => ({
|
|
21812
23069
|
name: a.name,
|
|
@@ -21987,8 +23244,8 @@ __export(wiki_client_exports, {
|
|
|
21987
23244
|
listDocuments: () => listDocuments,
|
|
21988
23245
|
listWorkspaces: () => listWorkspaces
|
|
21989
23246
|
});
|
|
21990
|
-
async function wikiFetch(config,
|
|
21991
|
-
const url = `${config.baseUrl}/api/v1${
|
|
23247
|
+
async function wikiFetch(config, path36, method = "GET", body) {
|
|
23248
|
+
const url = `${config.baseUrl}/api/v1${path36}`;
|
|
21992
23249
|
const headers = {
|
|
21993
23250
|
Authorization: `Bearer ${config.apiKey}`,
|
|
21994
23251
|
"Content-Type": "application/json"
|
|
@@ -22021,7 +23278,7 @@ async function wikiFetch(config, path34, method = "GET", body) {
|
|
|
22021
23278
|
}
|
|
22022
23279
|
}
|
|
22023
23280
|
if (!response.ok) {
|
|
22024
|
-
throw new Error(`Wiki API ${method} ${
|
|
23281
|
+
throw new Error(`Wiki API ${method} ${path36}: ${response.status} ${response.statusText}`);
|
|
22025
23282
|
}
|
|
22026
23283
|
return response.json();
|
|
22027
23284
|
} finally {
|
|
@@ -22631,12 +23888,12 @@ function SettingsView({ onBack }) {
|
|
|
22631
23888
|
}
|
|
22632
23889
|
setProviders(providerList);
|
|
22633
23890
|
try {
|
|
22634
|
-
const { existsSync:
|
|
23891
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
22635
23892
|
const { join } = await import("path");
|
|
22636
23893
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
22637
23894
|
const cfg = await loadConfig2();
|
|
22638
23895
|
const home = process.env.HOME ?? "";
|
|
22639
|
-
const hasKey =
|
|
23896
|
+
const hasKey = existsSync24(join(home, ".exe-os", "master.key"));
|
|
22640
23897
|
if (cfg.cloud) {
|
|
22641
23898
|
setCloud({
|
|
22642
23899
|
configured: true,
|
|
@@ -22649,22 +23906,22 @@ function SettingsView({ onBack }) {
|
|
|
22649
23906
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
22650
23907
|
let daemon = "unknown";
|
|
22651
23908
|
try {
|
|
22652
|
-
daemon =
|
|
23909
|
+
daemon = existsSync24(pidPath) ? "running" : "stopped";
|
|
22653
23910
|
} catch {
|
|
22654
23911
|
}
|
|
22655
23912
|
let version = "unknown";
|
|
22656
23913
|
try {
|
|
22657
|
-
const { readFileSync:
|
|
23914
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
22658
23915
|
const { createRequire } = await import("module");
|
|
22659
23916
|
const require2 = createRequire(import.meta.url);
|
|
22660
23917
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
22661
|
-
const pkg = JSON.parse(
|
|
23918
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
22662
23919
|
version = pkg.version;
|
|
22663
23920
|
} catch {
|
|
22664
23921
|
try {
|
|
22665
|
-
const { readFileSync:
|
|
23922
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
22666
23923
|
const { join: joinPath } = await import("path");
|
|
22667
|
-
const pkg = JSON.parse(
|
|
23924
|
+
const pkg = JSON.parse(readFileSync20(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
22668
23925
|
version = pkg.version;
|
|
22669
23926
|
} catch {
|
|
22670
23927
|
}
|
|
@@ -23271,8 +24528,8 @@ Unhandled rejection: ${reason}
|
|
|
23271
24528
|
});
|
|
23272
24529
|
|
|
23273
24530
|
// src/bin/cli.ts
|
|
23274
|
-
import { existsSync as
|
|
23275
|
-
import
|
|
24531
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSync as writeFileSync12, readdirSync as readdirSync7, rmSync } from "fs";
|
|
24532
|
+
import path35 from "path";
|
|
23276
24533
|
import os11 from "os";
|
|
23277
24534
|
var args = process.argv.slice(2);
|
|
23278
24535
|
if (args.includes("--global")) {
|
|
@@ -23336,11 +24593,11 @@ if (args.includes("--global")) {
|
|
|
23336
24593
|
});
|
|
23337
24594
|
await init_App2().then(() => App_exports);
|
|
23338
24595
|
} else {
|
|
23339
|
-
const claudeDir =
|
|
23340
|
-
const settingsPath =
|
|
23341
|
-
const hasClaudeCode =
|
|
24596
|
+
const claudeDir = path35.join(os11.homedir(), ".claude");
|
|
24597
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24598
|
+
const hasClaudeCode = existsSync23(settingsPath) && (() => {
|
|
23342
24599
|
try {
|
|
23343
|
-
const raw =
|
|
24600
|
+
const raw = readFileSync19(settingsPath, "utf8");
|
|
23344
24601
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
23345
24602
|
} catch {
|
|
23346
24603
|
return false;
|
|
@@ -23379,14 +24636,14 @@ async function runClaudeInstall() {
|
|
|
23379
24636
|
}
|
|
23380
24637
|
}
|
|
23381
24638
|
async function runClaudeCheck() {
|
|
23382
|
-
const claudeDir =
|
|
23383
|
-
const settingsPath =
|
|
23384
|
-
const claudeJsonPath =
|
|
24639
|
+
const claudeDir = path35.join(os11.homedir(), ".claude");
|
|
24640
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24641
|
+
const claudeJsonPath = path35.join(os11.homedir(), ".claude.json");
|
|
23385
24642
|
let ok = true;
|
|
23386
|
-
if (
|
|
24643
|
+
if (existsSync23(settingsPath)) {
|
|
23387
24644
|
let settings;
|
|
23388
24645
|
try {
|
|
23389
|
-
settings = JSON.parse(
|
|
24646
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
23390
24647
|
} catch {
|
|
23391
24648
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
23392
24649
|
ok = false;
|
|
@@ -23412,10 +24669,10 @@ async function runClaudeCheck() {
|
|
|
23412
24669
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
23413
24670
|
ok = false;
|
|
23414
24671
|
}
|
|
23415
|
-
if (
|
|
24672
|
+
if (existsSync23(claudeJsonPath)) {
|
|
23416
24673
|
let claudeJson;
|
|
23417
24674
|
try {
|
|
23418
|
-
claudeJson = JSON.parse(
|
|
24675
|
+
claudeJson = JSON.parse(readFileSync19(claudeJsonPath, "utf8"));
|
|
23419
24676
|
} catch {
|
|
23420
24677
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
23421
24678
|
ok = false;
|
|
@@ -23434,8 +24691,8 @@ async function runClaudeCheck() {
|
|
|
23434
24691
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
23435
24692
|
ok = false;
|
|
23436
24693
|
}
|
|
23437
|
-
const skillsDir =
|
|
23438
|
-
if (
|
|
24694
|
+
const skillsDir = path35.join(claudeDir, "skills");
|
|
24695
|
+
if (existsSync23(skillsDir)) {
|
|
23439
24696
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
23440
24697
|
} else {
|
|
23441
24698
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -23452,16 +24709,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23452
24709
|
const dryRun = flags.includes("--dry-run");
|
|
23453
24710
|
const purge = flags.includes("--purge");
|
|
23454
24711
|
const homeDir = os11.homedir();
|
|
23455
|
-
const claudeDir =
|
|
23456
|
-
const settingsPath =
|
|
23457
|
-
const claudeJsonPath =
|
|
23458
|
-
const exeOsDir =
|
|
24712
|
+
const claudeDir = path35.join(homeDir, ".claude");
|
|
24713
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24714
|
+
const claudeJsonPath = path35.join(homeDir, ".claude.json");
|
|
24715
|
+
const exeOsDir = path35.join(homeDir, ".exe-os");
|
|
23459
24716
|
let removed = 0;
|
|
23460
24717
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
23461
24718
|
let settings = {};
|
|
23462
|
-
if (
|
|
24719
|
+
if (existsSync23(settingsPath)) {
|
|
23463
24720
|
try {
|
|
23464
|
-
settings = JSON.parse(
|
|
24721
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
23465
24722
|
} catch {
|
|
23466
24723
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
23467
24724
|
if (purge) {
|
|
@@ -23499,15 +24756,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23499
24756
|
permCount = before - settings.permissions.allow.length;
|
|
23500
24757
|
}
|
|
23501
24758
|
if (!dryRun) {
|
|
23502
|
-
|
|
24759
|
+
writeFileSync12(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
23503
24760
|
}
|
|
23504
24761
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
23505
24762
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
23506
24763
|
removed++;
|
|
23507
24764
|
}
|
|
23508
24765
|
}
|
|
23509
|
-
if (
|
|
23510
|
-
const raw =
|
|
24766
|
+
if (existsSync23(claudeJsonPath)) {
|
|
24767
|
+
const raw = readFileSync19(claudeJsonPath, "utf8");
|
|
23511
24768
|
if (raw.length > 1e6) {
|
|
23512
24769
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
23513
24770
|
} else {
|
|
@@ -23528,7 +24785,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23528
24785
|
}
|
|
23529
24786
|
if (removedMcp) {
|
|
23530
24787
|
if (!dryRun) {
|
|
23531
|
-
|
|
24788
|
+
writeFileSync12(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
23532
24789
|
}
|
|
23533
24790
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
23534
24791
|
removed++;
|
|
@@ -23536,14 +24793,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23536
24793
|
}
|
|
23537
24794
|
}
|
|
23538
24795
|
}
|
|
23539
|
-
const skillsDir =
|
|
23540
|
-
if (
|
|
24796
|
+
const skillsDir = path35.join(claudeDir, "skills");
|
|
24797
|
+
if (existsSync23(skillsDir)) {
|
|
23541
24798
|
let skillCount = 0;
|
|
23542
24799
|
try {
|
|
23543
|
-
const entries =
|
|
24800
|
+
const entries = readdirSync7(skillsDir);
|
|
23544
24801
|
for (const entry of entries) {
|
|
23545
24802
|
if (entry.startsWith("exe")) {
|
|
23546
|
-
const fullPath =
|
|
24803
|
+
const fullPath = path35.join(skillsDir, entry);
|
|
23547
24804
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
23548
24805
|
skillCount++;
|
|
23549
24806
|
}
|
|
@@ -23555,30 +24812,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23555
24812
|
removed++;
|
|
23556
24813
|
}
|
|
23557
24814
|
}
|
|
23558
|
-
const claudeMdPath =
|
|
23559
|
-
if (
|
|
23560
|
-
const content =
|
|
24815
|
+
const claudeMdPath = path35.join(claudeDir, "CLAUDE.md");
|
|
24816
|
+
if (existsSync23(claudeMdPath)) {
|
|
24817
|
+
const content = readFileSync19(claudeMdPath, "utf8");
|
|
23561
24818
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
23562
24819
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
23563
24820
|
const startIdx = content.indexOf(startMarker);
|
|
23564
24821
|
const endIdx = content.indexOf(endMarker);
|
|
23565
24822
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
23566
24823
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
23567
|
-
if (!dryRun)
|
|
24824
|
+
if (!dryRun) writeFileSync12(claudeMdPath, cleaned);
|
|
23568
24825
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
23569
24826
|
removed++;
|
|
23570
24827
|
}
|
|
23571
24828
|
}
|
|
23572
|
-
const agentsDir =
|
|
23573
|
-
if (
|
|
24829
|
+
const agentsDir = path35.join(claudeDir, "agents");
|
|
24830
|
+
if (existsSync23(agentsDir)) {
|
|
23574
24831
|
let agentCount = 0;
|
|
23575
24832
|
try {
|
|
23576
|
-
const entries =
|
|
24833
|
+
const entries = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
23577
24834
|
let knownNames = /* @__PURE__ */ new Set();
|
|
23578
|
-
const rosterPath =
|
|
23579
|
-
if (
|
|
24835
|
+
const rosterPath = path35.join(exeOsDir, "exe-employees.json");
|
|
24836
|
+
if (existsSync23(rosterPath)) {
|
|
23580
24837
|
try {
|
|
23581
|
-
const roster = JSON.parse(
|
|
24838
|
+
const roster = JSON.parse(readFileSync19(rosterPath, "utf8"));
|
|
23582
24839
|
knownNames = new Set(roster.map((e) => e.name));
|
|
23583
24840
|
} catch {
|
|
23584
24841
|
}
|
|
@@ -23586,7 +24843,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23586
24843
|
for (const entry of entries) {
|
|
23587
24844
|
const name = entry.replace(/\.md$/, "");
|
|
23588
24845
|
if (knownNames.has(name)) {
|
|
23589
|
-
if (!dryRun) rmSync(
|
|
24846
|
+
if (!dryRun) rmSync(path35.join(agentsDir, entry), { force: true });
|
|
23590
24847
|
agentCount++;
|
|
23591
24848
|
}
|
|
23592
24849
|
}
|
|
@@ -23597,16 +24854,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23597
24854
|
removed++;
|
|
23598
24855
|
}
|
|
23599
24856
|
}
|
|
23600
|
-
const projectsDir =
|
|
23601
|
-
if (
|
|
24857
|
+
const projectsDir = path35.join(claudeDir, "projects");
|
|
24858
|
+
if (existsSync23(projectsDir)) {
|
|
23602
24859
|
let projectCount = 0;
|
|
23603
24860
|
try {
|
|
23604
|
-
const projects =
|
|
24861
|
+
const projects = readdirSync7(projectsDir);
|
|
23605
24862
|
for (const proj of projects) {
|
|
23606
|
-
const projSettings =
|
|
23607
|
-
if (!
|
|
24863
|
+
const projSettings = path35.join(projectsDir, proj, "settings.json");
|
|
24864
|
+
if (!existsSync23(projSettings)) continue;
|
|
23608
24865
|
try {
|
|
23609
|
-
const pSettings = JSON.parse(
|
|
24866
|
+
const pSettings = JSON.parse(readFileSync19(projSettings, "utf8"));
|
|
23610
24867
|
let changed = false;
|
|
23611
24868
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
23612
24869
|
const before = pSettings.permissions.allow.length;
|
|
@@ -23616,7 +24873,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23616
24873
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
23617
24874
|
}
|
|
23618
24875
|
if (changed && !dryRun) {
|
|
23619
|
-
|
|
24876
|
+
writeFileSync12(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
23620
24877
|
}
|
|
23621
24878
|
if (changed) projectCount++;
|
|
23622
24879
|
} catch {
|
|
@@ -23640,16 +24897,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23640
24897
|
};
|
|
23641
24898
|
const exeBinPath = findExeBin3();
|
|
23642
24899
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
23643
|
-
const binDir =
|
|
24900
|
+
const binDir = path35.dirname(exeBinPath);
|
|
23644
24901
|
let symlinkCount = 0;
|
|
23645
|
-
const rosterPath =
|
|
23646
|
-
if (
|
|
23647
|
-
const roster = JSON.parse(
|
|
24902
|
+
const rosterPath = path35.join(exeOsDir, "exe-employees.json");
|
|
24903
|
+
if (existsSync23(rosterPath)) {
|
|
24904
|
+
const roster = JSON.parse(readFileSync19(rosterPath, "utf8"));
|
|
23648
24905
|
for (const emp of roster) {
|
|
23649
24906
|
if (emp.name === "exe") continue;
|
|
23650
24907
|
for (const suffix of ["", "-opencode"]) {
|
|
23651
|
-
const linkPath =
|
|
23652
|
-
if (
|
|
24908
|
+
const linkPath = path35.join(binDir, `${emp.name}${suffix}`);
|
|
24909
|
+
if (existsSync23(linkPath)) {
|
|
23653
24910
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
23654
24911
|
symlinkCount++;
|
|
23655
24912
|
}
|
|
@@ -23662,7 +24919,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23662
24919
|
}
|
|
23663
24920
|
} catch {
|
|
23664
24921
|
}
|
|
23665
|
-
if (purge &&
|
|
24922
|
+
if (purge && existsSync23(exeOsDir)) {
|
|
23666
24923
|
if (!dryRun) {
|
|
23667
24924
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
23668
24925
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -23687,7 +24944,7 @@ async function checkForUpdateOnBoot() {
|
|
|
23687
24944
|
const config = await loadConfig2();
|
|
23688
24945
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
23689
24946
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
23690
|
-
const packageRoot =
|
|
24947
|
+
const packageRoot = path35.resolve(
|
|
23691
24948
|
new URL("../..", import.meta.url).pathname
|
|
23692
24949
|
);
|
|
23693
24950
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -23746,7 +25003,7 @@ async function runActivate(key) {
|
|
|
23746
25003
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
23747
25004
|
if (idTemplate) {
|
|
23748
25005
|
const idPath = identityPath2(name);
|
|
23749
|
-
const dir =
|
|
25006
|
+
const dir = path35.dirname(idPath);
|
|
23750
25007
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
23751
25008
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
23752
25009
|
}
|