@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/bin/scan-tasks.js
CHANGED
|
@@ -269,123 +269,19 @@ var init_provider_table = __esm({
|
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
// src/lib/intercom-queue.ts
|
|
273
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
274
|
-
import path2 from "path";
|
|
275
|
-
import os2 from "os";
|
|
276
|
-
function ensureDir() {
|
|
277
|
-
const dir = path2.dirname(QUEUE_PATH);
|
|
278
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
279
|
-
}
|
|
280
|
-
function readQueue() {
|
|
281
|
-
try {
|
|
282
|
-
if (!existsSync2(QUEUE_PATH)) return [];
|
|
283
|
-
return JSON.parse(readFileSync2(QUEUE_PATH, "utf8"));
|
|
284
|
-
} catch {
|
|
285
|
-
return [];
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function writeQueue(queue) {
|
|
289
|
-
ensureDir();
|
|
290
|
-
const tmp = `${QUEUE_PATH}.tmp`;
|
|
291
|
-
writeFileSync2(tmp, JSON.stringify(queue, null, 2));
|
|
292
|
-
renameSync(tmp, QUEUE_PATH);
|
|
293
|
-
}
|
|
294
|
-
function queueIntercom(targetSession, reason) {
|
|
295
|
-
const queue = readQueue();
|
|
296
|
-
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
297
|
-
if (existing) {
|
|
298
|
-
existing.attempts++;
|
|
299
|
-
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
300
|
-
existing.reason = reason;
|
|
301
|
-
} else {
|
|
302
|
-
queue.push({
|
|
303
|
-
targetSession,
|
|
304
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
305
|
-
attempts: 0,
|
|
306
|
-
reason
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
writeQueue(queue);
|
|
310
|
-
}
|
|
311
|
-
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
312
|
-
var init_intercom_queue = __esm({
|
|
313
|
-
"src/lib/intercom-queue.ts"() {
|
|
314
|
-
"use strict";
|
|
315
|
-
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
316
|
-
TTL_MS = 60 * 60 * 1e3;
|
|
317
|
-
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// src/lib/db-retry.ts
|
|
322
|
-
function isBusyError(err) {
|
|
323
|
-
if (err instanceof Error) {
|
|
324
|
-
const msg = err.message.toLowerCase();
|
|
325
|
-
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
326
|
-
}
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
function delay(ms) {
|
|
330
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
331
|
-
}
|
|
332
|
-
async function retryOnBusy(fn, label) {
|
|
333
|
-
let lastError;
|
|
334
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
335
|
-
try {
|
|
336
|
-
return await fn();
|
|
337
|
-
} catch (err) {
|
|
338
|
-
lastError = err;
|
|
339
|
-
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
340
|
-
throw err;
|
|
341
|
-
}
|
|
342
|
-
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
343
|
-
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
344
|
-
process.stderr.write(
|
|
345
|
-
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
346
|
-
`
|
|
347
|
-
);
|
|
348
|
-
await delay(backoff + jitter);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
throw lastError;
|
|
352
|
-
}
|
|
353
|
-
function wrapWithRetry(client) {
|
|
354
|
-
return new Proxy(client, {
|
|
355
|
-
get(target, prop, receiver) {
|
|
356
|
-
if (prop === "execute") {
|
|
357
|
-
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
358
|
-
}
|
|
359
|
-
if (prop === "batch") {
|
|
360
|
-
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
361
|
-
}
|
|
362
|
-
return Reflect.get(target, prop, receiver);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
367
|
-
var init_db_retry = __esm({
|
|
368
|
-
"src/lib/db-retry.ts"() {
|
|
369
|
-
"use strict";
|
|
370
|
-
MAX_RETRIES = 3;
|
|
371
|
-
BASE_DELAY_MS = 200;
|
|
372
|
-
MAX_JITTER_MS = 300;
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
|
|
376
272
|
// src/lib/config.ts
|
|
377
273
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
378
|
-
import { readFileSync as
|
|
379
|
-
import
|
|
380
|
-
import
|
|
274
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, renameSync } from "fs";
|
|
275
|
+
import path2 from "path";
|
|
276
|
+
import os2 from "os";
|
|
381
277
|
function resolveDataDir() {
|
|
382
278
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
383
279
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
384
|
-
const newDir =
|
|
385
|
-
const legacyDir =
|
|
386
|
-
if (!
|
|
280
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
281
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
282
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
387
283
|
try {
|
|
388
|
-
|
|
284
|
+
renameSync(legacyDir, newDir);
|
|
389
285
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
390
286
|
`);
|
|
391
287
|
} catch {
|
|
@@ -447,9 +343,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
447
343
|
async function loadConfig() {
|
|
448
344
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
449
345
|
await mkdir(dir, { recursive: true });
|
|
450
|
-
const configPath =
|
|
451
|
-
if (!
|
|
452
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
346
|
+
const configPath = path2.join(dir, "config.json");
|
|
347
|
+
if (!existsSync2(configPath)) {
|
|
348
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
453
349
|
}
|
|
454
350
|
const raw = await readFile(configPath, "utf-8");
|
|
455
351
|
try {
|
|
@@ -467,13 +363,13 @@ async function loadConfig() {
|
|
|
467
363
|
normalizeScalingRoadmap(migratedCfg);
|
|
468
364
|
normalizeSessionLifecycle(migratedCfg);
|
|
469
365
|
normalizeAutoUpdate(migratedCfg);
|
|
470
|
-
const config = { ...DEFAULT_CONFIG, dbPath:
|
|
366
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
471
367
|
if (config.dbPath.startsWith("~")) {
|
|
472
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
368
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
473
369
|
}
|
|
474
370
|
return config;
|
|
475
371
|
} catch {
|
|
476
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
372
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
477
373
|
}
|
|
478
374
|
}
|
|
479
375
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
@@ -481,10 +377,10 @@ var init_config = __esm({
|
|
|
481
377
|
"src/lib/config.ts"() {
|
|
482
378
|
"use strict";
|
|
483
379
|
EXE_AI_DIR = resolveDataDir();
|
|
484
|
-
DB_PATH =
|
|
485
|
-
MODELS_DIR =
|
|
486
|
-
CONFIG_PATH =
|
|
487
|
-
LEGACY_LANCE_PATH =
|
|
380
|
+
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
381
|
+
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
382
|
+
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
383
|
+
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
488
384
|
CURRENT_CONFIG_VERSION = 1;
|
|
489
385
|
DEFAULT_CONFIG = {
|
|
490
386
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -556,11 +452,166 @@ var init_config = __esm({
|
|
|
556
452
|
}
|
|
557
453
|
});
|
|
558
454
|
|
|
455
|
+
// src/lib/runtime-table.ts
|
|
456
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
457
|
+
var init_runtime_table = __esm({
|
|
458
|
+
"src/lib/runtime-table.ts"() {
|
|
459
|
+
"use strict";
|
|
460
|
+
RUNTIME_TABLE = {
|
|
461
|
+
codex: {
|
|
462
|
+
binary: "codex",
|
|
463
|
+
launchMode: "exec",
|
|
464
|
+
autoApproveFlag: "--full-auto",
|
|
465
|
+
inlineFlag: "--no-alt-screen",
|
|
466
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
467
|
+
defaultModel: "gpt-5.4"
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
DEFAULT_RUNTIME = "claude";
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// src/lib/agent-config.ts
|
|
475
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
476
|
+
import path3 from "path";
|
|
477
|
+
function loadAgentConfig() {
|
|
478
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
479
|
+
try {
|
|
480
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
481
|
+
} catch {
|
|
482
|
+
return {};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function getAgentRuntime(agentId) {
|
|
486
|
+
const config = loadAgentConfig();
|
|
487
|
+
const entry = config[agentId];
|
|
488
|
+
if (entry) return entry;
|
|
489
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
490
|
+
}
|
|
491
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
492
|
+
var init_agent_config = __esm({
|
|
493
|
+
"src/lib/agent-config.ts"() {
|
|
494
|
+
"use strict";
|
|
495
|
+
init_config();
|
|
496
|
+
init_runtime_table();
|
|
497
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
498
|
+
DEFAULT_MODELS = {
|
|
499
|
+
claude: "claude-opus-4",
|
|
500
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
501
|
+
opencode: "minimax-m2.7"
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// src/lib/intercom-queue.ts
|
|
507
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
508
|
+
import path4 from "path";
|
|
509
|
+
import os3 from "os";
|
|
510
|
+
function ensureDir() {
|
|
511
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
512
|
+
if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
|
|
513
|
+
}
|
|
514
|
+
function readQueue() {
|
|
515
|
+
try {
|
|
516
|
+
if (!existsSync4(QUEUE_PATH)) return [];
|
|
517
|
+
return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
|
|
518
|
+
} catch {
|
|
519
|
+
return [];
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function writeQueue(queue) {
|
|
523
|
+
ensureDir();
|
|
524
|
+
const tmp = `${QUEUE_PATH}.tmp`;
|
|
525
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
526
|
+
renameSync2(tmp, QUEUE_PATH);
|
|
527
|
+
}
|
|
528
|
+
function queueIntercom(targetSession, reason) {
|
|
529
|
+
const queue = readQueue();
|
|
530
|
+
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
531
|
+
if (existing) {
|
|
532
|
+
existing.attempts++;
|
|
533
|
+
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
534
|
+
existing.reason = reason;
|
|
535
|
+
} else {
|
|
536
|
+
queue.push({
|
|
537
|
+
targetSession,
|
|
538
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
539
|
+
attempts: 0,
|
|
540
|
+
reason
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
writeQueue(queue);
|
|
544
|
+
}
|
|
545
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
546
|
+
var init_intercom_queue = __esm({
|
|
547
|
+
"src/lib/intercom-queue.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
550
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
551
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// src/lib/db-retry.ts
|
|
556
|
+
function isBusyError(err) {
|
|
557
|
+
if (err instanceof Error) {
|
|
558
|
+
const msg = err.message.toLowerCase();
|
|
559
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
function delay(ms) {
|
|
564
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
565
|
+
}
|
|
566
|
+
async function retryOnBusy(fn, label) {
|
|
567
|
+
let lastError;
|
|
568
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
569
|
+
try {
|
|
570
|
+
return await fn();
|
|
571
|
+
} catch (err) {
|
|
572
|
+
lastError = err;
|
|
573
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
577
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
578
|
+
process.stderr.write(
|
|
579
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
580
|
+
`
|
|
581
|
+
);
|
|
582
|
+
await delay(backoff + jitter);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
throw lastError;
|
|
586
|
+
}
|
|
587
|
+
function wrapWithRetry(client) {
|
|
588
|
+
return new Proxy(client, {
|
|
589
|
+
get(target, prop, receiver) {
|
|
590
|
+
if (prop === "execute") {
|
|
591
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
592
|
+
}
|
|
593
|
+
if (prop === "batch") {
|
|
594
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
595
|
+
}
|
|
596
|
+
return Reflect.get(target, prop, receiver);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
601
|
+
var init_db_retry = __esm({
|
|
602
|
+
"src/lib/db-retry.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
MAX_RETRIES = 3;
|
|
605
|
+
BASE_DELAY_MS = 200;
|
|
606
|
+
MAX_JITTER_MS = 300;
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
559
610
|
// src/lib/employees.ts
|
|
560
611
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
561
|
-
import { existsSync as
|
|
612
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
562
613
|
import { execSync as execSync3 } from "child_process";
|
|
563
|
-
import
|
|
614
|
+
import path5 from "path";
|
|
564
615
|
import os4 from "os";
|
|
565
616
|
function normalizeRole(role) {
|
|
566
617
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -579,9 +630,9 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
579
630
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
580
631
|
}
|
|
581
632
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
582
|
-
if (!
|
|
633
|
+
if (!existsSync5(employeesPath)) return [];
|
|
583
634
|
try {
|
|
584
|
-
return JSON.parse(
|
|
635
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
585
636
|
} catch {
|
|
586
637
|
return [];
|
|
587
638
|
}
|
|
@@ -600,7 +651,7 @@ var init_employees = __esm({
|
|
|
600
651
|
"src/lib/employees.ts"() {
|
|
601
652
|
"use strict";
|
|
602
653
|
init_config();
|
|
603
|
-
EMPLOYEES_PATH =
|
|
654
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
604
655
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
605
656
|
COORDINATOR_ROLE = "COO";
|
|
606
657
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
@@ -611,8 +662,8 @@ var init_employees = __esm({
|
|
|
611
662
|
import net from "net";
|
|
612
663
|
import { spawn } from "child_process";
|
|
613
664
|
import { randomUUID } from "crypto";
|
|
614
|
-
import { existsSync as
|
|
615
|
-
import
|
|
665
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
666
|
+
import path6 from "path";
|
|
616
667
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
617
668
|
function handleData(chunk) {
|
|
618
669
|
_buffer += chunk.toString();
|
|
@@ -640,9 +691,9 @@ function handleData(chunk) {
|
|
|
640
691
|
}
|
|
641
692
|
}
|
|
642
693
|
function cleanupStaleFiles() {
|
|
643
|
-
if (
|
|
694
|
+
if (existsSync6(PID_PATH)) {
|
|
644
695
|
try {
|
|
645
|
-
const pid = parseInt(
|
|
696
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
646
697
|
if (pid > 0) {
|
|
647
698
|
try {
|
|
648
699
|
process.kill(pid, 0);
|
|
@@ -663,11 +714,11 @@ function cleanupStaleFiles() {
|
|
|
663
714
|
}
|
|
664
715
|
}
|
|
665
716
|
function findPackageRoot() {
|
|
666
|
-
let dir =
|
|
667
|
-
const { root } =
|
|
717
|
+
let dir = path6.dirname(fileURLToPath2(import.meta.url));
|
|
718
|
+
const { root } = path6.parse(dir);
|
|
668
719
|
while (dir !== root) {
|
|
669
|
-
if (
|
|
670
|
-
dir =
|
|
720
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
721
|
+
dir = path6.dirname(dir);
|
|
671
722
|
}
|
|
672
723
|
return null;
|
|
673
724
|
}
|
|
@@ -677,8 +728,8 @@ function spawnDaemon() {
|
|
|
677
728
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
678
729
|
return;
|
|
679
730
|
}
|
|
680
|
-
const daemonPath =
|
|
681
|
-
if (!
|
|
731
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
732
|
+
if (!existsSync6(daemonPath)) {
|
|
682
733
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
683
734
|
`);
|
|
684
735
|
return;
|
|
@@ -686,7 +737,7 @@ function spawnDaemon() {
|
|
|
686
737
|
const resolvedPath = daemonPath;
|
|
687
738
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
688
739
|
`);
|
|
689
|
-
const logPath =
|
|
740
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
690
741
|
let stderrFd = "ignore";
|
|
691
742
|
try {
|
|
692
743
|
stderrFd = openSync(logPath, "a");
|
|
@@ -831,9 +882,9 @@ var init_exe_daemon_client = __esm({
|
|
|
831
882
|
"src/lib/exe-daemon-client.ts"() {
|
|
832
883
|
"use strict";
|
|
833
884
|
init_config();
|
|
834
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
835
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
836
|
-
SPAWN_LOCK_PATH =
|
|
885
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
886
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
887
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
837
888
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
838
889
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
839
890
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2058,18 +2109,18 @@ var init_database = __esm({
|
|
|
2058
2109
|
});
|
|
2059
2110
|
|
|
2060
2111
|
// src/lib/license.ts
|
|
2061
|
-
import { readFileSync as
|
|
2112
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2062
2113
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2063
|
-
import
|
|
2114
|
+
import path7 from "path";
|
|
2064
2115
|
import { jwtVerify, importSPKI } from "jose";
|
|
2065
2116
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2066
2117
|
var init_license = __esm({
|
|
2067
2118
|
"src/lib/license.ts"() {
|
|
2068
2119
|
"use strict";
|
|
2069
2120
|
init_config();
|
|
2070
|
-
LICENSE_PATH =
|
|
2071
|
-
CACHE_PATH =
|
|
2072
|
-
DEVICE_ID_PATH =
|
|
2121
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2122
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2123
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2073
2124
|
PLAN_LIMITS = {
|
|
2074
2125
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2075
2126
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2081,12 +2132,12 @@ var init_license = __esm({
|
|
|
2081
2132
|
});
|
|
2082
2133
|
|
|
2083
2134
|
// src/lib/plan-limits.ts
|
|
2084
|
-
import { readFileSync as
|
|
2085
|
-
import
|
|
2135
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2136
|
+
import path8 from "path";
|
|
2086
2137
|
function getLicenseSync() {
|
|
2087
2138
|
try {
|
|
2088
|
-
if (!
|
|
2089
|
-
const raw = JSON.parse(
|
|
2139
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2140
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
2090
2141
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2091
2142
|
const parts = raw.token.split(".");
|
|
2092
2143
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2124,8 +2175,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2124
2175
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2125
2176
|
let count = 0;
|
|
2126
2177
|
try {
|
|
2127
|
-
if (
|
|
2128
|
-
const raw =
|
|
2178
|
+
if (existsSync8(filePath)) {
|
|
2179
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
2129
2180
|
const employees = JSON.parse(raw);
|
|
2130
2181
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2131
2182
|
}
|
|
@@ -2154,19 +2205,19 @@ var init_plan_limits = __esm({
|
|
|
2154
2205
|
this.name = "PlanLimitError";
|
|
2155
2206
|
}
|
|
2156
2207
|
};
|
|
2157
|
-
CACHE_PATH2 =
|
|
2208
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2158
2209
|
}
|
|
2159
2210
|
});
|
|
2160
2211
|
|
|
2161
2212
|
// src/lib/notifications.ts
|
|
2162
2213
|
import crypto from "crypto";
|
|
2163
|
-
import
|
|
2214
|
+
import path9 from "path";
|
|
2164
2215
|
import os5 from "os";
|
|
2165
2216
|
import {
|
|
2166
|
-
readFileSync as
|
|
2217
|
+
readFileSync as readFileSync9,
|
|
2167
2218
|
readdirSync,
|
|
2168
2219
|
unlinkSync as unlinkSync3,
|
|
2169
|
-
existsSync as
|
|
2220
|
+
existsSync as existsSync9,
|
|
2170
2221
|
rmdirSync
|
|
2171
2222
|
} from "fs";
|
|
2172
2223
|
async function writeNotification(notification) {
|
|
@@ -2301,11 +2352,11 @@ var init_state_bus = __esm({
|
|
|
2301
2352
|
|
|
2302
2353
|
// src/lib/tasks-crud.ts
|
|
2303
2354
|
import crypto3 from "crypto";
|
|
2304
|
-
import
|
|
2355
|
+
import path10 from "path";
|
|
2305
2356
|
import os6 from "os";
|
|
2306
2357
|
import { execSync as execSync4 } from "child_process";
|
|
2307
2358
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2308
|
-
import { existsSync as
|
|
2359
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2309
2360
|
async function writeCheckpoint(input) {
|
|
2310
2361
|
const client = getClient();
|
|
2311
2362
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2480,8 +2531,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2480
2531
|
}
|
|
2481
2532
|
if (input.baseDir) {
|
|
2482
2533
|
try {
|
|
2483
|
-
await mkdir3(
|
|
2484
|
-
await mkdir3(
|
|
2534
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2535
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2485
2536
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2486
2537
|
await ensureGitignoreExe(input.baseDir);
|
|
2487
2538
|
} catch {
|
|
@@ -2517,10 +2568,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2517
2568
|
});
|
|
2518
2569
|
if (input.baseDir) {
|
|
2519
2570
|
try {
|
|
2520
|
-
const EXE_OS_DIR =
|
|
2521
|
-
const mdPath =
|
|
2522
|
-
const mdDir =
|
|
2523
|
-
if (!
|
|
2571
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
2572
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2573
|
+
const mdDir = path10.dirname(mdPath);
|
|
2574
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2524
2575
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2525
2576
|
const mdContent = `# ${input.title}
|
|
2526
2577
|
|
|
@@ -2545,7 +2596,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2545
2596
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2546
2597
|
`;
|
|
2547
2598
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2548
|
-
} catch {
|
|
2599
|
+
} catch (err) {
|
|
2600
|
+
process.stderr.write(
|
|
2601
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2602
|
+
`
|
|
2603
|
+
);
|
|
2549
2604
|
}
|
|
2550
2605
|
}
|
|
2551
2606
|
return {
|
|
@@ -2805,9 +2860,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2805
2860
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2806
2861
|
}
|
|
2807
2862
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2808
|
-
const archPath =
|
|
2863
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2809
2864
|
try {
|
|
2810
|
-
if (
|
|
2865
|
+
if (existsSync10(archPath)) return;
|
|
2811
2866
|
const template = [
|
|
2812
2867
|
`# ${projectName} \u2014 System Architecture`,
|
|
2813
2868
|
"",
|
|
@@ -2840,10 +2895,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2840
2895
|
}
|
|
2841
2896
|
}
|
|
2842
2897
|
async function ensureGitignoreExe(baseDir) {
|
|
2843
|
-
const gitignorePath =
|
|
2898
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2844
2899
|
try {
|
|
2845
|
-
if (
|
|
2846
|
-
const content =
|
|
2900
|
+
if (existsSync10(gitignorePath)) {
|
|
2901
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2847
2902
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2848
2903
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2849
2904
|
} else {
|
|
@@ -2874,8 +2929,8 @@ var init_tasks_crud = __esm({
|
|
|
2874
2929
|
});
|
|
2875
2930
|
|
|
2876
2931
|
// src/lib/tasks-review.ts
|
|
2877
|
-
import
|
|
2878
|
-
import { existsSync as
|
|
2932
|
+
import path11 from "path";
|
|
2933
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2879
2934
|
async function countPendingReviews(sessionScope) {
|
|
2880
2935
|
const client = getClient();
|
|
2881
2936
|
if (sessionScope) {
|
|
@@ -3056,11 +3111,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3056
3111
|
);
|
|
3057
3112
|
}
|
|
3058
3113
|
try {
|
|
3059
|
-
const cacheDir =
|
|
3060
|
-
if (
|
|
3114
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3115
|
+
if (existsSync11(cacheDir)) {
|
|
3061
3116
|
for (const f of readdirSync2(cacheDir)) {
|
|
3062
3117
|
if (f.startsWith("review-notified-")) {
|
|
3063
|
-
unlinkSync4(
|
|
3118
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3064
3119
|
}
|
|
3065
3120
|
}
|
|
3066
3121
|
}
|
|
@@ -3081,7 +3136,7 @@ var init_tasks_review = __esm({
|
|
|
3081
3136
|
});
|
|
3082
3137
|
|
|
3083
3138
|
// src/lib/tasks-chain.ts
|
|
3084
|
-
import
|
|
3139
|
+
import path12 from "path";
|
|
3085
3140
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3086
3141
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3087
3142
|
const client = getClient();
|
|
@@ -3098,7 +3153,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3098
3153
|
});
|
|
3099
3154
|
for (const ur of unblockedRows.rows) {
|
|
3100
3155
|
try {
|
|
3101
|
-
const ubFile =
|
|
3156
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3102
3157
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3103
3158
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3104
3159
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3172,7 +3227,7 @@ __export(project_name_exports, {
|
|
|
3172
3227
|
getProjectName: () => getProjectName
|
|
3173
3228
|
});
|
|
3174
3229
|
import { execSync as execSync5 } from "child_process";
|
|
3175
|
-
import
|
|
3230
|
+
import path13 from "path";
|
|
3176
3231
|
function getProjectName(cwd) {
|
|
3177
3232
|
const dir = cwd ?? process.cwd();
|
|
3178
3233
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3185,7 +3240,7 @@ function getProjectName(cwd) {
|
|
|
3185
3240
|
timeout: 2e3,
|
|
3186
3241
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3187
3242
|
}).trim();
|
|
3188
|
-
repoRoot =
|
|
3243
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3189
3244
|
} catch {
|
|
3190
3245
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
3191
3246
|
cwd: dir,
|
|
@@ -3194,11 +3249,11 @@ function getProjectName(cwd) {
|
|
|
3194
3249
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3195
3250
|
}).trim();
|
|
3196
3251
|
}
|
|
3197
|
-
_cached2 =
|
|
3252
|
+
_cached2 = path13.basename(repoRoot);
|
|
3198
3253
|
_cachedCwd = dir;
|
|
3199
3254
|
return _cached2;
|
|
3200
3255
|
} catch {
|
|
3201
|
-
_cached2 =
|
|
3256
|
+
_cached2 = path13.basename(dir);
|
|
3202
3257
|
_cachedCwd = dir;
|
|
3203
3258
|
return _cached2;
|
|
3204
3259
|
}
|
|
@@ -3675,8 +3730,8 @@ __export(tasks_exports, {
|
|
|
3675
3730
|
updateTaskStatus: () => updateTaskStatus,
|
|
3676
3731
|
writeCheckpoint: () => writeCheckpoint
|
|
3677
3732
|
});
|
|
3678
|
-
import
|
|
3679
|
-
import { writeFileSync as
|
|
3733
|
+
import path14 from "path";
|
|
3734
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3680
3735
|
async function createTask(input) {
|
|
3681
3736
|
const result = await createTaskCore(input);
|
|
3682
3737
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3695,11 +3750,11 @@ async function updateTask(input) {
|
|
|
3695
3750
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3696
3751
|
try {
|
|
3697
3752
|
const agent = String(row.assigned_to);
|
|
3698
|
-
const cacheDir =
|
|
3699
|
-
const cachePath =
|
|
3753
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
3754
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3700
3755
|
if (input.status === "in_progress") {
|
|
3701
|
-
|
|
3702
|
-
|
|
3756
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3757
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3703
3758
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3704
3759
|
try {
|
|
3705
3760
|
unlinkSync5(cachePath);
|
|
@@ -4166,13 +4221,13 @@ __export(tmux_routing_exports, {
|
|
|
4166
4221
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4167
4222
|
});
|
|
4168
4223
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
4169
|
-
import { readFileSync as
|
|
4170
|
-
import
|
|
4224
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4225
|
+
import path15 from "path";
|
|
4171
4226
|
import os7 from "os";
|
|
4172
4227
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4173
4228
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4174
4229
|
function spawnLockPath(sessionName) {
|
|
4175
|
-
return
|
|
4230
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4176
4231
|
}
|
|
4177
4232
|
function isProcessAlive(pid) {
|
|
4178
4233
|
try {
|
|
@@ -4183,13 +4238,13 @@ function isProcessAlive(pid) {
|
|
|
4183
4238
|
}
|
|
4184
4239
|
}
|
|
4185
4240
|
function acquireSpawnLock2(sessionName) {
|
|
4186
|
-
if (!
|
|
4187
|
-
|
|
4241
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4242
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4188
4243
|
}
|
|
4189
4244
|
const lockFile = spawnLockPath(sessionName);
|
|
4190
|
-
if (
|
|
4245
|
+
if (existsSync12(lockFile)) {
|
|
4191
4246
|
try {
|
|
4192
|
-
const lock = JSON.parse(
|
|
4247
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4193
4248
|
const age = Date.now() - lock.timestamp;
|
|
4194
4249
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4195
4250
|
return false;
|
|
@@ -4197,7 +4252,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4197
4252
|
} catch {
|
|
4198
4253
|
}
|
|
4199
4254
|
}
|
|
4200
|
-
|
|
4255
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4201
4256
|
return true;
|
|
4202
4257
|
}
|
|
4203
4258
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4209,13 +4264,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4209
4264
|
function resolveBehaviorsExporterScript() {
|
|
4210
4265
|
try {
|
|
4211
4266
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
4212
|
-
const scriptPath =
|
|
4213
|
-
|
|
4267
|
+
const scriptPath = path15.join(
|
|
4268
|
+
path15.dirname(thisFile),
|
|
4214
4269
|
"..",
|
|
4215
4270
|
"bin",
|
|
4216
4271
|
"exe-export-behaviors.js"
|
|
4217
4272
|
);
|
|
4218
|
-
return
|
|
4273
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4219
4274
|
} catch {
|
|
4220
4275
|
return null;
|
|
4221
4276
|
}
|
|
@@ -4281,12 +4336,12 @@ function extractRootExe(name) {
|
|
|
4281
4336
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4282
4337
|
}
|
|
4283
4338
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4284
|
-
if (!
|
|
4285
|
-
|
|
4339
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4340
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4286
4341
|
}
|
|
4287
4342
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4288
|
-
const filePath =
|
|
4289
|
-
|
|
4343
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4344
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4290
4345
|
parentExe: rootExe,
|
|
4291
4346
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4292
4347
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4294,7 +4349,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4294
4349
|
}
|
|
4295
4350
|
function getParentExe(sessionKey) {
|
|
4296
4351
|
try {
|
|
4297
|
-
const data = JSON.parse(
|
|
4352
|
+
const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4298
4353
|
return data.parentExe || null;
|
|
4299
4354
|
} catch {
|
|
4300
4355
|
return null;
|
|
@@ -4302,8 +4357,8 @@ function getParentExe(sessionKey) {
|
|
|
4302
4357
|
}
|
|
4303
4358
|
function getDispatchedBy(sessionKey) {
|
|
4304
4359
|
try {
|
|
4305
|
-
const data = JSON.parse(
|
|
4306
|
-
|
|
4360
|
+
const data = JSON.parse(readFileSync11(
|
|
4361
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4307
4362
|
"utf8"
|
|
4308
4363
|
));
|
|
4309
4364
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4364,32 +4419,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4364
4419
|
}
|
|
4365
4420
|
function readDebounceState() {
|
|
4366
4421
|
try {
|
|
4367
|
-
if (!
|
|
4368
|
-
|
|
4422
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4423
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
4424
|
+
const state = {};
|
|
4425
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4426
|
+
if (typeof val === "number") {
|
|
4427
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4428
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4429
|
+
state[key] = val;
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
return state;
|
|
4369
4433
|
} catch {
|
|
4370
4434
|
return {};
|
|
4371
4435
|
}
|
|
4372
4436
|
}
|
|
4373
4437
|
function writeDebounceState(state) {
|
|
4374
4438
|
try {
|
|
4375
|
-
if (!
|
|
4376
|
-
|
|
4439
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4440
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4377
4441
|
} catch {
|
|
4378
4442
|
}
|
|
4379
4443
|
}
|
|
4380
4444
|
function isDebounced(targetSession) {
|
|
4381
4445
|
const state = readDebounceState();
|
|
4382
|
-
const
|
|
4383
|
-
|
|
4446
|
+
const entry = state[targetSession];
|
|
4447
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4448
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4449
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4450
|
+
state[targetSession].pending++;
|
|
4451
|
+
writeDebounceState(state);
|
|
4452
|
+
return true;
|
|
4453
|
+
}
|
|
4454
|
+
return false;
|
|
4384
4455
|
}
|
|
4385
4456
|
function recordDebounce(targetSession) {
|
|
4386
4457
|
const state = readDebounceState();
|
|
4387
|
-
state[targetSession]
|
|
4458
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4459
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4388
4460
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4389
4461
|
for (const key of Object.keys(state)) {
|
|
4390
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4462
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4391
4463
|
}
|
|
4392
4464
|
writeDebounceState(state);
|
|
4465
|
+
return batched;
|
|
4393
4466
|
}
|
|
4394
4467
|
function logIntercom(msg) {
|
|
4395
4468
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4434,7 +4507,7 @@ function sendIntercom(targetSession) {
|
|
|
4434
4507
|
return "skipped_exe";
|
|
4435
4508
|
}
|
|
4436
4509
|
if (isDebounced(targetSession)) {
|
|
4437
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4510
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4438
4511
|
return "debounced";
|
|
4439
4512
|
}
|
|
4440
4513
|
try {
|
|
@@ -4446,14 +4519,14 @@ function sendIntercom(targetSession) {
|
|
|
4446
4519
|
const sessionState = getSessionState(targetSession);
|
|
4447
4520
|
if (sessionState === "no_claude") {
|
|
4448
4521
|
queueIntercom(targetSession, "claude not running in session");
|
|
4449
|
-
recordDebounce(targetSession);
|
|
4450
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4522
|
+
const batched2 = recordDebounce(targetSession);
|
|
4523
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4451
4524
|
return "queued";
|
|
4452
4525
|
}
|
|
4453
4526
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4454
4527
|
queueIntercom(targetSession, "session busy at send time");
|
|
4455
|
-
recordDebounce(targetSession);
|
|
4456
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4528
|
+
const batched2 = recordDebounce(targetSession);
|
|
4529
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4457
4530
|
return "queued";
|
|
4458
4531
|
}
|
|
4459
4532
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4461,8 +4534,8 @@ function sendIntercom(targetSession) {
|
|
|
4461
4534
|
transport.sendKeys(targetSession, "q");
|
|
4462
4535
|
}
|
|
4463
4536
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4464
|
-
recordDebounce(targetSession);
|
|
4465
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4537
|
+
const batched = recordDebounce(targetSession);
|
|
4538
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4466
4539
|
return "delivered";
|
|
4467
4540
|
} catch {
|
|
4468
4541
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4564,26 +4637,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4564
4637
|
const transport = getTransport();
|
|
4565
4638
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4566
4639
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4567
|
-
const logDir =
|
|
4568
|
-
const logFile =
|
|
4569
|
-
if (!
|
|
4570
|
-
|
|
4640
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4641
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4642
|
+
if (!existsSync12(logDir)) {
|
|
4643
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4571
4644
|
}
|
|
4572
4645
|
transport.kill(sessionName);
|
|
4573
4646
|
let cleanupSuffix = "";
|
|
4574
4647
|
try {
|
|
4575
4648
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
4576
|
-
const cleanupScript =
|
|
4577
|
-
if (
|
|
4649
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4650
|
+
if (existsSync12(cleanupScript)) {
|
|
4578
4651
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4579
4652
|
}
|
|
4580
4653
|
} catch {
|
|
4581
4654
|
}
|
|
4582
4655
|
try {
|
|
4583
|
-
const claudeJsonPath =
|
|
4656
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
4584
4657
|
let claudeJson = {};
|
|
4585
4658
|
try {
|
|
4586
|
-
claudeJson = JSON.parse(
|
|
4659
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
4587
4660
|
} catch {
|
|
4588
4661
|
}
|
|
4589
4662
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4591,17 +4664,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4591
4664
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4592
4665
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4593
4666
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4594
|
-
|
|
4667
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4595
4668
|
} catch {
|
|
4596
4669
|
}
|
|
4597
4670
|
try {
|
|
4598
|
-
const settingsDir =
|
|
4671
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
4599
4672
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4600
|
-
const projSettingsDir =
|
|
4601
|
-
const settingsPath =
|
|
4673
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
4674
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4602
4675
|
let settings = {};
|
|
4603
4676
|
try {
|
|
4604
|
-
settings = JSON.parse(
|
|
4677
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
4605
4678
|
} catch {
|
|
4606
4679
|
}
|
|
4607
4680
|
const perms = settings.permissions ?? {};
|
|
@@ -4629,20 +4702,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4629
4702
|
if (changed) {
|
|
4630
4703
|
perms.allow = allow;
|
|
4631
4704
|
settings.permissions = perms;
|
|
4632
|
-
|
|
4633
|
-
|
|
4705
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4706
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4634
4707
|
}
|
|
4635
4708
|
} catch {
|
|
4636
4709
|
}
|
|
4637
4710
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4638
4711
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4639
|
-
const
|
|
4712
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4713
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4714
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4715
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4640
4716
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4641
4717
|
let identityFlag = "";
|
|
4642
4718
|
let behaviorsFlag = "";
|
|
4643
4719
|
let legacyFallbackWarned = false;
|
|
4644
4720
|
if (!useExeAgent && !useBinSymlink) {
|
|
4645
|
-
const identityPath =
|
|
4721
|
+
const identityPath = path15.join(
|
|
4646
4722
|
os7.homedir(),
|
|
4647
4723
|
".exe-os",
|
|
4648
4724
|
"identity",
|
|
@@ -4652,13 +4728,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4652
4728
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4653
4729
|
if (hasAgentFlag) {
|
|
4654
4730
|
identityFlag = ` --agent ${employeeName}`;
|
|
4655
|
-
} else if (
|
|
4731
|
+
} else if (existsSync12(identityPath)) {
|
|
4656
4732
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4657
4733
|
legacyFallbackWarned = true;
|
|
4658
4734
|
}
|
|
4659
4735
|
const behaviorsFile = exportBehaviorsSync(
|
|
4660
4736
|
employeeName,
|
|
4661
|
-
|
|
4737
|
+
path15.basename(spawnCwd),
|
|
4662
4738
|
sessionName
|
|
4663
4739
|
);
|
|
4664
4740
|
if (behaviorsFile) {
|
|
@@ -4673,16 +4749,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4673
4749
|
}
|
|
4674
4750
|
let sessionContextFlag = "";
|
|
4675
4751
|
try {
|
|
4676
|
-
const ctxDir =
|
|
4677
|
-
|
|
4678
|
-
const ctxFile =
|
|
4752
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4753
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4754
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4679
4755
|
const ctxContent = [
|
|
4680
4756
|
`## Session Context`,
|
|
4681
4757
|
`You are running in tmux session: ${sessionName}.`,
|
|
4682
4758
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4683
4759
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4684
4760
|
].join("\n");
|
|
4685
|
-
|
|
4761
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4686
4762
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4687
4763
|
} catch {
|
|
4688
4764
|
}
|
|
@@ -4696,9 +4772,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4696
4772
|
}
|
|
4697
4773
|
}
|
|
4698
4774
|
}
|
|
4775
|
+
if (useCodex) {
|
|
4776
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4777
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4778
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4779
|
+
if (keyVal) {
|
|
4780
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4784
|
+
}
|
|
4785
|
+
if (useOpencode) {
|
|
4786
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4787
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4788
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4789
|
+
if (keyVal) {
|
|
4790
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4793
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4794
|
+
}
|
|
4795
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4796
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4797
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4798
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4699
4801
|
let spawnCommand;
|
|
4700
4802
|
if (useExeAgent) {
|
|
4701
4803
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4804
|
+
} else if (useCodex) {
|
|
4805
|
+
process.stderr.write(
|
|
4806
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4807
|
+
`
|
|
4808
|
+
);
|
|
4809
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4810
|
+
} else if (useOpencode) {
|
|
4811
|
+
const binName = `${employeeName}-opencode`;
|
|
4812
|
+
process.stderr.write(
|
|
4813
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4814
|
+
`
|
|
4815
|
+
);
|
|
4816
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4702
4817
|
} else if (useBinSymlink) {
|
|
4703
4818
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4704
4819
|
process.stderr.write(
|
|
@@ -4720,11 +4835,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4720
4835
|
transport.pipeLog(sessionName, logFile);
|
|
4721
4836
|
try {
|
|
4722
4837
|
const mySession = getMySession();
|
|
4723
|
-
const dispatchInfo =
|
|
4724
|
-
|
|
4838
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4839
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4725
4840
|
dispatchedBy: mySession,
|
|
4726
4841
|
rootExe: exeSession,
|
|
4727
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4842
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4843
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4844
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4728
4845
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4729
4846
|
}));
|
|
4730
4847
|
} catch {
|
|
@@ -4742,6 +4859,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4742
4859
|
booted = true;
|
|
4743
4860
|
break;
|
|
4744
4861
|
}
|
|
4862
|
+
} else if (useCodex) {
|
|
4863
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4864
|
+
booted = true;
|
|
4865
|
+
break;
|
|
4866
|
+
}
|
|
4745
4867
|
} else {
|
|
4746
4868
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4747
4869
|
booted = true;
|
|
@@ -4753,9 +4875,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4753
4875
|
}
|
|
4754
4876
|
if (!booted) {
|
|
4755
4877
|
releaseSpawnLock2(sessionName);
|
|
4756
|
-
|
|
4878
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4879
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4757
4880
|
}
|
|
4758
|
-
if (!useExeAgent) {
|
|
4881
|
+
if (!useExeAgent && !useCodex) {
|
|
4759
4882
|
try {
|
|
4760
4883
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4761
4884
|
} catch {
|
|
@@ -4782,17 +4905,19 @@ var init_tmux_routing = __esm({
|
|
|
4782
4905
|
init_cc_agent_support();
|
|
4783
4906
|
init_mcp_prefix();
|
|
4784
4907
|
init_provider_table();
|
|
4908
|
+
init_agent_config();
|
|
4909
|
+
init_runtime_table();
|
|
4785
4910
|
init_intercom_queue();
|
|
4786
4911
|
init_plan_limits();
|
|
4787
4912
|
init_employees();
|
|
4788
|
-
SPAWN_LOCK_DIR =
|
|
4789
|
-
SESSION_CACHE =
|
|
4913
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
4914
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4790
4915
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4791
4916
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4792
4917
|
VERIFY_PANE_LINES = 200;
|
|
4793
4918
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4794
|
-
INTERCOM_LOG2 =
|
|
4795
|
-
DEBOUNCE_FILE =
|
|
4919
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4920
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4796
4921
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4797
4922
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4798
4923
|
}
|
|
@@ -4833,14 +4958,14 @@ var init_memory = __esm({
|
|
|
4833
4958
|
|
|
4834
4959
|
// src/lib/keychain.ts
|
|
4835
4960
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4836
|
-
import { existsSync as
|
|
4837
|
-
import
|
|
4961
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4962
|
+
import path16 from "path";
|
|
4838
4963
|
import os8 from "os";
|
|
4839
4964
|
function getKeyDir() {
|
|
4840
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4965
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os8.homedir(), ".exe-os");
|
|
4841
4966
|
}
|
|
4842
4967
|
function getKeyPath() {
|
|
4843
|
-
return
|
|
4968
|
+
return path16.join(getKeyDir(), "master.key");
|
|
4844
4969
|
}
|
|
4845
4970
|
async function tryKeytar() {
|
|
4846
4971
|
try {
|
|
@@ -4861,7 +4986,7 @@ async function getMasterKey() {
|
|
|
4861
4986
|
}
|
|
4862
4987
|
}
|
|
4863
4988
|
const keyPath = getKeyPath();
|
|
4864
|
-
if (!
|
|
4989
|
+
if (!existsSync13(keyPath)) {
|
|
4865
4990
|
process.stderr.write(
|
|
4866
4991
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4867
4992
|
`
|
|
@@ -4901,13 +5026,13 @@ __export(shard_manager_exports, {
|
|
|
4901
5026
|
listShards: () => listShards,
|
|
4902
5027
|
shardExists: () => shardExists
|
|
4903
5028
|
});
|
|
4904
|
-
import
|
|
4905
|
-
import { existsSync as
|
|
5029
|
+
import path17 from "path";
|
|
5030
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
4906
5031
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4907
5032
|
function initShardManager(encryptionKey) {
|
|
4908
5033
|
_encryptionKey = encryptionKey;
|
|
4909
|
-
if (!
|
|
4910
|
-
|
|
5034
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5035
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
4911
5036
|
}
|
|
4912
5037
|
_shardingEnabled = true;
|
|
4913
5038
|
}
|
|
@@ -4927,7 +5052,7 @@ function getShardClient(projectName) {
|
|
|
4927
5052
|
}
|
|
4928
5053
|
const cached = _shards.get(safeName);
|
|
4929
5054
|
if (cached) return cached;
|
|
4930
|
-
const dbPath =
|
|
5055
|
+
const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
|
|
4931
5056
|
const client = createClient2({
|
|
4932
5057
|
url: `file:${dbPath}`,
|
|
4933
5058
|
encryptionKey: _encryptionKey
|
|
@@ -4937,10 +5062,10 @@ function getShardClient(projectName) {
|
|
|
4937
5062
|
}
|
|
4938
5063
|
function shardExists(projectName) {
|
|
4939
5064
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4940
|
-
return
|
|
5065
|
+
return existsSync14(path17.join(SHARDS_DIR, `${safeName}.db`));
|
|
4941
5066
|
}
|
|
4942
5067
|
function listShards() {
|
|
4943
|
-
if (!
|
|
5068
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
4944
5069
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4945
5070
|
}
|
|
4946
5071
|
async function ensureShardSchema(client) {
|
|
@@ -5126,7 +5251,7 @@ var init_shard_manager = __esm({
|
|
|
5126
5251
|
"src/lib/shard-manager.ts"() {
|
|
5127
5252
|
"use strict";
|
|
5128
5253
|
init_config();
|
|
5129
|
-
SHARDS_DIR =
|
|
5254
|
+
SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
|
|
5130
5255
|
_shards = /* @__PURE__ */ new Map();
|
|
5131
5256
|
_encryptionKey = null;
|
|
5132
5257
|
_shardingEnabled = false;
|
|
@@ -5883,8 +6008,8 @@ var init_store = __esm({
|
|
|
5883
6008
|
});
|
|
5884
6009
|
|
|
5885
6010
|
// src/bin/scan-tasks.ts
|
|
5886
|
-
import { existsSync as
|
|
5887
|
-
import
|
|
6011
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
6012
|
+
import path18 from "path";
|
|
5888
6013
|
import os9 from "os";
|
|
5889
6014
|
|
|
5890
6015
|
// src/lib/is-main.ts
|
|
@@ -5906,14 +6031,14 @@ function isMainModule(importMetaUrl) {
|
|
|
5906
6031
|
init_task_scope();
|
|
5907
6032
|
function checkMcpHealth() {
|
|
5908
6033
|
try {
|
|
5909
|
-
const claudeJson =
|
|
5910
|
-
if (!
|
|
6034
|
+
const claudeJson = path18.join(os9.homedir(), ".claude.json");
|
|
6035
|
+
if (!existsSync15(claudeJson)) {
|
|
5911
6036
|
process.stderr.write(
|
|
5912
6037
|
"\u26A0\uFE0F MCP config missing (~/.claude.json not found) \u2014 close_task won't work. Run /exe-setup\n"
|
|
5913
6038
|
);
|
|
5914
6039
|
return;
|
|
5915
6040
|
}
|
|
5916
|
-
const config = JSON.parse(
|
|
6041
|
+
const config = JSON.parse(readFileSync12(claudeJson, "utf8"));
|
|
5917
6042
|
const servers = config.mcpServers;
|
|
5918
6043
|
if (!servers?.["exe-os"] && !servers?.["exe-mem"]) {
|
|
5919
6044
|
process.stderr.write(
|