@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/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 mkdirSync2, readdirSync } from "fs";
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
- mkdirSync2(SHARDS_DIR, { recursive: true });
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 execSync2 } from "child_process";
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
- writeFileSync(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
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
- writeFileSync(newIdentityPath, updatedContent, "utf-8");
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
- writeFileSync(newIdentityPath, content, "utf-8");
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
- writeFileSync(oldAgentPath, agentContent, "utf-8");
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 execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
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: existsSync22 } = await import("fs");
4734
- const path34 = await import("path");
4735
- const modelPath = path34.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4736
- if (!existsSync22(modelPath)) {
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 writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
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
- mkdirSync3(EXE_AI_DIR, { recursive: true });
4811
- writeFileSync2(DEVICE_ID_PATH, id, "utf8");
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
- mkdirSync3(EXE_AI_DIR, { recursive: true });
4824
- writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
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
- writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
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 startLicenseRevalidation(intervalMs = 36e5) {
5095
- if (_revalTimer) return;
5096
- _revalTimer = setInterval(async () => {
5097
- try {
5098
- const license = await checkLicense();
5099
- if (!license.valid) {
5100
- process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
5101
- }
5102
- } catch {
5103
- }
5104
- }, intervalMs);
5105
- if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
5106
- _revalTimer.unref();
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 stopLicenseRevalidation() {
5110
- if (_revalTimer) {
5111
- clearInterval(_revalTimer);
5112
- _revalTimer = null;
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 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;
5116
- var init_license = __esm({
5117
- "src/lib/license.ts"() {
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
- LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
5121
- CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
5122
- DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
5123
- API_BASE = "https://askexe.com/cloud";
5124
- RETRY_DELAY_MS = 500;
5125
- LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
5126
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
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 existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
5161
- import { readdirSync as readdirSync2 } from "fs";
5162
- import path12 from "path";
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 (!existsSync11(IDENTITY_DIR)) {
5166
- mkdirSync4(IDENTITY_DIR, { recursive: true });
6270
+ if (!existsSync12(IDENTITY_DIR)) {
6271
+ mkdirSync6(IDENTITY_DIR, { recursive: true });
5167
6272
  }
5168
6273
  }
5169
6274
  function identityPath(agentId) {
5170
- return path12.join(IDENTITY_DIR, `${agentId}.md`);
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 (!existsSync11(filePath)) return null;
5212
- const raw = readFileSync7(filePath, "utf-8");
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
- writeFileSync3(filePath, content, "utf-8");
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 = readdirSync2(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
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 = path12.join(EXE_AI_DIR, "identity");
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 crypto3 from "crypto";
5823
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
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 path13 from "path";
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(readFileSync8(SETUP_STATE_PATH, "utf8"));
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
- mkdirSync5(path13.dirname(SETUP_STATE_PATH), { recursive: true });
5836
- writeFileSync4(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
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
- unlinkSync4(SETUP_STATE_PATH);
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("Already set up on another device?");
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
- const skipSetup = await ask(rl, "Fresh install? (Y/n): ");
5878
- if (skipSetup.toLowerCase() === "n") {
5879
- log("Run `exe-link` to import your encryption key from another device.");
5880
- rl.close();
5881
- return;
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 (existsSync12(LEGACY_LANCE_PATH)) {
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 = crypto3.randomBytes(32);
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 (!state.completedSteps.includes(2)) {
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 = path13.join(os5.homedir(), ".claude.json");
7282
+ const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
5992
7283
  let claudeJson = {};
5993
7284
  try {
5994
- claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
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
- writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
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 (!state.completedSteps.includes(6)) {
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
- mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
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
- writeFileSync4(cooIdPath, replaced, "utf-8");
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
- mkdirSync5(path13.dirname(ctoIdPath), { recursive: true });
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
- writeFileSync4(ctoIdPath, replaced, "utf-8");
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
- mkdirSync5(path13.dirname(cmoIdPath), { recursive: true });
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
- writeFileSync4(cmoIdPath, replaced, "utf-8");
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 terminal. Works with your Claude Code subscription.`);
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 = path13.join(os5.homedir(), ".exe-os", "setup-state.json");
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 execSync3 } from "child_process";
6249
- import { readFileSync as readFileSync9 } from "fs";
6250
- import path14 from "path";
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 = path14.join(packageRoot, "package.json");
6253
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
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 = execSync3("npm view @askexenow/exe-os version", {
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 execSync4 } from "child_process";
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
- execSync4("npm cache clean --force", { stdio: "pipe" });
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
- execSync4("npm install -g @askexenow/exe-os@latest", {
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 = execSync4("npm list -g @askexenow/exe-os --depth=0 2>/dev/null", { encoding: "utf8" });
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, constants, YGEnums_default;
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
- constants = {
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 = constants;
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 = (path34) => {
10777
- return path34?.replace(`file://${cwd()}/`, "");
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 readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync14 } from "fs";
12807
- import { execSync as execSync5 } from "child_process";
12808
- import path15 from "path";
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 = path15.dirname(REGISTRY_PATH);
12812
- if (!existsSync14(dir)) {
12813
- mkdirSync6(dir, { recursive: true });
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
- writeFileSync5(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
14151
+ writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
12823
14152
  }
12824
14153
  function listSessions() {
12825
14154
  try {
12826
- const raw = readFileSync11(REGISTRY_PATH, "utf8");
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 = execSync5("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
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
- writeFileSync5(REGISTRY_PATH, JSON.stringify(alive, null, 2));
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 = path15.join(os6.homedir(), ".exe-os", "session-registry.json");
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 execSync6 } from "child_process";
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 = execSync6(`ps -p ${pid} -o ppid=,comm=`, {
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 execSync7 } from "child_process";
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 = execSync7("claude --help 2>&1", {
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 readFileSync12, writeFileSync as writeFileSync6, renameSync as renameSync4, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
13061
- import path16 from "path";
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 = path16.dirname(QUEUE_PATH);
13065
- if (!existsSync15(dir)) mkdirSync7(dir, { recursive: true });
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 (!existsSync15(QUEUE_PATH)) return [];
13070
- return JSON.parse(readFileSync12(QUEUE_PATH, "utf8"));
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
- writeFileSync6(tmp, JSON.stringify(queue, null, 2));
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 = path16.join(os7.homedir(), ".exe-os", "intercom-queue.json");
14431
+ QUEUE_PATH = path18.join(os7.homedir(), ".exe-os", "intercom-queue.json");
13103
14432
  TTL_MS = 60 * 60 * 1e3;
13104
- INTERCOM_LOG = path16.join(os7.homedir(), ".exe-os", "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 readFileSync13, existsSync as existsSync16 } from "fs";
13110
- import path17 from "path";
14438
+ import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
14439
+ import path19 from "path";
13111
14440
  function getLicenseSync() {
13112
14441
  try {
13113
- if (!existsSync16(CACHE_PATH2)) return freeLicense();
13114
- const raw = JSON.parse(readFileSync13(CACHE_PATH2, "utf8"));
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 (existsSync16(filePath)) {
13153
- const raw = readFileSync13(filePath, "utf8");
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 = path17.join(EXE_AI_DIR, "license-cache.json");
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 crypto4 from "crypto";
13188
- import path18 from "path";
14516
+ import crypto6 from "crypto";
14517
+ import path20 from "path";
13189
14518
  import os8 from "os";
13190
14519
  import {
13191
- readFileSync as readFileSync14,
13192
- readdirSync as readdirSync3,
13193
- unlinkSync as unlinkSync5,
13194
- existsSync as existsSync17,
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 = crypto4.randomUUID();
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 crypto5 from "crypto";
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
- crypto5.randomUUID(),
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 crypto6 from "crypto";
13289
- import path19 from "path";
13290
- import { execSync as execSync8 } from "child_process";
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 existsSync18, readFileSync as readFileSync15 } from "fs";
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 = crypto6.randomUUID();
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(path19.join(input.baseDir, "exe", "output"), { recursive: true });
13425
- await mkdir6(path19.join(input.baseDir, "exe", "research"), { recursive: true });
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 = execSync8(
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 = execSync8(
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 = path19.join(baseDir, "exe", "ARCHITECTURE.md");
14974
+ const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
13646
14975
  try {
13647
- if (existsSync18(archPath)) return;
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 = path19.join(baseDir, ".gitignore");
15009
+ const gitignorePath = path21.join(baseDir, ".gitignore");
13681
15010
  try {
13682
- if (existsSync18(gitignorePath)) {
13683
- const content = readFileSync15(gitignorePath, "utf-8");
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 path20 from "path";
13705
- import { existsSync as existsSync19, readdirSync as readdirSync4, unlinkSync as unlinkSync6 } from "fs";
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 = path20.join(EXE_AI_DIR, "session-cache");
13887
- if (existsSync19(cacheDir)) {
13888
- for (const f of readdirSync4(cacheDir)) {
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
- unlinkSync6(path20.join(cacheDir, f));
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 path21 from "path";
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 = path21.join(baseDir, String(ur.task_file));
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 execSync9 } from "child_process";
13997
- import path22 from "path";
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 = execSync9("git rev-parse --path-format=absolute --git-common-dir", {
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 = path22.dirname(gitCommonDir);
15339
+ repoRoot = path24.dirname(gitCommonDir);
14011
15340
  } catch {
14012
- repoRoot = execSync9("git rev-parse --show-toplevel", {
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 = path22.basename(repoRoot);
15348
+ _cached2 = path24.basename(repoRoot);
14020
15349
  _cachedCwd = dir;
14021
15350
  return _cached2;
14022
15351
  } catch {
14023
- _cached2 = path22.basename(dir);
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 crypto7 from "crypto";
15494
+ import crypto9 from "crypto";
14166
15495
  async function storeBehavior(opts) {
14167
15496
  const client = getClient();
14168
- const id = crypto7.randomUUID();
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 crypto8 from "crypto";
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 crypto8.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
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 = crypto8.randomUUID();
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 path23 from "path";
14496
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
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 = path23.join(EXE_AI_DIR, "session-cache");
14516
- const cachePath = path23.join(cacheDir, `current-task-${agent}.json`);
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
- mkdirSync8(cacheDir, { recursive: true });
14519
- writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
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
- unlinkSync7(cachePath);
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 execSync10 } from "child_process";
14964
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, existsSync as existsSync20, appendFileSync } from "fs";
14965
- import path24 from "path";
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 unlinkSync8 } from "fs";
16297
+ import { unlinkSync as unlinkSync10 } from "fs";
14969
16298
  function spawnLockPath(sessionName) {
14970
- return path24.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
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 (!existsSync20(SPAWN_LOCK_DIR)) {
14982
- mkdirSync9(SPAWN_LOCK_DIR, { recursive: true });
16310
+ if (!existsSync22(SPAWN_LOCK_DIR)) {
16311
+ mkdirSync12(SPAWN_LOCK_DIR, { recursive: true });
14983
16312
  }
14984
16313
  const lockFile = spawnLockPath(sessionName);
14985
- if (existsSync20(lockFile)) {
16314
+ if (existsSync22(lockFile)) {
14986
16315
  try {
14987
- const lock = JSON.parse(readFileSync16(lockFile, "utf8"));
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
- writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
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
- unlinkSync8(spawnLockPath(sessionName));
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 = path24.join(
15008
- path24.dirname(thisFile),
16336
+ const scriptPath = path26.join(
16337
+ path26.dirname(thisFile),
15009
16338
  "..",
15010
16339
  "bin",
15011
16340
  "exe-export-behaviors.js"
15012
16341
  );
15013
- return existsSync20(scriptPath) ? scriptPath : null;
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 (!existsSync20(SESSION_CACHE)) {
15078
- mkdirSync9(SESSION_CACHE, { recursive: true });
16406
+ if (!existsSync22(SESSION_CACHE)) {
16407
+ mkdirSync12(SESSION_CACHE, { recursive: true });
15079
16408
  }
15080
16409
  const rootExe = extractRootExe(parentExe) ?? parentExe;
15081
- const filePath = path24.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
15082
- writeFileSync8(filePath, JSON.stringify({
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(readFileSync16(path24.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
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(readFileSync16(
15099
- path24.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
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 (!existsSync20(DEBOUNCE_FILE)) return {};
15161
- return JSON.parse(readFileSync16(DEBOUNCE_FILE, "utf8"));
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 (!existsSync20(SESSION_CACHE)) mkdirSync9(SESSION_CACHE, { recursive: true });
15169
- writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
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
- appendFileSync(INTERCOM_LOG2, line);
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 = path24.join(os9.homedir(), ".exe-os", "session-logs");
15359
- const logFile = path24.join(logDir, `${instanceLabel}-${Date.now()}.log`);
15360
- if (!existsSync20(logDir)) {
15361
- mkdirSync9(logDir, { recursive: true });
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 = path24.join(path24.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
15368
- if (existsSync20(cleanupScript)) {
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 = path24.join(os9.homedir(), ".claude.json");
16703
+ const claudeJsonPath = path26.join(os9.homedir(), ".claude.json");
15375
16704
  let claudeJson = {};
15376
16705
  try {
15377
- claudeJson = JSON.parse(readFileSync16(claudeJsonPath, "utf8"));
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
- writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
16714
+ writeFileSync11(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
15386
16715
  } catch {
15387
16716
  }
15388
16717
  try {
15389
- const settingsDir = path24.join(os9.homedir(), ".claude", "projects");
16718
+ const settingsDir = path26.join(os9.homedir(), ".claude", "projects");
15390
16719
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
15391
- const projSettingsDir = path24.join(settingsDir, normalizedKey);
15392
- const settingsPath = path24.join(projSettingsDir, "settings.json");
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(readFileSync16(settingsPath, "utf8"));
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
- mkdirSync9(projSettingsDir, { recursive: true });
15424
- writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
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 = path24.join(
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 (existsSync20(identityPath2)) {
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
- path24.basename(spawnCwd),
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 = path24.join(os9.homedir(), ".exe-os", "session-cache");
15468
- mkdirSync9(ctxDir, { recursive: true });
15469
- const ctxFile = path24.join(ctxDir, `session-context-${sessionName}.md`);
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
- writeFileSync8(ctxFile, ctxContent);
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 = path24.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
15515
- writeFileSync8(dispatchInfo, JSON.stringify({
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
- execSync10("sleep 0.5");
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 = path24.join(os9.homedir(), ".exe-os", "spawn-locks");
15579
- SESSION_CACHE = path24.join(os9.homedir(), ".exe-os", "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 = path24.join(os9.homedir(), ".exe-os", "intercom.log");
15585
- DEBOUNCE_FILE = path24.join(SESSION_CACHE, "intercom-debounce.json");
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 execSync11 } from "child_process";
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
- execSync11("tmux display-message -p '#{session_name}' 2>/dev/null", {
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 = execSync11(`ps -p ${pid} -o comm= 2>/dev/null`, {
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 = execSync11(`ps -p ${pid} -o ppid= 2>/dev/null`, {
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 = execSync11("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
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 = execSync11(
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 = execSync11(
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 = execSync11("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
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 = execSync11(
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: existsSync22 } = await import("fs");
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(existsSync22(pidPath) ? "running" : "stopped");
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: execSync13 } = await import("child_process");
16029
- const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
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 path25 from "path";
19370
+ import path27 from "path";
18042
19371
  import os10 from "os";
18043
19372
  function checkPathSafety(filePath) {
18044
- const resolved = path25.resolve(filePath);
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 = path25.resolve(filePath);
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(path25.join(HOME, ".claude")),
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(path25.join(HOME, ".exe-os")),
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 = path25.basename(p);
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 path26 from "path";
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 = path26.isAbsolute(input.file_path) ? input.file_path : path26.resolve(context.cwd, input.file_path);
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 path27 from "path";
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 = path27.join(current, entry.name);
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 ? path27.isAbsolute(input.path) ? input.path : path27.resolve(context.cwd, input.path) : context.cwd;
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(path27.relative(baseDir, e.path), input.pattern)
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 path28 from "path";
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 = path28.join(dir, entry.name);
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 ? path28.isAbsolute(input.path) ? input.path : path28.resolve(context.cwd, input.path) : context.cwd;
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 path29 from "path";
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 = path29.isAbsolute(input.file_path) ? input.file_path : path29.resolve(context.cwd, input.file_path);
18438
- const dir = path29.dirname(filePath);
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 path30 from "path";
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 = path30.isAbsolute(input.file_path) ? input.file_path : path30.resolve(context.cwd, input.file_path);
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 path31 from "path";
18740
- import { homedir as homedir3 } from "os";
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: readFileSync18, existsSync: existsSync22 } = await import("fs");
20103
+ const { readFileSync: readFileSync20, existsSync: existsSync24 } = await import("fs");
18775
20104
  const { join } = await import("path");
18776
- const { homedir: homedir5 } = await import("os");
18777
- const configPath = join(homedir5(), ".exe-os", "config.json");
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 (existsSync22(configPath)) {
20109
+ if (existsSync24(configPath)) {
18781
20110
  try {
18782
- const raw = JSON.parse(readFileSync18(configPath, "utf8"));
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(homedir5(), ".exe-os", "session-cache");
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(readFileSync18(join(markerDir, f), "utf8"));
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: path31.join(homedir3(), p.projectName),
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: existsSync22 } = await import("fs");
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 ? existsSync22(join(projectDir, ".git")) : false;
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: existsSync22(pidPath) ? "running" : "stopped" }));
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: execSync13 } = __require("child_process");
19354
- const output = execSync13(
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: execSync13 } = __require("child_process");
19379
- execSync13(
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: execSync13 } = __require("child_process");
19388
- execSync13(`tmux send-keys -t ${JSON.stringify(sessionName)} Enter`, { timeout: 2e3 });
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 path32 from "path";
19974
- import { homedir as homedir4 } from "os";
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: path32.join(homedir4(), p.projectName),
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: execSync13 } = await import("child_process");
21392
+ const { execSync: execSync14 } = await import("child_process");
20064
21393
  const dir = projectDir || process.cwd();
20065
- execSync13(`tmux new-session -d -s ${JSON.stringify(entry.sessionName)} -c ${JSON.stringify(dir)}`, { timeout: 5e3 });
20066
- execSync13(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "claude --dangerously-skip-permissions" Enter`, { timeout: 3e3 });
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
- execSync13(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "/exe" Enter`, { timeout: 3e3 });
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: execSync13 } = await import("child_process");
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 = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
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 crypto9 from "crypto";
22096
+ import crypto11 from "crypto";
20768
22097
  function deriveWsAuthToken(masterKey) {
20769
- return Buffer.from(crypto9.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
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(crypto9.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
20773
- return crypto9.createHash("sha256").update(raw).digest("hex").slice(0, 32);
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 crypto9.createHash("sha256").update(token).digest("hex");
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 (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
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, LOCALHOST_PATTERNS;
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
- LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
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: execSync13 } = await import("child_process");
21130
- const ps = execSync13("pgrep -f exe-gateway 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
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: existsSync22, readFileSync: readFileSync18 } = await import("fs");
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 (existsSync22(configPath)) {
21141
- const raw = JSON.parse(readFileSync18(configPath, "utf8"));
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 execSync12 } from "child_process";
22936
+ import { execSync as execSync13 } from "child_process";
21608
22937
  function getAgentStatus(agentId) {
21609
22938
  try {
21610
- const sessions = execSync12("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
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 = execSync12(`tmux capture-pane -t "${agentSession}" -p 2>/dev/null | tail -3`, {
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: existsSync22, readFileSync: readFileSync18 } = await import("fs");
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 (existsSync22(gatewayConfig)) {
21769
- const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
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, path34, method = "GET", body) {
21951
- const url = `${config.baseUrl}/api/v1${path34}`;
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} ${path34}: ${response.status} ${response.statusText}`);
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: execSync13 } = await import("child_process");
22587
- execSync13("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
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: existsSync22 } = await import("fs");
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 = existsSync22(join(home, ".exe-os", "master.key"));
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 = existsSync22(pidPath) ? "running" : "stopped";
23941
+ daemon = existsSync24(pidPath) ? "running" : "stopped";
22613
23942
  } catch {
22614
23943
  }
22615
23944
  let version = "unknown";
22616
23945
  try {
22617
- const { readFileSync: readFileSync18 } = await import("fs");
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(readFileSync18(pkgPath, "utf8"));
23950
+ const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
22622
23951
  version = pkg.version;
22623
23952
  } catch {
22624
23953
  try {
22625
- const { readFileSync: readFileSync18 } = await import("fs");
23954
+ const { readFileSync: readFileSync20 } = await import("fs");
22626
23955
  const { join: joinPath } = await import("path");
22627
- const pkg = JSON.parse(readFileSync18(joinPath(process.cwd(), "package.json"), "utf8"));
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 existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as readdirSync5, rmSync } from "fs";
23235
- import path33 from "path";
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 = path33.join(os11.homedir(), ".claude");
23300
- const settingsPath = path33.join(claudeDir, "settings.json");
23301
- const hasClaudeCode = existsSync21(settingsPath) && (() => {
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 = readFileSync17(settingsPath, "utf8");
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 = path33.join(os11.homedir(), ".claude");
23343
- const settingsPath = path33.join(claudeDir, "settings.json");
23344
- const claudeJsonPath = path33.join(os11.homedir(), ".claude.json");
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 (existsSync21(settingsPath)) {
24675
+ if (existsSync23(settingsPath)) {
23347
24676
  let settings;
23348
24677
  try {
23349
- settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
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 (existsSync21(claudeJsonPath)) {
24704
+ if (existsSync23(claudeJsonPath)) {
23376
24705
  let claudeJson;
23377
24706
  try {
23378
- claudeJson = JSON.parse(readFileSync17(claudeJsonPath, "utf8"));
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 = path33.join(claudeDir, "skills");
23398
- if (existsSync21(skillsDir)) {
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 = path33.join(homeDir, ".claude");
23416
- const settingsPath = path33.join(claudeDir, "settings.json");
23417
- const claudeJsonPath = path33.join(homeDir, ".claude.json");
23418
- const exeOsDir = path33.join(homeDir, ".exe-os");
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 (existsSync21(settingsPath)) {
24751
+ if (existsSync23(settingsPath)) {
23423
24752
  try {
23424
- settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
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
- writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
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 (existsSync21(claudeJsonPath)) {
23470
- const raw = readFileSync17(claudeJsonPath, "utf8");
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
- writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
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 = path33.join(claudeDir, "skills");
23500
- if (existsSync21(skillsDir)) {
24828
+ const skillsDir = path35.join(claudeDir, "skills");
24829
+ if (existsSync23(skillsDir)) {
23501
24830
  let skillCount = 0;
23502
24831
  try {
23503
- const entries = readdirSync5(skillsDir);
24832
+ const entries = readdirSync7(skillsDir);
23504
24833
  for (const entry of entries) {
23505
24834
  if (entry.startsWith("exe")) {
23506
- const fullPath = path33.join(skillsDir, entry);
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 = path33.join(claudeDir, "CLAUDE.md");
23519
- if (existsSync21(claudeMdPath)) {
23520
- const content = readFileSync17(claudeMdPath, "utf8");
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) writeFileSync9(claudeMdPath, cleaned);
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 = path33.join(claudeDir, "agents");
23533
- if (existsSync21(agentsDir)) {
24861
+ const agentsDir = path35.join(claudeDir, "agents");
24862
+ if (existsSync23(agentsDir)) {
23534
24863
  let agentCount = 0;
23535
24864
  try {
23536
- const entries = readdirSync5(agentsDir).filter((f) => f.endsWith(".md"));
24865
+ const entries = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
23537
24866
  let knownNames = /* @__PURE__ */ new Set();
23538
- const rosterPath = path33.join(exeOsDir, "exe-employees.json");
23539
- if (existsSync21(rosterPath)) {
24867
+ const rosterPath = path35.join(exeOsDir, "exe-employees.json");
24868
+ if (existsSync23(rosterPath)) {
23540
24869
  try {
23541
- const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
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(path33.join(agentsDir, entry), { force: true });
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 = path33.join(claudeDir, "projects");
23561
- if (existsSync21(projectsDir)) {
24889
+ const projectsDir = path35.join(claudeDir, "projects");
24890
+ if (existsSync23(projectsDir)) {
23562
24891
  let projectCount = 0;
23563
24892
  try {
23564
- const projects = readdirSync5(projectsDir);
24893
+ const projects = readdirSync7(projectsDir);
23565
24894
  for (const proj of projects) {
23566
- const projSettings = path33.join(projectsDir, proj, "settings.json");
23567
- if (!existsSync21(projSettings)) continue;
24895
+ const projSettings = path35.join(projectsDir, proj, "settings.json");
24896
+ if (!existsSync23(projSettings)) continue;
23568
24897
  try {
23569
- const pSettings = JSON.parse(readFileSync17(projSettings, "utf8"));
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
- writeFileSync9(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
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: execSync13 } = await import("child_process");
24922
+ const { execSync: execSync14 } = await import("child_process");
23594
24923
  const findExeBin3 = () => {
23595
24924
  try {
23596
- return execSync13(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
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 = path33.dirname(exeBinPath);
24932
+ const binDir = path35.dirname(exeBinPath);
23604
24933
  let symlinkCount = 0;
23605
- const rosterPath = path33.join(exeOsDir, "exe-employees.json");
23606
- if (existsSync21(rosterPath)) {
23607
- const roster = JSON.parse(readFileSync17(rosterPath, "utf8"));
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 = path33.join(binDir, `${emp.name}${suffix}`);
23612
- if (existsSync21(linkPath)) {
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 && existsSync21(exeOsDir)) {
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 = path33.resolve(
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 = path33.dirname(idPath);
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
  }