@askexenow/exe-os 0.8.85 → 0.8.87
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/cleanup-stale-review-tasks.js +57 -19
- package/dist/bin/cli.js +510 -340
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-boot.js +344 -346
- package/dist/bin/exe-dispatch.js +375 -250
- package/dist/bin/exe-forget.js +5 -1
- package/dist/bin/exe-gateway.js +260 -135
- package/dist/bin/exe-healthcheck.js +133 -1
- package/dist/bin/exe-heartbeat.js +72 -31
- package/dist/bin/exe-link.js +25 -2
- package/dist/bin/exe-new-employee.js +22 -0
- package/dist/bin/exe-pending-messages.js +55 -17
- package/dist/bin/exe-pending-reviews.js +57 -19
- package/dist/bin/exe-search.js +6 -2
- package/dist/bin/exe-session-cleanup.js +260 -135
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +57 -19
- package/dist/bin/git-sweep.js +391 -266
- package/dist/bin/install.js +22 -0
- package/dist/bin/scan-tasks.js +394 -269
- package/dist/bin/setup.js +50 -5
- package/dist/gateway/index.js +257 -132
- package/dist/hooks/bug-report-worker.js +242 -117
- package/dist/hooks/commit-complete.js +389 -264
- package/dist/hooks/error-recall.js +6 -2
- package/dist/hooks/ingest-worker.js +314 -193
- package/dist/hooks/post-compact.js +84 -46
- package/dist/hooks/pre-compact.js +272 -147
- package/dist/hooks/pre-tool-use.js +104 -66
- package/dist/hooks/prompt-submit.js +126 -66
- package/dist/hooks/session-end.js +277 -152
- package/dist/hooks/session-start.js +70 -28
- package/dist/hooks/stop.js +90 -52
- package/dist/hooks/subagent-stop.js +84 -46
- package/dist/hooks/summary-worker.js +175 -114
- package/dist/index.js +296 -171
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +25 -2
- package/dist/lib/exe-daemon.js +338 -213
- package/dist/lib/hybrid-search.js +7 -2
- package/dist/lib/messaging.js +95 -39
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/tasks.js +242 -117
- package/dist/lib/tmux-routing.js +314 -189
- package/dist/mcp/server.js +573 -274
- package/dist/mcp/tools/create-task.js +260 -135
- package/dist/mcp/tools/list-tasks.js +68 -30
- package/dist/mcp/tools/send-message.js +100 -44
- package/dist/mcp/tools/update-task.js +123 -67
- package/dist/runtime/index.js +276 -151
- package/dist/tui/App.js +479 -354
- package/package.json +1 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/tui/App.js
CHANGED
|
@@ -322,110 +322,6 @@ var init_provider_table = __esm({
|
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
|
|
325
|
-
// src/lib/intercom-queue.ts
|
|
326
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
327
|
-
import path2 from "path";
|
|
328
|
-
import os2 from "os";
|
|
329
|
-
function ensureDir() {
|
|
330
|
-
const dir = path2.dirname(QUEUE_PATH);
|
|
331
|
-
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
|
|
332
|
-
}
|
|
333
|
-
function readQueue() {
|
|
334
|
-
try {
|
|
335
|
-
if (!existsSync3(QUEUE_PATH)) return [];
|
|
336
|
-
return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
|
|
337
|
-
} catch {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function writeQueue(queue) {
|
|
342
|
-
ensureDir();
|
|
343
|
-
const tmp = `${QUEUE_PATH}.tmp`;
|
|
344
|
-
writeFileSync2(tmp, JSON.stringify(queue, null, 2));
|
|
345
|
-
renameSync(tmp, QUEUE_PATH);
|
|
346
|
-
}
|
|
347
|
-
function queueIntercom(targetSession, reason) {
|
|
348
|
-
const queue = readQueue();
|
|
349
|
-
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
350
|
-
if (existing) {
|
|
351
|
-
existing.attempts++;
|
|
352
|
-
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
353
|
-
existing.reason = reason;
|
|
354
|
-
} else {
|
|
355
|
-
queue.push({
|
|
356
|
-
targetSession,
|
|
357
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
358
|
-
attempts: 0,
|
|
359
|
-
reason
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
writeQueue(queue);
|
|
363
|
-
}
|
|
364
|
-
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
365
|
-
var init_intercom_queue = __esm({
|
|
366
|
-
"src/lib/intercom-queue.ts"() {
|
|
367
|
-
"use strict";
|
|
368
|
-
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
369
|
-
TTL_MS = 60 * 60 * 1e3;
|
|
370
|
-
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// src/lib/db-retry.ts
|
|
375
|
-
function isBusyError(err) {
|
|
376
|
-
if (err instanceof Error) {
|
|
377
|
-
const msg = err.message.toLowerCase();
|
|
378
|
-
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
function delay(ms) {
|
|
383
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
384
|
-
}
|
|
385
|
-
async function retryOnBusy(fn, label) {
|
|
386
|
-
let lastError;
|
|
387
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
388
|
-
try {
|
|
389
|
-
return await fn();
|
|
390
|
-
} catch (err) {
|
|
391
|
-
lastError = err;
|
|
392
|
-
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
393
|
-
throw err;
|
|
394
|
-
}
|
|
395
|
-
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
396
|
-
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
397
|
-
process.stderr.write(
|
|
398
|
-
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
399
|
-
`
|
|
400
|
-
);
|
|
401
|
-
await delay(backoff + jitter);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
throw lastError;
|
|
405
|
-
}
|
|
406
|
-
function wrapWithRetry(client) {
|
|
407
|
-
return new Proxy(client, {
|
|
408
|
-
get(target, prop, receiver) {
|
|
409
|
-
if (prop === "execute") {
|
|
410
|
-
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
411
|
-
}
|
|
412
|
-
if (prop === "batch") {
|
|
413
|
-
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
414
|
-
}
|
|
415
|
-
return Reflect.get(target, prop, receiver);
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
420
|
-
var init_db_retry = __esm({
|
|
421
|
-
"src/lib/db-retry.ts"() {
|
|
422
|
-
"use strict";
|
|
423
|
-
MAX_RETRIES = 3;
|
|
424
|
-
BASE_DELAY_MS = 200;
|
|
425
|
-
MAX_JITTER_MS = 300;
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
325
|
// src/lib/config.ts
|
|
430
326
|
var config_exports = {};
|
|
431
327
|
__export(config_exports, {
|
|
@@ -443,17 +339,17 @@ __export(config_exports, {
|
|
|
443
339
|
saveConfig: () => saveConfig
|
|
444
340
|
});
|
|
445
341
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
446
|
-
import { readFileSync as
|
|
447
|
-
import
|
|
448
|
-
import
|
|
342
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync } from "fs";
|
|
343
|
+
import path2 from "path";
|
|
344
|
+
import os2 from "os";
|
|
449
345
|
function resolveDataDir() {
|
|
450
346
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
451
347
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
452
|
-
const newDir =
|
|
453
|
-
const legacyDir =
|
|
454
|
-
if (!
|
|
348
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
349
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
350
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
455
351
|
try {
|
|
456
|
-
|
|
352
|
+
renameSync(legacyDir, newDir);
|
|
457
353
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
458
354
|
`);
|
|
459
355
|
} catch {
|
|
@@ -515,9 +411,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
515
411
|
async function loadConfig() {
|
|
516
412
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
517
413
|
await mkdir(dir, { recursive: true });
|
|
518
|
-
const configPath =
|
|
519
|
-
if (!
|
|
520
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
414
|
+
const configPath = path2.join(dir, "config.json");
|
|
415
|
+
if (!existsSync3(configPath)) {
|
|
416
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
521
417
|
}
|
|
522
418
|
const raw = await readFile(configPath, "utf-8");
|
|
523
419
|
try {
|
|
@@ -535,38 +431,38 @@ async function loadConfig() {
|
|
|
535
431
|
normalizeScalingRoadmap(migratedCfg);
|
|
536
432
|
normalizeSessionLifecycle(migratedCfg);
|
|
537
433
|
normalizeAutoUpdate(migratedCfg);
|
|
538
|
-
const config = { ...DEFAULT_CONFIG, dbPath:
|
|
434
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
539
435
|
if (config.dbPath.startsWith("~")) {
|
|
540
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
436
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
541
437
|
}
|
|
542
438
|
return config;
|
|
543
439
|
} catch {
|
|
544
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
440
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
545
441
|
}
|
|
546
442
|
}
|
|
547
443
|
function loadConfigSync() {
|
|
548
444
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
549
|
-
const configPath =
|
|
550
|
-
if (!
|
|
551
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
445
|
+
const configPath = path2.join(dir, "config.json");
|
|
446
|
+
if (!existsSync3(configPath)) {
|
|
447
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
552
448
|
}
|
|
553
449
|
try {
|
|
554
|
-
const raw =
|
|
450
|
+
const raw = readFileSync3(configPath, "utf-8");
|
|
555
451
|
let parsed = JSON.parse(raw);
|
|
556
452
|
parsed = migrateLegacyConfig(parsed);
|
|
557
453
|
const { config: migratedCfg } = migrateConfig(parsed);
|
|
558
454
|
normalizeScalingRoadmap(migratedCfg);
|
|
559
455
|
normalizeSessionLifecycle(migratedCfg);
|
|
560
456
|
normalizeAutoUpdate(migratedCfg);
|
|
561
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
457
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
562
458
|
} catch {
|
|
563
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
459
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
564
460
|
}
|
|
565
461
|
}
|
|
566
462
|
async function saveConfig(config) {
|
|
567
463
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
568
464
|
await mkdir(dir, { recursive: true });
|
|
569
|
-
const configPath =
|
|
465
|
+
const configPath = path2.join(dir, "config.json");
|
|
570
466
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
571
467
|
if (config.cloud?.apiKey) {
|
|
572
468
|
await chmod(configPath, 384);
|
|
@@ -591,10 +487,10 @@ var init_config = __esm({
|
|
|
591
487
|
"src/lib/config.ts"() {
|
|
592
488
|
"use strict";
|
|
593
489
|
EXE_AI_DIR = resolveDataDir();
|
|
594
|
-
DB_PATH =
|
|
595
|
-
MODELS_DIR =
|
|
596
|
-
CONFIG_PATH =
|
|
597
|
-
LEGACY_LANCE_PATH =
|
|
490
|
+
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
491
|
+
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
492
|
+
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
493
|
+
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
598
494
|
CURRENT_CONFIG_VERSION = 1;
|
|
599
495
|
DEFAULT_CONFIG = {
|
|
600
496
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -666,6 +562,161 @@ var init_config = __esm({
|
|
|
666
562
|
}
|
|
667
563
|
});
|
|
668
564
|
|
|
565
|
+
// src/lib/runtime-table.ts
|
|
566
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
567
|
+
var init_runtime_table = __esm({
|
|
568
|
+
"src/lib/runtime-table.ts"() {
|
|
569
|
+
"use strict";
|
|
570
|
+
RUNTIME_TABLE = {
|
|
571
|
+
codex: {
|
|
572
|
+
binary: "codex",
|
|
573
|
+
launchMode: "exec",
|
|
574
|
+
autoApproveFlag: "--full-auto",
|
|
575
|
+
inlineFlag: "--no-alt-screen",
|
|
576
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
577
|
+
defaultModel: "gpt-5.4"
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
DEFAULT_RUNTIME = "claude";
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// src/lib/agent-config.ts
|
|
585
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
586
|
+
import path3 from "path";
|
|
587
|
+
function loadAgentConfig() {
|
|
588
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
589
|
+
try {
|
|
590
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
591
|
+
} catch {
|
|
592
|
+
return {};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function getAgentRuntime(agentId) {
|
|
596
|
+
const config = loadAgentConfig();
|
|
597
|
+
const entry = config[agentId];
|
|
598
|
+
if (entry) return entry;
|
|
599
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
600
|
+
}
|
|
601
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
602
|
+
var init_agent_config = __esm({
|
|
603
|
+
"src/lib/agent-config.ts"() {
|
|
604
|
+
"use strict";
|
|
605
|
+
init_config();
|
|
606
|
+
init_runtime_table();
|
|
607
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
608
|
+
DEFAULT_MODELS = {
|
|
609
|
+
claude: "claude-opus-4",
|
|
610
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
611
|
+
opencode: "minimax-m2.7"
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// src/lib/intercom-queue.ts
|
|
617
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
618
|
+
import path4 from "path";
|
|
619
|
+
import os3 from "os";
|
|
620
|
+
function ensureDir() {
|
|
621
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
622
|
+
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
623
|
+
}
|
|
624
|
+
function readQueue() {
|
|
625
|
+
try {
|
|
626
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
627
|
+
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
628
|
+
} catch {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function writeQueue(queue) {
|
|
633
|
+
ensureDir();
|
|
634
|
+
const tmp = `${QUEUE_PATH}.tmp`;
|
|
635
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
636
|
+
renameSync2(tmp, QUEUE_PATH);
|
|
637
|
+
}
|
|
638
|
+
function queueIntercom(targetSession, reason) {
|
|
639
|
+
const queue = readQueue();
|
|
640
|
+
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
641
|
+
if (existing) {
|
|
642
|
+
existing.attempts++;
|
|
643
|
+
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
644
|
+
existing.reason = reason;
|
|
645
|
+
} else {
|
|
646
|
+
queue.push({
|
|
647
|
+
targetSession,
|
|
648
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
649
|
+
attempts: 0,
|
|
650
|
+
reason
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
writeQueue(queue);
|
|
654
|
+
}
|
|
655
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
656
|
+
var init_intercom_queue = __esm({
|
|
657
|
+
"src/lib/intercom-queue.ts"() {
|
|
658
|
+
"use strict";
|
|
659
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
660
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
661
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// src/lib/db-retry.ts
|
|
666
|
+
function isBusyError(err) {
|
|
667
|
+
if (err instanceof Error) {
|
|
668
|
+
const msg = err.message.toLowerCase();
|
|
669
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
function delay(ms) {
|
|
674
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
675
|
+
}
|
|
676
|
+
async function retryOnBusy(fn, label) {
|
|
677
|
+
let lastError;
|
|
678
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
679
|
+
try {
|
|
680
|
+
return await fn();
|
|
681
|
+
} catch (err) {
|
|
682
|
+
lastError = err;
|
|
683
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
684
|
+
throw err;
|
|
685
|
+
}
|
|
686
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
687
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
688
|
+
process.stderr.write(
|
|
689
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
690
|
+
`
|
|
691
|
+
);
|
|
692
|
+
await delay(backoff + jitter);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
throw lastError;
|
|
696
|
+
}
|
|
697
|
+
function wrapWithRetry(client) {
|
|
698
|
+
return new Proxy(client, {
|
|
699
|
+
get(target, prop, receiver) {
|
|
700
|
+
if (prop === "execute") {
|
|
701
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
702
|
+
}
|
|
703
|
+
if (prop === "batch") {
|
|
704
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
705
|
+
}
|
|
706
|
+
return Reflect.get(target, prop, receiver);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
711
|
+
var init_db_retry = __esm({
|
|
712
|
+
"src/lib/db-retry.ts"() {
|
|
713
|
+
"use strict";
|
|
714
|
+
MAX_RETRIES = 3;
|
|
715
|
+
BASE_DELAY_MS = 200;
|
|
716
|
+
MAX_JITTER_MS = 300;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
669
720
|
// src/lib/employees.ts
|
|
670
721
|
var employees_exports = {};
|
|
671
722
|
__export(employees_exports, {
|
|
@@ -693,9 +744,9 @@ __export(employees_exports, {
|
|
|
693
744
|
validateEmployeeName: () => validateEmployeeName
|
|
694
745
|
});
|
|
695
746
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
696
|
-
import { existsSync as
|
|
747
|
+
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
697
748
|
import { execSync as execSync4 } from "child_process";
|
|
698
|
-
import
|
|
749
|
+
import path5 from "path";
|
|
699
750
|
import os4 from "os";
|
|
700
751
|
function normalizeRole(role) {
|
|
701
752
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -732,7 +783,7 @@ function validateEmployeeName(name) {
|
|
|
732
783
|
return { valid: true };
|
|
733
784
|
}
|
|
734
785
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
735
|
-
if (!
|
|
786
|
+
if (!existsSync6(employeesPath)) {
|
|
736
787
|
return [];
|
|
737
788
|
}
|
|
738
789
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -743,13 +794,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
743
794
|
}
|
|
744
795
|
}
|
|
745
796
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
746
|
-
await mkdir2(
|
|
797
|
+
await mkdir2(path5.dirname(employeesPath), { recursive: true });
|
|
747
798
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
748
799
|
}
|
|
749
800
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
750
|
-
if (!
|
|
801
|
+
if (!existsSync6(employeesPath)) return [];
|
|
751
802
|
try {
|
|
752
|
-
return JSON.parse(
|
|
803
|
+
return JSON.parse(readFileSync6(employeesPath, "utf-8"));
|
|
753
804
|
} catch {
|
|
754
805
|
return [];
|
|
755
806
|
}
|
|
@@ -800,14 +851,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
800
851
|
emp.name = emp.name.toLowerCase();
|
|
801
852
|
changed = true;
|
|
802
853
|
try {
|
|
803
|
-
const identityDir =
|
|
804
|
-
const oldPath =
|
|
805
|
-
const newPath =
|
|
806
|
-
if (
|
|
854
|
+
const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
|
|
855
|
+
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
856
|
+
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
857
|
+
if (existsSync6(oldPath) && !existsSync6(newPath)) {
|
|
807
858
|
renameSync3(oldPath, newPath);
|
|
808
|
-
} else if (
|
|
809
|
-
const content =
|
|
810
|
-
|
|
859
|
+
} else if (existsSync6(oldPath) && oldPath !== newPath) {
|
|
860
|
+
const content = readFileSync6(oldPath, "utf-8");
|
|
861
|
+
writeFileSync4(newPath, content, "utf-8");
|
|
811
862
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
812
863
|
unlinkSync(oldPath);
|
|
813
864
|
}
|
|
@@ -837,7 +888,7 @@ function registerBinSymlinks(name) {
|
|
|
837
888
|
errors.push("Could not find 'exe-os' in PATH");
|
|
838
889
|
return { created, skipped, errors };
|
|
839
890
|
}
|
|
840
|
-
const binDir =
|
|
891
|
+
const binDir = path5.dirname(exeBinPath);
|
|
841
892
|
let target;
|
|
842
893
|
try {
|
|
843
894
|
target = readlinkSync(exeBinPath);
|
|
@@ -847,8 +898,8 @@ function registerBinSymlinks(name) {
|
|
|
847
898
|
}
|
|
848
899
|
for (const suffix of ["", "-opencode"]) {
|
|
849
900
|
const linkName = `${name}${suffix}`;
|
|
850
|
-
const linkPath =
|
|
851
|
-
if (
|
|
901
|
+
const linkPath = path5.join(binDir, linkName);
|
|
902
|
+
if (existsSync6(linkPath)) {
|
|
852
903
|
skipped.push(linkName);
|
|
853
904
|
continue;
|
|
854
905
|
}
|
|
@@ -866,7 +917,7 @@ var init_employees = __esm({
|
|
|
866
917
|
"src/lib/employees.ts"() {
|
|
867
918
|
"use strict";
|
|
868
919
|
init_config();
|
|
869
|
-
EMPLOYEES_PATH =
|
|
920
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
870
921
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
871
922
|
COORDINATOR_ROLE = "COO";
|
|
872
923
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
@@ -877,8 +928,8 @@ var init_employees = __esm({
|
|
|
877
928
|
import net from "net";
|
|
878
929
|
import { spawn } from "child_process";
|
|
879
930
|
import { randomUUID } from "crypto";
|
|
880
|
-
import { existsSync as
|
|
881
|
-
import
|
|
931
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
932
|
+
import path6 from "path";
|
|
882
933
|
import { fileURLToPath } from "url";
|
|
883
934
|
function handleData(chunk) {
|
|
884
935
|
_buffer += chunk.toString();
|
|
@@ -906,9 +957,9 @@ function handleData(chunk) {
|
|
|
906
957
|
}
|
|
907
958
|
}
|
|
908
959
|
function cleanupStaleFiles() {
|
|
909
|
-
if (
|
|
960
|
+
if (existsSync7(PID_PATH)) {
|
|
910
961
|
try {
|
|
911
|
-
const pid = parseInt(
|
|
962
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
912
963
|
if (pid > 0) {
|
|
913
964
|
try {
|
|
914
965
|
process.kill(pid, 0);
|
|
@@ -929,11 +980,11 @@ function cleanupStaleFiles() {
|
|
|
929
980
|
}
|
|
930
981
|
}
|
|
931
982
|
function findPackageRoot() {
|
|
932
|
-
let dir =
|
|
933
|
-
const { root } =
|
|
983
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
984
|
+
const { root } = path6.parse(dir);
|
|
934
985
|
while (dir !== root) {
|
|
935
|
-
if (
|
|
936
|
-
dir =
|
|
986
|
+
if (existsSync7(path6.join(dir, "package.json"))) return dir;
|
|
987
|
+
dir = path6.dirname(dir);
|
|
937
988
|
}
|
|
938
989
|
return null;
|
|
939
990
|
}
|
|
@@ -943,8 +994,8 @@ function spawnDaemon() {
|
|
|
943
994
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
944
995
|
return;
|
|
945
996
|
}
|
|
946
|
-
const daemonPath =
|
|
947
|
-
if (!
|
|
997
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
998
|
+
if (!existsSync7(daemonPath)) {
|
|
948
999
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
949
1000
|
`);
|
|
950
1001
|
return;
|
|
@@ -952,7 +1003,7 @@ function spawnDaemon() {
|
|
|
952
1003
|
const resolvedPath = daemonPath;
|
|
953
1004
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
954
1005
|
`);
|
|
955
|
-
const logPath =
|
|
1006
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
956
1007
|
let stderrFd = "ignore";
|
|
957
1008
|
try {
|
|
958
1009
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1097,9 +1148,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1097
1148
|
"src/lib/exe-daemon-client.ts"() {
|
|
1098
1149
|
"use strict";
|
|
1099
1150
|
init_config();
|
|
1100
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1101
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1102
|
-
SPAWN_LOCK_PATH =
|
|
1151
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1152
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1153
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1103
1154
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1104
1155
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1105
1156
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2340,9 +2391,9 @@ __export(license_exports, {
|
|
|
2340
2391
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
2341
2392
|
validateLicense: () => validateLicense
|
|
2342
2393
|
});
|
|
2343
|
-
import { readFileSync as
|
|
2394
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2344
2395
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2345
|
-
import
|
|
2396
|
+
import path7 from "path";
|
|
2346
2397
|
import { jwtVerify, importSPKI } from "jose";
|
|
2347
2398
|
async function fetchRetry(url, init) {
|
|
2348
2399
|
try {
|
|
@@ -2353,37 +2404,37 @@ async function fetchRetry(url, init) {
|
|
|
2353
2404
|
}
|
|
2354
2405
|
}
|
|
2355
2406
|
function loadDeviceId() {
|
|
2356
|
-
const deviceJsonPath =
|
|
2407
|
+
const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
|
|
2357
2408
|
try {
|
|
2358
|
-
if (
|
|
2359
|
-
const data = JSON.parse(
|
|
2409
|
+
if (existsSync8(deviceJsonPath)) {
|
|
2410
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
2360
2411
|
if (data.deviceId) return data.deviceId;
|
|
2361
2412
|
}
|
|
2362
2413
|
} catch {
|
|
2363
2414
|
}
|
|
2364
2415
|
try {
|
|
2365
|
-
if (
|
|
2366
|
-
const id2 =
|
|
2416
|
+
if (existsSync8(DEVICE_ID_PATH)) {
|
|
2417
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
2367
2418
|
if (id2) return id2;
|
|
2368
2419
|
}
|
|
2369
2420
|
} catch {
|
|
2370
2421
|
}
|
|
2371
2422
|
const id = randomUUID2();
|
|
2372
|
-
|
|
2373
|
-
|
|
2423
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
2424
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
2374
2425
|
return id;
|
|
2375
2426
|
}
|
|
2376
2427
|
function loadLicense() {
|
|
2377
2428
|
try {
|
|
2378
|
-
if (!
|
|
2379
|
-
return
|
|
2429
|
+
if (!existsSync8(LICENSE_PATH)) return null;
|
|
2430
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
2380
2431
|
} catch {
|
|
2381
2432
|
return null;
|
|
2382
2433
|
}
|
|
2383
2434
|
}
|
|
2384
2435
|
function saveLicense(apiKey) {
|
|
2385
|
-
|
|
2386
|
-
|
|
2436
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
2437
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2387
2438
|
}
|
|
2388
2439
|
async function verifyLicenseJwt(token) {
|
|
2389
2440
|
try {
|
|
@@ -2409,8 +2460,8 @@ async function verifyLicenseJwt(token) {
|
|
|
2409
2460
|
}
|
|
2410
2461
|
async function getCachedLicense() {
|
|
2411
2462
|
try {
|
|
2412
|
-
if (!
|
|
2413
|
-
const raw = JSON.parse(
|
|
2463
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
2464
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
2414
2465
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
2415
2466
|
return await verifyLicenseJwt(raw.token);
|
|
2416
2467
|
} catch {
|
|
@@ -2419,8 +2470,8 @@ async function getCachedLicense() {
|
|
|
2419
2470
|
}
|
|
2420
2471
|
function readCachedToken() {
|
|
2421
2472
|
try {
|
|
2422
|
-
if (!
|
|
2423
|
-
const raw = JSON.parse(
|
|
2473
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
2474
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
2424
2475
|
return typeof raw.token === "string" ? raw.token : null;
|
|
2425
2476
|
} catch {
|
|
2426
2477
|
return null;
|
|
@@ -2454,7 +2505,7 @@ function getRawCachedPlan() {
|
|
|
2454
2505
|
}
|
|
2455
2506
|
function cacheResponse(token) {
|
|
2456
2507
|
try {
|
|
2457
|
-
|
|
2508
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
2458
2509
|
} catch {
|
|
2459
2510
|
}
|
|
2460
2511
|
}
|
|
@@ -2518,9 +2569,9 @@ async function checkLicense() {
|
|
|
2518
2569
|
let key = loadLicense();
|
|
2519
2570
|
if (!key) {
|
|
2520
2571
|
try {
|
|
2521
|
-
const configPath =
|
|
2522
|
-
if (
|
|
2523
|
-
const raw = JSON.parse(
|
|
2572
|
+
const configPath = path7.join(EXE_AI_DIR, "config.json");
|
|
2573
|
+
if (existsSync8(configPath)) {
|
|
2574
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
2524
2575
|
const cloud = raw.cloud;
|
|
2525
2576
|
if (cloud?.apiKey) {
|
|
2526
2577
|
key = cloud.apiKey;
|
|
@@ -2679,9 +2730,9 @@ var init_license = __esm({
|
|
|
2679
2730
|
"src/lib/license.ts"() {
|
|
2680
2731
|
"use strict";
|
|
2681
2732
|
init_config();
|
|
2682
|
-
LICENSE_PATH =
|
|
2683
|
-
CACHE_PATH =
|
|
2684
|
-
DEVICE_ID_PATH =
|
|
2733
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2734
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2735
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2685
2736
|
API_BASE = "https://askexe.com/cloud";
|
|
2686
2737
|
RETRY_DELAY_MS = 500;
|
|
2687
2738
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -2711,12 +2762,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2711
2762
|
});
|
|
2712
2763
|
|
|
2713
2764
|
// src/lib/plan-limits.ts
|
|
2714
|
-
import { readFileSync as
|
|
2715
|
-
import
|
|
2765
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2766
|
+
import path8 from "path";
|
|
2716
2767
|
function getLicenseSync() {
|
|
2717
2768
|
try {
|
|
2718
|
-
if (!
|
|
2719
|
-
const raw = JSON.parse(
|
|
2769
|
+
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
2770
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
2720
2771
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2721
2772
|
const parts = raw.token.split(".");
|
|
2722
2773
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2754,8 +2805,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2754
2805
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2755
2806
|
let count = 0;
|
|
2756
2807
|
try {
|
|
2757
|
-
if (
|
|
2758
|
-
const raw =
|
|
2808
|
+
if (existsSync9(filePath)) {
|
|
2809
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
2759
2810
|
const employees = JSON.parse(raw);
|
|
2760
2811
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2761
2812
|
}
|
|
@@ -2784,19 +2835,19 @@ var init_plan_limits = __esm({
|
|
|
2784
2835
|
this.name = "PlanLimitError";
|
|
2785
2836
|
}
|
|
2786
2837
|
};
|
|
2787
|
-
CACHE_PATH2 =
|
|
2838
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2788
2839
|
}
|
|
2789
2840
|
});
|
|
2790
2841
|
|
|
2791
2842
|
// src/lib/notifications.ts
|
|
2792
2843
|
import crypto from "crypto";
|
|
2793
|
-
import
|
|
2844
|
+
import path9 from "path";
|
|
2794
2845
|
import os5 from "os";
|
|
2795
2846
|
import {
|
|
2796
|
-
readFileSync as
|
|
2847
|
+
readFileSync as readFileSync10,
|
|
2797
2848
|
readdirSync,
|
|
2798
2849
|
unlinkSync as unlinkSync3,
|
|
2799
|
-
existsSync as
|
|
2850
|
+
existsSync as existsSync10,
|
|
2800
2851
|
rmdirSync
|
|
2801
2852
|
} from "fs";
|
|
2802
2853
|
async function writeNotification(notification) {
|
|
@@ -2947,11 +2998,11 @@ __export(tasks_crud_exports, {
|
|
|
2947
2998
|
writeCheckpoint: () => writeCheckpoint
|
|
2948
2999
|
});
|
|
2949
3000
|
import crypto3 from "crypto";
|
|
2950
|
-
import
|
|
3001
|
+
import path10 from "path";
|
|
2951
3002
|
import os6 from "os";
|
|
2952
3003
|
import { execSync as execSync5 } from "child_process";
|
|
2953
3004
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2954
|
-
import { existsSync as
|
|
3005
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
2955
3006
|
async function writeCheckpoint(input) {
|
|
2956
3007
|
const client = getClient();
|
|
2957
3008
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3126,8 +3177,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3126
3177
|
}
|
|
3127
3178
|
if (input.baseDir) {
|
|
3128
3179
|
try {
|
|
3129
|
-
await mkdir3(
|
|
3130
|
-
await mkdir3(
|
|
3180
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3181
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3131
3182
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3132
3183
|
await ensureGitignoreExe(input.baseDir);
|
|
3133
3184
|
} catch {
|
|
@@ -3163,10 +3214,10 @@ ${laneWarning}` : laneWarning;
|
|
|
3163
3214
|
});
|
|
3164
3215
|
if (input.baseDir) {
|
|
3165
3216
|
try {
|
|
3166
|
-
const EXE_OS_DIR =
|
|
3167
|
-
const mdPath =
|
|
3168
|
-
const mdDir =
|
|
3169
|
-
if (!
|
|
3217
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
3218
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
3219
|
+
const mdDir = path10.dirname(mdPath);
|
|
3220
|
+
if (!existsSync11(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3170
3221
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3171
3222
|
const mdContent = `# ${input.title}
|
|
3172
3223
|
|
|
@@ -3191,7 +3242,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
3191
3242
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3192
3243
|
`;
|
|
3193
3244
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3194
|
-
} catch {
|
|
3245
|
+
} catch (err) {
|
|
3246
|
+
process.stderr.write(
|
|
3247
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3248
|
+
`
|
|
3249
|
+
);
|
|
3195
3250
|
}
|
|
3196
3251
|
}
|
|
3197
3252
|
return {
|
|
@@ -3451,9 +3506,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3451
3506
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3452
3507
|
}
|
|
3453
3508
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3454
|
-
const archPath =
|
|
3509
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3455
3510
|
try {
|
|
3456
|
-
if (
|
|
3511
|
+
if (existsSync11(archPath)) return;
|
|
3457
3512
|
const template = [
|
|
3458
3513
|
`# ${projectName} \u2014 System Architecture`,
|
|
3459
3514
|
"",
|
|
@@ -3486,10 +3541,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3486
3541
|
}
|
|
3487
3542
|
}
|
|
3488
3543
|
async function ensureGitignoreExe(baseDir) {
|
|
3489
|
-
const gitignorePath =
|
|
3544
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
3490
3545
|
try {
|
|
3491
|
-
if (
|
|
3492
|
-
const content =
|
|
3546
|
+
if (existsSync11(gitignorePath)) {
|
|
3547
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
3493
3548
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3494
3549
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3495
3550
|
} else {
|
|
@@ -3520,8 +3575,8 @@ var init_tasks_crud = __esm({
|
|
|
3520
3575
|
});
|
|
3521
3576
|
|
|
3522
3577
|
// src/lib/tasks-review.ts
|
|
3523
|
-
import
|
|
3524
|
-
import { existsSync as
|
|
3578
|
+
import path11 from "path";
|
|
3579
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3525
3580
|
async function countPendingReviews(sessionScope) {
|
|
3526
3581
|
const client = getClient();
|
|
3527
3582
|
if (sessionScope) {
|
|
@@ -3702,11 +3757,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3702
3757
|
);
|
|
3703
3758
|
}
|
|
3704
3759
|
try {
|
|
3705
|
-
const cacheDir =
|
|
3706
|
-
if (
|
|
3760
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3761
|
+
if (existsSync12(cacheDir)) {
|
|
3707
3762
|
for (const f of readdirSync2(cacheDir)) {
|
|
3708
3763
|
if (f.startsWith("review-notified-")) {
|
|
3709
|
-
unlinkSync4(
|
|
3764
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3710
3765
|
}
|
|
3711
3766
|
}
|
|
3712
3767
|
}
|
|
@@ -3727,7 +3782,7 @@ var init_tasks_review = __esm({
|
|
|
3727
3782
|
});
|
|
3728
3783
|
|
|
3729
3784
|
// src/lib/tasks-chain.ts
|
|
3730
|
-
import
|
|
3785
|
+
import path12 from "path";
|
|
3731
3786
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3732
3787
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3733
3788
|
const client = getClient();
|
|
@@ -3744,7 +3799,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3744
3799
|
});
|
|
3745
3800
|
for (const ur of unblockedRows.rows) {
|
|
3746
3801
|
try {
|
|
3747
|
-
const ubFile =
|
|
3802
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3748
3803
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3749
3804
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3750
3805
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3813,7 +3868,7 @@ var init_tasks_chain = __esm({
|
|
|
3813
3868
|
|
|
3814
3869
|
// src/lib/project-name.ts
|
|
3815
3870
|
import { execSync as execSync6 } from "child_process";
|
|
3816
|
-
import
|
|
3871
|
+
import path13 from "path";
|
|
3817
3872
|
function getProjectName(cwd2) {
|
|
3818
3873
|
const dir = cwd2 ?? process.cwd();
|
|
3819
3874
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3826,7 +3881,7 @@ function getProjectName(cwd2) {
|
|
|
3826
3881
|
timeout: 2e3,
|
|
3827
3882
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3828
3883
|
}).trim();
|
|
3829
|
-
repoRoot =
|
|
3884
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3830
3885
|
} catch {
|
|
3831
3886
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3832
3887
|
cwd: dir,
|
|
@@ -3835,11 +3890,11 @@ function getProjectName(cwd2) {
|
|
|
3835
3890
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3836
3891
|
}).trim();
|
|
3837
3892
|
}
|
|
3838
|
-
_cached2 =
|
|
3893
|
+
_cached2 = path13.basename(repoRoot);
|
|
3839
3894
|
_cachedCwd = dir;
|
|
3840
3895
|
return _cached2;
|
|
3841
3896
|
} catch {
|
|
3842
|
-
_cached2 =
|
|
3897
|
+
_cached2 = path13.basename(dir);
|
|
3843
3898
|
_cachedCwd = dir;
|
|
3844
3899
|
return _cached2;
|
|
3845
3900
|
}
|
|
@@ -4312,8 +4367,8 @@ __export(tasks_exports, {
|
|
|
4312
4367
|
updateTaskStatus: () => updateTaskStatus,
|
|
4313
4368
|
writeCheckpoint: () => writeCheckpoint
|
|
4314
4369
|
});
|
|
4315
|
-
import
|
|
4316
|
-
import { writeFileSync as
|
|
4370
|
+
import path14 from "path";
|
|
4371
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4317
4372
|
async function createTask(input) {
|
|
4318
4373
|
const result = await createTaskCore(input);
|
|
4319
4374
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4332,11 +4387,11 @@ async function updateTask(input) {
|
|
|
4332
4387
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4333
4388
|
try {
|
|
4334
4389
|
const agent = String(row.assigned_to);
|
|
4335
|
-
const cacheDir =
|
|
4336
|
-
const cachePath =
|
|
4390
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4391
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
4337
4392
|
if (input.status === "in_progress") {
|
|
4338
|
-
|
|
4339
|
-
|
|
4393
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
4394
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4340
4395
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
4341
4396
|
try {
|
|
4342
4397
|
unlinkSync5(cachePath);
|
|
@@ -4803,13 +4858,13 @@ __export(tmux_routing_exports, {
|
|
|
4803
4858
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4804
4859
|
});
|
|
4805
4860
|
import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
|
|
4806
|
-
import { readFileSync as
|
|
4807
|
-
import
|
|
4861
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync } from "fs";
|
|
4862
|
+
import path15 from "path";
|
|
4808
4863
|
import os7 from "os";
|
|
4809
4864
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4810
4865
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4811
4866
|
function spawnLockPath(sessionName) {
|
|
4812
|
-
return
|
|
4867
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4813
4868
|
}
|
|
4814
4869
|
function isProcessAlive(pid) {
|
|
4815
4870
|
try {
|
|
@@ -4820,13 +4875,13 @@ function isProcessAlive(pid) {
|
|
|
4820
4875
|
}
|
|
4821
4876
|
}
|
|
4822
4877
|
function acquireSpawnLock2(sessionName) {
|
|
4823
|
-
if (!
|
|
4824
|
-
|
|
4878
|
+
if (!existsSync13(SPAWN_LOCK_DIR)) {
|
|
4879
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4825
4880
|
}
|
|
4826
4881
|
const lockFile = spawnLockPath(sessionName);
|
|
4827
|
-
if (
|
|
4882
|
+
if (existsSync13(lockFile)) {
|
|
4828
4883
|
try {
|
|
4829
|
-
const lock = JSON.parse(
|
|
4884
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
4830
4885
|
const age = Date.now() - lock.timestamp;
|
|
4831
4886
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4832
4887
|
return false;
|
|
@@ -4834,7 +4889,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4834
4889
|
} catch {
|
|
4835
4890
|
}
|
|
4836
4891
|
}
|
|
4837
|
-
|
|
4892
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4838
4893
|
return true;
|
|
4839
4894
|
}
|
|
4840
4895
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4846,13 +4901,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4846
4901
|
function resolveBehaviorsExporterScript() {
|
|
4847
4902
|
try {
|
|
4848
4903
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4849
|
-
const scriptPath =
|
|
4850
|
-
|
|
4904
|
+
const scriptPath = path15.join(
|
|
4905
|
+
path15.dirname(thisFile),
|
|
4851
4906
|
"..",
|
|
4852
4907
|
"bin",
|
|
4853
4908
|
"exe-export-behaviors.js"
|
|
4854
4909
|
);
|
|
4855
|
-
return
|
|
4910
|
+
return existsSync13(scriptPath) ? scriptPath : null;
|
|
4856
4911
|
} catch {
|
|
4857
4912
|
return null;
|
|
4858
4913
|
}
|
|
@@ -4918,12 +4973,12 @@ function extractRootExe(name) {
|
|
|
4918
4973
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4919
4974
|
}
|
|
4920
4975
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4921
|
-
if (!
|
|
4922
|
-
|
|
4976
|
+
if (!existsSync13(SESSION_CACHE)) {
|
|
4977
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4923
4978
|
}
|
|
4924
4979
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4925
|
-
const filePath =
|
|
4926
|
-
|
|
4980
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4981
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4927
4982
|
parentExe: rootExe,
|
|
4928
4983
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4929
4984
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4931,7 +4986,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4931
4986
|
}
|
|
4932
4987
|
function getParentExe(sessionKey) {
|
|
4933
4988
|
try {
|
|
4934
|
-
const data = JSON.parse(
|
|
4989
|
+
const data = JSON.parse(readFileSync12(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4935
4990
|
return data.parentExe || null;
|
|
4936
4991
|
} catch {
|
|
4937
4992
|
return null;
|
|
@@ -4939,8 +4994,8 @@ function getParentExe(sessionKey) {
|
|
|
4939
4994
|
}
|
|
4940
4995
|
function getDispatchedBy(sessionKey) {
|
|
4941
4996
|
try {
|
|
4942
|
-
const data = JSON.parse(
|
|
4943
|
-
|
|
4997
|
+
const data = JSON.parse(readFileSync12(
|
|
4998
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4944
4999
|
"utf8"
|
|
4945
5000
|
));
|
|
4946
5001
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5001,32 +5056,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5001
5056
|
}
|
|
5002
5057
|
function readDebounceState() {
|
|
5003
5058
|
try {
|
|
5004
|
-
if (!
|
|
5005
|
-
|
|
5059
|
+
if (!existsSync13(DEBOUNCE_FILE)) return {};
|
|
5060
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
5061
|
+
const state = {};
|
|
5062
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5063
|
+
if (typeof val === "number") {
|
|
5064
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5065
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5066
|
+
state[key] = val;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
return state;
|
|
5006
5070
|
} catch {
|
|
5007
5071
|
return {};
|
|
5008
5072
|
}
|
|
5009
5073
|
}
|
|
5010
5074
|
function writeDebounceState(state) {
|
|
5011
5075
|
try {
|
|
5012
|
-
if (!
|
|
5013
|
-
|
|
5076
|
+
if (!existsSync13(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5077
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5014
5078
|
} catch {
|
|
5015
5079
|
}
|
|
5016
5080
|
}
|
|
5017
5081
|
function isDebounced(targetSession) {
|
|
5018
5082
|
const state = readDebounceState();
|
|
5019
|
-
const
|
|
5020
|
-
|
|
5083
|
+
const entry = state[targetSession];
|
|
5084
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5085
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5086
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5087
|
+
state[targetSession].pending++;
|
|
5088
|
+
writeDebounceState(state);
|
|
5089
|
+
return true;
|
|
5090
|
+
}
|
|
5091
|
+
return false;
|
|
5021
5092
|
}
|
|
5022
5093
|
function recordDebounce(targetSession) {
|
|
5023
5094
|
const state = readDebounceState();
|
|
5024
|
-
state[targetSession]
|
|
5095
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5096
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
5025
5097
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
5026
5098
|
for (const key of Object.keys(state)) {
|
|
5027
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5099
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
5028
5100
|
}
|
|
5029
5101
|
writeDebounceState(state);
|
|
5102
|
+
return batched;
|
|
5030
5103
|
}
|
|
5031
5104
|
function logIntercom(msg) {
|
|
5032
5105
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -5071,7 +5144,7 @@ function sendIntercom(targetSession) {
|
|
|
5071
5144
|
return "skipped_exe";
|
|
5072
5145
|
}
|
|
5073
5146
|
if (isDebounced(targetSession)) {
|
|
5074
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5147
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
5075
5148
|
return "debounced";
|
|
5076
5149
|
}
|
|
5077
5150
|
try {
|
|
@@ -5083,14 +5156,14 @@ function sendIntercom(targetSession) {
|
|
|
5083
5156
|
const sessionState = getSessionState(targetSession);
|
|
5084
5157
|
if (sessionState === "no_claude") {
|
|
5085
5158
|
queueIntercom(targetSession, "claude not running in session");
|
|
5086
|
-
recordDebounce(targetSession);
|
|
5087
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5159
|
+
const batched2 = recordDebounce(targetSession);
|
|
5160
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5088
5161
|
return "queued";
|
|
5089
5162
|
}
|
|
5090
5163
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
5091
5164
|
queueIntercom(targetSession, "session busy at send time");
|
|
5092
|
-
recordDebounce(targetSession);
|
|
5093
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5165
|
+
const batched2 = recordDebounce(targetSession);
|
|
5166
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5094
5167
|
return "queued";
|
|
5095
5168
|
}
|
|
5096
5169
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -5098,8 +5171,8 @@ function sendIntercom(targetSession) {
|
|
|
5098
5171
|
transport.sendKeys(targetSession, "q");
|
|
5099
5172
|
}
|
|
5100
5173
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
5101
|
-
recordDebounce(targetSession);
|
|
5102
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5174
|
+
const batched = recordDebounce(targetSession);
|
|
5175
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
5103
5176
|
return "delivered";
|
|
5104
5177
|
} catch {
|
|
5105
5178
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -5201,26 +5274,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5201
5274
|
const transport = getTransport();
|
|
5202
5275
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5203
5276
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5204
|
-
const logDir =
|
|
5205
|
-
const logFile =
|
|
5206
|
-
if (!
|
|
5207
|
-
|
|
5277
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
5278
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5279
|
+
if (!existsSync13(logDir)) {
|
|
5280
|
+
mkdirSync6(logDir, { recursive: true });
|
|
5208
5281
|
}
|
|
5209
5282
|
transport.kill(sessionName);
|
|
5210
5283
|
let cleanupSuffix = "";
|
|
5211
5284
|
try {
|
|
5212
5285
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5213
|
-
const cleanupScript =
|
|
5214
|
-
if (
|
|
5286
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5287
|
+
if (existsSync13(cleanupScript)) {
|
|
5215
5288
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5216
5289
|
}
|
|
5217
5290
|
} catch {
|
|
5218
5291
|
}
|
|
5219
5292
|
try {
|
|
5220
|
-
const claudeJsonPath =
|
|
5293
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
5221
5294
|
let claudeJson = {};
|
|
5222
5295
|
try {
|
|
5223
|
-
claudeJson = JSON.parse(
|
|
5296
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
5224
5297
|
} catch {
|
|
5225
5298
|
}
|
|
5226
5299
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5228,17 +5301,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5228
5301
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5229
5302
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5230
5303
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5231
|
-
|
|
5304
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5232
5305
|
} catch {
|
|
5233
5306
|
}
|
|
5234
5307
|
try {
|
|
5235
|
-
const settingsDir =
|
|
5308
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
5236
5309
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5237
|
-
const projSettingsDir =
|
|
5238
|
-
const settingsPath =
|
|
5310
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
5311
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
5239
5312
|
let settings = {};
|
|
5240
5313
|
try {
|
|
5241
|
-
settings = JSON.parse(
|
|
5314
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
5242
5315
|
} catch {
|
|
5243
5316
|
}
|
|
5244
5317
|
const perms = settings.permissions ?? {};
|
|
@@ -5266,20 +5339,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5266
5339
|
if (changed) {
|
|
5267
5340
|
perms.allow = allow;
|
|
5268
5341
|
settings.permissions = perms;
|
|
5269
|
-
|
|
5270
|
-
|
|
5342
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
5343
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5271
5344
|
}
|
|
5272
5345
|
} catch {
|
|
5273
5346
|
}
|
|
5274
5347
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
5275
5348
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
5276
|
-
const
|
|
5349
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
5350
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
5351
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
5352
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
5277
5353
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
5278
5354
|
let identityFlag = "";
|
|
5279
5355
|
let behaviorsFlag = "";
|
|
5280
5356
|
let legacyFallbackWarned = false;
|
|
5281
5357
|
if (!useExeAgent && !useBinSymlink) {
|
|
5282
|
-
const identityPath =
|
|
5358
|
+
const identityPath = path15.join(
|
|
5283
5359
|
os7.homedir(),
|
|
5284
5360
|
".exe-os",
|
|
5285
5361
|
"identity",
|
|
@@ -5289,13 +5365,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5289
5365
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5290
5366
|
if (hasAgentFlag) {
|
|
5291
5367
|
identityFlag = ` --agent ${employeeName}`;
|
|
5292
|
-
} else if (
|
|
5368
|
+
} else if (existsSync13(identityPath)) {
|
|
5293
5369
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5294
5370
|
legacyFallbackWarned = true;
|
|
5295
5371
|
}
|
|
5296
5372
|
const behaviorsFile = exportBehaviorsSync(
|
|
5297
5373
|
employeeName,
|
|
5298
|
-
|
|
5374
|
+
path15.basename(spawnCwd),
|
|
5299
5375
|
sessionName
|
|
5300
5376
|
);
|
|
5301
5377
|
if (behaviorsFile) {
|
|
@@ -5310,16 +5386,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5310
5386
|
}
|
|
5311
5387
|
let sessionContextFlag = "";
|
|
5312
5388
|
try {
|
|
5313
|
-
const ctxDir =
|
|
5314
|
-
|
|
5315
|
-
const ctxFile =
|
|
5389
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
5390
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
5391
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5316
5392
|
const ctxContent = [
|
|
5317
5393
|
`## Session Context`,
|
|
5318
5394
|
`You are running in tmux session: ${sessionName}.`,
|
|
5319
5395
|
`Your parent coordinator session is ${exeSession}.`,
|
|
5320
5396
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
5321
5397
|
].join("\n");
|
|
5322
|
-
|
|
5398
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
5323
5399
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5324
5400
|
} catch {
|
|
5325
5401
|
}
|
|
@@ -5333,9 +5409,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5333
5409
|
}
|
|
5334
5410
|
}
|
|
5335
5411
|
}
|
|
5412
|
+
if (useCodex) {
|
|
5413
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
5414
|
+
if (codexCfg?.apiKeyEnv) {
|
|
5415
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
5416
|
+
if (keyVal) {
|
|
5417
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
5421
|
+
}
|
|
5422
|
+
if (useOpencode) {
|
|
5423
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
5424
|
+
if (ocCfg?.apiKeyEnv) {
|
|
5425
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
5426
|
+
if (keyVal) {
|
|
5427
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5431
|
+
}
|
|
5432
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
5433
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
5434
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
5435
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5336
5438
|
let spawnCommand;
|
|
5337
5439
|
if (useExeAgent) {
|
|
5338
5440
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
5441
|
+
} else if (useCodex) {
|
|
5442
|
+
process.stderr.write(
|
|
5443
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
5444
|
+
`
|
|
5445
|
+
);
|
|
5446
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
5447
|
+
} else if (useOpencode) {
|
|
5448
|
+
const binName = `${employeeName}-opencode`;
|
|
5449
|
+
process.stderr.write(
|
|
5450
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
5451
|
+
`
|
|
5452
|
+
);
|
|
5453
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
5339
5454
|
} else if (useBinSymlink) {
|
|
5340
5455
|
const binName = `${employeeName}-${ccProvider}`;
|
|
5341
5456
|
process.stderr.write(
|
|
@@ -5357,11 +5472,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5357
5472
|
transport.pipeLog(sessionName, logFile);
|
|
5358
5473
|
try {
|
|
5359
5474
|
const mySession = getMySession();
|
|
5360
|
-
const dispatchInfo =
|
|
5361
|
-
|
|
5475
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5476
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5362
5477
|
dispatchedBy: mySession,
|
|
5363
5478
|
rootExe: exeSession,
|
|
5364
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
5479
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
5480
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
5481
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
5365
5482
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5366
5483
|
}));
|
|
5367
5484
|
} catch {
|
|
@@ -5379,6 +5496,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5379
5496
|
booted = true;
|
|
5380
5497
|
break;
|
|
5381
5498
|
}
|
|
5499
|
+
} else if (useCodex) {
|
|
5500
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
5501
|
+
booted = true;
|
|
5502
|
+
break;
|
|
5503
|
+
}
|
|
5382
5504
|
} else {
|
|
5383
5505
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
5384
5506
|
booted = true;
|
|
@@ -5390,9 +5512,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5390
5512
|
}
|
|
5391
5513
|
if (!booted) {
|
|
5392
5514
|
releaseSpawnLock2(sessionName);
|
|
5393
|
-
|
|
5515
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5516
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
5394
5517
|
}
|
|
5395
|
-
if (!useExeAgent) {
|
|
5518
|
+
if (!useExeAgent && !useCodex) {
|
|
5396
5519
|
try {
|
|
5397
5520
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
5398
5521
|
} catch {
|
|
@@ -5419,17 +5542,19 @@ var init_tmux_routing = __esm({
|
|
|
5419
5542
|
init_cc_agent_support();
|
|
5420
5543
|
init_mcp_prefix();
|
|
5421
5544
|
init_provider_table();
|
|
5545
|
+
init_agent_config();
|
|
5546
|
+
init_runtime_table();
|
|
5422
5547
|
init_intercom_queue();
|
|
5423
5548
|
init_plan_limits();
|
|
5424
5549
|
init_employees();
|
|
5425
|
-
SPAWN_LOCK_DIR =
|
|
5426
|
-
SESSION_CACHE =
|
|
5550
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
5551
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
5427
5552
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5428
5553
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5429
5554
|
VERIFY_PANE_LINES = 200;
|
|
5430
5555
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5431
|
-
INTERCOM_LOG2 =
|
|
5432
|
-
DEBOUNCE_FILE =
|
|
5556
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
5557
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5433
5558
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5434
5559
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
5435
5560
|
}
|
|
@@ -7298,10 +7423,10 @@ var init_hooks = __esm({
|
|
|
7298
7423
|
});
|
|
7299
7424
|
|
|
7300
7425
|
// src/runtime/safety-checks.ts
|
|
7301
|
-
import
|
|
7426
|
+
import path16 from "path";
|
|
7302
7427
|
import os8 from "os";
|
|
7303
7428
|
function checkPathSafety(filePath) {
|
|
7304
|
-
const resolved =
|
|
7429
|
+
const resolved = path16.resolve(filePath);
|
|
7305
7430
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
7306
7431
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
7307
7432
|
if (matches) {
|
|
@@ -7311,7 +7436,7 @@ function checkPathSafety(filePath) {
|
|
|
7311
7436
|
return { safe: true, bypassImmune: true };
|
|
7312
7437
|
}
|
|
7313
7438
|
function checkReadPathSafety(filePath) {
|
|
7314
|
-
const resolved =
|
|
7439
|
+
const resolved = path16.resolve(filePath);
|
|
7315
7440
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
7316
7441
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
7317
7442
|
);
|
|
@@ -7337,11 +7462,11 @@ var init_safety_checks = __esm({
|
|
|
7337
7462
|
reason: "Git config can set hooks and command execution"
|
|
7338
7463
|
},
|
|
7339
7464
|
{
|
|
7340
|
-
pattern: (p) => p.startsWith(
|
|
7465
|
+
pattern: (p) => p.startsWith(path16.join(HOME, ".claude")),
|
|
7341
7466
|
reason: "Claude configuration files are protected"
|
|
7342
7467
|
},
|
|
7343
7468
|
{
|
|
7344
|
-
pattern: (p) => p.startsWith(
|
|
7469
|
+
pattern: (p) => p.startsWith(path16.join(HOME, ".exe-os")),
|
|
7345
7470
|
reason: "exe-os configuration files are protected"
|
|
7346
7471
|
},
|
|
7347
7472
|
{
|
|
@@ -7358,7 +7483,7 @@ var init_safety_checks = __esm({
|
|
|
7358
7483
|
},
|
|
7359
7484
|
{
|
|
7360
7485
|
pattern: (p) => {
|
|
7361
|
-
const name =
|
|
7486
|
+
const name = path16.basename(p);
|
|
7362
7487
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
7363
7488
|
},
|
|
7364
7489
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -7385,7 +7510,7 @@ __export(file_read_exports, {
|
|
|
7385
7510
|
FileReadTool: () => FileReadTool
|
|
7386
7511
|
});
|
|
7387
7512
|
import fs3 from "fs/promises";
|
|
7388
|
-
import
|
|
7513
|
+
import path17 from "path";
|
|
7389
7514
|
import { z } from "zod";
|
|
7390
7515
|
function isBinary(buf) {
|
|
7391
7516
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -7421,7 +7546,7 @@ var init_file_read = __esm({
|
|
|
7421
7546
|
return { behavior: "allow" };
|
|
7422
7547
|
},
|
|
7423
7548
|
async call(input, context) {
|
|
7424
|
-
const filePath =
|
|
7549
|
+
const filePath = path17.isAbsolute(input.file_path) ? input.file_path : path17.resolve(context.cwd, input.file_path);
|
|
7425
7550
|
let stat;
|
|
7426
7551
|
try {
|
|
7427
7552
|
stat = await fs3.stat(filePath);
|
|
@@ -7461,7 +7586,7 @@ __export(glob_exports, {
|
|
|
7461
7586
|
GlobTool: () => GlobTool
|
|
7462
7587
|
});
|
|
7463
7588
|
import fs4 from "fs/promises";
|
|
7464
|
-
import
|
|
7589
|
+
import path18 from "path";
|
|
7465
7590
|
import { z as z2 } from "zod";
|
|
7466
7591
|
async function walkDir(dir, maxDepth = 10) {
|
|
7467
7592
|
const results = [];
|
|
@@ -7477,7 +7602,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
7477
7602
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
7478
7603
|
continue;
|
|
7479
7604
|
}
|
|
7480
|
-
const fullPath =
|
|
7605
|
+
const fullPath = path18.join(current, entry.name);
|
|
7481
7606
|
if (entry.isDirectory()) {
|
|
7482
7607
|
await walk(fullPath, depth + 1);
|
|
7483
7608
|
} else {
|
|
@@ -7511,11 +7636,11 @@ var init_glob = __esm({
|
|
|
7511
7636
|
inputSchema: inputSchema2,
|
|
7512
7637
|
isReadOnly: true,
|
|
7513
7638
|
async call(input, context) {
|
|
7514
|
-
const baseDir = input.path ?
|
|
7639
|
+
const baseDir = input.path ? path18.isAbsolute(input.path) ? input.path : path18.resolve(context.cwd, input.path) : context.cwd;
|
|
7515
7640
|
try {
|
|
7516
7641
|
const entries = await walkDir(baseDir);
|
|
7517
7642
|
const matched = entries.filter(
|
|
7518
|
-
(e) => simpleGlobMatch(
|
|
7643
|
+
(e) => simpleGlobMatch(path18.relative(baseDir, e.path), input.pattern)
|
|
7519
7644
|
);
|
|
7520
7645
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
7521
7646
|
if (matched.length === 0) {
|
|
@@ -7541,7 +7666,7 @@ __export(grep_exports, {
|
|
|
7541
7666
|
});
|
|
7542
7667
|
import { spawn as spawn2 } from "child_process";
|
|
7543
7668
|
import fs5 from "fs/promises";
|
|
7544
|
-
import
|
|
7669
|
+
import path19 from "path";
|
|
7545
7670
|
import { z as z3 } from "zod";
|
|
7546
7671
|
function runRipgrep(input, searchPath, context) {
|
|
7547
7672
|
return new Promise((resolve, reject) => {
|
|
@@ -7595,7 +7720,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
7595
7720
|
}
|
|
7596
7721
|
for (const entry of entries) {
|
|
7597
7722
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
7598
|
-
const fullPath =
|
|
7723
|
+
const fullPath = path19.join(dir, entry.name);
|
|
7599
7724
|
if (entry.isDirectory()) {
|
|
7600
7725
|
await walk(fullPath);
|
|
7601
7726
|
} else {
|
|
@@ -7641,7 +7766,7 @@ var init_grep = __esm({
|
|
|
7641
7766
|
inputSchema: inputSchema3,
|
|
7642
7767
|
isReadOnly: true,
|
|
7643
7768
|
async call(input, context) {
|
|
7644
|
-
const searchPath = input.path ?
|
|
7769
|
+
const searchPath = input.path ? path19.isAbsolute(input.path) ? input.path : path19.resolve(context.cwd, input.path) : context.cwd;
|
|
7645
7770
|
try {
|
|
7646
7771
|
const result = await runRipgrep(input, searchPath, context);
|
|
7647
7772
|
return result;
|
|
@@ -7666,7 +7791,7 @@ __export(file_write_exports, {
|
|
|
7666
7791
|
FileWriteTool: () => FileWriteTool
|
|
7667
7792
|
});
|
|
7668
7793
|
import fs6 from "fs/promises";
|
|
7669
|
-
import
|
|
7794
|
+
import path20 from "path";
|
|
7670
7795
|
import { z as z4 } from "zod";
|
|
7671
7796
|
var inputSchema4, FileWriteTool;
|
|
7672
7797
|
var init_file_write = __esm({
|
|
@@ -7694,8 +7819,8 @@ var init_file_write = __esm({
|
|
|
7694
7819
|
return { behavior: "allow" };
|
|
7695
7820
|
},
|
|
7696
7821
|
async call(input, context) {
|
|
7697
|
-
const filePath =
|
|
7698
|
-
const dir =
|
|
7822
|
+
const filePath = path20.isAbsolute(input.file_path) ? input.file_path : path20.resolve(context.cwd, input.file_path);
|
|
7823
|
+
const dir = path20.dirname(filePath);
|
|
7699
7824
|
await fs6.mkdir(dir, { recursive: true });
|
|
7700
7825
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
7701
7826
|
return {
|
|
@@ -7713,7 +7838,7 @@ __export(file_edit_exports, {
|
|
|
7713
7838
|
FileEditTool: () => FileEditTool
|
|
7714
7839
|
});
|
|
7715
7840
|
import fs7 from "fs/promises";
|
|
7716
|
-
import
|
|
7841
|
+
import path21 from "path";
|
|
7717
7842
|
import { z as z5 } from "zod";
|
|
7718
7843
|
function countOccurrences(haystack, needle) {
|
|
7719
7844
|
let count = 0;
|
|
@@ -7754,7 +7879,7 @@ var init_file_edit = __esm({
|
|
|
7754
7879
|
return { behavior: "allow" };
|
|
7755
7880
|
},
|
|
7756
7881
|
async call(input, context) {
|
|
7757
|
-
const filePath =
|
|
7882
|
+
const filePath = path21.isAbsolute(input.file_path) ? input.file_path : path21.resolve(context.cwd, input.file_path);
|
|
7758
7883
|
let content;
|
|
7759
7884
|
try {
|
|
7760
7885
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -8419,14 +8544,14 @@ __export(keychain_exports, {
|
|
|
8419
8544
|
setMasterKey: () => setMasterKey
|
|
8420
8545
|
});
|
|
8421
8546
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
8422
|
-
import { existsSync as
|
|
8423
|
-
import
|
|
8547
|
+
import { existsSync as existsSync14 } from "fs";
|
|
8548
|
+
import path24 from "path";
|
|
8424
8549
|
import os9 from "os";
|
|
8425
8550
|
function getKeyDir() {
|
|
8426
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
8551
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os9.homedir(), ".exe-os");
|
|
8427
8552
|
}
|
|
8428
8553
|
function getKeyPath() {
|
|
8429
|
-
return
|
|
8554
|
+
return path24.join(getKeyDir(), "master.key");
|
|
8430
8555
|
}
|
|
8431
8556
|
async function tryKeytar() {
|
|
8432
8557
|
try {
|
|
@@ -8447,7 +8572,7 @@ async function getMasterKey() {
|
|
|
8447
8572
|
}
|
|
8448
8573
|
}
|
|
8449
8574
|
const keyPath = getKeyPath();
|
|
8450
|
-
if (!
|
|
8575
|
+
if (!existsSync14(keyPath)) {
|
|
8451
8576
|
process.stderr.write(
|
|
8452
8577
|
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
8453
8578
|
`
|
|
@@ -8490,7 +8615,7 @@ async function deleteMasterKey() {
|
|
|
8490
8615
|
}
|
|
8491
8616
|
}
|
|
8492
8617
|
const keyPath = getKeyPath();
|
|
8493
|
-
if (
|
|
8618
|
+
if (existsSync14(keyPath)) {
|
|
8494
8619
|
await unlink(keyPath);
|
|
8495
8620
|
}
|
|
8496
8621
|
}
|
|
@@ -8790,8 +8915,8 @@ __export(wiki_client_exports, {
|
|
|
8790
8915
|
listDocuments: () => listDocuments,
|
|
8791
8916
|
listWorkspaces: () => listWorkspaces
|
|
8792
8917
|
});
|
|
8793
|
-
async function wikiFetch(config,
|
|
8794
|
-
const url = `${config.baseUrl}/api/v1${
|
|
8918
|
+
async function wikiFetch(config, path26, method = "GET", body) {
|
|
8919
|
+
const url = `${config.baseUrl}/api/v1${path26}`;
|
|
8795
8920
|
const headers = {
|
|
8796
8921
|
Authorization: `Bearer ${config.apiKey}`,
|
|
8797
8922
|
"Content-Type": "application/json"
|
|
@@ -8824,7 +8949,7 @@ async function wikiFetch(config, path25, method = "GET", body) {
|
|
|
8824
8949
|
}
|
|
8825
8950
|
}
|
|
8826
8951
|
if (!response.ok) {
|
|
8827
|
-
throw new Error(`Wiki API ${method} ${
|
|
8952
|
+
throw new Error(`Wiki API ${method} ${path26}: ${response.status} ${response.statusText}`);
|
|
8828
8953
|
}
|
|
8829
8954
|
return response.json();
|
|
8830
8955
|
} finally {
|
|
@@ -8945,13 +9070,13 @@ __export(shard_manager_exports, {
|
|
|
8945
9070
|
listShards: () => listShards,
|
|
8946
9071
|
shardExists: () => shardExists
|
|
8947
9072
|
});
|
|
8948
|
-
import
|
|
8949
|
-
import { existsSync as
|
|
9073
|
+
import path25 from "path";
|
|
9074
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
8950
9075
|
import { createClient as createClient2 } from "@libsql/client";
|
|
8951
9076
|
function initShardManager(encryptionKey) {
|
|
8952
9077
|
_encryptionKey = encryptionKey;
|
|
8953
|
-
if (!
|
|
8954
|
-
|
|
9078
|
+
if (!existsSync15(SHARDS_DIR)) {
|
|
9079
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
8955
9080
|
}
|
|
8956
9081
|
_shardingEnabled = true;
|
|
8957
9082
|
}
|
|
@@ -8971,7 +9096,7 @@ function getShardClient(projectName) {
|
|
|
8971
9096
|
}
|
|
8972
9097
|
const cached = _shards.get(safeName);
|
|
8973
9098
|
if (cached) return cached;
|
|
8974
|
-
const dbPath =
|
|
9099
|
+
const dbPath = path25.join(SHARDS_DIR, `${safeName}.db`);
|
|
8975
9100
|
const client = createClient2({
|
|
8976
9101
|
url: `file:${dbPath}`,
|
|
8977
9102
|
encryptionKey: _encryptionKey
|
|
@@ -8981,10 +9106,10 @@ function getShardClient(projectName) {
|
|
|
8981
9106
|
}
|
|
8982
9107
|
function shardExists(projectName) {
|
|
8983
9108
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
8984
|
-
return
|
|
9109
|
+
return existsSync15(path25.join(SHARDS_DIR, `${safeName}.db`));
|
|
8985
9110
|
}
|
|
8986
9111
|
function listShards() {
|
|
8987
|
-
if (!
|
|
9112
|
+
if (!existsSync15(SHARDS_DIR)) return [];
|
|
8988
9113
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
8989
9114
|
}
|
|
8990
9115
|
async function ensureShardSchema(client) {
|
|
@@ -9170,7 +9295,7 @@ var init_shard_manager = __esm({
|
|
|
9170
9295
|
"src/lib/shard-manager.ts"() {
|
|
9171
9296
|
"use strict";
|
|
9172
9297
|
init_config();
|
|
9173
|
-
SHARDS_DIR =
|
|
9298
|
+
SHARDS_DIR = path25.join(EXE_AI_DIR, "shards");
|
|
9174
9299
|
_shards = /* @__PURE__ */ new Map();
|
|
9175
9300
|
_encryptionKey = null;
|
|
9176
9301
|
_shardingEnabled = false;
|
|
@@ -13740,8 +13865,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
13740
13865
|
}
|
|
13741
13866
|
|
|
13742
13867
|
// src/tui/ink/components/ErrorOverview.js
|
|
13743
|
-
var cleanupPath = (
|
|
13744
|
-
return
|
|
13868
|
+
var cleanupPath = (path26) => {
|
|
13869
|
+
return path26?.replace(`file://${cwd()}/`, "");
|
|
13745
13870
|
};
|
|
13746
13871
|
var stackUtils = new StackUtils({
|
|
13747
13872
|
cwd: cwd(),
|
|
@@ -15673,11 +15798,11 @@ function Footer() {
|
|
|
15673
15798
|
} catch {
|
|
15674
15799
|
}
|
|
15675
15800
|
try {
|
|
15676
|
-
const { existsSync:
|
|
15801
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
15677
15802
|
const { join } = await import("path");
|
|
15678
15803
|
const home = process.env.HOME ?? "";
|
|
15679
15804
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
15680
|
-
setDaemon(
|
|
15805
|
+
setDaemon(existsSync16(pidPath) ? "running" : "stopped");
|
|
15681
15806
|
} catch {
|
|
15682
15807
|
setDaemon("unknown");
|
|
15683
15808
|
}
|
|
@@ -15759,7 +15884,7 @@ function Footer() {
|
|
|
15759
15884
|
// src/tui/views/CommandCenter.tsx
|
|
15760
15885
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
15761
15886
|
import TextInput from "ink-text-input";
|
|
15762
|
-
import
|
|
15887
|
+
import path22 from "path";
|
|
15763
15888
|
import { homedir } from "os";
|
|
15764
15889
|
|
|
15765
15890
|
// src/tui/components/StatusDot.tsx
|
|
@@ -16546,15 +16671,15 @@ function CommandCenterView({
|
|
|
16546
16671
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
16547
16672
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
16548
16673
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
16549
|
-
const { readFileSync:
|
|
16674
|
+
const { readFileSync: readFileSync13, existsSync: existsSync16 } = await import("fs");
|
|
16550
16675
|
const { join } = await import("path");
|
|
16551
16676
|
const { homedir: homedir3 } = await import("os");
|
|
16552
16677
|
const configPath = join(homedir3(), ".exe-os", "config.json");
|
|
16553
16678
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
16554
16679
|
let providerConfigs = {};
|
|
16555
|
-
if (
|
|
16680
|
+
if (existsSync16(configPath)) {
|
|
16556
16681
|
try {
|
|
16557
|
-
const raw = JSON.parse(
|
|
16682
|
+
const raw = JSON.parse(readFileSync13(configPath, "utf8"));
|
|
16558
16683
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
16559
16684
|
if (raw.providers && typeof raw.providers === "object") {
|
|
16560
16685
|
providerConfigs = raw.providers;
|
|
@@ -16615,7 +16740,7 @@ function CommandCenterView({
|
|
|
16615
16740
|
const markerDir = join(homedir3(), ".exe-os", "session-cache");
|
|
16616
16741
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
16617
16742
|
for (const f of agentFiles) {
|
|
16618
|
-
const data = JSON.parse(
|
|
16743
|
+
const data = JSON.parse(readFileSync13(join(markerDir, f), "utf8"));
|
|
16619
16744
|
if (data.agentRole) {
|
|
16620
16745
|
agentRole = data.agentRole;
|
|
16621
16746
|
break;
|
|
@@ -16760,7 +16885,7 @@ function CommandCenterView({
|
|
|
16760
16885
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
16761
16886
|
projectName: p.projectName,
|
|
16762
16887
|
exeSession: p.exeSession,
|
|
16763
|
-
projectDir:
|
|
16888
|
+
projectDir: path22.join(homedir(), p.projectName),
|
|
16764
16889
|
employeeCount: p.employees.length,
|
|
16765
16890
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
16766
16891
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -16798,7 +16923,7 @@ function CommandCenterView({
|
|
|
16798
16923
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
16799
16924
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
16800
16925
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
16801
|
-
const { existsSync:
|
|
16926
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
16802
16927
|
const { join } = await import("path");
|
|
16803
16928
|
const client = getClient2();
|
|
16804
16929
|
if (!client) {
|
|
@@ -16869,7 +16994,7 @@ function CommandCenterView({
|
|
|
16869
16994
|
}
|
|
16870
16995
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
16871
16996
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
16872
|
-
const hasGit = projectDir ?
|
|
16997
|
+
const hasGit = projectDir ? existsSync16(join(projectDir, ".git")) : false;
|
|
16873
16998
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
16874
16999
|
projectList.push({
|
|
16875
17000
|
projectName: name,
|
|
@@ -16894,7 +17019,7 @@ function CommandCenterView({
|
|
|
16894
17019
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
16895
17020
|
try {
|
|
16896
17021
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
16897
|
-
setHealth((h) => ({ ...h, daemon:
|
|
17022
|
+
setHealth((h) => ({ ...h, daemon: existsSync16(pidPath) ? "running" : "stopped" }));
|
|
16898
17023
|
} catch {
|
|
16899
17024
|
}
|
|
16900
17025
|
const activityResult = await client.execute(
|
|
@@ -17109,7 +17234,7 @@ function ChatMessageRow({ msg }) {
|
|
|
17109
17234
|
|
|
17110
17235
|
// src/tui/views/Sessions.tsx
|
|
17111
17236
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
17112
|
-
import
|
|
17237
|
+
import path23 from "path";
|
|
17113
17238
|
import { homedir as homedir2 } from "os";
|
|
17114
17239
|
|
|
17115
17240
|
// src/tui/components/TmuxPane.tsx
|
|
@@ -17412,7 +17537,7 @@ function SessionsView({
|
|
|
17412
17537
|
if (demo) {
|
|
17413
17538
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
17414
17539
|
...p,
|
|
17415
|
-
projectDir:
|
|
17540
|
+
projectDir: path23.join(homedir2(), p.projectName),
|
|
17416
17541
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
17417
17542
|
})));
|
|
17418
17543
|
return;
|
|
@@ -18309,12 +18434,12 @@ async function loadGatewayConfig() {
|
|
|
18309
18434
|
state.running = false;
|
|
18310
18435
|
}
|
|
18311
18436
|
try {
|
|
18312
|
-
const { existsSync:
|
|
18437
|
+
const { existsSync: existsSync16, readFileSync: readFileSync13 } = await import("fs");
|
|
18313
18438
|
const { join } = await import("path");
|
|
18314
18439
|
const home = process.env.HOME ?? "";
|
|
18315
18440
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
18316
|
-
if (
|
|
18317
|
-
const raw = JSON.parse(
|
|
18441
|
+
if (existsSync16(configPath)) {
|
|
18442
|
+
const raw = JSON.parse(readFileSync13(configPath, "utf8"));
|
|
18318
18443
|
state.port = raw.port ?? 3100;
|
|
18319
18444
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
18320
18445
|
if (raw.adapters) {
|
|
@@ -18840,12 +18965,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
18840
18965
|
setMembers(teamData);
|
|
18841
18966
|
setDbError(null);
|
|
18842
18967
|
try {
|
|
18843
|
-
const { existsSync:
|
|
18968
|
+
const { existsSync: existsSync16, readFileSync: readFileSync13 } = await import("fs");
|
|
18844
18969
|
const { join } = await import("path");
|
|
18845
18970
|
const home = process.env.HOME ?? "";
|
|
18846
18971
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
18847
|
-
if (
|
|
18848
|
-
const raw = JSON.parse(
|
|
18972
|
+
if (existsSync16(gatewayConfig)) {
|
|
18973
|
+
const raw = JSON.parse(readFileSync13(gatewayConfig, "utf8"));
|
|
18849
18974
|
if (raw.agents && raw.agents.length > 0) {
|
|
18850
18975
|
setExternals(raw.agents.map((a) => ({
|
|
18851
18976
|
name: a.name,
|
|
@@ -19509,12 +19634,12 @@ function SettingsView({ onBack }) {
|
|
|
19509
19634
|
}
|
|
19510
19635
|
setProviders(providerList);
|
|
19511
19636
|
try {
|
|
19512
|
-
const { existsSync:
|
|
19637
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
19513
19638
|
const { join } = await import("path");
|
|
19514
19639
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
19515
19640
|
const cfg = await loadConfig2();
|
|
19516
19641
|
const home = process.env.HOME ?? "";
|
|
19517
|
-
const hasKey =
|
|
19642
|
+
const hasKey = existsSync16(join(home, ".exe-os", "master.key"));
|
|
19518
19643
|
if (cfg.cloud) {
|
|
19519
19644
|
setCloud({
|
|
19520
19645
|
configured: true,
|
|
@@ -19527,22 +19652,22 @@ function SettingsView({ onBack }) {
|
|
|
19527
19652
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
19528
19653
|
let daemon = "unknown";
|
|
19529
19654
|
try {
|
|
19530
|
-
daemon =
|
|
19655
|
+
daemon = existsSync16(pidPath) ? "running" : "stopped";
|
|
19531
19656
|
} catch {
|
|
19532
19657
|
}
|
|
19533
19658
|
let version = "unknown";
|
|
19534
19659
|
try {
|
|
19535
|
-
const { readFileSync:
|
|
19660
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
19536
19661
|
const { createRequire } = await import("module");
|
|
19537
19662
|
const require2 = createRequire(import.meta.url);
|
|
19538
19663
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
19539
|
-
const pkg = JSON.parse(
|
|
19664
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
19540
19665
|
version = pkg.version;
|
|
19541
19666
|
} catch {
|
|
19542
19667
|
try {
|
|
19543
|
-
const { readFileSync:
|
|
19668
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
19544
19669
|
const { join: joinPath } = await import("path");
|
|
19545
|
-
const pkg = JSON.parse(
|
|
19670
|
+
const pkg = JSON.parse(readFileSync13(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
19546
19671
|
version = pkg.version;
|
|
19547
19672
|
} catch {
|
|
19548
19673
|
}
|