@askexenow/exe-os 0.8.60 → 0.8.62
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/assets/tmux.conf +37 -0
- package/dist/bin/cli.js +1745 -416
- package/dist/bin/exe-boot.js +21 -12
- package/dist/bin/exe-link.js +21 -12
- package/dist/bin/exe-new-employee.js +862 -114
- package/dist/bin/exe-start.sh +133 -0
- package/dist/bin/install.js +182 -20
- package/dist/bin/setup.js +1492 -115
- package/dist/hooks/response-ingest-worker.js +3 -1
- package/dist/hooks/summary-worker.js +24 -13
- package/dist/lib/cloud-sync.js +21 -12
- package/dist/lib/session-wrappers.js +107 -0
- package/package.json +3 -3
package/dist/bin/cli.js
CHANGED
|
@@ -509,12 +509,14 @@ __export(installer_exports, {
|
|
|
509
509
|
mergeHooks: () => mergeHooks,
|
|
510
510
|
registerMcpServer: () => registerMcpServer,
|
|
511
511
|
resolvePackageRoot: () => resolvePackageRoot,
|
|
512
|
-
runInstaller: () => runInstaller
|
|
512
|
+
runInstaller: () => runInstaller,
|
|
513
|
+
setupTmux: () => setupTmux
|
|
513
514
|
});
|
|
514
515
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
515
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
516
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, copyFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
516
517
|
import path4 from "path";
|
|
517
518
|
import os3 from "os";
|
|
519
|
+
import { execSync as execSync2 } from "child_process";
|
|
518
520
|
import { fileURLToPath } from "url";
|
|
519
521
|
function resolvePackageRoot() {
|
|
520
522
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -961,6 +963,44 @@ async function runInstaller(homeDir) {
|
|
|
961
963
|
exe-os installed successfully.
|
|
962
964
|
`);
|
|
963
965
|
}
|
|
966
|
+
function setupTmux(home) {
|
|
967
|
+
const homeDir = home ?? os3.homedir();
|
|
968
|
+
const exeDir = path4.join(homeDir, ".exe-os");
|
|
969
|
+
const exeTmuxConf = path4.join(exeDir, "tmux.conf");
|
|
970
|
+
const userTmuxConf = path4.join(homeDir, ".tmux.conf");
|
|
971
|
+
const backupPath = path4.join(homeDir, ".tmux.conf.backup");
|
|
972
|
+
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
973
|
+
const pkgRoot = resolvePackageRoot();
|
|
974
|
+
const assetPath = path4.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
975
|
+
if (!existsSync4(assetPath)) {
|
|
976
|
+
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
977
|
+
`);
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
mkdirSync2(exeDir, { recursive: true });
|
|
981
|
+
copyFileSync(assetPath, exeTmuxConf);
|
|
982
|
+
if (existsSync4(userTmuxConf)) {
|
|
983
|
+
const existing = readFileSync3(userTmuxConf, "utf8");
|
|
984
|
+
if (!existing.includes(sourceLine)) {
|
|
985
|
+
if (!existsSync4(backupPath)) {
|
|
986
|
+
copyFileSync(userTmuxConf, backupPath);
|
|
987
|
+
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
988
|
+
`);
|
|
989
|
+
}
|
|
990
|
+
writeFileSync(userTmuxConf, `${sourceLine}
|
|
991
|
+
${existing}`);
|
|
992
|
+
}
|
|
993
|
+
} else {
|
|
994
|
+
writeFileSync(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
995
|
+
${sourceLine}
|
|
996
|
+
`);
|
|
997
|
+
}
|
|
998
|
+
try {
|
|
999
|
+
execSync2(`tmux source-file ${exeTmuxConf} 2>/dev/null`);
|
|
1000
|
+
} catch {
|
|
1001
|
+
}
|
|
1002
|
+
process.stderr.write("exe-os: tmux config installed\n");
|
|
1003
|
+
}
|
|
964
1004
|
function summarizeSymlinkResults(results) {
|
|
965
1005
|
if (results.length === 0) return "no employees in roster";
|
|
966
1006
|
const created = results.filter((r) => r.action === "created").length;
|
|
@@ -2149,12 +2189,12 @@ __export(shard_manager_exports, {
|
|
|
2149
2189
|
shardExists: () => shardExists
|
|
2150
2190
|
});
|
|
2151
2191
|
import path6 from "path";
|
|
2152
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
2192
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync } from "fs";
|
|
2153
2193
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2154
2194
|
function initShardManager(encryptionKey) {
|
|
2155
2195
|
_encryptionKey = encryptionKey;
|
|
2156
2196
|
if (!existsSync6(SHARDS_DIR)) {
|
|
2157
|
-
|
|
2197
|
+
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
2158
2198
|
}
|
|
2159
2199
|
_shardingEnabled = true;
|
|
2160
2200
|
}
|
|
@@ -4409,8 +4449,8 @@ __export(exe_rename_exports, {
|
|
|
4409
4449
|
main: () => main,
|
|
4410
4450
|
renameEmployee: () => renameEmployee
|
|
4411
4451
|
});
|
|
4412
|
-
import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
|
|
4413
|
-
import { execSync as
|
|
4452
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
|
|
4453
|
+
import { execSync as execSync3 } from "child_process";
|
|
4414
4454
|
import path9 from "path";
|
|
4415
4455
|
import { homedir as homedir2 } from "os";
|
|
4416
4456
|
async function renameEmployee(oldName, newName, opts = {}) {
|
|
@@ -4442,7 +4482,7 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
4442
4482
|
undo: () => {
|
|
4443
4483
|
employee.name = originalName;
|
|
4444
4484
|
employee.systemPrompt = originalPrompt;
|
|
4445
|
-
|
|
4485
|
+
writeFileSync2(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
4446
4486
|
}
|
|
4447
4487
|
});
|
|
4448
4488
|
const oldIdentityPath = path9.join(identityDir, `${oldName}.md`);
|
|
@@ -4454,12 +4494,12 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
4454
4494
|
`$1${newName}`
|
|
4455
4495
|
);
|
|
4456
4496
|
renameSync2(oldIdentityPath, newIdentityPath);
|
|
4457
|
-
|
|
4497
|
+
writeFileSync2(newIdentityPath, updatedContent, "utf-8");
|
|
4458
4498
|
rollbackStack.push({
|
|
4459
4499
|
description: "restore identity file",
|
|
4460
4500
|
undo: () => {
|
|
4461
4501
|
if (existsSync8(newIdentityPath)) {
|
|
4462
|
-
|
|
4502
|
+
writeFileSync2(newIdentityPath, content, "utf-8");
|
|
4463
4503
|
renameSync2(newIdentityPath, oldIdentityPath);
|
|
4464
4504
|
}
|
|
4465
4505
|
}
|
|
@@ -4475,7 +4515,7 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
4475
4515
|
undo: () => {
|
|
4476
4516
|
if (existsSync8(newAgentPath)) {
|
|
4477
4517
|
renameSync2(newAgentPath, oldAgentPath);
|
|
4478
|
-
|
|
4518
|
+
writeFileSync2(oldAgentPath, agentContent, "utf-8");
|
|
4479
4519
|
}
|
|
4480
4520
|
}
|
|
4481
4521
|
});
|
|
@@ -4544,7 +4584,7 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
4544
4584
|
}
|
|
4545
4585
|
function findExeBin2() {
|
|
4546
4586
|
try {
|
|
4547
|
-
return
|
|
4587
|
+
return execSync3(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
4548
4588
|
} catch {
|
|
4549
4589
|
return null;
|
|
4550
4590
|
}
|
|
@@ -4730,10 +4770,10 @@ async function disposeEmbedder() {
|
|
|
4730
4770
|
async function embedDirect(text) {
|
|
4731
4771
|
const llamaCpp = await import("node-llama-cpp");
|
|
4732
4772
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4733
|
-
const { existsSync:
|
|
4734
|
-
const
|
|
4735
|
-
const modelPath =
|
|
4736
|
-
if (!
|
|
4773
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
4774
|
+
const path36 = await import("path");
|
|
4775
|
+
const modelPath = path36.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4776
|
+
if (!existsSync24(modelPath)) {
|
|
4737
4777
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4738
4778
|
}
|
|
4739
4779
|
const llama = await llamaCpp.getLlama();
|
|
@@ -4778,7 +4818,7 @@ __export(license_exports, {
|
|
|
4778
4818
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4779
4819
|
validateLicense: () => validateLicense
|
|
4780
4820
|
});
|
|
4781
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
4821
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
4782
4822
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4783
4823
|
import path11 from "path";
|
|
4784
4824
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -4807,8 +4847,8 @@ function loadDeviceId() {
|
|
|
4807
4847
|
} catch {
|
|
4808
4848
|
}
|
|
4809
4849
|
const id = randomUUID3();
|
|
4810
|
-
|
|
4811
|
-
|
|
4850
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
4851
|
+
writeFileSync3(DEVICE_ID_PATH, id, "utf8");
|
|
4812
4852
|
return id;
|
|
4813
4853
|
}
|
|
4814
4854
|
function loadLicense() {
|
|
@@ -4820,8 +4860,8 @@ function loadLicense() {
|
|
|
4820
4860
|
}
|
|
4821
4861
|
}
|
|
4822
4862
|
function saveLicense(apiKey) {
|
|
4823
|
-
|
|
4824
|
-
|
|
4863
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
4864
|
+
writeFileSync3(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
4825
4865
|
}
|
|
4826
4866
|
async function verifyLicenseJwt(token) {
|
|
4827
4867
|
try {
|
|
@@ -4892,7 +4932,7 @@ function getRawCachedPlan() {
|
|
|
4892
4932
|
}
|
|
4893
4933
|
function cacheResponse(token) {
|
|
4894
4934
|
try {
|
|
4895
|
-
|
|
4935
|
+
writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
4896
4936
|
} catch {
|
|
4897
4937
|
}
|
|
4898
4938
|
}
|
|
@@ -5087,64 +5127,1129 @@ async function assertVpsLicense(opts) {
|
|
|
5087
5127
|
}
|
|
5088
5128
|
} catch {
|
|
5089
5129
|
}
|
|
5090
|
-
throw new Error(
|
|
5091
|
-
`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.`
|
|
5130
|
+
throw new Error(
|
|
5131
|
+
`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
|
+
);
|
|
5133
|
+
}
|
|
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();
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
function stopLicenseRevalidation() {
|
|
5150
|
+
if (_revalTimer) {
|
|
5151
|
+
clearInterval(_revalTimer);
|
|
5152
|
+
_revalTimer = null;
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
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;
|
|
5156
|
+
var init_license = __esm({
|
|
5157
|
+
"src/lib/license.ts"() {
|
|
5158
|
+
"use strict";
|
|
5159
|
+
init_config();
|
|
5160
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
5161
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
5162
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
5163
|
+
API_BASE = "https://askexe.com/cloud";
|
|
5164
|
+
RETRY_DELAY_MS = 500;
|
|
5165
|
+
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
5166
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
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;
|
|
5188
|
+
}
|
|
5189
|
+
});
|
|
5190
|
+
|
|
5191
|
+
// src/lib/crypto.ts
|
|
5192
|
+
var crypto_exports = {};
|
|
5193
|
+
__export(crypto_exports, {
|
|
5194
|
+
decryptSyncBlob: () => decryptSyncBlob,
|
|
5195
|
+
encryptSyncBlob: () => encryptSyncBlob,
|
|
5196
|
+
initSyncCrypto: () => initSyncCrypto,
|
|
5197
|
+
isSyncCryptoInitialized: () => isSyncCryptoInitialized
|
|
5198
|
+
});
|
|
5199
|
+
import crypto3 from "crypto";
|
|
5200
|
+
function initSyncCrypto(masterKey) {
|
|
5201
|
+
if (masterKey.length !== 32) {
|
|
5202
|
+
throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
|
|
5203
|
+
}
|
|
5204
|
+
_syncKey = Buffer.from(
|
|
5205
|
+
crypto3.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
|
|
5206
|
+
);
|
|
5207
|
+
}
|
|
5208
|
+
function isSyncCryptoInitialized() {
|
|
5209
|
+
return _syncKey !== null;
|
|
5210
|
+
}
|
|
5211
|
+
function requireSyncKey() {
|
|
5212
|
+
if (!_syncKey) {
|
|
5213
|
+
throw new Error("Sync crypto not initialized. Call initSyncCrypto(masterKey) first.");
|
|
5214
|
+
}
|
|
5215
|
+
return _syncKey;
|
|
5216
|
+
}
|
|
5217
|
+
function encryptSyncBlob(data) {
|
|
5218
|
+
const key = requireSyncKey();
|
|
5219
|
+
const iv = crypto3.randomBytes(IV_LENGTH);
|
|
5220
|
+
const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
|
|
5221
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
5222
|
+
const tag = cipher.getAuthTag();
|
|
5223
|
+
return Buffer.concat([iv, encrypted, tag]).toString("base64");
|
|
5224
|
+
}
|
|
5225
|
+
function decryptSyncBlob(ciphertext) {
|
|
5226
|
+
const key = requireSyncKey();
|
|
5227
|
+
const combined = Buffer.from(ciphertext, "base64");
|
|
5228
|
+
if (combined.length < IV_LENGTH + TAG_LENGTH) {
|
|
5229
|
+
throw new Error("Sync blob too short to contain IV + tag");
|
|
5230
|
+
}
|
|
5231
|
+
const iv = combined.subarray(0, IV_LENGTH);
|
|
5232
|
+
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
|
5233
|
+
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
5234
|
+
const decipher = crypto3.createDecipheriv(ALGORITHM, key, iv);
|
|
5235
|
+
decipher.setAuthTag(tag);
|
|
5236
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
5237
|
+
}
|
|
5238
|
+
var ALGORITHM, IV_LENGTH, TAG_LENGTH, SYNC_HKDF_INFO, _syncKey;
|
|
5239
|
+
var init_crypto = __esm({
|
|
5240
|
+
"src/lib/crypto.ts"() {
|
|
5241
|
+
"use strict";
|
|
5242
|
+
ALGORITHM = "aes-256-gcm";
|
|
5243
|
+
IV_LENGTH = 12;
|
|
5244
|
+
TAG_LENGTH = 16;
|
|
5245
|
+
SYNC_HKDF_INFO = "exe-mem-sync-v2";
|
|
5246
|
+
_syncKey = null;
|
|
5247
|
+
}
|
|
5248
|
+
});
|
|
5249
|
+
|
|
5250
|
+
// src/lib/compress.ts
|
|
5251
|
+
import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
|
|
5252
|
+
function compress(input) {
|
|
5253
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
5254
|
+
return brotliCompressSync(input, {
|
|
5255
|
+
params: {
|
|
5256
|
+
[constants.BROTLI_PARAM_QUALITY]: 4
|
|
5257
|
+
}
|
|
5258
|
+
});
|
|
5259
|
+
}
|
|
5260
|
+
function decompress(input) {
|
|
5261
|
+
if (input.length === 0) return Buffer.alloc(0);
|
|
5262
|
+
return brotliDecompressSync(input);
|
|
5263
|
+
}
|
|
5264
|
+
var init_compress = __esm({
|
|
5265
|
+
"src/lib/compress.ts"() {
|
|
5266
|
+
"use strict";
|
|
5267
|
+
}
|
|
5268
|
+
});
|
|
5269
|
+
|
|
5270
|
+
// src/lib/cloud-sync.ts
|
|
5271
|
+
var cloud_sync_exports = {};
|
|
5272
|
+
__export(cloud_sync_exports, {
|
|
5273
|
+
assertSecureEndpoint: () => assertSecureEndpoint,
|
|
5274
|
+
buildRosterBlob: () => buildRosterBlob,
|
|
5275
|
+
cloudPull: () => cloudPull,
|
|
5276
|
+
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
5277
|
+
cloudPullBlob: () => cloudPullBlob,
|
|
5278
|
+
cloudPullConversations: () => cloudPullConversations,
|
|
5279
|
+
cloudPullDocuments: () => cloudPullDocuments,
|
|
5280
|
+
cloudPullGlobalProcedures: () => cloudPullGlobalProcedures,
|
|
5281
|
+
cloudPullGraphRAG: () => cloudPullGraphRAG,
|
|
5282
|
+
cloudPullRoster: () => cloudPullRoster,
|
|
5283
|
+
cloudPullTasks: () => cloudPullTasks,
|
|
5284
|
+
cloudPush: () => cloudPush,
|
|
5285
|
+
cloudPushBehaviors: () => cloudPushBehaviors,
|
|
5286
|
+
cloudPushBlob: () => cloudPushBlob,
|
|
5287
|
+
cloudPushConversations: () => cloudPushConversations,
|
|
5288
|
+
cloudPushDocuments: () => cloudPushDocuments,
|
|
5289
|
+
cloudPushGlobalProcedures: () => cloudPushGlobalProcedures,
|
|
5290
|
+
cloudPushGraphRAG: () => cloudPushGraphRAG,
|
|
5291
|
+
cloudPushRoster: () => cloudPushRoster,
|
|
5292
|
+
cloudPushTasks: () => cloudPushTasks,
|
|
5293
|
+
cloudSync: () => cloudSync,
|
|
5294
|
+
mergeConfig: () => mergeConfig,
|
|
5295
|
+
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
5296
|
+
recordRosterDeletion: () => recordRosterDeletion
|
|
5297
|
+
});
|
|
5298
|
+
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";
|
|
5299
|
+
import crypto4 from "crypto";
|
|
5300
|
+
import path12 from "path";
|
|
5301
|
+
import { homedir as homedir3 } from "os";
|
|
5302
|
+
function sqlSafe(v) {
|
|
5303
|
+
return v === void 0 ? null : v;
|
|
5304
|
+
}
|
|
5305
|
+
function logError(msg) {
|
|
5306
|
+
try {
|
|
5307
|
+
const logPath = path12.join(homedir3(), ".exe-os", "workers.log");
|
|
5308
|
+
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
5309
|
+
`);
|
|
5310
|
+
} catch {
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
async function withRosterLock(fn) {
|
|
5314
|
+
try {
|
|
5315
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
5316
|
+
closeSync2(fd);
|
|
5317
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5318
|
+
} catch (err) {
|
|
5319
|
+
if (err.code === "EEXIST") {
|
|
5320
|
+
try {
|
|
5321
|
+
const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
5322
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
5323
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
5324
|
+
}
|
|
5325
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
5326
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
5327
|
+
closeSync2(fd);
|
|
5328
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5329
|
+
} catch (retryErr) {
|
|
5330
|
+
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
5331
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
5332
|
+
}
|
|
5333
|
+
} else {
|
|
5334
|
+
throw err;
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
try {
|
|
5338
|
+
return await fn();
|
|
5339
|
+
} finally {
|
|
5340
|
+
try {
|
|
5341
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
5342
|
+
} catch {
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
async function fetchWithRetry(url, init) {
|
|
5347
|
+
const MAX_RETRIES2 = 3;
|
|
5348
|
+
const BASE_DELAY_MS2 = 200;
|
|
5349
|
+
let lastError;
|
|
5350
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
5351
|
+
try {
|
|
5352
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
5353
|
+
const resp = await fetch(url, { ...init, signal });
|
|
5354
|
+
if (resp && resp.status >= 500 && attempt < MAX_RETRIES2) {
|
|
5355
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
5356
|
+
continue;
|
|
5357
|
+
}
|
|
5358
|
+
return resp;
|
|
5359
|
+
} catch (err) {
|
|
5360
|
+
lastError = err;
|
|
5361
|
+
if (attempt === MAX_RETRIES2) throw err;
|
|
5362
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
throw lastError;
|
|
5366
|
+
}
|
|
5367
|
+
function assertSecureEndpoint(endpoint) {
|
|
5368
|
+
if (endpoint.startsWith("https://")) return;
|
|
5369
|
+
if (endpoint.startsWith("http://")) {
|
|
5370
|
+
try {
|
|
5371
|
+
const parsed = new URL(endpoint);
|
|
5372
|
+
if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
|
|
5373
|
+
} catch {
|
|
5374
|
+
return;
|
|
5375
|
+
}
|
|
5376
|
+
throw new Error(
|
|
5377
|
+
`Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
async function cloudPush(records, maxVersion, config) {
|
|
5382
|
+
if (records.length === 0) return true;
|
|
5383
|
+
assertSecureEndpoint(config.endpoint);
|
|
5384
|
+
try {
|
|
5385
|
+
const json = JSON.stringify(records);
|
|
5386
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5387
|
+
const blob = encryptSyncBlob(compressed);
|
|
5388
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
|
|
5389
|
+
method: "POST",
|
|
5390
|
+
headers: {
|
|
5391
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5392
|
+
"Content-Type": "application/json",
|
|
5393
|
+
"X-Device-Id": loadDeviceId(),
|
|
5394
|
+
"X-Expected-Version": String(maxVersion)
|
|
5395
|
+
},
|
|
5396
|
+
body: JSON.stringify({ version: maxVersion, blob })
|
|
5397
|
+
});
|
|
5398
|
+
if (resp == null) {
|
|
5399
|
+
logError("[cloud-sync] PUSH FAILED: no response from server");
|
|
5400
|
+
return false;
|
|
5401
|
+
}
|
|
5402
|
+
if (resp.status === 409) {
|
|
5403
|
+
logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
|
|
5404
|
+
return false;
|
|
5405
|
+
}
|
|
5406
|
+
return resp.ok;
|
|
5407
|
+
} catch (err) {
|
|
5408
|
+
logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
5409
|
+
return false;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
async function cloudPull(sinceVersion, config) {
|
|
5413
|
+
assertSecureEndpoint(config.endpoint);
|
|
5414
|
+
try {
|
|
5415
|
+
const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
|
|
5416
|
+
method: "POST",
|
|
5417
|
+
headers: {
|
|
5418
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5419
|
+
"Content-Type": "application/json",
|
|
5420
|
+
"X-Device-Id": loadDeviceId()
|
|
5421
|
+
},
|
|
5422
|
+
body: JSON.stringify({ since_version: sinceVersion })
|
|
5423
|
+
});
|
|
5424
|
+
if (response == null) {
|
|
5425
|
+
logError("[cloud-sync] PULL FAILED: no response from server");
|
|
5426
|
+
return { records: [], maxVersion: sinceVersion };
|
|
5427
|
+
}
|
|
5428
|
+
if (!response.ok) return { records: [], maxVersion: sinceVersion };
|
|
5429
|
+
const data = await response.json();
|
|
5430
|
+
const allRecords = [];
|
|
5431
|
+
for (const { blob } of data.blobs ?? []) {
|
|
5432
|
+
try {
|
|
5433
|
+
const compressed = decryptSyncBlob(blob);
|
|
5434
|
+
const json = decompress(compressed).toString("utf8");
|
|
5435
|
+
const records = JSON.parse(json);
|
|
5436
|
+
allRecords.push(...records);
|
|
5437
|
+
} catch {
|
|
5438
|
+
continue;
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
|
|
5442
|
+
} catch (err) {
|
|
5443
|
+
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
5444
|
+
return { records: [], maxVersion: sinceVersion };
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5447
|
+
async function cloudSync(config) {
|
|
5448
|
+
let client;
|
|
5449
|
+
try {
|
|
5450
|
+
client = getClient();
|
|
5451
|
+
} catch {
|
|
5452
|
+
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
5453
|
+
}
|
|
5454
|
+
try {
|
|
5455
|
+
await client.execute(
|
|
5456
|
+
"CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
|
|
5457
|
+
);
|
|
5458
|
+
} catch (e) {
|
|
5459
|
+
logError(`[cloud-sync] sync_meta CREATE failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5460
|
+
}
|
|
5461
|
+
const pullMeta = await client.execute(
|
|
5462
|
+
"SELECT value FROM sync_meta WHERE key = 'last_cloud_pull_version'"
|
|
5463
|
+
);
|
|
5464
|
+
const lastPullVersion = pullMeta.rows.length > 0 ? Number(pullMeta.rows[0].value) : 0;
|
|
5465
|
+
const pullResult = await cloudPull(lastPullVersion, config);
|
|
5466
|
+
let pulled = 0;
|
|
5467
|
+
if (pullResult.records.length > 0) {
|
|
5468
|
+
const stmts = pullResult.records.map((rec) => ({
|
|
5469
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
5470
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
5471
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
5472
|
+
author_device_id, scope)
|
|
5473
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5474
|
+
args: [
|
|
5475
|
+
sqlSafe(rec.id),
|
|
5476
|
+
sqlSafe(rec.agent_id),
|
|
5477
|
+
sqlSafe(rec.agent_role),
|
|
5478
|
+
sqlSafe(rec.session_id),
|
|
5479
|
+
sqlSafe(rec.timestamp),
|
|
5480
|
+
sqlSafe(rec.tool_name),
|
|
5481
|
+
sqlSafe(rec.project_name),
|
|
5482
|
+
sqlSafe(rec.has_error ?? 0),
|
|
5483
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
5484
|
+
sqlSafe(rec.version ?? 0),
|
|
5485
|
+
sqlSafe(rec.author_device_id),
|
|
5486
|
+
sqlSafe(rec.scope ?? "business")
|
|
5487
|
+
]
|
|
5488
|
+
}));
|
|
5489
|
+
await client.batch(stmts, "write");
|
|
5490
|
+
pulled = pullResult.records.length;
|
|
5491
|
+
}
|
|
5492
|
+
if (pullResult.maxVersion > lastPullVersion) {
|
|
5493
|
+
await client.execute({
|
|
5494
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?)",
|
|
5495
|
+
args: [String(pullResult.maxVersion)]
|
|
5496
|
+
});
|
|
5497
|
+
}
|
|
5498
|
+
const pushMeta = await client.execute(
|
|
5499
|
+
"SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
|
|
5500
|
+
);
|
|
5501
|
+
const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
|
|
5502
|
+
let pushed = 0;
|
|
5503
|
+
let batchCursor = lastPushVersion;
|
|
5504
|
+
while (true) {
|
|
5505
|
+
const recordsResult = await client.execute({
|
|
5506
|
+
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
5507
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
5508
|
+
author_device_id, scope
|
|
5509
|
+
FROM memories
|
|
5510
|
+
WHERE version > ?
|
|
5511
|
+
AND (scope IS NULL OR scope != 'personal')
|
|
5512
|
+
ORDER BY version ASC
|
|
5513
|
+
LIMIT ?`,
|
|
5514
|
+
args: [batchCursor, PUSH_BATCH_SIZE]
|
|
5515
|
+
});
|
|
5516
|
+
if (recordsResult.rows.length === 0) break;
|
|
5517
|
+
const records = recordsResult.rows.map((row) => ({
|
|
5518
|
+
id: row.id,
|
|
5519
|
+
agent_id: row.agent_id,
|
|
5520
|
+
agent_role: row.agent_role,
|
|
5521
|
+
session_id: row.session_id,
|
|
5522
|
+
timestamp: row.timestamp,
|
|
5523
|
+
tool_name: row.tool_name,
|
|
5524
|
+
project_name: row.project_name,
|
|
5525
|
+
has_error: row.has_error,
|
|
5526
|
+
raw_text: row.raw_text,
|
|
5527
|
+
version: row.version,
|
|
5528
|
+
author_device_id: row.author_device_id,
|
|
5529
|
+
scope: row.scope
|
|
5530
|
+
}));
|
|
5531
|
+
const maxVersion = Number(records[records.length - 1].version);
|
|
5532
|
+
const pushOk = await cloudPush(records, maxVersion, config);
|
|
5533
|
+
if (!pushOk) break;
|
|
5534
|
+
await client.execute({
|
|
5535
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
5536
|
+
args: [String(maxVersion)]
|
|
5537
|
+
});
|
|
5538
|
+
pushed += records.length;
|
|
5539
|
+
batchCursor = maxVersion;
|
|
5540
|
+
if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
|
|
5541
|
+
}
|
|
5542
|
+
try {
|
|
5543
|
+
await cloudPushRoster(config);
|
|
5544
|
+
} catch (err) {
|
|
5545
|
+
logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5546
|
+
}
|
|
5547
|
+
try {
|
|
5548
|
+
await cloudPullRoster(config);
|
|
5549
|
+
} catch (err) {
|
|
5550
|
+
logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5551
|
+
}
|
|
5552
|
+
try {
|
|
5553
|
+
await cloudPushGlobalProcedures(config);
|
|
5554
|
+
} catch (err) {
|
|
5555
|
+
logError(`[cloud-sync] Global procedures push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5556
|
+
}
|
|
5557
|
+
try {
|
|
5558
|
+
await cloudPullGlobalProcedures(config);
|
|
5559
|
+
} catch (err) {
|
|
5560
|
+
logError(`[cloud-sync] Global procedures pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5561
|
+
}
|
|
5562
|
+
let behaviorsResult = { pushed: false, pulled: 0 };
|
|
5563
|
+
try {
|
|
5564
|
+
behaviorsResult.pushed = await cloudPushBehaviors(config);
|
|
5565
|
+
} catch (err) {
|
|
5566
|
+
logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5567
|
+
}
|
|
5568
|
+
try {
|
|
5569
|
+
const pullResult2 = await cloudPullBehaviors(config);
|
|
5570
|
+
behaviorsResult.pulled = pullResult2.pulled;
|
|
5571
|
+
} catch (err) {
|
|
5572
|
+
logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5573
|
+
}
|
|
5574
|
+
let graphragResult = { pushed: false, pulled: 0 };
|
|
5575
|
+
try {
|
|
5576
|
+
graphragResult.pushed = await cloudPushGraphRAG(config);
|
|
5577
|
+
} catch (err) {
|
|
5578
|
+
logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5579
|
+
}
|
|
5580
|
+
try {
|
|
5581
|
+
const pullResult2 = await cloudPullGraphRAG(config);
|
|
5582
|
+
graphragResult.pulled = pullResult2.pulled;
|
|
5583
|
+
} catch (err) {
|
|
5584
|
+
logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5585
|
+
}
|
|
5586
|
+
let tasksResult = { pushed: false, pulled: 0 };
|
|
5587
|
+
try {
|
|
5588
|
+
tasksResult.pushed = await cloudPushTasks(config);
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5591
|
+
}
|
|
5592
|
+
try {
|
|
5593
|
+
const pullResult2 = await cloudPullTasks(config);
|
|
5594
|
+
tasksResult.pulled = pullResult2.pulled;
|
|
5595
|
+
} catch (err) {
|
|
5596
|
+
logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5597
|
+
}
|
|
5598
|
+
let conversationsResult = { pushed: false, pulled: 0 };
|
|
5599
|
+
try {
|
|
5600
|
+
conversationsResult.pushed = await cloudPushConversations(config);
|
|
5601
|
+
} catch (err) {
|
|
5602
|
+
logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5603
|
+
}
|
|
5604
|
+
try {
|
|
5605
|
+
const pullResult2 = await cloudPullConversations(config);
|
|
5606
|
+
conversationsResult.pulled = pullResult2.pulled;
|
|
5607
|
+
} catch (err) {
|
|
5608
|
+
logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5609
|
+
}
|
|
5610
|
+
let documentsResult = { pushed: false, pulled: 0 };
|
|
5611
|
+
try {
|
|
5612
|
+
documentsResult.pushed = await cloudPushDocuments(config);
|
|
5613
|
+
} catch (err) {
|
|
5614
|
+
logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5615
|
+
}
|
|
5616
|
+
try {
|
|
5617
|
+
const pullResult2 = await cloudPullDocuments(config);
|
|
5618
|
+
documentsResult.pulled = pullResult2.pulled;
|
|
5619
|
+
} catch (err) {
|
|
5620
|
+
logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5621
|
+
}
|
|
5622
|
+
return {
|
|
5623
|
+
pushed,
|
|
5624
|
+
pulled,
|
|
5625
|
+
behaviors: behaviorsResult,
|
|
5626
|
+
graphrag: graphragResult,
|
|
5627
|
+
tasks: tasksResult,
|
|
5628
|
+
conversations: conversationsResult,
|
|
5629
|
+
documents: documentsResult
|
|
5630
|
+
};
|
|
5631
|
+
}
|
|
5632
|
+
function recordRosterDeletion(name) {
|
|
5633
|
+
let deletions = [];
|
|
5634
|
+
try {
|
|
5635
|
+
if (existsSync11(ROSTER_DELETIONS_PATH)) {
|
|
5636
|
+
deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5637
|
+
}
|
|
5638
|
+
} catch {
|
|
5639
|
+
}
|
|
5640
|
+
if (!deletions.includes(name)) deletions.push(name);
|
|
5641
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
5642
|
+
}
|
|
5643
|
+
function consumeRosterDeletions() {
|
|
5644
|
+
try {
|
|
5645
|
+
if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
|
|
5646
|
+
const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5647
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
|
|
5648
|
+
return deletions;
|
|
5649
|
+
} catch {
|
|
5650
|
+
return [];
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
function buildRosterBlob(paths) {
|
|
5654
|
+
const rosterPath = paths?.rosterPath ?? path12.join(EXE_AI_DIR, "exe-employees.json");
|
|
5655
|
+
const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
|
|
5656
|
+
const configPath = paths?.configPath ?? path12.join(EXE_AI_DIR, "config.json");
|
|
5657
|
+
let roster = [];
|
|
5658
|
+
if (existsSync11(rosterPath)) {
|
|
5659
|
+
try {
|
|
5660
|
+
roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
|
|
5661
|
+
} catch {
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
const identities = {};
|
|
5665
|
+
if (existsSync11(identityDir)) {
|
|
5666
|
+
for (const file of readdirSync2(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
5667
|
+
try {
|
|
5668
|
+
identities[file] = readFileSync7(path12.join(identityDir, file), "utf-8");
|
|
5669
|
+
} catch {
|
|
5670
|
+
}
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
let config;
|
|
5674
|
+
if (existsSync11(configPath)) {
|
|
5675
|
+
try {
|
|
5676
|
+
config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
5677
|
+
} catch {
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
const deletedNames = consumeRosterDeletions();
|
|
5681
|
+
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
5682
|
+
const hash = crypto4.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5683
|
+
return { roster, identities, config, deletedNames, version: hash };
|
|
5684
|
+
}
|
|
5685
|
+
async function cloudPushRoster(config) {
|
|
5686
|
+
assertSecureEndpoint(config.endpoint);
|
|
5687
|
+
const blob = buildRosterBlob();
|
|
5688
|
+
if (blob.roster.length === 0) return true;
|
|
5689
|
+
try {
|
|
5690
|
+
const client = getClient();
|
|
5691
|
+
const meta = await client.execute(
|
|
5692
|
+
"SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
|
|
5693
|
+
);
|
|
5694
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
5695
|
+
if (blob.version === lastVersion) return true;
|
|
5696
|
+
} catch {
|
|
5697
|
+
}
|
|
5698
|
+
try {
|
|
5699
|
+
const json = JSON.stringify(blob);
|
|
5700
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5701
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
5702
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
|
|
5703
|
+
method: "POST",
|
|
5704
|
+
headers: {
|
|
5705
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5706
|
+
"Content-Type": "application/json",
|
|
5707
|
+
"X-Device-Id": loadDeviceId()
|
|
5708
|
+
},
|
|
5709
|
+
body: JSON.stringify({ blob: encrypted })
|
|
5710
|
+
});
|
|
5711
|
+
if (resp.ok) {
|
|
5712
|
+
try {
|
|
5713
|
+
const client = getClient();
|
|
5714
|
+
await client.execute({
|
|
5715
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?)",
|
|
5716
|
+
args: [String(blob.version)]
|
|
5717
|
+
});
|
|
5718
|
+
} catch {
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
return resp.ok;
|
|
5722
|
+
} catch (err) {
|
|
5723
|
+
process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
5724
|
+
`);
|
|
5725
|
+
return false;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
async function cloudPullRoster(config) {
|
|
5729
|
+
assertSecureEndpoint(config.endpoint);
|
|
5730
|
+
try {
|
|
5731
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
|
|
5732
|
+
method: "GET",
|
|
5733
|
+
headers: {
|
|
5734
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5735
|
+
"X-Device-Id": loadDeviceId()
|
|
5736
|
+
}
|
|
5737
|
+
});
|
|
5738
|
+
if (!resp.ok) return { added: 0 };
|
|
5739
|
+
const data = await resp.json();
|
|
5740
|
+
if (!data.blob) return { added: 0 };
|
|
5741
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
5742
|
+
const json = decompress(compressed).toString("utf8");
|
|
5743
|
+
const remote = JSON.parse(json);
|
|
5744
|
+
return mergeRosterFromRemote(remote);
|
|
5745
|
+
} catch (err) {
|
|
5746
|
+
process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
5747
|
+
`);
|
|
5748
|
+
return { added: 0 };
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
function mergeConfig(remoteConfig, configPath) {
|
|
5752
|
+
const cfgPath = configPath ?? path12.join(EXE_AI_DIR, "config.json");
|
|
5753
|
+
let local = {};
|
|
5754
|
+
if (existsSync11(cfgPath)) {
|
|
5755
|
+
try {
|
|
5756
|
+
local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
|
|
5757
|
+
} catch {
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
const merged = { ...remoteConfig, ...local };
|
|
5761
|
+
const dir = path12.dirname(cfgPath);
|
|
5762
|
+
if (!existsSync11(dir)) mkdirSync5(dir, { recursive: true });
|
|
5763
|
+
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
5764
|
+
}
|
|
5765
|
+
async function mergeRosterFromRemote(remote, paths) {
|
|
5766
|
+
return withRosterLock(async () => {
|
|
5767
|
+
const rosterPath = paths?.rosterPath ?? void 0;
|
|
5768
|
+
const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
|
|
5769
|
+
const localEmployees = await loadEmployees(rosterPath);
|
|
5770
|
+
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
5771
|
+
let added = 0;
|
|
5772
|
+
let identitiesUpdated = 0;
|
|
5773
|
+
for (const remoteEmp of remote.roster) {
|
|
5774
|
+
if (!localNames.has(remoteEmp.name)) {
|
|
5775
|
+
localEmployees.push(remoteEmp);
|
|
5776
|
+
localNames.add(remoteEmp.name);
|
|
5777
|
+
added++;
|
|
5778
|
+
try {
|
|
5779
|
+
registerBinSymlinks(remoteEmp.name);
|
|
5780
|
+
} catch {
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
const remoteIdentity = remote.identities[`${remoteEmp.name}.md`];
|
|
5784
|
+
if (remoteIdentity) {
|
|
5785
|
+
if (!existsSync11(identityDir)) mkdirSync5(identityDir, { recursive: true });
|
|
5786
|
+
const idPath = path12.join(identityDir, `${remoteEmp.name}.md`);
|
|
5787
|
+
let localIdentity = null;
|
|
5788
|
+
try {
|
|
5789
|
+
localIdentity = existsSync11(idPath) ? readFileSync7(idPath, "utf-8") : null;
|
|
5790
|
+
} catch {
|
|
5791
|
+
}
|
|
5792
|
+
if (localIdentity !== remoteIdentity) {
|
|
5793
|
+
writeFileSync4(idPath, remoteIdentity, "utf-8");
|
|
5794
|
+
identitiesUpdated++;
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
let removed = 0;
|
|
5799
|
+
if (remote.deletedNames && remote.deletedNames.length > 0) {
|
|
5800
|
+
const toRemove = new Set(remote.deletedNames);
|
|
5801
|
+
const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
|
|
5802
|
+
removed = localEmployees.length - filtered.length;
|
|
5803
|
+
if (removed > 0) {
|
|
5804
|
+
localEmployees.length = 0;
|
|
5805
|
+
localEmployees.push(...filtered);
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
if (added > 0 || removed > 0) {
|
|
5809
|
+
await saveEmployees(localEmployees, rosterPath);
|
|
5810
|
+
}
|
|
5811
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
5812
|
+
try {
|
|
5813
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
5814
|
+
} catch {
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
return { added, identitiesUpdated };
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5820
|
+
async function cloudPushBlob(route, data, metaKey, config) {
|
|
5821
|
+
if (data.length === 0) return { ok: true };
|
|
5822
|
+
assertSecureEndpoint(config.endpoint);
|
|
5823
|
+
const json = JSON.stringify(data);
|
|
5824
|
+
const version = Buffer.from(json).length;
|
|
5825
|
+
try {
|
|
5826
|
+
const client = getClient();
|
|
5827
|
+
const meta = await client.execute({
|
|
5828
|
+
sql: "SELECT value FROM sync_meta WHERE key = ?",
|
|
5829
|
+
args: [metaKey]
|
|
5830
|
+
});
|
|
5831
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
5832
|
+
if (version === lastVersion) return { ok: true };
|
|
5833
|
+
} catch {
|
|
5834
|
+
}
|
|
5835
|
+
try {
|
|
5836
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
5837
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
5838
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
5839
|
+
method: "POST",
|
|
5840
|
+
headers: {
|
|
5841
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5842
|
+
"Content-Type": "application/json",
|
|
5843
|
+
"X-Device-Id": loadDeviceId()
|
|
5844
|
+
},
|
|
5845
|
+
body: JSON.stringify({ blob: encrypted })
|
|
5846
|
+
});
|
|
5847
|
+
if (resp.ok) {
|
|
5848
|
+
try {
|
|
5849
|
+
const client = getClient();
|
|
5850
|
+
await client.execute({
|
|
5851
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
|
|
5852
|
+
args: [metaKey, String(version)]
|
|
5853
|
+
});
|
|
5854
|
+
} catch {
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
return { ok: resp.ok };
|
|
5858
|
+
} catch (err) {
|
|
5859
|
+
logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5860
|
+
return { ok: false };
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
async function cloudPullBlob(route, config) {
|
|
5864
|
+
assertSecureEndpoint(config.endpoint);
|
|
5865
|
+
try {
|
|
5866
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
5867
|
+
method: "GET",
|
|
5868
|
+
headers: {
|
|
5869
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5870
|
+
"X-Device-Id": loadDeviceId()
|
|
5871
|
+
}
|
|
5872
|
+
});
|
|
5873
|
+
if (!resp.ok) return null;
|
|
5874
|
+
const data = await resp.json();
|
|
5875
|
+
if (!data.blob) return null;
|
|
5876
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
5877
|
+
const json = decompress(compressed).toString("utf8");
|
|
5878
|
+
return JSON.parse(json);
|
|
5879
|
+
} catch (err) {
|
|
5880
|
+
logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5881
|
+
return null;
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
async function cloudPushGlobalProcedures(config) {
|
|
5885
|
+
const client = getClient();
|
|
5886
|
+
const result = await client.execute("SELECT * FROM global_procedures LIMIT 1000");
|
|
5887
|
+
const rows = result.rows;
|
|
5888
|
+
const { ok } = await cloudPushBlob(
|
|
5889
|
+
"/sync/push-global-procedures",
|
|
5890
|
+
rows,
|
|
5891
|
+
"last_global_procedures_push_version",
|
|
5892
|
+
config
|
|
5893
|
+
);
|
|
5894
|
+
return ok;
|
|
5895
|
+
}
|
|
5896
|
+
async function cloudPullGlobalProcedures(config) {
|
|
5897
|
+
const remoteProcs = await cloudPullBlob(
|
|
5898
|
+
"/sync/pull-global-procedures",
|
|
5899
|
+
config
|
|
5900
|
+
);
|
|
5901
|
+
if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
|
|
5902
|
+
const client = getClient();
|
|
5903
|
+
const stmts = remoteProcs.map((p) => ({
|
|
5904
|
+
sql: `INSERT INTO global_procedures
|
|
5905
|
+
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
5906
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
5907
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
5908
|
+
title = excluded.title,
|
|
5909
|
+
content = excluded.content,
|
|
5910
|
+
priority = excluded.priority,
|
|
5911
|
+
domain = excluded.domain,
|
|
5912
|
+
active = excluded.active,
|
|
5913
|
+
updated_at = excluded.updated_at
|
|
5914
|
+
WHERE excluded.updated_at > global_procedures.updated_at`,
|
|
5915
|
+
args: [
|
|
5916
|
+
sqlSafe(p.id),
|
|
5917
|
+
sqlSafe(p.title),
|
|
5918
|
+
sqlSafe(p.content),
|
|
5919
|
+
sqlSafe(p.priority ?? "p0"),
|
|
5920
|
+
sqlSafe(p.domain),
|
|
5921
|
+
sqlSafe(p.active ?? 1),
|
|
5922
|
+
sqlSafe(p.created_at),
|
|
5923
|
+
sqlSafe(p.updated_at)
|
|
5924
|
+
]
|
|
5925
|
+
}));
|
|
5926
|
+
await client.batch(stmts, "write");
|
|
5927
|
+
return { pulled: remoteProcs.length };
|
|
5928
|
+
}
|
|
5929
|
+
async function cloudPushBehaviors(config) {
|
|
5930
|
+
const client = getClient();
|
|
5931
|
+
const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
5932
|
+
const rows = result.rows;
|
|
5933
|
+
const { ok } = await cloudPushBlob(
|
|
5934
|
+
"/sync/push-behaviors",
|
|
5935
|
+
rows,
|
|
5936
|
+
"last_behaviors_push_version",
|
|
5937
|
+
config
|
|
5938
|
+
);
|
|
5939
|
+
return ok;
|
|
5940
|
+
}
|
|
5941
|
+
async function cloudPullBehaviors(config) {
|
|
5942
|
+
const remoteBehaviors = await cloudPullBlob(
|
|
5943
|
+
"/sync/pull-behaviors",
|
|
5944
|
+
config
|
|
5945
|
+
);
|
|
5946
|
+
if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
|
|
5947
|
+
const client = getClient();
|
|
5948
|
+
let pulled = 0;
|
|
5949
|
+
for (const behavior of remoteBehaviors) {
|
|
5950
|
+
const existing = await client.execute({
|
|
5951
|
+
sql: `SELECT COUNT(*) as cnt FROM behaviors
|
|
5952
|
+
WHERE agent_id = ? AND content = ?`,
|
|
5953
|
+
args: [sqlSafe(behavior.agent_id), sqlSafe(behavior.content)]
|
|
5954
|
+
});
|
|
5955
|
+
if (Number(existing.rows[0]?.cnt) > 0) continue;
|
|
5956
|
+
await client.execute({
|
|
5957
|
+
sql: `INSERT OR IGNORE INTO behaviors
|
|
5958
|
+
(id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
|
|
5959
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5960
|
+
args: [
|
|
5961
|
+
sqlSafe(behavior.id),
|
|
5962
|
+
sqlSafe(behavior.agent_id),
|
|
5963
|
+
sqlSafe(behavior.project_name),
|
|
5964
|
+
sqlSafe(behavior.domain),
|
|
5965
|
+
sqlSafe(behavior.content),
|
|
5966
|
+
sqlSafe(behavior.active ?? 1),
|
|
5967
|
+
sqlSafe(behavior.priority ?? "p1"),
|
|
5968
|
+
sqlSafe(behavior.created_at),
|
|
5969
|
+
sqlSafe(behavior.updated_at)
|
|
5970
|
+
]
|
|
5971
|
+
});
|
|
5972
|
+
pulled++;
|
|
5973
|
+
}
|
|
5974
|
+
return { pulled };
|
|
5975
|
+
}
|
|
5976
|
+
async function cloudPushGraphRAG(config) {
|
|
5977
|
+
const client = getClient();
|
|
5978
|
+
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
5979
|
+
client.execute("SELECT * FROM entities LIMIT 50000"),
|
|
5980
|
+
client.execute("SELECT * FROM relationships LIMIT 50000"),
|
|
5981
|
+
client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
|
|
5982
|
+
client.execute("SELECT * FROM entity_memories LIMIT 50000"),
|
|
5983
|
+
client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
|
|
5984
|
+
client.execute("SELECT * FROM hyperedges LIMIT 50000"),
|
|
5985
|
+
client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
|
|
5986
|
+
]);
|
|
5987
|
+
const blob = {
|
|
5988
|
+
entities: entities.rows,
|
|
5989
|
+
relationships: relationships.rows,
|
|
5990
|
+
entity_aliases: aliases.rows,
|
|
5991
|
+
entity_memories: entityMems.rows,
|
|
5992
|
+
relationship_memories: relMems.rows,
|
|
5993
|
+
hyperedges: hyperedges.rows,
|
|
5994
|
+
hyperedge_nodes: hyperedgeNodes.rows
|
|
5995
|
+
};
|
|
5996
|
+
const { ok } = await cloudPushBlob(
|
|
5997
|
+
"/sync/push-graphrag",
|
|
5998
|
+
[blob],
|
|
5999
|
+
"last_graphrag_push_version",
|
|
6000
|
+
config
|
|
6001
|
+
);
|
|
6002
|
+
return ok;
|
|
6003
|
+
}
|
|
6004
|
+
async function cloudPullGraphRAG(config) {
|
|
6005
|
+
const data = await cloudPullBlob(
|
|
6006
|
+
"/sync/pull-graphrag",
|
|
6007
|
+
config
|
|
6008
|
+
);
|
|
6009
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
6010
|
+
const blob = data[0];
|
|
6011
|
+
const client = getClient();
|
|
6012
|
+
let pulled = 0;
|
|
6013
|
+
if (blob.entities.length > 0) {
|
|
6014
|
+
const stmts = blob.entities.map((e) => ({
|
|
6015
|
+
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
|
|
6016
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
6017
|
+
args: [sqlSafe(e.id), sqlSafe(e.name), sqlSafe(e.type), sqlSafe(e.first_seen), sqlSafe(e.last_seen), sqlSafe(e.properties ?? "{}")]
|
|
6018
|
+
}));
|
|
6019
|
+
await client.batch(stmts, "write");
|
|
6020
|
+
pulled += stmts.length;
|
|
6021
|
+
}
|
|
6022
|
+
if (blob.relationships.length > 0) {
|
|
6023
|
+
const stmts = blob.relationships.map((r) => ({
|
|
6024
|
+
sql: `INSERT OR IGNORE INTO relationships
|
|
6025
|
+
(id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
|
|
6026
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6027
|
+
args: [
|
|
6028
|
+
sqlSafe(r.id),
|
|
6029
|
+
sqlSafe(r.source_entity_id),
|
|
6030
|
+
sqlSafe(r.target_entity_id),
|
|
6031
|
+
sqlSafe(r.type),
|
|
6032
|
+
sqlSafe(r.weight ?? 1),
|
|
6033
|
+
sqlSafe(r.timestamp),
|
|
6034
|
+
sqlSafe(r.properties ?? "{}"),
|
|
6035
|
+
sqlSafe(r.confidence ?? 1),
|
|
6036
|
+
sqlSafe(r.confidence_label ?? "extracted")
|
|
6037
|
+
]
|
|
6038
|
+
}));
|
|
6039
|
+
await client.batch(stmts, "write");
|
|
6040
|
+
pulled += stmts.length;
|
|
6041
|
+
}
|
|
6042
|
+
if (blob.entity_aliases.length > 0) {
|
|
6043
|
+
const stmts = blob.entity_aliases.map((a) => ({
|
|
6044
|
+
sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
|
|
6045
|
+
args: [sqlSafe(a.alias), sqlSafe(a.canonical_entity_id)]
|
|
6046
|
+
}));
|
|
6047
|
+
await client.batch(stmts, "write");
|
|
6048
|
+
pulled += stmts.length;
|
|
6049
|
+
}
|
|
6050
|
+
if (blob.entity_memories.length > 0) {
|
|
6051
|
+
const stmts = blob.entity_memories.map((em) => ({
|
|
6052
|
+
sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
|
|
6053
|
+
args: [sqlSafe(em.entity_id), sqlSafe(em.memory_id)]
|
|
6054
|
+
}));
|
|
6055
|
+
await client.batch(stmts, "write");
|
|
6056
|
+
pulled += stmts.length;
|
|
6057
|
+
}
|
|
6058
|
+
if (blob.relationship_memories.length > 0) {
|
|
6059
|
+
const stmts = blob.relationship_memories.map((rm) => ({
|
|
6060
|
+
sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
|
|
6061
|
+
args: [sqlSafe(rm.relationship_id), sqlSafe(rm.memory_id)]
|
|
6062
|
+
}));
|
|
6063
|
+
await client.batch(stmts, "write");
|
|
6064
|
+
pulled += stmts.length;
|
|
6065
|
+
}
|
|
6066
|
+
if (blob.hyperedges.length > 0) {
|
|
6067
|
+
const stmts = blob.hyperedges.map((h) => ({
|
|
6068
|
+
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
6069
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
6070
|
+
args: [sqlSafe(h.id), sqlSafe(h.label), sqlSafe(h.relation), sqlSafe(h.confidence ?? 1), sqlSafe(h.timestamp)]
|
|
6071
|
+
}));
|
|
6072
|
+
await client.batch(stmts, "write");
|
|
6073
|
+
pulled += stmts.length;
|
|
6074
|
+
}
|
|
6075
|
+
if (blob.hyperedge_nodes.length > 0) {
|
|
6076
|
+
const stmts = blob.hyperedge_nodes.map((hn) => ({
|
|
6077
|
+
sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
|
|
6078
|
+
args: [sqlSafe(hn.hyperedge_id), sqlSafe(hn.entity_id)]
|
|
6079
|
+
}));
|
|
6080
|
+
await client.batch(stmts, "write");
|
|
6081
|
+
pulled += stmts.length;
|
|
6082
|
+
}
|
|
6083
|
+
return { pulled };
|
|
6084
|
+
}
|
|
6085
|
+
async function cloudPushTasks(config) {
|
|
6086
|
+
const client = getClient();
|
|
6087
|
+
const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
6088
|
+
const rows = result.rows;
|
|
6089
|
+
const { ok } = await cloudPushBlob(
|
|
6090
|
+
"/sync/push-tasks",
|
|
6091
|
+
rows,
|
|
6092
|
+
"last_tasks_push_version",
|
|
6093
|
+
config
|
|
5092
6094
|
);
|
|
6095
|
+
return ok;
|
|
5093
6096
|
}
|
|
5094
|
-
function
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
6097
|
+
async function cloudPullTasks(config) {
|
|
6098
|
+
const remoteTasks = await cloudPullBlob(
|
|
6099
|
+
"/sync/pull-tasks",
|
|
6100
|
+
config
|
|
6101
|
+
);
|
|
6102
|
+
if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
|
|
6103
|
+
const client = getClient();
|
|
6104
|
+
const stmts = remoteTasks.map((t) => ({
|
|
6105
|
+
sql: `INSERT OR IGNORE INTO tasks
|
|
6106
|
+
(id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
|
|
6107
|
+
blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
|
|
6108
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6109
|
+
args: [
|
|
6110
|
+
sqlSafe(t.id),
|
|
6111
|
+
sqlSafe(t.title),
|
|
6112
|
+
sqlSafe(t.assigned_to),
|
|
6113
|
+
sqlSafe(t.assigned_by),
|
|
6114
|
+
sqlSafe(t.project_name),
|
|
6115
|
+
sqlSafe(t.priority ?? "p1"),
|
|
6116
|
+
sqlSafe(t.status ?? "open"),
|
|
6117
|
+
sqlSafe(t.task_file),
|
|
6118
|
+
sqlSafe(t.created_at),
|
|
6119
|
+
sqlSafe(t.updated_at),
|
|
6120
|
+
sqlSafe(t.blocked_by),
|
|
6121
|
+
sqlSafe(t.parent_task_id),
|
|
6122
|
+
sqlSafe(t.budget_tokens),
|
|
6123
|
+
sqlSafe(t.budget_fallback_model),
|
|
6124
|
+
sqlSafe(t.tokens_used ?? 0),
|
|
6125
|
+
sqlSafe(t.tokens_warned_at)
|
|
6126
|
+
]
|
|
6127
|
+
}));
|
|
6128
|
+
await client.batch(stmts, "write");
|
|
6129
|
+
return { pulled: remoteTasks.length };
|
|
5108
6130
|
}
|
|
5109
|
-
function
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
6131
|
+
async function cloudPushConversations(config) {
|
|
6132
|
+
const client = getClient();
|
|
6133
|
+
const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
6134
|
+
const rows = result.rows;
|
|
6135
|
+
const { ok } = await cloudPushBlob(
|
|
6136
|
+
"/sync/push-conversations",
|
|
6137
|
+
rows,
|
|
6138
|
+
"last_conversations_push_version",
|
|
6139
|
+
config
|
|
6140
|
+
);
|
|
6141
|
+
return ok;
|
|
6142
|
+
}
|
|
6143
|
+
async function cloudPullConversations(config) {
|
|
6144
|
+
const remoteConvos = await cloudPullBlob(
|
|
6145
|
+
"/sync/pull-conversations",
|
|
6146
|
+
config
|
|
6147
|
+
);
|
|
6148
|
+
if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
|
|
6149
|
+
const client = getClient();
|
|
6150
|
+
const stmts = remoteConvos.map((c) => ({
|
|
6151
|
+
sql: `INSERT OR IGNORE INTO conversations
|
|
6152
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
6153
|
+
recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
|
|
6154
|
+
content_metadata, agent_response, agent_name, timestamp, ingested_at)
|
|
6155
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6156
|
+
args: [
|
|
6157
|
+
sqlSafe(c.id),
|
|
6158
|
+
sqlSafe(c.platform),
|
|
6159
|
+
sqlSafe(c.external_id),
|
|
6160
|
+
sqlSafe(c.sender_id),
|
|
6161
|
+
sqlSafe(c.sender_name),
|
|
6162
|
+
sqlSafe(c.sender_phone),
|
|
6163
|
+
sqlSafe(c.sender_email),
|
|
6164
|
+
sqlSafe(c.recipient_id),
|
|
6165
|
+
sqlSafe(c.channel_id),
|
|
6166
|
+
sqlSafe(c.thread_id),
|
|
6167
|
+
sqlSafe(c.reply_to_id),
|
|
6168
|
+
sqlSafe(c.content_text),
|
|
6169
|
+
sqlSafe(c.content_media),
|
|
6170
|
+
sqlSafe(c.content_metadata),
|
|
6171
|
+
sqlSafe(c.agent_response),
|
|
6172
|
+
sqlSafe(c.agent_name),
|
|
6173
|
+
sqlSafe(c.timestamp),
|
|
6174
|
+
sqlSafe(c.ingested_at)
|
|
6175
|
+
]
|
|
6176
|
+
}));
|
|
6177
|
+
await client.batch(stmts, "write");
|
|
6178
|
+
return { pulled: remoteConvos.length };
|
|
6179
|
+
}
|
|
6180
|
+
async function cloudPushDocuments(config) {
|
|
6181
|
+
const client = getClient();
|
|
6182
|
+
const [workspaces, documents] = await Promise.all([
|
|
6183
|
+
client.execute("SELECT * FROM workspaces LIMIT 1000"),
|
|
6184
|
+
client.execute("SELECT * FROM documents LIMIT 10000")
|
|
6185
|
+
]);
|
|
6186
|
+
const blob = {
|
|
6187
|
+
workspaces: workspaces.rows,
|
|
6188
|
+
documents: documents.rows
|
|
6189
|
+
};
|
|
6190
|
+
const { ok } = await cloudPushBlob(
|
|
6191
|
+
"/sync/push-documents",
|
|
6192
|
+
[blob],
|
|
6193
|
+
"last_documents_push_version",
|
|
6194
|
+
config
|
|
6195
|
+
);
|
|
6196
|
+
return ok;
|
|
6197
|
+
}
|
|
6198
|
+
async function cloudPullDocuments(config) {
|
|
6199
|
+
const data = await cloudPullBlob(
|
|
6200
|
+
"/sync/pull-documents",
|
|
6201
|
+
config
|
|
6202
|
+
);
|
|
6203
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
6204
|
+
const blob = data[0];
|
|
6205
|
+
const client = getClient();
|
|
6206
|
+
let pulled = 0;
|
|
6207
|
+
if (blob.workspaces.length > 0) {
|
|
6208
|
+
const stmts = blob.workspaces.map((w) => ({
|
|
6209
|
+
sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
|
|
6210
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
6211
|
+
args: [sqlSafe(w.id), sqlSafe(w.slug), sqlSafe(w.name), sqlSafe(w.owner_agent_id), sqlSafe(w.created_at), sqlSafe(w.metadata)]
|
|
6212
|
+
}));
|
|
6213
|
+
await client.batch(stmts, "write");
|
|
6214
|
+
pulled += stmts.length;
|
|
6215
|
+
}
|
|
6216
|
+
if (blob.documents.length > 0) {
|
|
6217
|
+
const stmts = blob.documents.map((d) => ({
|
|
6218
|
+
sql: `INSERT OR IGNORE INTO documents
|
|
6219
|
+
(id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
|
|
6220
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6221
|
+
args: [
|
|
6222
|
+
sqlSafe(d.id),
|
|
6223
|
+
sqlSafe(d.workspace_id),
|
|
6224
|
+
sqlSafe(d.filename),
|
|
6225
|
+
sqlSafe(d.mime),
|
|
6226
|
+
sqlSafe(d.source_type),
|
|
6227
|
+
sqlSafe(d.user_id),
|
|
6228
|
+
sqlSafe(d.uploaded_at),
|
|
6229
|
+
sqlSafe(d.metadata)
|
|
6230
|
+
]
|
|
6231
|
+
}));
|
|
6232
|
+
await client.batch(stmts, "write");
|
|
6233
|
+
pulled += stmts.length;
|
|
5113
6234
|
}
|
|
6235
|
+
return { pulled };
|
|
5114
6236
|
}
|
|
5115
|
-
var
|
|
5116
|
-
var
|
|
5117
|
-
"src/lib/
|
|
6237
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
6238
|
+
var init_cloud_sync = __esm({
|
|
6239
|
+
"src/lib/cloud-sync.ts"() {
|
|
5118
6240
|
"use strict";
|
|
6241
|
+
init_database();
|
|
6242
|
+
init_crypto();
|
|
6243
|
+
init_compress();
|
|
6244
|
+
init_license();
|
|
5119
6245
|
init_config();
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
5128
|
-
-----END PUBLIC KEY-----`;
|
|
5129
|
-
LICENSE_JWT_ALG = "ES256";
|
|
5130
|
-
PLAN_LIMITS = {
|
|
5131
|
-
free: { devices: 1, employees: 1, memories: 5e4 },
|
|
5132
|
-
pro: { devices: 2, employees: 5, memories: 25e4 },
|
|
5133
|
-
team: { devices: 10, employees: 20, memories: 1e6 },
|
|
5134
|
-
agency: { devices: 50, employees: 100, memories: 1e7 },
|
|
5135
|
-
enterprise: { devices: -1, employees: -1, memories: -1 }
|
|
5136
|
-
};
|
|
5137
|
-
FREE_LICENSE = {
|
|
5138
|
-
valid: true,
|
|
5139
|
-
plan: "free",
|
|
5140
|
-
email: "",
|
|
5141
|
-
expiresAt: null,
|
|
5142
|
-
deviceLimit: 1,
|
|
5143
|
-
employeeLimit: 1,
|
|
5144
|
-
memoryLimit: 5e4
|
|
5145
|
-
};
|
|
5146
|
-
CACHE_MAX_AGE_MS = 36e5;
|
|
5147
|
-
_revalTimer = null;
|
|
6246
|
+
init_employees();
|
|
6247
|
+
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
6248
|
+
FETCH_TIMEOUT_MS = 3e4;
|
|
6249
|
+
PUSH_BATCH_SIZE = 5e3;
|
|
6250
|
+
ROSTER_LOCK_PATH = path12.join(EXE_AI_DIR, "roster-merge.lock");
|
|
6251
|
+
LOCK_STALE_MS = 3e4;
|
|
6252
|
+
ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5148
6253
|
}
|
|
5149
6254
|
});
|
|
5150
6255
|
|
|
@@ -5157,17 +6262,17 @@ __export(identity_exports, {
|
|
|
5157
6262
|
listIdentities: () => listIdentities,
|
|
5158
6263
|
updateIdentity: () => updateIdentity
|
|
5159
6264
|
});
|
|
5160
|
-
import { existsSync as
|
|
5161
|
-
import { readdirSync as
|
|
5162
|
-
import
|
|
6265
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
6266
|
+
import { readdirSync as readdirSync3 } from "fs";
|
|
6267
|
+
import path13 from "path";
|
|
5163
6268
|
import { createHash as createHash2 } from "crypto";
|
|
5164
6269
|
function ensureDir() {
|
|
5165
|
-
if (!
|
|
5166
|
-
|
|
6270
|
+
if (!existsSync12(IDENTITY_DIR)) {
|
|
6271
|
+
mkdirSync6(IDENTITY_DIR, { recursive: true });
|
|
5167
6272
|
}
|
|
5168
6273
|
}
|
|
5169
6274
|
function identityPath(agentId) {
|
|
5170
|
-
return
|
|
6275
|
+
return path13.join(IDENTITY_DIR, `${agentId}.md`);
|
|
5171
6276
|
}
|
|
5172
6277
|
function parseFrontmatter(raw) {
|
|
5173
6278
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -5208,8 +6313,8 @@ function contentHash(content) {
|
|
|
5208
6313
|
}
|
|
5209
6314
|
function getIdentity(agentId) {
|
|
5210
6315
|
const filePath = identityPath(agentId);
|
|
5211
|
-
if (!
|
|
5212
|
-
const raw =
|
|
6316
|
+
if (!existsSync12(filePath)) return null;
|
|
6317
|
+
const raw = readFileSync8(filePath, "utf-8");
|
|
5213
6318
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
5214
6319
|
return {
|
|
5215
6320
|
agentId,
|
|
@@ -5223,7 +6328,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
5223
6328
|
ensureDir();
|
|
5224
6329
|
const filePath = identityPath(agentId);
|
|
5225
6330
|
const hash = contentHash(content);
|
|
5226
|
-
|
|
6331
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
5227
6332
|
try {
|
|
5228
6333
|
const client = getClient();
|
|
5229
6334
|
await client.execute({
|
|
@@ -5240,7 +6345,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
5240
6345
|
}
|
|
5241
6346
|
function listIdentities() {
|
|
5242
6347
|
ensureDir();
|
|
5243
|
-
const files =
|
|
6348
|
+
const files = readdirSync3(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
5244
6349
|
const results = [];
|
|
5245
6350
|
for (const file of files) {
|
|
5246
6351
|
const agentId = file.replace(".md", "");
|
|
@@ -5279,7 +6384,7 @@ var init_identity = __esm({
|
|
|
5279
6384
|
"use strict";
|
|
5280
6385
|
init_config();
|
|
5281
6386
|
init_database();
|
|
5282
|
-
IDENTITY_DIR =
|
|
6387
|
+
IDENTITY_DIR = path13.join(EXE_AI_DIR, "identity");
|
|
5283
6388
|
}
|
|
5284
6389
|
});
|
|
5285
6390
|
|
|
@@ -5813,31 +6918,162 @@ ${PLAN_MODE_COMPAT}
|
|
|
5813
6918
|
}
|
|
5814
6919
|
});
|
|
5815
6920
|
|
|
6921
|
+
// src/lib/session-wrappers.ts
|
|
6922
|
+
var session_wrappers_exports = {};
|
|
6923
|
+
__export(session_wrappers_exports, {
|
|
6924
|
+
generateSessionWrappers: () => generateSessionWrappers
|
|
6925
|
+
});
|
|
6926
|
+
import {
|
|
6927
|
+
existsSync as existsSync13,
|
|
6928
|
+
readFileSync as readFileSync9,
|
|
6929
|
+
writeFileSync as writeFileSync6,
|
|
6930
|
+
mkdirSync as mkdirSync7,
|
|
6931
|
+
chmodSync,
|
|
6932
|
+
readdirSync as readdirSync4,
|
|
6933
|
+
unlinkSync as unlinkSync5
|
|
6934
|
+
} from "fs";
|
|
6935
|
+
import path14 from "path";
|
|
6936
|
+
import { homedir as homedir4 } from "os";
|
|
6937
|
+
function generateSessionWrappers(packageRoot, homeDir) {
|
|
6938
|
+
const home = homeDir ?? homedir4();
|
|
6939
|
+
const binDir = path14.join(home, ".exe-os", "bin");
|
|
6940
|
+
const rosterPath = path14.join(home, ".exe-os", "exe-employees.json");
|
|
6941
|
+
mkdirSync7(binDir, { recursive: true });
|
|
6942
|
+
const exeStartDst = path14.join(binDir, "exe-start");
|
|
6943
|
+
const candidates = [
|
|
6944
|
+
path14.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
6945
|
+
path14.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
6946
|
+
];
|
|
6947
|
+
for (const src of candidates) {
|
|
6948
|
+
if (existsSync13(src)) {
|
|
6949
|
+
writeFileSync6(exeStartDst, readFileSync9(src));
|
|
6950
|
+
chmodSync(exeStartDst, 493);
|
|
6951
|
+
break;
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
let employees = [];
|
|
6955
|
+
try {
|
|
6956
|
+
employees = JSON.parse(readFileSync9(rosterPath, "utf8"));
|
|
6957
|
+
} catch {
|
|
6958
|
+
return { created: 0, pathConfigured: false };
|
|
6959
|
+
}
|
|
6960
|
+
if (employees.length === 0) {
|
|
6961
|
+
return { created: 0, pathConfigured: false };
|
|
6962
|
+
}
|
|
6963
|
+
try {
|
|
6964
|
+
for (const f of readdirSync4(binDir)) {
|
|
6965
|
+
if (f === "exe-start") continue;
|
|
6966
|
+
const fPath = path14.join(binDir, f);
|
|
6967
|
+
try {
|
|
6968
|
+
const content = readFileSync9(fPath, "utf8");
|
|
6969
|
+
if (content.includes("exe-start")) {
|
|
6970
|
+
unlinkSync5(fPath);
|
|
6971
|
+
}
|
|
6972
|
+
} catch {
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6975
|
+
} catch {
|
|
6976
|
+
}
|
|
6977
|
+
let created = 0;
|
|
6978
|
+
const wrapperContent = `#!/bin/bash
|
|
6979
|
+
exec "${exeStartDst}" "$0" "$@"
|
|
6980
|
+
`;
|
|
6981
|
+
for (const emp of employees) {
|
|
6982
|
+
for (let n = 1; n <= MAX_N; n++) {
|
|
6983
|
+
const wrapperPath = path14.join(binDir, `${emp.name}${n}`);
|
|
6984
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
6985
|
+
chmodSync(wrapperPath, 493);
|
|
6986
|
+
created++;
|
|
6987
|
+
}
|
|
6988
|
+
}
|
|
6989
|
+
const pathConfigured = ensurePath(home, binDir);
|
|
6990
|
+
return { created, pathConfigured };
|
|
6991
|
+
}
|
|
6992
|
+
function ensurePath(home, binDir) {
|
|
6993
|
+
if (process.env.PATH?.split(":").includes(binDir)) {
|
|
6994
|
+
return false;
|
|
6995
|
+
}
|
|
6996
|
+
const exportLine = `
|
|
6997
|
+
# exe-os session commands
|
|
6998
|
+
export PATH="${binDir}:$PATH"
|
|
6999
|
+
`;
|
|
7000
|
+
const shell = process.env.SHELL ?? "/bin/bash";
|
|
7001
|
+
const profilePaths = [];
|
|
7002
|
+
if (shell.includes("zsh")) {
|
|
7003
|
+
profilePaths.push(path14.join(home, ".zshrc"));
|
|
7004
|
+
} else if (shell.includes("bash")) {
|
|
7005
|
+
profilePaths.push(path14.join(home, ".bashrc"));
|
|
7006
|
+
profilePaths.push(path14.join(home, ".bash_profile"));
|
|
7007
|
+
} else {
|
|
7008
|
+
profilePaths.push(path14.join(home, ".profile"));
|
|
7009
|
+
}
|
|
7010
|
+
for (const profilePath of profilePaths) {
|
|
7011
|
+
try {
|
|
7012
|
+
let content = "";
|
|
7013
|
+
try {
|
|
7014
|
+
content = readFileSync9(profilePath, "utf8");
|
|
7015
|
+
} catch {
|
|
7016
|
+
}
|
|
7017
|
+
if (content.includes(".exe-os/bin")) {
|
|
7018
|
+
return false;
|
|
7019
|
+
}
|
|
7020
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
7021
|
+
return true;
|
|
7022
|
+
} catch {
|
|
7023
|
+
continue;
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
7026
|
+
return false;
|
|
7027
|
+
}
|
|
7028
|
+
var MAX_N;
|
|
7029
|
+
var init_session_wrappers = __esm({
|
|
7030
|
+
"src/lib/session-wrappers.ts"() {
|
|
7031
|
+
"use strict";
|
|
7032
|
+
MAX_N = 9;
|
|
7033
|
+
}
|
|
7034
|
+
});
|
|
7035
|
+
|
|
5816
7036
|
// src/lib/setup-wizard.ts
|
|
5817
7037
|
var setup_wizard_exports = {};
|
|
5818
7038
|
__export(setup_wizard_exports, {
|
|
5819
7039
|
runSetupWizard: () => runSetupWizard,
|
|
5820
7040
|
validateModel: () => validateModel
|
|
5821
7041
|
});
|
|
5822
|
-
import
|
|
5823
|
-
import { existsSync as
|
|
7042
|
+
import crypto5 from "crypto";
|
|
7043
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
5824
7044
|
import os5 from "os";
|
|
5825
|
-
import
|
|
7045
|
+
import path15 from "path";
|
|
5826
7046
|
import { createInterface as createInterface2 } from "readline";
|
|
7047
|
+
function findPackageRoot2() {
|
|
7048
|
+
let dir = path15.dirname(new URL(import.meta.url).pathname);
|
|
7049
|
+
const root = path15.parse(dir).root;
|
|
7050
|
+
while (dir !== root) {
|
|
7051
|
+
const pkgPath = path15.join(dir, "package.json");
|
|
7052
|
+
if (existsSync14(pkgPath)) {
|
|
7053
|
+
try {
|
|
7054
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
7055
|
+
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
7056
|
+
} catch {
|
|
7057
|
+
}
|
|
7058
|
+
}
|
|
7059
|
+
dir = path15.dirname(dir);
|
|
7060
|
+
}
|
|
7061
|
+
return null;
|
|
7062
|
+
}
|
|
5827
7063
|
function loadSetupState() {
|
|
5828
7064
|
try {
|
|
5829
|
-
return JSON.parse(
|
|
7065
|
+
return JSON.parse(readFileSync10(SETUP_STATE_PATH, "utf8"));
|
|
5830
7066
|
} catch {
|
|
5831
7067
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5832
7068
|
}
|
|
5833
7069
|
}
|
|
5834
7070
|
function saveSetupState(state) {
|
|
5835
|
-
|
|
5836
|
-
|
|
7071
|
+
mkdirSync8(path15.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
7072
|
+
writeFileSync7(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5837
7073
|
}
|
|
5838
7074
|
function clearSetupState() {
|
|
5839
7075
|
try {
|
|
5840
|
-
|
|
7076
|
+
unlinkSync6(SETUP_STATE_PATH);
|
|
5841
7077
|
} catch {
|
|
5842
7078
|
}
|
|
5843
7079
|
}
|
|
@@ -5871,20 +7107,67 @@ async function runSetupWizard(opts = {}) {
|
|
|
5871
7107
|
log("");
|
|
5872
7108
|
log("=== exe-os Setup ===");
|
|
5873
7109
|
log("");
|
|
5874
|
-
log("
|
|
5875
|
-
log(" Run `exe-link` to sync your encryption key and skip setup.");
|
|
7110
|
+
log("Is this a new installation or pairing with an existing device?");
|
|
5876
7111
|
log("");
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
7112
|
+
log(" [1] New installation (first device)");
|
|
7113
|
+
log(" [2] Pair with existing device (I have a 24-word phrase)");
|
|
7114
|
+
log("");
|
|
7115
|
+
const installType = await ask(rl, "Choice (1/2): ");
|
|
7116
|
+
let isPairing = false;
|
|
7117
|
+
let pairingRosterPulled = false;
|
|
7118
|
+
if (installType === "2") {
|
|
7119
|
+
isPairing = true;
|
|
7120
|
+
log("");
|
|
7121
|
+
const { importMnemonic: importMnemonic2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|
|
7122
|
+
const mnemonic = await ask(rl, "Paste your 24-word recovery phrase: ");
|
|
7123
|
+
try {
|
|
7124
|
+
const key = importMnemonic2(mnemonic);
|
|
7125
|
+
await setMasterKey(key);
|
|
7126
|
+
log("Master key imported and stored securely.");
|
|
7127
|
+
log("");
|
|
7128
|
+
log("Enter the API key from your existing device.");
|
|
7129
|
+
log("(Find it in ~/.exe-os/config.json under cloud.apiKey, or ask your team lead.)");
|
|
7130
|
+
log("");
|
|
7131
|
+
const apiKey = await ask(rl, "API key (exe_sk_...): ");
|
|
7132
|
+
if (apiKey && apiKey.startsWith("exe_sk_")) {
|
|
7133
|
+
const cloudEndpoint = "https://askexe.com/cloud";
|
|
7134
|
+
const cloudCfg = { apiKey, endpoint: cloudEndpoint };
|
|
7135
|
+
const earlyConfig = await loadConfig();
|
|
7136
|
+
earlyConfig.cloud = cloudCfg;
|
|
7137
|
+
await saveConfig(earlyConfig);
|
|
7138
|
+
const { saveLicense: saveLic, mirrorLicenseKey: mirrorLic } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
7139
|
+
saveLic(apiKey);
|
|
7140
|
+
mirrorLic(apiKey);
|
|
7141
|
+
log("Cloud sync configured.");
|
|
7142
|
+
try {
|
|
7143
|
+
const { initSyncCrypto: initSyncCrypto2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
7144
|
+
const { cloudPullRoster: cloudPullRoster2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
|
|
7145
|
+
initSyncCrypto2(key);
|
|
7146
|
+
const result = await cloudPullRoster2({ apiKey, endpoint: cloudEndpoint });
|
|
7147
|
+
if (result.added > 0) {
|
|
7148
|
+
log(`Pulled ${result.added} employee(s) from Exe Cloud.`);
|
|
7149
|
+
pairingRosterPulled = true;
|
|
7150
|
+
}
|
|
7151
|
+
} catch {
|
|
7152
|
+
log("Could not pull roster from cloud \u2014 you can set up employees manually.");
|
|
7153
|
+
}
|
|
7154
|
+
} else {
|
|
7155
|
+
log("No API key provided \u2014 cloud sync will need to be configured later.");
|
|
7156
|
+
log("Run /exe-cloud after setup to connect.");
|
|
7157
|
+
}
|
|
7158
|
+
log("");
|
|
7159
|
+
} catch (err) {
|
|
7160
|
+
log(`Key import failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7161
|
+
log("Check your phrase and try again, or choose option 1 for a fresh install.");
|
|
7162
|
+
rl.close();
|
|
7163
|
+
return;
|
|
7164
|
+
}
|
|
5882
7165
|
}
|
|
5883
7166
|
const state = loadSetupState();
|
|
5884
7167
|
if (state.completedSteps.length > 0) {
|
|
5885
7168
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5886
7169
|
}
|
|
5887
|
-
if (
|
|
7170
|
+
if (existsSync14(LEGACY_LANCE_PATH)) {
|
|
5888
7171
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5889
7172
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5890
7173
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -5896,7 +7179,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5896
7179
|
log("Encryption key already exists \u2014 skipping generation.");
|
|
5897
7180
|
} else {
|
|
5898
7181
|
log("Generating 256-bit encryption key...");
|
|
5899
|
-
const key =
|
|
7182
|
+
const key = crypto5.randomBytes(32);
|
|
5900
7183
|
await setMasterKey(key);
|
|
5901
7184
|
log("Encryption key generated and stored securely.");
|
|
5902
7185
|
}
|
|
@@ -5907,7 +7190,15 @@ async function runSetupWizard(opts = {}) {
|
|
|
5907
7190
|
}
|
|
5908
7191
|
log("");
|
|
5909
7192
|
let cloudConfig;
|
|
5910
|
-
if (
|
|
7193
|
+
if (isPairing) {
|
|
7194
|
+
const pairingConfig = await loadConfig();
|
|
7195
|
+
if (pairingConfig.cloud?.apiKey) {
|
|
7196
|
+
cloudConfig = pairingConfig.cloud;
|
|
7197
|
+
log("Cloud sync: using shared API key from pairing.");
|
|
7198
|
+
}
|
|
7199
|
+
state.completedSteps.push(2);
|
|
7200
|
+
saveSetupState(state);
|
|
7201
|
+
} else if (!state.completedSteps.includes(2)) {
|
|
5911
7202
|
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5912
7203
|
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5913
7204
|
log("only your encryption key can decrypt it.");
|
|
@@ -5988,10 +7279,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
5988
7279
|
await saveConfig(config);
|
|
5989
7280
|
log("");
|
|
5990
7281
|
try {
|
|
5991
|
-
const claudeJsonPath =
|
|
7282
|
+
const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
|
|
5992
7283
|
let claudeJson = {};
|
|
5993
7284
|
try {
|
|
5994
|
-
claudeJson = JSON.parse(
|
|
7285
|
+
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
5995
7286
|
} catch {
|
|
5996
7287
|
}
|
|
5997
7288
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6000,7 +7291,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6000
7291
|
if (!projects[dir]) projects[dir] = {};
|
|
6001
7292
|
projects[dir].hasTrustDialogAccepted = true;
|
|
6002
7293
|
}
|
|
6003
|
-
|
|
7294
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6004
7295
|
} catch {
|
|
6005
7296
|
}
|
|
6006
7297
|
state.completedSteps.push(5);
|
|
@@ -6026,7 +7317,34 @@ async function runSetupWizard(opts = {}) {
|
|
|
6026
7317
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
6027
7318
|
const createdEmployees = [];
|
|
6028
7319
|
let cooName = "exe";
|
|
6029
|
-
if (
|
|
7320
|
+
if (pairingRosterPulled) {
|
|
7321
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
7322
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
7323
|
+
if (existingCoo) {
|
|
7324
|
+
cooName = existingCoo.name;
|
|
7325
|
+
log(`Team synced from cloud. COO: ${cooName}`);
|
|
7326
|
+
const teamList = roster.map((e) => `${e.name} (${e.role})`).join(", ");
|
|
7327
|
+
log(`Team: ${teamList}`);
|
|
7328
|
+
createdEmployees.push(...roster.map((e) => ({ name: e.name, role: e.role })));
|
|
7329
|
+
}
|
|
7330
|
+
for (const emp of roster) {
|
|
7331
|
+
registerBinSymlinks2(emp.name);
|
|
7332
|
+
}
|
|
7333
|
+
try {
|
|
7334
|
+
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
7335
|
+
const pkgRoot = findPackageRoot2();
|
|
7336
|
+
if (pkgRoot) {
|
|
7337
|
+
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
7338
|
+
if (wrapResult.created > 0) {
|
|
7339
|
+
log(`Session shortcuts generated: ${roster.map((e) => `${e.name}1`).join(", ")}, ...`);
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
} catch {
|
|
7343
|
+
}
|
|
7344
|
+
state.completedSteps.push(6, 7, 8);
|
|
7345
|
+
saveSetupState(state);
|
|
7346
|
+
log("");
|
|
7347
|
+
} else if (!state.completedSteps.includes(6)) {
|
|
6030
7348
|
log("=== Your Team ===");
|
|
6031
7349
|
log("");
|
|
6032
7350
|
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
@@ -6052,9 +7370,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6052
7370
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
6053
7371
|
if (cooIdentityContent) {
|
|
6054
7372
|
const cooIdPath = identityPath2(cooName);
|
|
6055
|
-
|
|
7373
|
+
mkdirSync8(path15.dirname(cooIdPath), { recursive: true });
|
|
6056
7374
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
6057
|
-
|
|
7375
|
+
writeFileSync7(cooIdPath, replaced, "utf-8");
|
|
6058
7376
|
}
|
|
6059
7377
|
registerBinSymlinks2(cooName);
|
|
6060
7378
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -6153,9 +7471,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6153
7471
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
6154
7472
|
if (ctoIdentityContent) {
|
|
6155
7473
|
const ctoIdPath = identityPath2(ctoName);
|
|
6156
|
-
|
|
7474
|
+
mkdirSync8(path15.dirname(ctoIdPath), { recursive: true });
|
|
6157
7475
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
6158
|
-
|
|
7476
|
+
writeFileSync7(ctoIdPath, replaced, "utf-8");
|
|
6159
7477
|
}
|
|
6160
7478
|
registerBinSymlinks2(ctoName);
|
|
6161
7479
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -6181,9 +7499,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6181
7499
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
6182
7500
|
if (cmoIdentityContent) {
|
|
6183
7501
|
const cmoIdPath = identityPath2(cmoName);
|
|
6184
|
-
|
|
7502
|
+
mkdirSync8(path15.dirname(cmoIdPath), { recursive: true });
|
|
6185
7503
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
6186
|
-
|
|
7504
|
+
writeFileSync7(cmoIdPath, replaced, "utf-8");
|
|
6187
7505
|
}
|
|
6188
7506
|
registerBinSymlinks2(cmoName);
|
|
6189
7507
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -6196,11 +7514,24 @@ async function runSetupWizard(opts = {}) {
|
|
|
6196
7514
|
} else {
|
|
6197
7515
|
log("Step 8 already complete \u2014 skipping.");
|
|
6198
7516
|
}
|
|
7517
|
+
if (!pairingRosterPulled) {
|
|
7518
|
+
try {
|
|
7519
|
+
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
7520
|
+
const pkgRoot = findPackageRoot2();
|
|
7521
|
+
if (pkgRoot) {
|
|
7522
|
+
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
7523
|
+
if (wrapResult.created > 0) {
|
|
7524
|
+
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
} catch {
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
6199
7530
|
clearSetupState();
|
|
6200
7531
|
log("=== Two Ways to Work ===");
|
|
6201
7532
|
log("");
|
|
6202
7533
|
log(" 1. Claude Code mode");
|
|
6203
|
-
log(` Type \`${cooName}\` in your
|
|
7534
|
+
log(` Type \`${cooName}1\` in your project folder. Works with your Claude Code subscription.`);
|
|
6204
7535
|
log(" Best for developers who live in the terminal.");
|
|
6205
7536
|
log("");
|
|
6206
7537
|
log(" 2. Dashboard mode");
|
|
@@ -6210,8 +7541,6 @@ async function runSetupWizard(opts = {}) {
|
|
|
6210
7541
|
log(" Both modes share the same memory, employees, and data.");
|
|
6211
7542
|
log(" You can switch anytime.");
|
|
6212
7543
|
log("");
|
|
6213
|
-
log(" For Claude Code mode, run: exe-os claude");
|
|
6214
|
-
log("");
|
|
6215
7544
|
log("=== Setup Complete ===");
|
|
6216
7545
|
log("Database: " + config.dbPath);
|
|
6217
7546
|
if (cloudConfig) {
|
|
@@ -6227,7 +7556,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6227
7556
|
log("Team: " + createdEmployees.map((e) => `${e.name} (${e.role})`).join(", "));
|
|
6228
7557
|
}
|
|
6229
7558
|
log("");
|
|
6230
|
-
log(`Type \`${cooName}\` to start (Claude Code) or \`exe-os\` for dashboard.`);
|
|
7559
|
+
log(`Type \`${cooName}1\` to start (Claude Code) or \`exe-os\` for dashboard.`);
|
|
6231
7560
|
log("");
|
|
6232
7561
|
} finally {
|
|
6233
7562
|
rl.close();
|
|
@@ -6240,22 +7569,22 @@ var init_setup_wizard = __esm({
|
|
|
6240
7569
|
init_config();
|
|
6241
7570
|
init_keychain();
|
|
6242
7571
|
init_model_downloader();
|
|
6243
|
-
SETUP_STATE_PATH =
|
|
7572
|
+
SETUP_STATE_PATH = path15.join(os5.homedir(), ".exe-os", "setup-state.json");
|
|
6244
7573
|
}
|
|
6245
7574
|
});
|
|
6246
7575
|
|
|
6247
7576
|
// src/lib/update-check.ts
|
|
6248
|
-
import { execSync as
|
|
6249
|
-
import { readFileSync as
|
|
6250
|
-
import
|
|
7577
|
+
import { execSync as execSync4 } from "child_process";
|
|
7578
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7579
|
+
import path16 from "path";
|
|
6251
7580
|
function getLocalVersion(packageRoot) {
|
|
6252
|
-
const pkgPath =
|
|
6253
|
-
const pkg = JSON.parse(
|
|
7581
|
+
const pkgPath = path16.join(packageRoot, "package.json");
|
|
7582
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
6254
7583
|
return pkg.version;
|
|
6255
7584
|
}
|
|
6256
7585
|
function getRemoteVersion() {
|
|
6257
7586
|
try {
|
|
6258
|
-
const output =
|
|
7587
|
+
const output = execSync4("npm view @askexenow/exe-os version", {
|
|
6259
7588
|
encoding: "utf-8",
|
|
6260
7589
|
timeout: 15e3,
|
|
6261
7590
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6294,7 +7623,7 @@ __export(update_exports, {
|
|
|
6294
7623
|
getRemoteVersion: () => getRemoteVersion,
|
|
6295
7624
|
runUpdate: () => runUpdate
|
|
6296
7625
|
});
|
|
6297
|
-
import { execSync as
|
|
7626
|
+
import { execSync as execSync5 } from "child_process";
|
|
6298
7627
|
import { createInterface as createInterface3 } from "readline";
|
|
6299
7628
|
async function runUpdate(cliArgs) {
|
|
6300
7629
|
const args2 = cliArgs ?? process.argv.slice(2);
|
|
@@ -6336,14 +7665,14 @@ async function runUpdate(cliArgs) {
|
|
|
6336
7665
|
}
|
|
6337
7666
|
console.log("\n\u{1F9F9} Clearing npm cache...");
|
|
6338
7667
|
try {
|
|
6339
|
-
|
|
7668
|
+
execSync5("npm cache clean --force", { stdio: "pipe" });
|
|
6340
7669
|
console.log(" Done");
|
|
6341
7670
|
} catch {
|
|
6342
7671
|
console.log(" Skipped (non-critical)");
|
|
6343
7672
|
}
|
|
6344
7673
|
console.log("\u{1F4E5} Installing @askexenow/exe-os@latest...");
|
|
6345
7674
|
try {
|
|
6346
|
-
|
|
7675
|
+
execSync5("npm install -g @askexenow/exe-os@latest", {
|
|
6347
7676
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6348
7677
|
timeout: 12e4
|
|
6349
7678
|
});
|
|
@@ -6360,7 +7689,7 @@ async function runUpdate(cliArgs) {
|
|
|
6360
7689
|
newVersion = getLocalVersion(packageRoot);
|
|
6361
7690
|
} catch {
|
|
6362
7691
|
try {
|
|
6363
|
-
const out =
|
|
7692
|
+
const out = execSync5("npm list -g @askexenow/exe-os --depth=0 2>/dev/null", { encoding: "utf8" });
|
|
6364
7693
|
const match = out.match(/@askexenow\/exe-os@(\S+)/);
|
|
6365
7694
|
newVersion = match?.[1] ?? "unknown";
|
|
6366
7695
|
} catch {
|
|
@@ -7885,7 +9214,7 @@ var init_yoga_wasm_base64_esm = __esm({
|
|
|
7885
9214
|
});
|
|
7886
9215
|
|
|
7887
9216
|
// node_modules/yoga-layout/dist/src/generated/YGEnums.js
|
|
7888
|
-
var Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap,
|
|
9217
|
+
var Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, constants2, YGEnums_default;
|
|
7889
9218
|
var init_YGEnums = __esm({
|
|
7890
9219
|
"node_modules/yoga-layout/dist/src/generated/YGEnums.js"() {
|
|
7891
9220
|
"use strict";
|
|
@@ -8015,7 +9344,7 @@ var init_YGEnums = __esm({
|
|
|
8015
9344
|
Wrap2[Wrap2["WrapReverse"] = 2] = "WrapReverse";
|
|
8016
9345
|
return Wrap2;
|
|
8017
9346
|
})({});
|
|
8018
|
-
|
|
9347
|
+
constants2 = {
|
|
8019
9348
|
ALIGN_AUTO: Align.Auto,
|
|
8020
9349
|
ALIGN_FLEX_START: Align.FlexStart,
|
|
8021
9350
|
ALIGN_CENTER: Align.Center,
|
|
@@ -8089,7 +9418,7 @@ var init_YGEnums = __esm({
|
|
|
8089
9418
|
WRAP_WRAP: Wrap.Wrap,
|
|
8090
9419
|
WRAP_WRAP_REVERSE: Wrap.WrapReverse
|
|
8091
9420
|
};
|
|
8092
|
-
YGEnums_default =
|
|
9421
|
+
YGEnums_default = constants2;
|
|
8093
9422
|
}
|
|
8094
9423
|
});
|
|
8095
9424
|
|
|
@@ -10773,8 +12102,8 @@ var init_ErrorOverview = __esm({
|
|
|
10773
12102
|
"use strict";
|
|
10774
12103
|
init_Box();
|
|
10775
12104
|
init_Text();
|
|
10776
|
-
cleanupPath = (
|
|
10777
|
-
return
|
|
12105
|
+
cleanupPath = (path36) => {
|
|
12106
|
+
return path36?.replace(`file://${cwd()}/`, "");
|
|
10778
12107
|
};
|
|
10779
12108
|
stackUtils = new StackUtils({
|
|
10780
12109
|
cwd: cwd(),
|
|
@@ -12803,14 +14132,14 @@ __export(session_registry_exports, {
|
|
|
12803
14132
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
12804
14133
|
registerSession: () => registerSession
|
|
12805
14134
|
});
|
|
12806
|
-
import { readFileSync as
|
|
12807
|
-
import { execSync as
|
|
12808
|
-
import
|
|
14135
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, existsSync as existsSync16 } from "fs";
|
|
14136
|
+
import { execSync as execSync6 } from "child_process";
|
|
14137
|
+
import path17 from "path";
|
|
12809
14138
|
import os6 from "os";
|
|
12810
14139
|
function registerSession(entry) {
|
|
12811
|
-
const dir =
|
|
12812
|
-
if (!
|
|
12813
|
-
|
|
14140
|
+
const dir = path17.dirname(REGISTRY_PATH);
|
|
14141
|
+
if (!existsSync16(dir)) {
|
|
14142
|
+
mkdirSync9(dir, { recursive: true });
|
|
12814
14143
|
}
|
|
12815
14144
|
const sessions = listSessions();
|
|
12816
14145
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -12819,11 +14148,11 @@ function registerSession(entry) {
|
|
|
12819
14148
|
} else {
|
|
12820
14149
|
sessions.push(entry);
|
|
12821
14150
|
}
|
|
12822
|
-
|
|
14151
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
12823
14152
|
}
|
|
12824
14153
|
function listSessions() {
|
|
12825
14154
|
try {
|
|
12826
|
-
const raw =
|
|
14155
|
+
const raw = readFileSync13(REGISTRY_PATH, "utf8");
|
|
12827
14156
|
return JSON.parse(raw);
|
|
12828
14157
|
} catch {
|
|
12829
14158
|
return [];
|
|
@@ -12834,7 +14163,7 @@ function pruneStaleSessions() {
|
|
|
12834
14163
|
if (sessions.length === 0) return 0;
|
|
12835
14164
|
let liveSessions = [];
|
|
12836
14165
|
try {
|
|
12837
|
-
liveSessions =
|
|
14166
|
+
liveSessions = execSync6("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
12838
14167
|
encoding: "utf8"
|
|
12839
14168
|
}).trim().split("\n").filter(Boolean);
|
|
12840
14169
|
} catch {
|
|
@@ -12844,7 +14173,7 @@ function pruneStaleSessions() {
|
|
|
12844
14173
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
12845
14174
|
const pruned = sessions.length - alive.length;
|
|
12846
14175
|
if (pruned > 0) {
|
|
12847
|
-
|
|
14176
|
+
writeFileSync8(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
12848
14177
|
}
|
|
12849
14178
|
return pruned;
|
|
12850
14179
|
}
|
|
@@ -12852,18 +14181,18 @@ var REGISTRY_PATH;
|
|
|
12852
14181
|
var init_session_registry = __esm({
|
|
12853
14182
|
"src/lib/session-registry.ts"() {
|
|
12854
14183
|
"use strict";
|
|
12855
|
-
REGISTRY_PATH =
|
|
14184
|
+
REGISTRY_PATH = path17.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
12856
14185
|
}
|
|
12857
14186
|
});
|
|
12858
14187
|
|
|
12859
14188
|
// src/lib/session-key.ts
|
|
12860
|
-
import { execSync as
|
|
14189
|
+
import { execSync as execSync7 } from "child_process";
|
|
12861
14190
|
function getSessionKey() {
|
|
12862
14191
|
if (_cached) return _cached;
|
|
12863
14192
|
let pid = process.ppid;
|
|
12864
14193
|
for (let i = 0; i < 10; i++) {
|
|
12865
14194
|
try {
|
|
12866
|
-
const info =
|
|
14195
|
+
const info = execSync7(`ps -p ${pid} -o ppid=,comm=`, {
|
|
12867
14196
|
encoding: "utf8",
|
|
12868
14197
|
timeout: 2e3
|
|
12869
14198
|
}).trim();
|
|
@@ -13007,14 +14336,14 @@ var init_transport = __esm({
|
|
|
13007
14336
|
});
|
|
13008
14337
|
|
|
13009
14338
|
// src/lib/cc-agent-support.ts
|
|
13010
|
-
import { execSync as
|
|
14339
|
+
import { execSync as execSync8 } from "child_process";
|
|
13011
14340
|
function _resetCcAgentSupportCache() {
|
|
13012
14341
|
_cachedSupport = null;
|
|
13013
14342
|
}
|
|
13014
14343
|
function claudeSupportsAgentFlag() {
|
|
13015
14344
|
if (_cachedSupport !== null) return _cachedSupport;
|
|
13016
14345
|
try {
|
|
13017
|
-
const helpOutput =
|
|
14346
|
+
const helpOutput = execSync8("claude --help 2>&1", {
|
|
13018
14347
|
encoding: "utf-8",
|
|
13019
14348
|
timeout: 5e3
|
|
13020
14349
|
});
|
|
@@ -13057,17 +14386,17 @@ var init_provider_table = __esm({
|
|
|
13057
14386
|
});
|
|
13058
14387
|
|
|
13059
14388
|
// src/lib/intercom-queue.ts
|
|
13060
|
-
import { readFileSync as
|
|
13061
|
-
import
|
|
14389
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, renameSync as renameSync4, existsSync as existsSync17, mkdirSync as mkdirSync10 } from "fs";
|
|
14390
|
+
import path18 from "path";
|
|
13062
14391
|
import os7 from "os";
|
|
13063
14392
|
function ensureDir2() {
|
|
13064
|
-
const dir =
|
|
13065
|
-
if (!
|
|
14393
|
+
const dir = path18.dirname(QUEUE_PATH);
|
|
14394
|
+
if (!existsSync17(dir)) mkdirSync10(dir, { recursive: true });
|
|
13066
14395
|
}
|
|
13067
14396
|
function readQueue() {
|
|
13068
14397
|
try {
|
|
13069
|
-
if (!
|
|
13070
|
-
return JSON.parse(
|
|
14398
|
+
if (!existsSync17(QUEUE_PATH)) return [];
|
|
14399
|
+
return JSON.parse(readFileSync14(QUEUE_PATH, "utf8"));
|
|
13071
14400
|
} catch {
|
|
13072
14401
|
return [];
|
|
13073
14402
|
}
|
|
@@ -13075,7 +14404,7 @@ function readQueue() {
|
|
|
13075
14404
|
function writeQueue(queue) {
|
|
13076
14405
|
ensureDir2();
|
|
13077
14406
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
13078
|
-
|
|
14407
|
+
writeFileSync9(tmp, JSON.stringify(queue, null, 2));
|
|
13079
14408
|
renameSync4(tmp, QUEUE_PATH);
|
|
13080
14409
|
}
|
|
13081
14410
|
function queueIntercom(targetSession, reason) {
|
|
@@ -13099,19 +14428,19 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
13099
14428
|
var init_intercom_queue = __esm({
|
|
13100
14429
|
"src/lib/intercom-queue.ts"() {
|
|
13101
14430
|
"use strict";
|
|
13102
|
-
QUEUE_PATH =
|
|
14431
|
+
QUEUE_PATH = path18.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
13103
14432
|
TTL_MS = 60 * 60 * 1e3;
|
|
13104
|
-
INTERCOM_LOG =
|
|
14433
|
+
INTERCOM_LOG = path18.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
13105
14434
|
}
|
|
13106
14435
|
});
|
|
13107
14436
|
|
|
13108
14437
|
// src/lib/plan-limits.ts
|
|
13109
|
-
import { readFileSync as
|
|
13110
|
-
import
|
|
14438
|
+
import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
|
|
14439
|
+
import path19 from "path";
|
|
13111
14440
|
function getLicenseSync() {
|
|
13112
14441
|
try {
|
|
13113
|
-
if (!
|
|
13114
|
-
const raw = JSON.parse(
|
|
14442
|
+
if (!existsSync18(CACHE_PATH2)) return freeLicense();
|
|
14443
|
+
const raw = JSON.parse(readFileSync15(CACHE_PATH2, "utf8"));
|
|
13115
14444
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
13116
14445
|
const parts = raw.token.split(".");
|
|
13117
14446
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -13149,8 +14478,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
13149
14478
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
13150
14479
|
let count = 0;
|
|
13151
14480
|
try {
|
|
13152
|
-
if (
|
|
13153
|
-
const raw =
|
|
14481
|
+
if (existsSync18(filePath)) {
|
|
14482
|
+
const raw = readFileSync15(filePath, "utf8");
|
|
13154
14483
|
const employees = JSON.parse(raw);
|
|
13155
14484
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
13156
14485
|
}
|
|
@@ -13179,25 +14508,25 @@ var init_plan_limits = __esm({
|
|
|
13179
14508
|
this.name = "PlanLimitError";
|
|
13180
14509
|
}
|
|
13181
14510
|
};
|
|
13182
|
-
CACHE_PATH2 =
|
|
14511
|
+
CACHE_PATH2 = path19.join(EXE_AI_DIR, "license-cache.json");
|
|
13183
14512
|
}
|
|
13184
14513
|
});
|
|
13185
14514
|
|
|
13186
14515
|
// src/lib/notifications.ts
|
|
13187
|
-
import
|
|
13188
|
-
import
|
|
14516
|
+
import crypto6 from "crypto";
|
|
14517
|
+
import path20 from "path";
|
|
13189
14518
|
import os8 from "os";
|
|
13190
14519
|
import {
|
|
13191
|
-
readFileSync as
|
|
13192
|
-
readdirSync as
|
|
13193
|
-
unlinkSync as
|
|
13194
|
-
existsSync as
|
|
14520
|
+
readFileSync as readFileSync16,
|
|
14521
|
+
readdirSync as readdirSync5,
|
|
14522
|
+
unlinkSync as unlinkSync7,
|
|
14523
|
+
existsSync as existsSync19,
|
|
13195
14524
|
rmdirSync
|
|
13196
14525
|
} from "fs";
|
|
13197
14526
|
async function writeNotification(notification) {
|
|
13198
14527
|
try {
|
|
13199
14528
|
const client = getClient();
|
|
13200
|
-
const id =
|
|
14529
|
+
const id = crypto6.randomUUID();
|
|
13201
14530
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13202
14531
|
await client.execute({
|
|
13203
14532
|
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
@@ -13236,7 +14565,7 @@ var init_notifications = __esm({
|
|
|
13236
14565
|
});
|
|
13237
14566
|
|
|
13238
14567
|
// src/lib/session-kill-telemetry.ts
|
|
13239
|
-
import
|
|
14568
|
+
import crypto7 from "crypto";
|
|
13240
14569
|
async function recordSessionKill(input) {
|
|
13241
14570
|
try {
|
|
13242
14571
|
const client = getClient();
|
|
@@ -13246,7 +14575,7 @@ async function recordSessionKill(input) {
|
|
|
13246
14575
|
ticks_idle, estimated_tokens_saved)
|
|
13247
14576
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
13248
14577
|
args: [
|
|
13249
|
-
|
|
14578
|
+
crypto7.randomUUID(),
|
|
13250
14579
|
input.sessionName,
|
|
13251
14580
|
input.agentId,
|
|
13252
14581
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13285,11 +14614,11 @@ __export(tasks_crud_exports, {
|
|
|
13285
14614
|
updateTaskStatus: () => updateTaskStatus,
|
|
13286
14615
|
writeCheckpoint: () => writeCheckpoint
|
|
13287
14616
|
});
|
|
13288
|
-
import
|
|
13289
|
-
import
|
|
13290
|
-
import { execSync as
|
|
14617
|
+
import crypto8 from "crypto";
|
|
14618
|
+
import path21 from "path";
|
|
14619
|
+
import { execSync as execSync9 } from "child_process";
|
|
13291
14620
|
import { mkdir as mkdir6, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
13292
|
-
import { existsSync as
|
|
14621
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
13293
14622
|
async function writeCheckpoint(input) {
|
|
13294
14623
|
const client = getClient();
|
|
13295
14624
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -13376,7 +14705,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
13376
14705
|
}
|
|
13377
14706
|
async function createTaskCore(input) {
|
|
13378
14707
|
const client = getClient();
|
|
13379
|
-
const id =
|
|
14708
|
+
const id = crypto8.randomUUID();
|
|
13380
14709
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13381
14710
|
const slug = slugify(input.title);
|
|
13382
14711
|
const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
|
|
@@ -13421,8 +14750,8 @@ async function createTaskCore(input) {
|
|
|
13421
14750
|
}
|
|
13422
14751
|
if (input.baseDir) {
|
|
13423
14752
|
try {
|
|
13424
|
-
await mkdir6(
|
|
13425
|
-
await mkdir6(
|
|
14753
|
+
await mkdir6(path21.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
14754
|
+
await mkdir6(path21.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
13426
14755
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
13427
14756
|
await ensureGitignoreExe(input.baseDir);
|
|
13428
14757
|
} catch {
|
|
@@ -13534,12 +14863,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
13534
14863
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
13535
14864
|
try {
|
|
13536
14865
|
const since = new Date(taskCreatedAt).toISOString();
|
|
13537
|
-
const branch =
|
|
14866
|
+
const branch = execSync9(
|
|
13538
14867
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
13539
14868
|
{ encoding: "utf8", timeout: 3e3 }
|
|
13540
14869
|
).trim();
|
|
13541
14870
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
13542
|
-
const commitCount =
|
|
14871
|
+
const commitCount = execSync9(
|
|
13543
14872
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
13544
14873
|
{ encoding: "utf8", timeout: 5e3 }
|
|
13545
14874
|
).trim();
|
|
@@ -13642,9 +14971,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
13642
14971
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
13643
14972
|
}
|
|
13644
14973
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
13645
|
-
const archPath =
|
|
14974
|
+
const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
13646
14975
|
try {
|
|
13647
|
-
if (
|
|
14976
|
+
if (existsSync20(archPath)) return;
|
|
13648
14977
|
const template = [
|
|
13649
14978
|
`# ${projectName} \u2014 System Architecture`,
|
|
13650
14979
|
"",
|
|
@@ -13677,10 +15006,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
13677
15006
|
}
|
|
13678
15007
|
}
|
|
13679
15008
|
async function ensureGitignoreExe(baseDir) {
|
|
13680
|
-
const gitignorePath =
|
|
15009
|
+
const gitignorePath = path21.join(baseDir, ".gitignore");
|
|
13681
15010
|
try {
|
|
13682
|
-
if (
|
|
13683
|
-
const content =
|
|
15011
|
+
if (existsSync20(gitignorePath)) {
|
|
15012
|
+
const content = readFileSync17(gitignorePath, "utf-8");
|
|
13684
15013
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
13685
15014
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
13686
15015
|
} else {
|
|
@@ -13701,8 +15030,8 @@ var init_tasks_crud = __esm({
|
|
|
13701
15030
|
});
|
|
13702
15031
|
|
|
13703
15032
|
// src/lib/tasks-review.ts
|
|
13704
|
-
import
|
|
13705
|
-
import { existsSync as
|
|
15033
|
+
import path22 from "path";
|
|
15034
|
+
import { existsSync as existsSync21, readdirSync as readdirSync6, unlinkSync as unlinkSync8 } from "fs";
|
|
13706
15035
|
async function countPendingReviews(sessionScope) {
|
|
13707
15036
|
const client = getClient();
|
|
13708
15037
|
if (sessionScope) {
|
|
@@ -13883,11 +15212,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
13883
15212
|
);
|
|
13884
15213
|
}
|
|
13885
15214
|
try {
|
|
13886
|
-
const cacheDir =
|
|
13887
|
-
if (
|
|
13888
|
-
for (const f of
|
|
15215
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
15216
|
+
if (existsSync21(cacheDir)) {
|
|
15217
|
+
for (const f of readdirSync6(cacheDir)) {
|
|
13889
15218
|
if (f.startsWith("review-notified-")) {
|
|
13890
|
-
|
|
15219
|
+
unlinkSync8(path22.join(cacheDir, f));
|
|
13891
15220
|
}
|
|
13892
15221
|
}
|
|
13893
15222
|
}
|
|
@@ -13908,7 +15237,7 @@ var init_tasks_review = __esm({
|
|
|
13908
15237
|
});
|
|
13909
15238
|
|
|
13910
15239
|
// src/lib/tasks-chain.ts
|
|
13911
|
-
import
|
|
15240
|
+
import path23 from "path";
|
|
13912
15241
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
13913
15242
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
13914
15243
|
const client = getClient();
|
|
@@ -13925,7 +15254,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
13925
15254
|
});
|
|
13926
15255
|
for (const ur of unblockedRows.rows) {
|
|
13927
15256
|
try {
|
|
13928
|
-
const ubFile =
|
|
15257
|
+
const ubFile = path23.join(baseDir, String(ur.task_file));
|
|
13929
15258
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
13930
15259
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
13931
15260
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -13993,34 +15322,34 @@ var init_tasks_chain = __esm({
|
|
|
13993
15322
|
});
|
|
13994
15323
|
|
|
13995
15324
|
// src/lib/project-name.ts
|
|
13996
|
-
import { execSync as
|
|
13997
|
-
import
|
|
15325
|
+
import { execSync as execSync10 } from "child_process";
|
|
15326
|
+
import path24 from "path";
|
|
13998
15327
|
function getProjectName(cwd2) {
|
|
13999
15328
|
const dir = cwd2 ?? process.cwd();
|
|
14000
15329
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
14001
15330
|
try {
|
|
14002
15331
|
let repoRoot;
|
|
14003
15332
|
try {
|
|
14004
|
-
const gitCommonDir =
|
|
15333
|
+
const gitCommonDir = execSync10("git rev-parse --path-format=absolute --git-common-dir", {
|
|
14005
15334
|
cwd: dir,
|
|
14006
15335
|
encoding: "utf8",
|
|
14007
15336
|
timeout: 2e3,
|
|
14008
15337
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14009
15338
|
}).trim();
|
|
14010
|
-
repoRoot =
|
|
15339
|
+
repoRoot = path24.dirname(gitCommonDir);
|
|
14011
15340
|
} catch {
|
|
14012
|
-
repoRoot =
|
|
15341
|
+
repoRoot = execSync10("git rev-parse --show-toplevel", {
|
|
14013
15342
|
cwd: dir,
|
|
14014
15343
|
encoding: "utf8",
|
|
14015
15344
|
timeout: 2e3,
|
|
14016
15345
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14017
15346
|
}).trim();
|
|
14018
15347
|
}
|
|
14019
|
-
_cached2 =
|
|
15348
|
+
_cached2 = path24.basename(repoRoot);
|
|
14020
15349
|
_cachedCwd = dir;
|
|
14021
15350
|
return _cached2;
|
|
14022
15351
|
} catch {
|
|
14023
|
-
_cached2 =
|
|
15352
|
+
_cached2 = path24.basename(dir);
|
|
14024
15353
|
_cachedCwd = dir;
|
|
14025
15354
|
return _cached2;
|
|
14026
15355
|
}
|
|
@@ -14162,10 +15491,10 @@ var init_tasks_notify = __esm({
|
|
|
14162
15491
|
});
|
|
14163
15492
|
|
|
14164
15493
|
// src/lib/behaviors.ts
|
|
14165
|
-
import
|
|
15494
|
+
import crypto9 from "crypto";
|
|
14166
15495
|
async function storeBehavior(opts) {
|
|
14167
15496
|
const client = getClient();
|
|
14168
|
-
const id =
|
|
15497
|
+
const id = crypto9.randomUUID();
|
|
14169
15498
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14170
15499
|
await client.execute({
|
|
14171
15500
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -14194,7 +15523,7 @@ __export(skill_learning_exports, {
|
|
|
14194
15523
|
storeTrajectory: () => storeTrajectory,
|
|
14195
15524
|
sweepTrajectories: () => sweepTrajectories
|
|
14196
15525
|
});
|
|
14197
|
-
import
|
|
15526
|
+
import crypto10 from "crypto";
|
|
14198
15527
|
async function extractTrajectory(taskId, agentId) {
|
|
14199
15528
|
const client = getClient();
|
|
14200
15529
|
const result = await client.execute({
|
|
@@ -14223,11 +15552,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
14223
15552
|
return signature;
|
|
14224
15553
|
}
|
|
14225
15554
|
function hashSignature(signature) {
|
|
14226
|
-
return
|
|
15555
|
+
return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
14227
15556
|
}
|
|
14228
15557
|
async function storeTrajectory(opts) {
|
|
14229
15558
|
const client = getClient();
|
|
14230
|
-
const id =
|
|
15559
|
+
const id = crypto10.randomUUID();
|
|
14231
15560
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14232
15561
|
const signatureHash = hashSignature(opts.signature);
|
|
14233
15562
|
await client.execute({
|
|
@@ -14492,8 +15821,8 @@ __export(tasks_exports, {
|
|
|
14492
15821
|
updateTaskStatus: () => updateTaskStatus,
|
|
14493
15822
|
writeCheckpoint: () => writeCheckpoint
|
|
14494
15823
|
});
|
|
14495
|
-
import
|
|
14496
|
-
import { writeFileSync as
|
|
15824
|
+
import path25 from "path";
|
|
15825
|
+
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync11, unlinkSync as unlinkSync9 } from "fs";
|
|
14497
15826
|
async function createTask(input) {
|
|
14498
15827
|
const result = await createTaskCore(input);
|
|
14499
15828
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -14512,14 +15841,14 @@ async function updateTask(input) {
|
|
|
14512
15841
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
14513
15842
|
try {
|
|
14514
15843
|
const agent = String(row.assigned_to);
|
|
14515
|
-
const cacheDir =
|
|
14516
|
-
const cachePath =
|
|
15844
|
+
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
15845
|
+
const cachePath = path25.join(cacheDir, `current-task-${agent}.json`);
|
|
14517
15846
|
if (input.status === "in_progress") {
|
|
14518
|
-
|
|
14519
|
-
|
|
15847
|
+
mkdirSync11(cacheDir, { recursive: true });
|
|
15848
|
+
writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
14520
15849
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
14521
15850
|
try {
|
|
14522
|
-
|
|
15851
|
+
unlinkSync9(cachePath);
|
|
14523
15852
|
} catch {
|
|
14524
15853
|
}
|
|
14525
15854
|
}
|
|
@@ -14960,14 +16289,14 @@ __export(tmux_routing_exports, {
|
|
|
14960
16289
|
spawnEmployee: () => spawnEmployee,
|
|
14961
16290
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
14962
16291
|
});
|
|
14963
|
-
import { execFileSync as execFileSync3, execSync as
|
|
14964
|
-
import { readFileSync as
|
|
14965
|
-
import
|
|
16292
|
+
import { execFileSync as execFileSync3, execSync as execSync11 } from "child_process";
|
|
16293
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, existsSync as existsSync22, appendFileSync as appendFileSync2 } from "fs";
|
|
16294
|
+
import path26 from "path";
|
|
14966
16295
|
import os9 from "os";
|
|
14967
16296
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
14968
|
-
import { unlinkSync as
|
|
16297
|
+
import { unlinkSync as unlinkSync10 } from "fs";
|
|
14969
16298
|
function spawnLockPath(sessionName) {
|
|
14970
|
-
return
|
|
16299
|
+
return path26.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
14971
16300
|
}
|
|
14972
16301
|
function isProcessAlive(pid) {
|
|
14973
16302
|
try {
|
|
@@ -14978,13 +16307,13 @@ function isProcessAlive(pid) {
|
|
|
14978
16307
|
}
|
|
14979
16308
|
}
|
|
14980
16309
|
function acquireSpawnLock2(sessionName) {
|
|
14981
|
-
if (!
|
|
14982
|
-
|
|
16310
|
+
if (!existsSync22(SPAWN_LOCK_DIR)) {
|
|
16311
|
+
mkdirSync12(SPAWN_LOCK_DIR, { recursive: true });
|
|
14983
16312
|
}
|
|
14984
16313
|
const lockFile = spawnLockPath(sessionName);
|
|
14985
|
-
if (
|
|
16314
|
+
if (existsSync22(lockFile)) {
|
|
14986
16315
|
try {
|
|
14987
|
-
const lock = JSON.parse(
|
|
16316
|
+
const lock = JSON.parse(readFileSync18(lockFile, "utf8"));
|
|
14988
16317
|
const age = Date.now() - lock.timestamp;
|
|
14989
16318
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
14990
16319
|
return false;
|
|
@@ -14992,25 +16321,25 @@ function acquireSpawnLock2(sessionName) {
|
|
|
14992
16321
|
} catch {
|
|
14993
16322
|
}
|
|
14994
16323
|
}
|
|
14995
|
-
|
|
16324
|
+
writeFileSync11(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
14996
16325
|
return true;
|
|
14997
16326
|
}
|
|
14998
16327
|
function releaseSpawnLock2(sessionName) {
|
|
14999
16328
|
try {
|
|
15000
|
-
|
|
16329
|
+
unlinkSync10(spawnLockPath(sessionName));
|
|
15001
16330
|
} catch {
|
|
15002
16331
|
}
|
|
15003
16332
|
}
|
|
15004
16333
|
function resolveBehaviorsExporterScript() {
|
|
15005
16334
|
try {
|
|
15006
16335
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
15007
|
-
const scriptPath =
|
|
15008
|
-
|
|
16336
|
+
const scriptPath = path26.join(
|
|
16337
|
+
path26.dirname(thisFile),
|
|
15009
16338
|
"..",
|
|
15010
16339
|
"bin",
|
|
15011
16340
|
"exe-export-behaviors.js"
|
|
15012
16341
|
);
|
|
15013
|
-
return
|
|
16342
|
+
return existsSync22(scriptPath) ? scriptPath : null;
|
|
15014
16343
|
} catch {
|
|
15015
16344
|
return null;
|
|
15016
16345
|
}
|
|
@@ -15074,12 +16403,12 @@ function extractRootExe(name) {
|
|
|
15074
16403
|
return match?.[1] ?? null;
|
|
15075
16404
|
}
|
|
15076
16405
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
15077
|
-
if (!
|
|
15078
|
-
|
|
16406
|
+
if (!existsSync22(SESSION_CACHE)) {
|
|
16407
|
+
mkdirSync12(SESSION_CACHE, { recursive: true });
|
|
15079
16408
|
}
|
|
15080
16409
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
15081
|
-
const filePath =
|
|
15082
|
-
|
|
16410
|
+
const filePath = path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
16411
|
+
writeFileSync11(filePath, JSON.stringify({
|
|
15083
16412
|
parentExe: rootExe,
|
|
15084
16413
|
dispatchedBy: dispatchedBy || rootExe,
|
|
15085
16414
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15087,7 +16416,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
15087
16416
|
}
|
|
15088
16417
|
function getParentExe(sessionKey) {
|
|
15089
16418
|
try {
|
|
15090
|
-
const data = JSON.parse(
|
|
16419
|
+
const data = JSON.parse(readFileSync18(path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
15091
16420
|
return data.parentExe || null;
|
|
15092
16421
|
} catch {
|
|
15093
16422
|
return null;
|
|
@@ -15095,8 +16424,8 @@ function getParentExe(sessionKey) {
|
|
|
15095
16424
|
}
|
|
15096
16425
|
function getDispatchedBy(sessionKey) {
|
|
15097
16426
|
try {
|
|
15098
|
-
const data = JSON.parse(
|
|
15099
|
-
|
|
16427
|
+
const data = JSON.parse(readFileSync18(
|
|
16428
|
+
path26.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
15100
16429
|
"utf8"
|
|
15101
16430
|
));
|
|
15102
16431
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -15157,16 +16486,16 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
15157
16486
|
}
|
|
15158
16487
|
function readDebounceState() {
|
|
15159
16488
|
try {
|
|
15160
|
-
if (!
|
|
15161
|
-
return JSON.parse(
|
|
16489
|
+
if (!existsSync22(DEBOUNCE_FILE)) return {};
|
|
16490
|
+
return JSON.parse(readFileSync18(DEBOUNCE_FILE, "utf8"));
|
|
15162
16491
|
} catch {
|
|
15163
16492
|
return {};
|
|
15164
16493
|
}
|
|
15165
16494
|
}
|
|
15166
16495
|
function writeDebounceState(state) {
|
|
15167
16496
|
try {
|
|
15168
|
-
if (!
|
|
15169
|
-
|
|
16497
|
+
if (!existsSync22(SESSION_CACHE)) mkdirSync12(SESSION_CACHE, { recursive: true });
|
|
16498
|
+
writeFileSync11(DEBOUNCE_FILE, JSON.stringify(state));
|
|
15170
16499
|
} catch {
|
|
15171
16500
|
}
|
|
15172
16501
|
}
|
|
@@ -15190,7 +16519,7 @@ function logIntercom(msg) {
|
|
|
15190
16519
|
process.stderr.write(`[intercom] ${msg}
|
|
15191
16520
|
`);
|
|
15192
16521
|
try {
|
|
15193
|
-
|
|
16522
|
+
appendFileSync2(INTERCOM_LOG2, line);
|
|
15194
16523
|
} catch {
|
|
15195
16524
|
}
|
|
15196
16525
|
}
|
|
@@ -15355,26 +16684,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15355
16684
|
const transport = getTransport();
|
|
15356
16685
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
15357
16686
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
15358
|
-
const logDir =
|
|
15359
|
-
const logFile =
|
|
15360
|
-
if (!
|
|
15361
|
-
|
|
16687
|
+
const logDir = path26.join(os9.homedir(), ".exe-os", "session-logs");
|
|
16688
|
+
const logFile = path26.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
16689
|
+
if (!existsSync22(logDir)) {
|
|
16690
|
+
mkdirSync12(logDir, { recursive: true });
|
|
15362
16691
|
}
|
|
15363
16692
|
transport.kill(sessionName);
|
|
15364
16693
|
let cleanupSuffix = "";
|
|
15365
16694
|
try {
|
|
15366
16695
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
15367
|
-
const cleanupScript =
|
|
15368
|
-
if (
|
|
16696
|
+
const cleanupScript = path26.join(path26.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
16697
|
+
if (existsSync22(cleanupScript)) {
|
|
15369
16698
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
15370
16699
|
}
|
|
15371
16700
|
} catch {
|
|
15372
16701
|
}
|
|
15373
16702
|
try {
|
|
15374
|
-
const claudeJsonPath =
|
|
16703
|
+
const claudeJsonPath = path26.join(os9.homedir(), ".claude.json");
|
|
15375
16704
|
let claudeJson = {};
|
|
15376
16705
|
try {
|
|
15377
|
-
claudeJson = JSON.parse(
|
|
16706
|
+
claudeJson = JSON.parse(readFileSync18(claudeJsonPath, "utf8"));
|
|
15378
16707
|
} catch {
|
|
15379
16708
|
}
|
|
15380
16709
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -15382,17 +16711,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15382
16711
|
const trustDir = opts?.cwd ?? projectDir;
|
|
15383
16712
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
15384
16713
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
15385
|
-
|
|
16714
|
+
writeFileSync11(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
15386
16715
|
} catch {
|
|
15387
16716
|
}
|
|
15388
16717
|
try {
|
|
15389
|
-
const settingsDir =
|
|
16718
|
+
const settingsDir = path26.join(os9.homedir(), ".claude", "projects");
|
|
15390
16719
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
15391
|
-
const projSettingsDir =
|
|
15392
|
-
const settingsPath =
|
|
16720
|
+
const projSettingsDir = path26.join(settingsDir, normalizedKey);
|
|
16721
|
+
const settingsPath = path26.join(projSettingsDir, "settings.json");
|
|
15393
16722
|
let settings = {};
|
|
15394
16723
|
try {
|
|
15395
|
-
settings = JSON.parse(
|
|
16724
|
+
settings = JSON.parse(readFileSync18(settingsPath, "utf8"));
|
|
15396
16725
|
} catch {
|
|
15397
16726
|
}
|
|
15398
16727
|
const perms = settings.permissions ?? {};
|
|
@@ -15420,8 +16749,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15420
16749
|
if (changed) {
|
|
15421
16750
|
perms.allow = allow;
|
|
15422
16751
|
settings.permissions = perms;
|
|
15423
|
-
|
|
15424
|
-
|
|
16752
|
+
mkdirSync12(projSettingsDir, { recursive: true });
|
|
16753
|
+
writeFileSync11(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
15425
16754
|
}
|
|
15426
16755
|
} catch {
|
|
15427
16756
|
}
|
|
@@ -15433,7 +16762,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15433
16762
|
let behaviorsFlag = "";
|
|
15434
16763
|
let legacyFallbackWarned = false;
|
|
15435
16764
|
if (!useExeAgent && !useBinSymlink) {
|
|
15436
|
-
const identityPath2 =
|
|
16765
|
+
const identityPath2 = path26.join(
|
|
15437
16766
|
os9.homedir(),
|
|
15438
16767
|
".exe-os",
|
|
15439
16768
|
"identity",
|
|
@@ -15443,13 +16772,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15443
16772
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
15444
16773
|
if (hasAgentFlag) {
|
|
15445
16774
|
identityFlag = ` --agent ${employeeName}`;
|
|
15446
|
-
} else if (
|
|
16775
|
+
} else if (existsSync22(identityPath2)) {
|
|
15447
16776
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
15448
16777
|
legacyFallbackWarned = true;
|
|
15449
16778
|
}
|
|
15450
16779
|
const behaviorsFile = exportBehaviorsSync(
|
|
15451
16780
|
employeeName,
|
|
15452
|
-
|
|
16781
|
+
path26.basename(spawnCwd),
|
|
15453
16782
|
sessionName
|
|
15454
16783
|
);
|
|
15455
16784
|
if (behaviorsFile) {
|
|
@@ -15464,16 +16793,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15464
16793
|
}
|
|
15465
16794
|
let sessionContextFlag = "";
|
|
15466
16795
|
try {
|
|
15467
|
-
const ctxDir =
|
|
15468
|
-
|
|
15469
|
-
const ctxFile =
|
|
16796
|
+
const ctxDir = path26.join(os9.homedir(), ".exe-os", "session-cache");
|
|
16797
|
+
mkdirSync12(ctxDir, { recursive: true });
|
|
16798
|
+
const ctxFile = path26.join(ctxDir, `session-context-${sessionName}.md`);
|
|
15470
16799
|
const ctxContent = [
|
|
15471
16800
|
`## Session Context`,
|
|
15472
16801
|
`You are running in tmux session: ${sessionName}.`,
|
|
15473
16802
|
`Your parent exe session is ${exeSession}.`,
|
|
15474
16803
|
`Your employees (if any) use the -${exeSession} suffix (e.g., tom-${exeSession}).`
|
|
15475
16804
|
].join("\n");
|
|
15476
|
-
|
|
16805
|
+
writeFileSync11(ctxFile, ctxContent);
|
|
15477
16806
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
15478
16807
|
} catch {
|
|
15479
16808
|
}
|
|
@@ -15511,8 +16840,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15511
16840
|
transport.pipeLog(sessionName, logFile);
|
|
15512
16841
|
try {
|
|
15513
16842
|
const mySession = getMySession();
|
|
15514
|
-
const dispatchInfo =
|
|
15515
|
-
|
|
16843
|
+
const dispatchInfo = path26.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
16844
|
+
writeFileSync11(dispatchInfo, JSON.stringify({
|
|
15516
16845
|
dispatchedBy: mySession,
|
|
15517
16846
|
rootExe: exeSession,
|
|
15518
16847
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
@@ -15523,7 +16852,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
15523
16852
|
let booted = false;
|
|
15524
16853
|
for (let i = 0; i < 30; i++) {
|
|
15525
16854
|
try {
|
|
15526
|
-
|
|
16855
|
+
execSync11("sleep 0.5");
|
|
15527
16856
|
} catch {
|
|
15528
16857
|
}
|
|
15529
16858
|
try {
|
|
@@ -15575,14 +16904,14 @@ var init_tmux_routing = __esm({
|
|
|
15575
16904
|
init_provider_table();
|
|
15576
16905
|
init_intercom_queue();
|
|
15577
16906
|
init_plan_limits();
|
|
15578
|
-
SPAWN_LOCK_DIR =
|
|
15579
|
-
SESSION_CACHE =
|
|
16907
|
+
SPAWN_LOCK_DIR = path26.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
16908
|
+
SESSION_CACHE = path26.join(os9.homedir(), ".exe-os", "session-cache");
|
|
15580
16909
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
15581
16910
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
15582
16911
|
VERIFY_PANE_LINES = 200;
|
|
15583
16912
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
15584
|
-
INTERCOM_LOG2 =
|
|
15585
|
-
DEBOUNCE_FILE =
|
|
16913
|
+
INTERCOM_LOG2 = path26.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
16914
|
+
DEBOUNCE_FILE = path26.join(SESSION_CACHE, "intercom-debounce.json");
|
|
15586
16915
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
15587
16916
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
15588
16917
|
}
|
|
@@ -15699,13 +17028,13 @@ __export(tmux_status_exports, {
|
|
|
15699
17028
|
parseActivity: () => parseActivity,
|
|
15700
17029
|
parseContextPercentage: () => parseContextPercentage
|
|
15701
17030
|
});
|
|
15702
|
-
import { execSync as
|
|
17031
|
+
import { execSync as execSync12 } from "child_process";
|
|
15703
17032
|
function inTmux() {
|
|
15704
17033
|
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
15705
17034
|
const term = process.env.TERM ?? "";
|
|
15706
17035
|
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
15707
17036
|
try {
|
|
15708
|
-
|
|
17037
|
+
execSync12("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
15709
17038
|
encoding: "utf8",
|
|
15710
17039
|
timeout: 2e3
|
|
15711
17040
|
});
|
|
@@ -15715,12 +17044,12 @@ function inTmux() {
|
|
|
15715
17044
|
try {
|
|
15716
17045
|
let pid = process.ppid;
|
|
15717
17046
|
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
15718
|
-
const comm =
|
|
17047
|
+
const comm = execSync12(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
15719
17048
|
encoding: "utf8",
|
|
15720
17049
|
timeout: 1e3
|
|
15721
17050
|
}).trim();
|
|
15722
17051
|
if (/tmux/.test(comm)) return true;
|
|
15723
|
-
const ppid =
|
|
17052
|
+
const ppid = execSync12(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
15724
17053
|
encoding: "utf8",
|
|
15725
17054
|
timeout: 1e3
|
|
15726
17055
|
}).trim();
|
|
@@ -15733,7 +17062,7 @@ function inTmux() {
|
|
|
15733
17062
|
}
|
|
15734
17063
|
function listTmuxSessions() {
|
|
15735
17064
|
try {
|
|
15736
|
-
const out =
|
|
17065
|
+
const out = execSync12("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
15737
17066
|
encoding: "utf8",
|
|
15738
17067
|
timeout: 3e3
|
|
15739
17068
|
});
|
|
@@ -15744,7 +17073,7 @@ function listTmuxSessions() {
|
|
|
15744
17073
|
}
|
|
15745
17074
|
function capturePaneLines(windowName, lines = 10) {
|
|
15746
17075
|
try {
|
|
15747
|
-
const out =
|
|
17076
|
+
const out = execSync12(
|
|
15748
17077
|
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
15749
17078
|
{ encoding: "utf8", timeout: 3e3 }
|
|
15750
17079
|
);
|
|
@@ -15755,7 +17084,7 @@ function capturePaneLines(windowName, lines = 10) {
|
|
|
15755
17084
|
}
|
|
15756
17085
|
function getPaneCwd(windowName) {
|
|
15757
17086
|
try {
|
|
15758
|
-
const out =
|
|
17087
|
+
const out = execSync12(
|
|
15759
17088
|
`tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
|
|
15760
17089
|
{ encoding: "utf8", timeout: 3e3 }
|
|
15761
17090
|
);
|
|
@@ -15766,7 +17095,7 @@ function getPaneCwd(windowName) {
|
|
|
15766
17095
|
}
|
|
15767
17096
|
function projectFromPath(dir) {
|
|
15768
17097
|
try {
|
|
15769
|
-
const root =
|
|
17098
|
+
const root = execSync12("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
|
|
15770
17099
|
encoding: "utf8",
|
|
15771
17100
|
timeout: 3e3
|
|
15772
17101
|
}).trim();
|
|
@@ -15835,7 +17164,7 @@ function getEmployeeStatuses(employeeNames) {
|
|
|
15835
17164
|
}
|
|
15836
17165
|
let paneAlive = true;
|
|
15837
17166
|
try {
|
|
15838
|
-
const paneStatus =
|
|
17167
|
+
const paneStatus = execSync12(
|
|
15839
17168
|
`tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
|
|
15840
17169
|
{ encoding: "utf8", timeout: 3e3 }
|
|
15841
17170
|
).trim();
|
|
@@ -16003,11 +17332,11 @@ function Footer() {
|
|
|
16003
17332
|
} catch {
|
|
16004
17333
|
}
|
|
16005
17334
|
try {
|
|
16006
|
-
const { existsSync:
|
|
17335
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
16007
17336
|
const { join } = await import("path");
|
|
16008
17337
|
const home = process.env.HOME ?? "";
|
|
16009
17338
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
16010
|
-
setDaemon(
|
|
17339
|
+
setDaemon(existsSync24(pidPath) ? "running" : "stopped");
|
|
16011
17340
|
} catch {
|
|
16012
17341
|
setDaemon("unknown");
|
|
16013
17342
|
}
|
|
@@ -16025,8 +17354,8 @@ function Footer() {
|
|
|
16025
17354
|
setSessions(allSessions.length);
|
|
16026
17355
|
if (!currentSession) {
|
|
16027
17356
|
try {
|
|
16028
|
-
const { execSync:
|
|
16029
|
-
const name =
|
|
17357
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
17358
|
+
const name = execSync14("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
16030
17359
|
encoding: "utf8",
|
|
16031
17360
|
timeout: 2e3
|
|
16032
17361
|
}).trim();
|
|
@@ -18038,10 +19367,10 @@ var init_hooks = __esm({
|
|
|
18038
19367
|
});
|
|
18039
19368
|
|
|
18040
19369
|
// src/runtime/safety-checks.ts
|
|
18041
|
-
import
|
|
19370
|
+
import path27 from "path";
|
|
18042
19371
|
import os10 from "os";
|
|
18043
19372
|
function checkPathSafety(filePath) {
|
|
18044
|
-
const resolved =
|
|
19373
|
+
const resolved = path27.resolve(filePath);
|
|
18045
19374
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
18046
19375
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
18047
19376
|
if (matches) {
|
|
@@ -18051,7 +19380,7 @@ function checkPathSafety(filePath) {
|
|
|
18051
19380
|
return { safe: true, bypassImmune: true };
|
|
18052
19381
|
}
|
|
18053
19382
|
function checkReadPathSafety(filePath) {
|
|
18054
|
-
const resolved =
|
|
19383
|
+
const resolved = path27.resolve(filePath);
|
|
18055
19384
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
18056
19385
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
18057
19386
|
);
|
|
@@ -18077,11 +19406,11 @@ var init_safety_checks = __esm({
|
|
|
18077
19406
|
reason: "Git config can set hooks and command execution"
|
|
18078
19407
|
},
|
|
18079
19408
|
{
|
|
18080
|
-
pattern: (p) => p.startsWith(
|
|
19409
|
+
pattern: (p) => p.startsWith(path27.join(HOME, ".claude")),
|
|
18081
19410
|
reason: "Claude configuration files are protected"
|
|
18082
19411
|
},
|
|
18083
19412
|
{
|
|
18084
|
-
pattern: (p) => p.startsWith(
|
|
19413
|
+
pattern: (p) => p.startsWith(path27.join(HOME, ".exe-os")),
|
|
18085
19414
|
reason: "exe-os configuration files are protected"
|
|
18086
19415
|
},
|
|
18087
19416
|
{
|
|
@@ -18098,7 +19427,7 @@ var init_safety_checks = __esm({
|
|
|
18098
19427
|
},
|
|
18099
19428
|
{
|
|
18100
19429
|
pattern: (p) => {
|
|
18101
|
-
const name =
|
|
19430
|
+
const name = path27.basename(p);
|
|
18102
19431
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
18103
19432
|
},
|
|
18104
19433
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -18125,7 +19454,7 @@ __export(file_read_exports, {
|
|
|
18125
19454
|
FileReadTool: () => FileReadTool
|
|
18126
19455
|
});
|
|
18127
19456
|
import fs3 from "fs/promises";
|
|
18128
|
-
import
|
|
19457
|
+
import path28 from "path";
|
|
18129
19458
|
import { z } from "zod";
|
|
18130
19459
|
function isBinary(buf) {
|
|
18131
19460
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -18161,7 +19490,7 @@ var init_file_read = __esm({
|
|
|
18161
19490
|
return { behavior: "allow" };
|
|
18162
19491
|
},
|
|
18163
19492
|
async call(input, context) {
|
|
18164
|
-
const filePath =
|
|
19493
|
+
const filePath = path28.isAbsolute(input.file_path) ? input.file_path : path28.resolve(context.cwd, input.file_path);
|
|
18165
19494
|
let stat2;
|
|
18166
19495
|
try {
|
|
18167
19496
|
stat2 = await fs3.stat(filePath);
|
|
@@ -18201,7 +19530,7 @@ __export(glob_exports, {
|
|
|
18201
19530
|
GlobTool: () => GlobTool
|
|
18202
19531
|
});
|
|
18203
19532
|
import fs4 from "fs/promises";
|
|
18204
|
-
import
|
|
19533
|
+
import path29 from "path";
|
|
18205
19534
|
import { z as z2 } from "zod";
|
|
18206
19535
|
async function walkDir(dir, maxDepth = 10) {
|
|
18207
19536
|
const results = [];
|
|
@@ -18217,7 +19546,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
18217
19546
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
18218
19547
|
continue;
|
|
18219
19548
|
}
|
|
18220
|
-
const fullPath =
|
|
19549
|
+
const fullPath = path29.join(current, entry.name);
|
|
18221
19550
|
if (entry.isDirectory()) {
|
|
18222
19551
|
await walk(fullPath, depth + 1);
|
|
18223
19552
|
} else {
|
|
@@ -18251,11 +19580,11 @@ var init_glob = __esm({
|
|
|
18251
19580
|
inputSchema: inputSchema2,
|
|
18252
19581
|
isReadOnly: true,
|
|
18253
19582
|
async call(input, context) {
|
|
18254
|
-
const baseDir = input.path ?
|
|
19583
|
+
const baseDir = input.path ? path29.isAbsolute(input.path) ? input.path : path29.resolve(context.cwd, input.path) : context.cwd;
|
|
18255
19584
|
try {
|
|
18256
19585
|
const entries = await walkDir(baseDir);
|
|
18257
19586
|
const matched = entries.filter(
|
|
18258
|
-
(e) => simpleGlobMatch(
|
|
19587
|
+
(e) => simpleGlobMatch(path29.relative(baseDir, e.path), input.pattern)
|
|
18259
19588
|
);
|
|
18260
19589
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
18261
19590
|
if (matched.length === 0) {
|
|
@@ -18281,7 +19610,7 @@ __export(grep_exports, {
|
|
|
18281
19610
|
});
|
|
18282
19611
|
import { spawn as spawn2 } from "child_process";
|
|
18283
19612
|
import fs5 from "fs/promises";
|
|
18284
|
-
import
|
|
19613
|
+
import path30 from "path";
|
|
18285
19614
|
import { z as z3 } from "zod";
|
|
18286
19615
|
function runRipgrep(input, searchPath, context) {
|
|
18287
19616
|
return new Promise((resolve, reject) => {
|
|
@@ -18335,7 +19664,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
18335
19664
|
}
|
|
18336
19665
|
for (const entry of entries) {
|
|
18337
19666
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
18338
|
-
const fullPath =
|
|
19667
|
+
const fullPath = path30.join(dir, entry.name);
|
|
18339
19668
|
if (entry.isDirectory()) {
|
|
18340
19669
|
await walk(fullPath);
|
|
18341
19670
|
} else {
|
|
@@ -18381,7 +19710,7 @@ var init_grep = __esm({
|
|
|
18381
19710
|
inputSchema: inputSchema3,
|
|
18382
19711
|
isReadOnly: true,
|
|
18383
19712
|
async call(input, context) {
|
|
18384
|
-
const searchPath = input.path ?
|
|
19713
|
+
const searchPath = input.path ? path30.isAbsolute(input.path) ? input.path : path30.resolve(context.cwd, input.path) : context.cwd;
|
|
18385
19714
|
try {
|
|
18386
19715
|
const result = await runRipgrep(input, searchPath, context);
|
|
18387
19716
|
return result;
|
|
@@ -18406,7 +19735,7 @@ __export(file_write_exports, {
|
|
|
18406
19735
|
FileWriteTool: () => FileWriteTool
|
|
18407
19736
|
});
|
|
18408
19737
|
import fs6 from "fs/promises";
|
|
18409
|
-
import
|
|
19738
|
+
import path31 from "path";
|
|
18410
19739
|
import { z as z4 } from "zod";
|
|
18411
19740
|
var inputSchema4, FileWriteTool;
|
|
18412
19741
|
var init_file_write = __esm({
|
|
@@ -18434,8 +19763,8 @@ var init_file_write = __esm({
|
|
|
18434
19763
|
return { behavior: "allow" };
|
|
18435
19764
|
},
|
|
18436
19765
|
async call(input, context) {
|
|
18437
|
-
const filePath =
|
|
18438
|
-
const dir =
|
|
19766
|
+
const filePath = path31.isAbsolute(input.file_path) ? input.file_path : path31.resolve(context.cwd, input.file_path);
|
|
19767
|
+
const dir = path31.dirname(filePath);
|
|
18439
19768
|
await fs6.mkdir(dir, { recursive: true });
|
|
18440
19769
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
18441
19770
|
return {
|
|
@@ -18453,7 +19782,7 @@ __export(file_edit_exports, {
|
|
|
18453
19782
|
FileEditTool: () => FileEditTool
|
|
18454
19783
|
});
|
|
18455
19784
|
import fs7 from "fs/promises";
|
|
18456
|
-
import
|
|
19785
|
+
import path32 from "path";
|
|
18457
19786
|
import { z as z5 } from "zod";
|
|
18458
19787
|
function countOccurrences(haystack, needle) {
|
|
18459
19788
|
let count = 0;
|
|
@@ -18494,7 +19823,7 @@ var init_file_edit = __esm({
|
|
|
18494
19823
|
return { behavior: "allow" };
|
|
18495
19824
|
},
|
|
18496
19825
|
async call(input, context) {
|
|
18497
|
-
const filePath =
|
|
19826
|
+
const filePath = path32.isAbsolute(input.file_path) ? input.file_path : path32.resolve(context.cwd, input.file_path);
|
|
18498
19827
|
let content;
|
|
18499
19828
|
try {
|
|
18500
19829
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -18736,8 +20065,8 @@ var init_bash = __esm({
|
|
|
18736
20065
|
// src/tui/views/CommandCenter.tsx
|
|
18737
20066
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
18738
20067
|
import TextInput from "ink-text-input";
|
|
18739
|
-
import
|
|
18740
|
-
import { homedir as
|
|
20068
|
+
import path33 from "path";
|
|
20069
|
+
import { homedir as homedir5 } from "os";
|
|
18741
20070
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
18742
20071
|
function CommandCenterView({
|
|
18743
20072
|
onSelectProject,
|
|
@@ -18771,15 +20100,15 @@ function CommandCenterView({
|
|
|
18771
20100
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
18772
20101
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
18773
20102
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
18774
|
-
const { readFileSync:
|
|
20103
|
+
const { readFileSync: readFileSync20, existsSync: existsSync24 } = await import("fs");
|
|
18775
20104
|
const { join } = await import("path");
|
|
18776
|
-
const { homedir:
|
|
18777
|
-
const configPath = join(
|
|
20105
|
+
const { homedir: homedir7 } = await import("os");
|
|
20106
|
+
const configPath = join(homedir7(), ".exe-os", "config.json");
|
|
18778
20107
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
18779
20108
|
let providerConfigs = {};
|
|
18780
|
-
if (
|
|
20109
|
+
if (existsSync24(configPath)) {
|
|
18781
20110
|
try {
|
|
18782
|
-
const raw = JSON.parse(
|
|
20111
|
+
const raw = JSON.parse(readFileSync20(configPath, "utf8"));
|
|
18783
20112
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
18784
20113
|
if (raw.providers && typeof raw.providers === "object") {
|
|
18785
20114
|
providerConfigs = raw.providers;
|
|
@@ -18837,10 +20166,10 @@ function CommandCenterView({
|
|
|
18837
20166
|
registry.register(BashTool2);
|
|
18838
20167
|
let agentRole = "CTO";
|
|
18839
20168
|
try {
|
|
18840
|
-
const markerDir = join(
|
|
20169
|
+
const markerDir = join(homedir7(), ".exe-os", "session-cache");
|
|
18841
20170
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
18842
20171
|
for (const f of agentFiles) {
|
|
18843
|
-
const data = JSON.parse(
|
|
20172
|
+
const data = JSON.parse(readFileSync20(join(markerDir, f), "utf8"));
|
|
18844
20173
|
if (data.agentRole) {
|
|
18845
20174
|
agentRole = data.agentRole;
|
|
18846
20175
|
break;
|
|
@@ -18977,7 +20306,7 @@ function CommandCenterView({
|
|
|
18977
20306
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
18978
20307
|
projectName: p.projectName,
|
|
18979
20308
|
exeSession: p.exeSession,
|
|
18980
|
-
projectDir:
|
|
20309
|
+
projectDir: path33.join(homedir5(), p.projectName),
|
|
18981
20310
|
employeeCount: p.employees.length,
|
|
18982
20311
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
18983
20312
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -19015,7 +20344,7 @@ function CommandCenterView({
|
|
|
19015
20344
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
19016
20345
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
19017
20346
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
19018
|
-
const { existsSync:
|
|
20347
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
19019
20348
|
const { join } = await import("path");
|
|
19020
20349
|
const client = getClient2();
|
|
19021
20350
|
if (!client) {
|
|
@@ -19084,7 +20413,7 @@ function CommandCenterView({
|
|
|
19084
20413
|
}
|
|
19085
20414
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
19086
20415
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
19087
|
-
const hasGit = projectDir ?
|
|
20416
|
+
const hasGit = projectDir ? existsSync24(join(projectDir, ".git")) : false;
|
|
19088
20417
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
19089
20418
|
projectList.push({
|
|
19090
20419
|
projectName: name,
|
|
@@ -19109,7 +20438,7 @@ function CommandCenterView({
|
|
|
19109
20438
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
19110
20439
|
try {
|
|
19111
20440
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
19112
|
-
setHealth((h) => ({ ...h, daemon:
|
|
20441
|
+
setHealth((h) => ({ ...h, daemon: existsSync24(pidPath) ? "running" : "stopped" }));
|
|
19113
20442
|
} catch {
|
|
19114
20443
|
}
|
|
19115
20444
|
const activityResult = await client.execute(
|
|
@@ -19350,8 +20679,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
19350
20679
|
}
|
|
19351
20680
|
const capture = () => {
|
|
19352
20681
|
try {
|
|
19353
|
-
const { execSync:
|
|
19354
|
-
const output =
|
|
20682
|
+
const { execSync: execSync14 } = __require("child_process");
|
|
20683
|
+
const output = execSync14(
|
|
19355
20684
|
`tmux capture-pane -t ${JSON.stringify(sessionName)} -p -e 2>/dev/null | tail -${CAPTURE_LINES}`,
|
|
19356
20685
|
{ encoding: "utf8", timeout: 3e3 }
|
|
19357
20686
|
);
|
|
@@ -19375,8 +20704,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
19375
20704
|
if (key.return) {
|
|
19376
20705
|
if (!demo && inputBuffer.trim()) {
|
|
19377
20706
|
try {
|
|
19378
|
-
const { execSync:
|
|
19379
|
-
|
|
20707
|
+
const { execSync: execSync14 } = __require("child_process");
|
|
20708
|
+
execSync14(
|
|
19380
20709
|
`tmux send-keys -t ${JSON.stringify(sessionName)} ${JSON.stringify(inputBuffer)} Enter`,
|
|
19381
20710
|
{ timeout: 2e3 }
|
|
19382
20711
|
);
|
|
@@ -19384,8 +20713,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
19384
20713
|
}
|
|
19385
20714
|
} else if (!demo) {
|
|
19386
20715
|
try {
|
|
19387
|
-
const { execSync:
|
|
19388
|
-
|
|
20716
|
+
const { execSync: execSync14 } = __require("child_process");
|
|
20717
|
+
execSync14(`tmux send-keys -t ${JSON.stringify(sessionName)} Enter`, { timeout: 2e3 });
|
|
19389
20718
|
} catch {
|
|
19390
20719
|
}
|
|
19391
20720
|
}
|
|
@@ -19970,8 +21299,8 @@ var init_useOrchestrator = __esm({
|
|
|
19970
21299
|
|
|
19971
21300
|
// src/tui/views/Sessions.tsx
|
|
19972
21301
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
19973
|
-
import
|
|
19974
|
-
import { homedir as
|
|
21302
|
+
import path34 from "path";
|
|
21303
|
+
import { homedir as homedir6 } from "os";
|
|
19975
21304
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
19976
21305
|
function SessionsView({
|
|
19977
21306
|
initialProject,
|
|
@@ -20005,7 +21334,7 @@ function SessionsView({
|
|
|
20005
21334
|
if (demo) {
|
|
20006
21335
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
20007
21336
|
...p,
|
|
20008
|
-
projectDir:
|
|
21337
|
+
projectDir: path34.join(homedir6(), p.projectName),
|
|
20009
21338
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
20010
21339
|
})));
|
|
20011
21340
|
return;
|
|
@@ -20060,12 +21389,12 @@ function SessionsView({
|
|
|
20060
21389
|
return;
|
|
20061
21390
|
}
|
|
20062
21391
|
} else {
|
|
20063
|
-
const { execSync:
|
|
21392
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
20064
21393
|
const dir = projectDir || process.cwd();
|
|
20065
|
-
|
|
20066
|
-
|
|
21394
|
+
execSync14(`tmux new-session -d -s ${JSON.stringify(entry.sessionName)} -c ${JSON.stringify(dir)}`, { timeout: 5e3 });
|
|
21395
|
+
execSync14(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "claude --dangerously-skip-permissions" Enter`, { timeout: 3e3 });
|
|
20067
21396
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
20068
|
-
|
|
21397
|
+
execSync14(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "/exe" Enter`, { timeout: 3e3 });
|
|
20069
21398
|
}
|
|
20070
21399
|
const updated = { ...entry, status: "active", activity: "Starting...", attached: false };
|
|
20071
21400
|
setViewingEmployee(updated);
|
|
@@ -20167,7 +21496,7 @@ function SessionsView({
|
|
|
20167
21496
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
20168
21497
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
20169
21498
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
20170
|
-
const { execSync:
|
|
21499
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
20171
21500
|
if (!inTmux2()) {
|
|
20172
21501
|
setTmuxAvailable(false);
|
|
20173
21502
|
setProjects([]);
|
|
@@ -20176,7 +21505,7 @@ function SessionsView({
|
|
|
20176
21505
|
setTmuxAvailable(true);
|
|
20177
21506
|
const attachedMap = /* @__PURE__ */ new Map();
|
|
20178
21507
|
try {
|
|
20179
|
-
const out =
|
|
21508
|
+
const out = execSync14("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
|
|
20180
21509
|
encoding: "utf8",
|
|
20181
21510
|
timeout: 3e3
|
|
20182
21511
|
});
|
|
@@ -20764,16 +22093,16 @@ __export(ws_auth_exports, {
|
|
|
20764
22093
|
deriveWsAuthToken: () => deriveWsAuthToken,
|
|
20765
22094
|
hashAuthToken: () => hashAuthToken
|
|
20766
22095
|
});
|
|
20767
|
-
import
|
|
22096
|
+
import crypto11 from "crypto";
|
|
20768
22097
|
function deriveWsAuthToken(masterKey) {
|
|
20769
|
-
return Buffer.from(
|
|
22098
|
+
return Buffer.from(crypto11.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
|
|
20770
22099
|
}
|
|
20771
22100
|
function deriveOrgId(masterKey) {
|
|
20772
|
-
const raw = Buffer.from(
|
|
20773
|
-
return
|
|
22101
|
+
const raw = Buffer.from(crypto11.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
|
|
22102
|
+
return crypto11.createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
20774
22103
|
}
|
|
20775
22104
|
function hashAuthToken(token) {
|
|
20776
|
-
return
|
|
22105
|
+
return crypto11.createHash("sha256").update(token).digest("hex");
|
|
20777
22106
|
}
|
|
20778
22107
|
var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
|
|
20779
22108
|
var init_ws_auth = __esm({
|
|
@@ -20977,7 +22306,7 @@ function assertSecureWsUrl(url) {
|
|
|
20977
22306
|
`Malformed WebSocket URL rejected: "${url}".`
|
|
20978
22307
|
);
|
|
20979
22308
|
}
|
|
20980
|
-
if (
|
|
22309
|
+
if (LOCALHOST_PATTERNS2.test(parsed.hostname)) return;
|
|
20981
22310
|
throw new Error(
|
|
20982
22311
|
`Insecure WebSocket URL rejected: "${url}". Use wss:// for remote hosts. Plain ws:// is only allowed for localhost.`
|
|
20983
22312
|
);
|
|
@@ -20986,7 +22315,7 @@ function assertSecureWsUrl(url) {
|
|
|
20986
22315
|
function isGatewayEvent(msg) {
|
|
20987
22316
|
return typeof msg.type === "string" && GATEWAY_EVENT_TYPES.has(msg.type);
|
|
20988
22317
|
}
|
|
20989
|
-
var MIN_RECONNECT_MS, MAX_RECONNECT_MS, AUTH_RESPONSE_TIMEOUT_MS, DEFAULT_CHANNELS, GATEWAY_EVENT_TYPES,
|
|
22318
|
+
var MIN_RECONNECT_MS, MAX_RECONNECT_MS, AUTH_RESPONSE_TIMEOUT_MS, DEFAULT_CHANNELS, GATEWAY_EVENT_TYPES, LOCALHOST_PATTERNS2;
|
|
20990
22319
|
var init_gateway_client = __esm({
|
|
20991
22320
|
"src/lib/gateway-client.ts"() {
|
|
20992
22321
|
"use strict";
|
|
@@ -21002,7 +22331,7 @@ var init_gateway_client = __esm({
|
|
|
21002
22331
|
"health",
|
|
21003
22332
|
"escalation"
|
|
21004
22333
|
]);
|
|
21005
|
-
|
|
22334
|
+
LOCALHOST_PATTERNS2 = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
21006
22335
|
}
|
|
21007
22336
|
});
|
|
21008
22337
|
|
|
@@ -21126,19 +22455,19 @@ function upsertConversation(conversations, platform, senderId, message) {
|
|
|
21126
22455
|
async function loadGatewayConfig() {
|
|
21127
22456
|
const state = { running: false, port: 3100, adapters: [], agents: [], gatewayUrl: "" };
|
|
21128
22457
|
try {
|
|
21129
|
-
const { execSync:
|
|
21130
|
-
const ps =
|
|
22458
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
22459
|
+
const ps = execSync14("pgrep -f exe-gateway 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
|
|
21131
22460
|
state.running = ps.trim().length > 0;
|
|
21132
22461
|
} catch {
|
|
21133
22462
|
state.running = false;
|
|
21134
22463
|
}
|
|
21135
22464
|
try {
|
|
21136
|
-
const { existsSync:
|
|
22465
|
+
const { existsSync: existsSync24, readFileSync: readFileSync20 } = await import("fs");
|
|
21137
22466
|
const { join } = await import("path");
|
|
21138
22467
|
const home = process.env.HOME ?? "";
|
|
21139
22468
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
21140
|
-
if (
|
|
21141
|
-
const raw = JSON.parse(
|
|
22469
|
+
if (existsSync24(configPath)) {
|
|
22470
|
+
const raw = JSON.parse(readFileSync20(configPath, "utf8"));
|
|
21142
22471
|
state.port = raw.port ?? 3100;
|
|
21143
22472
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
21144
22473
|
if (raw.adapters) {
|
|
@@ -21604,10 +22933,10 @@ var init_Gateway = __esm({
|
|
|
21604
22933
|
});
|
|
21605
22934
|
|
|
21606
22935
|
// src/tui/utils/agent-status.ts
|
|
21607
|
-
import { execSync as
|
|
22936
|
+
import { execSync as execSync13 } from "child_process";
|
|
21608
22937
|
function getAgentStatus(agentId) {
|
|
21609
22938
|
try {
|
|
21610
|
-
const sessions =
|
|
22939
|
+
const sessions = execSync13("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
21611
22940
|
encoding: "utf8",
|
|
21612
22941
|
timeout: 2e3
|
|
21613
22942
|
}).trim().split("\n");
|
|
@@ -21618,7 +22947,7 @@ function getAgentStatus(agentId) {
|
|
|
21618
22947
|
return /^\d?-/.test(suffix) || /^\d+$/.test(suffix);
|
|
21619
22948
|
});
|
|
21620
22949
|
if (!agentSession) return { label: "offline", color: "gray" };
|
|
21621
|
-
const pane =
|
|
22950
|
+
const pane = execSync13(`tmux capture-pane -t "${agentSession}" -p 2>/dev/null | tail -3`, {
|
|
21622
22951
|
encoding: "utf8",
|
|
21623
22952
|
timeout: 2e3
|
|
21624
22953
|
});
|
|
@@ -21761,12 +23090,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
21761
23090
|
setMembers(teamData);
|
|
21762
23091
|
setDbError(null);
|
|
21763
23092
|
try {
|
|
21764
|
-
const { existsSync:
|
|
23093
|
+
const { existsSync: existsSync24, readFileSync: readFileSync20 } = await import("fs");
|
|
21765
23094
|
const { join } = await import("path");
|
|
21766
23095
|
const home = process.env.HOME ?? "";
|
|
21767
23096
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
21768
|
-
if (
|
|
21769
|
-
const raw = JSON.parse(
|
|
23097
|
+
if (existsSync24(gatewayConfig)) {
|
|
23098
|
+
const raw = JSON.parse(readFileSync20(gatewayConfig, "utf8"));
|
|
21770
23099
|
if (raw.agents && raw.agents.length > 0) {
|
|
21771
23100
|
setExternals(raw.agents.map((a) => ({
|
|
21772
23101
|
name: a.name,
|
|
@@ -21947,8 +23276,8 @@ __export(wiki_client_exports, {
|
|
|
21947
23276
|
listDocuments: () => listDocuments,
|
|
21948
23277
|
listWorkspaces: () => listWorkspaces
|
|
21949
23278
|
});
|
|
21950
|
-
async function wikiFetch(config,
|
|
21951
|
-
const url = `${config.baseUrl}/api/v1${
|
|
23279
|
+
async function wikiFetch(config, path36, method = "GET", body) {
|
|
23280
|
+
const url = `${config.baseUrl}/api/v1${path36}`;
|
|
21952
23281
|
const headers = {
|
|
21953
23282
|
Authorization: `Bearer ${config.apiKey}`,
|
|
21954
23283
|
"Content-Type": "application/json"
|
|
@@ -21981,7 +23310,7 @@ async function wikiFetch(config, path34, method = "GET", body) {
|
|
|
21981
23310
|
}
|
|
21982
23311
|
}
|
|
21983
23312
|
if (!response.ok) {
|
|
21984
|
-
throw new Error(`Wiki API ${method} ${
|
|
23313
|
+
throw new Error(`Wiki API ${method} ${path36}: ${response.status} ${response.statusText}`);
|
|
21985
23314
|
}
|
|
21986
23315
|
return response.json();
|
|
21987
23316
|
} finally {
|
|
@@ -22583,20 +23912,20 @@ function SettingsView({ onBack }) {
|
|
|
22583
23912
|
};
|
|
22584
23913
|
});
|
|
22585
23914
|
try {
|
|
22586
|
-
const { execSync:
|
|
22587
|
-
|
|
23915
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
23916
|
+
execSync14("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
|
|
22588
23917
|
providerList.push({ name: "Ollama", configured: true, detail: "localhost:11434" });
|
|
22589
23918
|
} catch {
|
|
22590
23919
|
providerList.push({ name: "Ollama", configured: false, detail: "not running" });
|
|
22591
23920
|
}
|
|
22592
23921
|
setProviders(providerList);
|
|
22593
23922
|
try {
|
|
22594
|
-
const { existsSync:
|
|
23923
|
+
const { existsSync: existsSync24 } = await import("fs");
|
|
22595
23924
|
const { join } = await import("path");
|
|
22596
23925
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
22597
23926
|
const cfg = await loadConfig2();
|
|
22598
23927
|
const home = process.env.HOME ?? "";
|
|
22599
|
-
const hasKey =
|
|
23928
|
+
const hasKey = existsSync24(join(home, ".exe-os", "master.key"));
|
|
22600
23929
|
if (cfg.cloud) {
|
|
22601
23930
|
setCloud({
|
|
22602
23931
|
configured: true,
|
|
@@ -22609,22 +23938,22 @@ function SettingsView({ onBack }) {
|
|
|
22609
23938
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
22610
23939
|
let daemon = "unknown";
|
|
22611
23940
|
try {
|
|
22612
|
-
daemon =
|
|
23941
|
+
daemon = existsSync24(pidPath) ? "running" : "stopped";
|
|
22613
23942
|
} catch {
|
|
22614
23943
|
}
|
|
22615
23944
|
let version = "unknown";
|
|
22616
23945
|
try {
|
|
22617
|
-
const { readFileSync:
|
|
23946
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
22618
23947
|
const { createRequire } = await import("module");
|
|
22619
23948
|
const require2 = createRequire(import.meta.url);
|
|
22620
23949
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
22621
|
-
const pkg = JSON.parse(
|
|
23950
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
22622
23951
|
version = pkg.version;
|
|
22623
23952
|
} catch {
|
|
22624
23953
|
try {
|
|
22625
|
-
const { readFileSync:
|
|
23954
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
22626
23955
|
const { join: joinPath } = await import("path");
|
|
22627
|
-
const pkg = JSON.parse(
|
|
23956
|
+
const pkg = JSON.parse(readFileSync20(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
22628
23957
|
version = pkg.version;
|
|
22629
23958
|
} catch {
|
|
22630
23959
|
}
|
|
@@ -23231,8 +24560,8 @@ Unhandled rejection: ${reason}
|
|
|
23231
24560
|
});
|
|
23232
24561
|
|
|
23233
24562
|
// src/bin/cli.ts
|
|
23234
|
-
import { existsSync as
|
|
23235
|
-
import
|
|
24563
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSync as writeFileSync12, readdirSync as readdirSync7, rmSync } from "fs";
|
|
24564
|
+
import path35 from "path";
|
|
23236
24565
|
import os11 from "os";
|
|
23237
24566
|
var args = process.argv.slice(2);
|
|
23238
24567
|
if (args.includes("--global")) {
|
|
@@ -23296,11 +24625,11 @@ if (args.includes("--global")) {
|
|
|
23296
24625
|
});
|
|
23297
24626
|
await init_App2().then(() => App_exports);
|
|
23298
24627
|
} else {
|
|
23299
|
-
const claudeDir =
|
|
23300
|
-
const settingsPath =
|
|
23301
|
-
const hasClaudeCode =
|
|
24628
|
+
const claudeDir = path35.join(os11.homedir(), ".claude");
|
|
24629
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24630
|
+
const hasClaudeCode = existsSync23(settingsPath) && (() => {
|
|
23302
24631
|
try {
|
|
23303
|
-
const raw =
|
|
24632
|
+
const raw = readFileSync19(settingsPath, "utf8");
|
|
23304
24633
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
23305
24634
|
} catch {
|
|
23306
24635
|
return false;
|
|
@@ -23339,14 +24668,14 @@ async function runClaudeInstall() {
|
|
|
23339
24668
|
}
|
|
23340
24669
|
}
|
|
23341
24670
|
async function runClaudeCheck() {
|
|
23342
|
-
const claudeDir =
|
|
23343
|
-
const settingsPath =
|
|
23344
|
-
const claudeJsonPath =
|
|
24671
|
+
const claudeDir = path35.join(os11.homedir(), ".claude");
|
|
24672
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24673
|
+
const claudeJsonPath = path35.join(os11.homedir(), ".claude.json");
|
|
23345
24674
|
let ok = true;
|
|
23346
|
-
if (
|
|
24675
|
+
if (existsSync23(settingsPath)) {
|
|
23347
24676
|
let settings;
|
|
23348
24677
|
try {
|
|
23349
|
-
settings = JSON.parse(
|
|
24678
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
23350
24679
|
} catch {
|
|
23351
24680
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
23352
24681
|
ok = false;
|
|
@@ -23372,10 +24701,10 @@ async function runClaudeCheck() {
|
|
|
23372
24701
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
23373
24702
|
ok = false;
|
|
23374
24703
|
}
|
|
23375
|
-
if (
|
|
24704
|
+
if (existsSync23(claudeJsonPath)) {
|
|
23376
24705
|
let claudeJson;
|
|
23377
24706
|
try {
|
|
23378
|
-
claudeJson = JSON.parse(
|
|
24707
|
+
claudeJson = JSON.parse(readFileSync19(claudeJsonPath, "utf8"));
|
|
23379
24708
|
} catch {
|
|
23380
24709
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
23381
24710
|
ok = false;
|
|
@@ -23394,8 +24723,8 @@ async function runClaudeCheck() {
|
|
|
23394
24723
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
23395
24724
|
ok = false;
|
|
23396
24725
|
}
|
|
23397
|
-
const skillsDir =
|
|
23398
|
-
if (
|
|
24726
|
+
const skillsDir = path35.join(claudeDir, "skills");
|
|
24727
|
+
if (existsSync23(skillsDir)) {
|
|
23399
24728
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
23400
24729
|
} else {
|
|
23401
24730
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -23412,16 +24741,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23412
24741
|
const dryRun = flags.includes("--dry-run");
|
|
23413
24742
|
const purge = flags.includes("--purge");
|
|
23414
24743
|
const homeDir = os11.homedir();
|
|
23415
|
-
const claudeDir =
|
|
23416
|
-
const settingsPath =
|
|
23417
|
-
const claudeJsonPath =
|
|
23418
|
-
const exeOsDir =
|
|
24744
|
+
const claudeDir = path35.join(homeDir, ".claude");
|
|
24745
|
+
const settingsPath = path35.join(claudeDir, "settings.json");
|
|
24746
|
+
const claudeJsonPath = path35.join(homeDir, ".claude.json");
|
|
24747
|
+
const exeOsDir = path35.join(homeDir, ".exe-os");
|
|
23419
24748
|
let removed = 0;
|
|
23420
24749
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
23421
24750
|
let settings = {};
|
|
23422
|
-
if (
|
|
24751
|
+
if (existsSync23(settingsPath)) {
|
|
23423
24752
|
try {
|
|
23424
|
-
settings = JSON.parse(
|
|
24753
|
+
settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
|
|
23425
24754
|
} catch {
|
|
23426
24755
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
23427
24756
|
if (purge) {
|
|
@@ -23459,15 +24788,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23459
24788
|
permCount = before - settings.permissions.allow.length;
|
|
23460
24789
|
}
|
|
23461
24790
|
if (!dryRun) {
|
|
23462
|
-
|
|
24791
|
+
writeFileSync12(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
23463
24792
|
}
|
|
23464
24793
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
23465
24794
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
23466
24795
|
removed++;
|
|
23467
24796
|
}
|
|
23468
24797
|
}
|
|
23469
|
-
if (
|
|
23470
|
-
const raw =
|
|
24798
|
+
if (existsSync23(claudeJsonPath)) {
|
|
24799
|
+
const raw = readFileSync19(claudeJsonPath, "utf8");
|
|
23471
24800
|
if (raw.length > 1e6) {
|
|
23472
24801
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
23473
24802
|
} else {
|
|
@@ -23488,7 +24817,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23488
24817
|
}
|
|
23489
24818
|
if (removedMcp) {
|
|
23490
24819
|
if (!dryRun) {
|
|
23491
|
-
|
|
24820
|
+
writeFileSync12(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
23492
24821
|
}
|
|
23493
24822
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
23494
24823
|
removed++;
|
|
@@ -23496,14 +24825,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23496
24825
|
}
|
|
23497
24826
|
}
|
|
23498
24827
|
}
|
|
23499
|
-
const skillsDir =
|
|
23500
|
-
if (
|
|
24828
|
+
const skillsDir = path35.join(claudeDir, "skills");
|
|
24829
|
+
if (existsSync23(skillsDir)) {
|
|
23501
24830
|
let skillCount = 0;
|
|
23502
24831
|
try {
|
|
23503
|
-
const entries =
|
|
24832
|
+
const entries = readdirSync7(skillsDir);
|
|
23504
24833
|
for (const entry of entries) {
|
|
23505
24834
|
if (entry.startsWith("exe")) {
|
|
23506
|
-
const fullPath =
|
|
24835
|
+
const fullPath = path35.join(skillsDir, entry);
|
|
23507
24836
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
23508
24837
|
skillCount++;
|
|
23509
24838
|
}
|
|
@@ -23515,30 +24844,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23515
24844
|
removed++;
|
|
23516
24845
|
}
|
|
23517
24846
|
}
|
|
23518
|
-
const claudeMdPath =
|
|
23519
|
-
if (
|
|
23520
|
-
const content =
|
|
24847
|
+
const claudeMdPath = path35.join(claudeDir, "CLAUDE.md");
|
|
24848
|
+
if (existsSync23(claudeMdPath)) {
|
|
24849
|
+
const content = readFileSync19(claudeMdPath, "utf8");
|
|
23521
24850
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
23522
24851
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
23523
24852
|
const startIdx = content.indexOf(startMarker);
|
|
23524
24853
|
const endIdx = content.indexOf(endMarker);
|
|
23525
24854
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
23526
24855
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
23527
|
-
if (!dryRun)
|
|
24856
|
+
if (!dryRun) writeFileSync12(claudeMdPath, cleaned);
|
|
23528
24857
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
23529
24858
|
removed++;
|
|
23530
24859
|
}
|
|
23531
24860
|
}
|
|
23532
|
-
const agentsDir =
|
|
23533
|
-
if (
|
|
24861
|
+
const agentsDir = path35.join(claudeDir, "agents");
|
|
24862
|
+
if (existsSync23(agentsDir)) {
|
|
23534
24863
|
let agentCount = 0;
|
|
23535
24864
|
try {
|
|
23536
|
-
const entries =
|
|
24865
|
+
const entries = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
23537
24866
|
let knownNames = /* @__PURE__ */ new Set();
|
|
23538
|
-
const rosterPath =
|
|
23539
|
-
if (
|
|
24867
|
+
const rosterPath = path35.join(exeOsDir, "exe-employees.json");
|
|
24868
|
+
if (existsSync23(rosterPath)) {
|
|
23540
24869
|
try {
|
|
23541
|
-
const roster = JSON.parse(
|
|
24870
|
+
const roster = JSON.parse(readFileSync19(rosterPath, "utf8"));
|
|
23542
24871
|
knownNames = new Set(roster.map((e) => e.name));
|
|
23543
24872
|
} catch {
|
|
23544
24873
|
}
|
|
@@ -23546,7 +24875,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23546
24875
|
for (const entry of entries) {
|
|
23547
24876
|
const name = entry.replace(/\.md$/, "");
|
|
23548
24877
|
if (knownNames.has(name)) {
|
|
23549
|
-
if (!dryRun) rmSync(
|
|
24878
|
+
if (!dryRun) rmSync(path35.join(agentsDir, entry), { force: true });
|
|
23550
24879
|
agentCount++;
|
|
23551
24880
|
}
|
|
23552
24881
|
}
|
|
@@ -23557,16 +24886,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23557
24886
|
removed++;
|
|
23558
24887
|
}
|
|
23559
24888
|
}
|
|
23560
|
-
const projectsDir =
|
|
23561
|
-
if (
|
|
24889
|
+
const projectsDir = path35.join(claudeDir, "projects");
|
|
24890
|
+
if (existsSync23(projectsDir)) {
|
|
23562
24891
|
let projectCount = 0;
|
|
23563
24892
|
try {
|
|
23564
|
-
const projects =
|
|
24893
|
+
const projects = readdirSync7(projectsDir);
|
|
23565
24894
|
for (const proj of projects) {
|
|
23566
|
-
const projSettings =
|
|
23567
|
-
if (!
|
|
24895
|
+
const projSettings = path35.join(projectsDir, proj, "settings.json");
|
|
24896
|
+
if (!existsSync23(projSettings)) continue;
|
|
23568
24897
|
try {
|
|
23569
|
-
const pSettings = JSON.parse(
|
|
24898
|
+
const pSettings = JSON.parse(readFileSync19(projSettings, "utf8"));
|
|
23570
24899
|
let changed = false;
|
|
23571
24900
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
23572
24901
|
const before = pSettings.permissions.allow.length;
|
|
@@ -23576,7 +24905,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23576
24905
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
23577
24906
|
}
|
|
23578
24907
|
if (changed && !dryRun) {
|
|
23579
|
-
|
|
24908
|
+
writeFileSync12(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
23580
24909
|
}
|
|
23581
24910
|
if (changed) projectCount++;
|
|
23582
24911
|
} catch {
|
|
@@ -23590,26 +24919,26 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23590
24919
|
}
|
|
23591
24920
|
}
|
|
23592
24921
|
try {
|
|
23593
|
-
const { execSync:
|
|
24922
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
23594
24923
|
const findExeBin3 = () => {
|
|
23595
24924
|
try {
|
|
23596
|
-
return
|
|
24925
|
+
return execSync14(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
23597
24926
|
} catch {
|
|
23598
24927
|
return null;
|
|
23599
24928
|
}
|
|
23600
24929
|
};
|
|
23601
24930
|
const exeBinPath = findExeBin3();
|
|
23602
24931
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
23603
|
-
const binDir =
|
|
24932
|
+
const binDir = path35.dirname(exeBinPath);
|
|
23604
24933
|
let symlinkCount = 0;
|
|
23605
|
-
const rosterPath =
|
|
23606
|
-
if (
|
|
23607
|
-
const roster = JSON.parse(
|
|
24934
|
+
const rosterPath = path35.join(exeOsDir, "exe-employees.json");
|
|
24935
|
+
if (existsSync23(rosterPath)) {
|
|
24936
|
+
const roster = JSON.parse(readFileSync19(rosterPath, "utf8"));
|
|
23608
24937
|
for (const emp of roster) {
|
|
23609
24938
|
if (emp.name === "exe") continue;
|
|
23610
24939
|
for (const suffix of ["", "-opencode"]) {
|
|
23611
|
-
const linkPath =
|
|
23612
|
-
if (
|
|
24940
|
+
const linkPath = path35.join(binDir, `${emp.name}${suffix}`);
|
|
24941
|
+
if (existsSync23(linkPath)) {
|
|
23613
24942
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
23614
24943
|
symlinkCount++;
|
|
23615
24944
|
}
|
|
@@ -23622,7 +24951,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
23622
24951
|
}
|
|
23623
24952
|
} catch {
|
|
23624
24953
|
}
|
|
23625
|
-
if (purge &&
|
|
24954
|
+
if (purge && existsSync23(exeOsDir)) {
|
|
23626
24955
|
if (!dryRun) {
|
|
23627
24956
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
23628
24957
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -23647,7 +24976,7 @@ async function checkForUpdateOnBoot() {
|
|
|
23647
24976
|
const config = await loadConfig2();
|
|
23648
24977
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
23649
24978
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
23650
|
-
const packageRoot =
|
|
24979
|
+
const packageRoot = path35.resolve(
|
|
23651
24980
|
new URL("../..", import.meta.url).pathname
|
|
23652
24981
|
);
|
|
23653
24982
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -23706,7 +25035,7 @@ async function runActivate(key) {
|
|
|
23706
25035
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
23707
25036
|
if (idTemplate) {
|
|
23708
25037
|
const idPath = identityPath2(name);
|
|
23709
|
-
const dir =
|
|
25038
|
+
const dir = path35.dirname(idPath);
|
|
23710
25039
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
23711
25040
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
23712
25041
|
}
|