@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
|
@@ -268,123 +268,19 @@ var init_provider_table = __esm({
|
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
-
// src/lib/intercom-queue.ts
|
|
272
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
273
|
-
import path2 from "path";
|
|
274
|
-
import os2 from "os";
|
|
275
|
-
function ensureDir() {
|
|
276
|
-
const dir = path2.dirname(QUEUE_PATH);
|
|
277
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
278
|
-
}
|
|
279
|
-
function readQueue() {
|
|
280
|
-
try {
|
|
281
|
-
if (!existsSync2(QUEUE_PATH)) return [];
|
|
282
|
-
return JSON.parse(readFileSync2(QUEUE_PATH, "utf8"));
|
|
283
|
-
} catch {
|
|
284
|
-
return [];
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
function writeQueue(queue) {
|
|
288
|
-
ensureDir();
|
|
289
|
-
const tmp = `${QUEUE_PATH}.tmp`;
|
|
290
|
-
writeFileSync2(tmp, JSON.stringify(queue, null, 2));
|
|
291
|
-
renameSync(tmp, QUEUE_PATH);
|
|
292
|
-
}
|
|
293
|
-
function queueIntercom(targetSession, reason) {
|
|
294
|
-
const queue = readQueue();
|
|
295
|
-
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
296
|
-
if (existing) {
|
|
297
|
-
existing.attempts++;
|
|
298
|
-
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
|
-
existing.reason = reason;
|
|
300
|
-
} else {
|
|
301
|
-
queue.push({
|
|
302
|
-
targetSession,
|
|
303
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
304
|
-
attempts: 0,
|
|
305
|
-
reason
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
writeQueue(queue);
|
|
309
|
-
}
|
|
310
|
-
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
311
|
-
var init_intercom_queue = __esm({
|
|
312
|
-
"src/lib/intercom-queue.ts"() {
|
|
313
|
-
"use strict";
|
|
314
|
-
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
315
|
-
TTL_MS = 60 * 60 * 1e3;
|
|
316
|
-
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// src/lib/db-retry.ts
|
|
321
|
-
function isBusyError(err) {
|
|
322
|
-
if (err instanceof Error) {
|
|
323
|
-
const msg = err.message.toLowerCase();
|
|
324
|
-
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
325
|
-
}
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
function delay(ms) {
|
|
329
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
330
|
-
}
|
|
331
|
-
async function retryOnBusy(fn, label) {
|
|
332
|
-
let lastError;
|
|
333
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
334
|
-
try {
|
|
335
|
-
return await fn();
|
|
336
|
-
} catch (err) {
|
|
337
|
-
lastError = err;
|
|
338
|
-
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
339
|
-
throw err;
|
|
340
|
-
}
|
|
341
|
-
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
342
|
-
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
343
|
-
process.stderr.write(
|
|
344
|
-
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
345
|
-
`
|
|
346
|
-
);
|
|
347
|
-
await delay(backoff + jitter);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
throw lastError;
|
|
351
|
-
}
|
|
352
|
-
function wrapWithRetry(client) {
|
|
353
|
-
return new Proxy(client, {
|
|
354
|
-
get(target, prop, receiver) {
|
|
355
|
-
if (prop === "execute") {
|
|
356
|
-
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
357
|
-
}
|
|
358
|
-
if (prop === "batch") {
|
|
359
|
-
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
360
|
-
}
|
|
361
|
-
return Reflect.get(target, prop, receiver);
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
366
|
-
var init_db_retry = __esm({
|
|
367
|
-
"src/lib/db-retry.ts"() {
|
|
368
|
-
"use strict";
|
|
369
|
-
MAX_RETRIES = 3;
|
|
370
|
-
BASE_DELAY_MS = 200;
|
|
371
|
-
MAX_JITTER_MS = 300;
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
|
|
375
271
|
// src/lib/config.ts
|
|
376
272
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
377
|
-
import { readFileSync as
|
|
378
|
-
import
|
|
379
|
-
import
|
|
273
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, renameSync } from "fs";
|
|
274
|
+
import path2 from "path";
|
|
275
|
+
import os2 from "os";
|
|
380
276
|
function resolveDataDir() {
|
|
381
277
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
382
278
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
383
|
-
const newDir =
|
|
384
|
-
const legacyDir =
|
|
385
|
-
if (!
|
|
279
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
280
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
281
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
386
282
|
try {
|
|
387
|
-
|
|
283
|
+
renameSync(legacyDir, newDir);
|
|
388
284
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
389
285
|
`);
|
|
390
286
|
} catch {
|
|
@@ -446,9 +342,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
446
342
|
async function loadConfig() {
|
|
447
343
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
448
344
|
await mkdir(dir, { recursive: true });
|
|
449
|
-
const configPath =
|
|
450
|
-
if (!
|
|
451
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
345
|
+
const configPath = path2.join(dir, "config.json");
|
|
346
|
+
if (!existsSync2(configPath)) {
|
|
347
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
452
348
|
}
|
|
453
349
|
const raw = await readFile(configPath, "utf-8");
|
|
454
350
|
try {
|
|
@@ -466,13 +362,13 @@ async function loadConfig() {
|
|
|
466
362
|
normalizeScalingRoadmap(migratedCfg);
|
|
467
363
|
normalizeSessionLifecycle(migratedCfg);
|
|
468
364
|
normalizeAutoUpdate(migratedCfg);
|
|
469
|
-
const config = { ...DEFAULT_CONFIG, dbPath:
|
|
365
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
470
366
|
if (config.dbPath.startsWith("~")) {
|
|
471
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
367
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
472
368
|
}
|
|
473
369
|
return config;
|
|
474
370
|
} catch {
|
|
475
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
371
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
476
372
|
}
|
|
477
373
|
}
|
|
478
374
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
@@ -480,10 +376,10 @@ var init_config = __esm({
|
|
|
480
376
|
"src/lib/config.ts"() {
|
|
481
377
|
"use strict";
|
|
482
378
|
EXE_AI_DIR = resolveDataDir();
|
|
483
|
-
DB_PATH =
|
|
484
|
-
MODELS_DIR =
|
|
485
|
-
CONFIG_PATH =
|
|
486
|
-
LEGACY_LANCE_PATH =
|
|
379
|
+
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
380
|
+
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
381
|
+
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
382
|
+
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
487
383
|
CURRENT_CONFIG_VERSION = 1;
|
|
488
384
|
DEFAULT_CONFIG = {
|
|
489
385
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -555,11 +451,166 @@ var init_config = __esm({
|
|
|
555
451
|
}
|
|
556
452
|
});
|
|
557
453
|
|
|
454
|
+
// src/lib/runtime-table.ts
|
|
455
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
456
|
+
var init_runtime_table = __esm({
|
|
457
|
+
"src/lib/runtime-table.ts"() {
|
|
458
|
+
"use strict";
|
|
459
|
+
RUNTIME_TABLE = {
|
|
460
|
+
codex: {
|
|
461
|
+
binary: "codex",
|
|
462
|
+
launchMode: "exec",
|
|
463
|
+
autoApproveFlag: "--full-auto",
|
|
464
|
+
inlineFlag: "--no-alt-screen",
|
|
465
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
466
|
+
defaultModel: "gpt-5.4"
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
DEFAULT_RUNTIME = "claude";
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// src/lib/agent-config.ts
|
|
474
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
475
|
+
import path3 from "path";
|
|
476
|
+
function loadAgentConfig() {
|
|
477
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
478
|
+
try {
|
|
479
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
480
|
+
} catch {
|
|
481
|
+
return {};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function getAgentRuntime(agentId) {
|
|
485
|
+
const config = loadAgentConfig();
|
|
486
|
+
const entry = config[agentId];
|
|
487
|
+
if (entry) return entry;
|
|
488
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
489
|
+
}
|
|
490
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
491
|
+
var init_agent_config = __esm({
|
|
492
|
+
"src/lib/agent-config.ts"() {
|
|
493
|
+
"use strict";
|
|
494
|
+
init_config();
|
|
495
|
+
init_runtime_table();
|
|
496
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
497
|
+
DEFAULT_MODELS = {
|
|
498
|
+
claude: "claude-opus-4",
|
|
499
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
500
|
+
opencode: "minimax-m2.7"
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// src/lib/intercom-queue.ts
|
|
506
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
507
|
+
import path4 from "path";
|
|
508
|
+
import os3 from "os";
|
|
509
|
+
function ensureDir() {
|
|
510
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
511
|
+
if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
|
|
512
|
+
}
|
|
513
|
+
function readQueue() {
|
|
514
|
+
try {
|
|
515
|
+
if (!existsSync4(QUEUE_PATH)) return [];
|
|
516
|
+
return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
|
|
517
|
+
} catch {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function writeQueue(queue) {
|
|
522
|
+
ensureDir();
|
|
523
|
+
const tmp = `${QUEUE_PATH}.tmp`;
|
|
524
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
525
|
+
renameSync2(tmp, QUEUE_PATH);
|
|
526
|
+
}
|
|
527
|
+
function queueIntercom(targetSession, reason) {
|
|
528
|
+
const queue = readQueue();
|
|
529
|
+
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
530
|
+
if (existing) {
|
|
531
|
+
existing.attempts++;
|
|
532
|
+
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
533
|
+
existing.reason = reason;
|
|
534
|
+
} else {
|
|
535
|
+
queue.push({
|
|
536
|
+
targetSession,
|
|
537
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
538
|
+
attempts: 0,
|
|
539
|
+
reason
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
writeQueue(queue);
|
|
543
|
+
}
|
|
544
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
545
|
+
var init_intercom_queue = __esm({
|
|
546
|
+
"src/lib/intercom-queue.ts"() {
|
|
547
|
+
"use strict";
|
|
548
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
549
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
550
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// src/lib/db-retry.ts
|
|
555
|
+
function isBusyError(err) {
|
|
556
|
+
if (err instanceof Error) {
|
|
557
|
+
const msg = err.message.toLowerCase();
|
|
558
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
function delay(ms) {
|
|
563
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
564
|
+
}
|
|
565
|
+
async function retryOnBusy(fn, label) {
|
|
566
|
+
let lastError;
|
|
567
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
568
|
+
try {
|
|
569
|
+
return await fn();
|
|
570
|
+
} catch (err) {
|
|
571
|
+
lastError = err;
|
|
572
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
573
|
+
throw err;
|
|
574
|
+
}
|
|
575
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
576
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
577
|
+
process.stderr.write(
|
|
578
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
579
|
+
`
|
|
580
|
+
);
|
|
581
|
+
await delay(backoff + jitter);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
throw lastError;
|
|
585
|
+
}
|
|
586
|
+
function wrapWithRetry(client) {
|
|
587
|
+
return new Proxy(client, {
|
|
588
|
+
get(target, prop, receiver) {
|
|
589
|
+
if (prop === "execute") {
|
|
590
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
591
|
+
}
|
|
592
|
+
if (prop === "batch") {
|
|
593
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
594
|
+
}
|
|
595
|
+
return Reflect.get(target, prop, receiver);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
600
|
+
var init_db_retry = __esm({
|
|
601
|
+
"src/lib/db-retry.ts"() {
|
|
602
|
+
"use strict";
|
|
603
|
+
MAX_RETRIES = 3;
|
|
604
|
+
BASE_DELAY_MS = 200;
|
|
605
|
+
MAX_JITTER_MS = 300;
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
558
609
|
// src/lib/employees.ts
|
|
559
610
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
560
|
-
import { existsSync as
|
|
611
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
561
612
|
import { execSync as execSync3 } from "child_process";
|
|
562
|
-
import
|
|
613
|
+
import path5 from "path";
|
|
563
614
|
import os4 from "os";
|
|
564
615
|
function normalizeRole(role) {
|
|
565
616
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -578,9 +629,9 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
578
629
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
579
630
|
}
|
|
580
631
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
581
|
-
if (!
|
|
632
|
+
if (!existsSync5(employeesPath)) return [];
|
|
582
633
|
try {
|
|
583
|
-
return JSON.parse(
|
|
634
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
584
635
|
} catch {
|
|
585
636
|
return [];
|
|
586
637
|
}
|
|
@@ -599,7 +650,7 @@ var init_employees = __esm({
|
|
|
599
650
|
"src/lib/employees.ts"() {
|
|
600
651
|
"use strict";
|
|
601
652
|
init_config();
|
|
602
|
-
EMPLOYEES_PATH =
|
|
653
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
603
654
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
604
655
|
COORDINATOR_ROLE = "COO";
|
|
605
656
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
@@ -610,8 +661,8 @@ var init_employees = __esm({
|
|
|
610
661
|
import net from "net";
|
|
611
662
|
import { spawn } from "child_process";
|
|
612
663
|
import { randomUUID } from "crypto";
|
|
613
|
-
import { existsSync as
|
|
614
|
-
import
|
|
664
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
665
|
+
import path6 from "path";
|
|
615
666
|
import { fileURLToPath } from "url";
|
|
616
667
|
function handleData(chunk) {
|
|
617
668
|
_buffer += chunk.toString();
|
|
@@ -639,9 +690,9 @@ function handleData(chunk) {
|
|
|
639
690
|
}
|
|
640
691
|
}
|
|
641
692
|
function cleanupStaleFiles() {
|
|
642
|
-
if (
|
|
693
|
+
if (existsSync6(PID_PATH)) {
|
|
643
694
|
try {
|
|
644
|
-
const pid = parseInt(
|
|
695
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
645
696
|
if (pid > 0) {
|
|
646
697
|
try {
|
|
647
698
|
process.kill(pid, 0);
|
|
@@ -662,11 +713,11 @@ function cleanupStaleFiles() {
|
|
|
662
713
|
}
|
|
663
714
|
}
|
|
664
715
|
function findPackageRoot() {
|
|
665
|
-
let dir =
|
|
666
|
-
const { root } =
|
|
716
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
717
|
+
const { root } = path6.parse(dir);
|
|
667
718
|
while (dir !== root) {
|
|
668
|
-
if (
|
|
669
|
-
dir =
|
|
719
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
720
|
+
dir = path6.dirname(dir);
|
|
670
721
|
}
|
|
671
722
|
return null;
|
|
672
723
|
}
|
|
@@ -676,8 +727,8 @@ function spawnDaemon() {
|
|
|
676
727
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
677
728
|
return;
|
|
678
729
|
}
|
|
679
|
-
const daemonPath =
|
|
680
|
-
if (!
|
|
730
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
731
|
+
if (!existsSync6(daemonPath)) {
|
|
681
732
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
682
733
|
`);
|
|
683
734
|
return;
|
|
@@ -685,7 +736,7 @@ function spawnDaemon() {
|
|
|
685
736
|
const resolvedPath = daemonPath;
|
|
686
737
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
687
738
|
`);
|
|
688
|
-
const logPath =
|
|
739
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
689
740
|
let stderrFd = "ignore";
|
|
690
741
|
try {
|
|
691
742
|
stderrFd = openSync(logPath, "a");
|
|
@@ -830,9 +881,9 @@ var init_exe_daemon_client = __esm({
|
|
|
830
881
|
"src/lib/exe-daemon-client.ts"() {
|
|
831
882
|
"use strict";
|
|
832
883
|
init_config();
|
|
833
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
834
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
835
|
-
SPAWN_LOCK_PATH =
|
|
884
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
885
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
886
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
836
887
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
837
888
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
838
889
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2057,18 +2108,18 @@ var init_database = __esm({
|
|
|
2057
2108
|
});
|
|
2058
2109
|
|
|
2059
2110
|
// src/lib/license.ts
|
|
2060
|
-
import { readFileSync as
|
|
2111
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2061
2112
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2062
|
-
import
|
|
2113
|
+
import path7 from "path";
|
|
2063
2114
|
import { jwtVerify, importSPKI } from "jose";
|
|
2064
2115
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2065
2116
|
var init_license = __esm({
|
|
2066
2117
|
"src/lib/license.ts"() {
|
|
2067
2118
|
"use strict";
|
|
2068
2119
|
init_config();
|
|
2069
|
-
LICENSE_PATH =
|
|
2070
|
-
CACHE_PATH =
|
|
2071
|
-
DEVICE_ID_PATH =
|
|
2120
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2121
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2122
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2072
2123
|
PLAN_LIMITS = {
|
|
2073
2124
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2074
2125
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2080,12 +2131,12 @@ var init_license = __esm({
|
|
|
2080
2131
|
});
|
|
2081
2132
|
|
|
2082
2133
|
// src/lib/plan-limits.ts
|
|
2083
|
-
import { readFileSync as
|
|
2084
|
-
import
|
|
2134
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2135
|
+
import path8 from "path";
|
|
2085
2136
|
function getLicenseSync() {
|
|
2086
2137
|
try {
|
|
2087
|
-
if (!
|
|
2088
|
-
const raw = JSON.parse(
|
|
2138
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2139
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
2089
2140
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2090
2141
|
const parts = raw.token.split(".");
|
|
2091
2142
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2123,8 +2174,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2123
2174
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2124
2175
|
let count = 0;
|
|
2125
2176
|
try {
|
|
2126
|
-
if (
|
|
2127
|
-
const raw =
|
|
2177
|
+
if (existsSync8(filePath)) {
|
|
2178
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
2128
2179
|
const employees = JSON.parse(raw);
|
|
2129
2180
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2130
2181
|
}
|
|
@@ -2153,19 +2204,19 @@ var init_plan_limits = __esm({
|
|
|
2153
2204
|
this.name = "PlanLimitError";
|
|
2154
2205
|
}
|
|
2155
2206
|
};
|
|
2156
|
-
CACHE_PATH2 =
|
|
2207
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2157
2208
|
}
|
|
2158
2209
|
});
|
|
2159
2210
|
|
|
2160
2211
|
// src/lib/notifications.ts
|
|
2161
2212
|
import crypto from "crypto";
|
|
2162
|
-
import
|
|
2213
|
+
import path9 from "path";
|
|
2163
2214
|
import os5 from "os";
|
|
2164
2215
|
import {
|
|
2165
|
-
readFileSync as
|
|
2216
|
+
readFileSync as readFileSync9,
|
|
2166
2217
|
readdirSync,
|
|
2167
2218
|
unlinkSync as unlinkSync3,
|
|
2168
|
-
existsSync as
|
|
2219
|
+
existsSync as existsSync9,
|
|
2169
2220
|
rmdirSync
|
|
2170
2221
|
} from "fs";
|
|
2171
2222
|
async function writeNotification(notification) {
|
|
@@ -2316,11 +2367,11 @@ __export(tasks_crud_exports, {
|
|
|
2316
2367
|
writeCheckpoint: () => writeCheckpoint
|
|
2317
2368
|
});
|
|
2318
2369
|
import crypto3 from "crypto";
|
|
2319
|
-
import
|
|
2370
|
+
import path10 from "path";
|
|
2320
2371
|
import os6 from "os";
|
|
2321
2372
|
import { execSync as execSync4 } from "child_process";
|
|
2322
2373
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2323
|
-
import { existsSync as
|
|
2374
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2324
2375
|
async function writeCheckpoint(input) {
|
|
2325
2376
|
const client = getClient();
|
|
2326
2377
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2495,8 +2546,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2495
2546
|
}
|
|
2496
2547
|
if (input.baseDir) {
|
|
2497
2548
|
try {
|
|
2498
|
-
await mkdir3(
|
|
2499
|
-
await mkdir3(
|
|
2549
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2550
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2500
2551
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2501
2552
|
await ensureGitignoreExe(input.baseDir);
|
|
2502
2553
|
} catch {
|
|
@@ -2532,10 +2583,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2532
2583
|
});
|
|
2533
2584
|
if (input.baseDir) {
|
|
2534
2585
|
try {
|
|
2535
|
-
const EXE_OS_DIR =
|
|
2536
|
-
const mdPath =
|
|
2537
|
-
const mdDir =
|
|
2538
|
-
if (!
|
|
2586
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
2587
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2588
|
+
const mdDir = path10.dirname(mdPath);
|
|
2589
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2539
2590
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2540
2591
|
const mdContent = `# ${input.title}
|
|
2541
2592
|
|
|
@@ -2560,7 +2611,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2560
2611
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2561
2612
|
`;
|
|
2562
2613
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2563
|
-
} catch {
|
|
2614
|
+
} catch (err) {
|
|
2615
|
+
process.stderr.write(
|
|
2616
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2617
|
+
`
|
|
2618
|
+
);
|
|
2564
2619
|
}
|
|
2565
2620
|
}
|
|
2566
2621
|
return {
|
|
@@ -2820,9 +2875,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2820
2875
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2821
2876
|
}
|
|
2822
2877
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2823
|
-
const archPath =
|
|
2878
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2824
2879
|
try {
|
|
2825
|
-
if (
|
|
2880
|
+
if (existsSync10(archPath)) return;
|
|
2826
2881
|
const template = [
|
|
2827
2882
|
`# ${projectName} \u2014 System Architecture`,
|
|
2828
2883
|
"",
|
|
@@ -2855,10 +2910,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2855
2910
|
}
|
|
2856
2911
|
}
|
|
2857
2912
|
async function ensureGitignoreExe(baseDir) {
|
|
2858
|
-
const gitignorePath =
|
|
2913
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2859
2914
|
try {
|
|
2860
|
-
if (
|
|
2861
|
-
const content =
|
|
2915
|
+
if (existsSync10(gitignorePath)) {
|
|
2916
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2862
2917
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2863
2918
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2864
2919
|
} else {
|
|
@@ -2889,8 +2944,8 @@ var init_tasks_crud = __esm({
|
|
|
2889
2944
|
});
|
|
2890
2945
|
|
|
2891
2946
|
// src/lib/tasks-review.ts
|
|
2892
|
-
import
|
|
2893
|
-
import { existsSync as
|
|
2947
|
+
import path11 from "path";
|
|
2948
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2894
2949
|
async function countPendingReviews(sessionScope) {
|
|
2895
2950
|
const client = getClient();
|
|
2896
2951
|
if (sessionScope) {
|
|
@@ -3071,11 +3126,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3071
3126
|
);
|
|
3072
3127
|
}
|
|
3073
3128
|
try {
|
|
3074
|
-
const cacheDir =
|
|
3075
|
-
if (
|
|
3129
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3130
|
+
if (existsSync11(cacheDir)) {
|
|
3076
3131
|
for (const f of readdirSync2(cacheDir)) {
|
|
3077
3132
|
if (f.startsWith("review-notified-")) {
|
|
3078
|
-
unlinkSync4(
|
|
3133
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3079
3134
|
}
|
|
3080
3135
|
}
|
|
3081
3136
|
}
|
|
@@ -3096,7 +3151,7 @@ var init_tasks_review = __esm({
|
|
|
3096
3151
|
});
|
|
3097
3152
|
|
|
3098
3153
|
// src/lib/tasks-chain.ts
|
|
3099
|
-
import
|
|
3154
|
+
import path12 from "path";
|
|
3100
3155
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3101
3156
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3102
3157
|
const client = getClient();
|
|
@@ -3113,7 +3168,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3113
3168
|
});
|
|
3114
3169
|
for (const ur of unblockedRows.rows) {
|
|
3115
3170
|
try {
|
|
3116
|
-
const ubFile =
|
|
3171
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3117
3172
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3118
3173
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3119
3174
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3182,7 +3237,7 @@ var init_tasks_chain = __esm({
|
|
|
3182
3237
|
|
|
3183
3238
|
// src/lib/project-name.ts
|
|
3184
3239
|
import { execSync as execSync5 } from "child_process";
|
|
3185
|
-
import
|
|
3240
|
+
import path13 from "path";
|
|
3186
3241
|
function getProjectName(cwd) {
|
|
3187
3242
|
const dir = cwd ?? process.cwd();
|
|
3188
3243
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3195,7 +3250,7 @@ function getProjectName(cwd) {
|
|
|
3195
3250
|
timeout: 2e3,
|
|
3196
3251
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3197
3252
|
}).trim();
|
|
3198
|
-
repoRoot =
|
|
3253
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3199
3254
|
} catch {
|
|
3200
3255
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
3201
3256
|
cwd: dir,
|
|
@@ -3204,11 +3259,11 @@ function getProjectName(cwd) {
|
|
|
3204
3259
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3205
3260
|
}).trim();
|
|
3206
3261
|
}
|
|
3207
|
-
_cached2 =
|
|
3262
|
+
_cached2 = path13.basename(repoRoot);
|
|
3208
3263
|
_cachedCwd = dir;
|
|
3209
3264
|
return _cached2;
|
|
3210
3265
|
} catch {
|
|
3211
|
-
_cached2 =
|
|
3266
|
+
_cached2 = path13.basename(dir);
|
|
3212
3267
|
_cachedCwd = dir;
|
|
3213
3268
|
return _cached2;
|
|
3214
3269
|
}
|
|
@@ -3681,8 +3736,8 @@ __export(tasks_exports, {
|
|
|
3681
3736
|
updateTaskStatus: () => updateTaskStatus,
|
|
3682
3737
|
writeCheckpoint: () => writeCheckpoint
|
|
3683
3738
|
});
|
|
3684
|
-
import
|
|
3685
|
-
import { writeFileSync as
|
|
3739
|
+
import path14 from "path";
|
|
3740
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3686
3741
|
async function createTask(input) {
|
|
3687
3742
|
const result = await createTaskCore(input);
|
|
3688
3743
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3701,11 +3756,11 @@ async function updateTask(input) {
|
|
|
3701
3756
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3702
3757
|
try {
|
|
3703
3758
|
const agent = String(row.assigned_to);
|
|
3704
|
-
const cacheDir =
|
|
3705
|
-
const cachePath =
|
|
3759
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
3760
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3706
3761
|
if (input.status === "in_progress") {
|
|
3707
|
-
|
|
3708
|
-
|
|
3762
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3763
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3709
3764
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3710
3765
|
try {
|
|
3711
3766
|
unlinkSync5(cachePath);
|
|
@@ -4172,13 +4227,13 @@ __export(tmux_routing_exports, {
|
|
|
4172
4227
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4173
4228
|
});
|
|
4174
4229
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
4175
|
-
import { readFileSync as
|
|
4176
|
-
import
|
|
4230
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4231
|
+
import path15 from "path";
|
|
4177
4232
|
import os7 from "os";
|
|
4178
4233
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4179
4234
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4180
4235
|
function spawnLockPath(sessionName) {
|
|
4181
|
-
return
|
|
4236
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4182
4237
|
}
|
|
4183
4238
|
function isProcessAlive(pid) {
|
|
4184
4239
|
try {
|
|
@@ -4189,13 +4244,13 @@ function isProcessAlive(pid) {
|
|
|
4189
4244
|
}
|
|
4190
4245
|
}
|
|
4191
4246
|
function acquireSpawnLock2(sessionName) {
|
|
4192
|
-
if (!
|
|
4193
|
-
|
|
4247
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4248
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4194
4249
|
}
|
|
4195
4250
|
const lockFile = spawnLockPath(sessionName);
|
|
4196
|
-
if (
|
|
4251
|
+
if (existsSync12(lockFile)) {
|
|
4197
4252
|
try {
|
|
4198
|
-
const lock = JSON.parse(
|
|
4253
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4199
4254
|
const age = Date.now() - lock.timestamp;
|
|
4200
4255
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4201
4256
|
return false;
|
|
@@ -4203,7 +4258,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4203
4258
|
} catch {
|
|
4204
4259
|
}
|
|
4205
4260
|
}
|
|
4206
|
-
|
|
4261
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4207
4262
|
return true;
|
|
4208
4263
|
}
|
|
4209
4264
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4215,13 +4270,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4215
4270
|
function resolveBehaviorsExporterScript() {
|
|
4216
4271
|
try {
|
|
4217
4272
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4218
|
-
const scriptPath =
|
|
4219
|
-
|
|
4273
|
+
const scriptPath = path15.join(
|
|
4274
|
+
path15.dirname(thisFile),
|
|
4220
4275
|
"..",
|
|
4221
4276
|
"bin",
|
|
4222
4277
|
"exe-export-behaviors.js"
|
|
4223
4278
|
);
|
|
4224
|
-
return
|
|
4279
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4225
4280
|
} catch {
|
|
4226
4281
|
return null;
|
|
4227
4282
|
}
|
|
@@ -4287,12 +4342,12 @@ function extractRootExe(name) {
|
|
|
4287
4342
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4288
4343
|
}
|
|
4289
4344
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4290
|
-
if (!
|
|
4291
|
-
|
|
4345
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4346
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4292
4347
|
}
|
|
4293
4348
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4294
|
-
const filePath =
|
|
4295
|
-
|
|
4349
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4350
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4296
4351
|
parentExe: rootExe,
|
|
4297
4352
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4298
4353
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4300,7 +4355,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4300
4355
|
}
|
|
4301
4356
|
function getParentExe(sessionKey) {
|
|
4302
4357
|
try {
|
|
4303
|
-
const data = JSON.parse(
|
|
4358
|
+
const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4304
4359
|
return data.parentExe || null;
|
|
4305
4360
|
} catch {
|
|
4306
4361
|
return null;
|
|
@@ -4308,8 +4363,8 @@ function getParentExe(sessionKey) {
|
|
|
4308
4363
|
}
|
|
4309
4364
|
function getDispatchedBy(sessionKey) {
|
|
4310
4365
|
try {
|
|
4311
|
-
const data = JSON.parse(
|
|
4312
|
-
|
|
4366
|
+
const data = JSON.parse(readFileSync11(
|
|
4367
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4313
4368
|
"utf8"
|
|
4314
4369
|
));
|
|
4315
4370
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4370,32 +4425,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4370
4425
|
}
|
|
4371
4426
|
function readDebounceState() {
|
|
4372
4427
|
try {
|
|
4373
|
-
if (!
|
|
4374
|
-
|
|
4428
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4429
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
4430
|
+
const state = {};
|
|
4431
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4432
|
+
if (typeof val === "number") {
|
|
4433
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4434
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4435
|
+
state[key] = val;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
return state;
|
|
4375
4439
|
} catch {
|
|
4376
4440
|
return {};
|
|
4377
4441
|
}
|
|
4378
4442
|
}
|
|
4379
4443
|
function writeDebounceState(state) {
|
|
4380
4444
|
try {
|
|
4381
|
-
if (!
|
|
4382
|
-
|
|
4445
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4446
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4383
4447
|
} catch {
|
|
4384
4448
|
}
|
|
4385
4449
|
}
|
|
4386
4450
|
function isDebounced(targetSession) {
|
|
4387
4451
|
const state = readDebounceState();
|
|
4388
|
-
const
|
|
4389
|
-
|
|
4452
|
+
const entry = state[targetSession];
|
|
4453
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4454
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4455
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4456
|
+
state[targetSession].pending++;
|
|
4457
|
+
writeDebounceState(state);
|
|
4458
|
+
return true;
|
|
4459
|
+
}
|
|
4460
|
+
return false;
|
|
4390
4461
|
}
|
|
4391
4462
|
function recordDebounce(targetSession) {
|
|
4392
4463
|
const state = readDebounceState();
|
|
4393
|
-
state[targetSession]
|
|
4464
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4465
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4394
4466
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4395
4467
|
for (const key of Object.keys(state)) {
|
|
4396
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4468
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4397
4469
|
}
|
|
4398
4470
|
writeDebounceState(state);
|
|
4471
|
+
return batched;
|
|
4399
4472
|
}
|
|
4400
4473
|
function logIntercom(msg) {
|
|
4401
4474
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4440,7 +4513,7 @@ function sendIntercom(targetSession) {
|
|
|
4440
4513
|
return "skipped_exe";
|
|
4441
4514
|
}
|
|
4442
4515
|
if (isDebounced(targetSession)) {
|
|
4443
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4516
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4444
4517
|
return "debounced";
|
|
4445
4518
|
}
|
|
4446
4519
|
try {
|
|
@@ -4452,14 +4525,14 @@ function sendIntercom(targetSession) {
|
|
|
4452
4525
|
const sessionState = getSessionState(targetSession);
|
|
4453
4526
|
if (sessionState === "no_claude") {
|
|
4454
4527
|
queueIntercom(targetSession, "claude not running in session");
|
|
4455
|
-
recordDebounce(targetSession);
|
|
4456
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4528
|
+
const batched2 = recordDebounce(targetSession);
|
|
4529
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4457
4530
|
return "queued";
|
|
4458
4531
|
}
|
|
4459
4532
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4460
4533
|
queueIntercom(targetSession, "session busy at send time");
|
|
4461
|
-
recordDebounce(targetSession);
|
|
4462
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4534
|
+
const batched2 = recordDebounce(targetSession);
|
|
4535
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4463
4536
|
return "queued";
|
|
4464
4537
|
}
|
|
4465
4538
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4467,8 +4540,8 @@ function sendIntercom(targetSession) {
|
|
|
4467
4540
|
transport.sendKeys(targetSession, "q");
|
|
4468
4541
|
}
|
|
4469
4542
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4470
|
-
recordDebounce(targetSession);
|
|
4471
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4543
|
+
const batched = recordDebounce(targetSession);
|
|
4544
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4472
4545
|
return "delivered";
|
|
4473
4546
|
} catch {
|
|
4474
4547
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4570,26 +4643,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4570
4643
|
const transport = getTransport();
|
|
4571
4644
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4572
4645
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4573
|
-
const logDir =
|
|
4574
|
-
const logFile =
|
|
4575
|
-
if (!
|
|
4576
|
-
|
|
4646
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4647
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4648
|
+
if (!existsSync12(logDir)) {
|
|
4649
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4577
4650
|
}
|
|
4578
4651
|
transport.kill(sessionName);
|
|
4579
4652
|
let cleanupSuffix = "";
|
|
4580
4653
|
try {
|
|
4581
4654
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4582
|
-
const cleanupScript =
|
|
4583
|
-
if (
|
|
4655
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4656
|
+
if (existsSync12(cleanupScript)) {
|
|
4584
4657
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4585
4658
|
}
|
|
4586
4659
|
} catch {
|
|
4587
4660
|
}
|
|
4588
4661
|
try {
|
|
4589
|
-
const claudeJsonPath =
|
|
4662
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
4590
4663
|
let claudeJson = {};
|
|
4591
4664
|
try {
|
|
4592
|
-
claudeJson = JSON.parse(
|
|
4665
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
4593
4666
|
} catch {
|
|
4594
4667
|
}
|
|
4595
4668
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4597,17 +4670,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4597
4670
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4598
4671
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4599
4672
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4600
|
-
|
|
4673
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4601
4674
|
} catch {
|
|
4602
4675
|
}
|
|
4603
4676
|
try {
|
|
4604
|
-
const settingsDir =
|
|
4677
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
4605
4678
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4606
|
-
const projSettingsDir =
|
|
4607
|
-
const settingsPath =
|
|
4679
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
4680
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4608
4681
|
let settings = {};
|
|
4609
4682
|
try {
|
|
4610
|
-
settings = JSON.parse(
|
|
4683
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
4611
4684
|
} catch {
|
|
4612
4685
|
}
|
|
4613
4686
|
const perms = settings.permissions ?? {};
|
|
@@ -4635,20 +4708,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4635
4708
|
if (changed) {
|
|
4636
4709
|
perms.allow = allow;
|
|
4637
4710
|
settings.permissions = perms;
|
|
4638
|
-
|
|
4639
|
-
|
|
4711
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4712
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4640
4713
|
}
|
|
4641
4714
|
} catch {
|
|
4642
4715
|
}
|
|
4643
4716
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4644
4717
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4645
|
-
const
|
|
4718
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4719
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4720
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4721
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4646
4722
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4647
4723
|
let identityFlag = "";
|
|
4648
4724
|
let behaviorsFlag = "";
|
|
4649
4725
|
let legacyFallbackWarned = false;
|
|
4650
4726
|
if (!useExeAgent && !useBinSymlink) {
|
|
4651
|
-
const identityPath =
|
|
4727
|
+
const identityPath = path15.join(
|
|
4652
4728
|
os7.homedir(),
|
|
4653
4729
|
".exe-os",
|
|
4654
4730
|
"identity",
|
|
@@ -4658,13 +4734,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4658
4734
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4659
4735
|
if (hasAgentFlag) {
|
|
4660
4736
|
identityFlag = ` --agent ${employeeName}`;
|
|
4661
|
-
} else if (
|
|
4737
|
+
} else if (existsSync12(identityPath)) {
|
|
4662
4738
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4663
4739
|
legacyFallbackWarned = true;
|
|
4664
4740
|
}
|
|
4665
4741
|
const behaviorsFile = exportBehaviorsSync(
|
|
4666
4742
|
employeeName,
|
|
4667
|
-
|
|
4743
|
+
path15.basename(spawnCwd),
|
|
4668
4744
|
sessionName
|
|
4669
4745
|
);
|
|
4670
4746
|
if (behaviorsFile) {
|
|
@@ -4679,16 +4755,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4679
4755
|
}
|
|
4680
4756
|
let sessionContextFlag = "";
|
|
4681
4757
|
try {
|
|
4682
|
-
const ctxDir =
|
|
4683
|
-
|
|
4684
|
-
const ctxFile =
|
|
4758
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4759
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4760
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4685
4761
|
const ctxContent = [
|
|
4686
4762
|
`## Session Context`,
|
|
4687
4763
|
`You are running in tmux session: ${sessionName}.`,
|
|
4688
4764
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4689
4765
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4690
4766
|
].join("\n");
|
|
4691
|
-
|
|
4767
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4692
4768
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4693
4769
|
} catch {
|
|
4694
4770
|
}
|
|
@@ -4702,9 +4778,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4702
4778
|
}
|
|
4703
4779
|
}
|
|
4704
4780
|
}
|
|
4781
|
+
if (useCodex) {
|
|
4782
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4783
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4784
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4785
|
+
if (keyVal) {
|
|
4786
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4790
|
+
}
|
|
4791
|
+
if (useOpencode) {
|
|
4792
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4793
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4794
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4795
|
+
if (keyVal) {
|
|
4796
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4800
|
+
}
|
|
4801
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4802
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4803
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4804
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4705
4807
|
let spawnCommand;
|
|
4706
4808
|
if (useExeAgent) {
|
|
4707
4809
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4810
|
+
} else if (useCodex) {
|
|
4811
|
+
process.stderr.write(
|
|
4812
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4813
|
+
`
|
|
4814
|
+
);
|
|
4815
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4816
|
+
} else if (useOpencode) {
|
|
4817
|
+
const binName = `${employeeName}-opencode`;
|
|
4818
|
+
process.stderr.write(
|
|
4819
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4820
|
+
`
|
|
4821
|
+
);
|
|
4822
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4708
4823
|
} else if (useBinSymlink) {
|
|
4709
4824
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4710
4825
|
process.stderr.write(
|
|
@@ -4726,11 +4841,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4726
4841
|
transport.pipeLog(sessionName, logFile);
|
|
4727
4842
|
try {
|
|
4728
4843
|
const mySession = getMySession();
|
|
4729
|
-
const dispatchInfo =
|
|
4730
|
-
|
|
4844
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4845
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4731
4846
|
dispatchedBy: mySession,
|
|
4732
4847
|
rootExe: exeSession,
|
|
4733
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4848
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4849
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4850
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4734
4851
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4735
4852
|
}));
|
|
4736
4853
|
} catch {
|
|
@@ -4748,6 +4865,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4748
4865
|
booted = true;
|
|
4749
4866
|
break;
|
|
4750
4867
|
}
|
|
4868
|
+
} else if (useCodex) {
|
|
4869
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4870
|
+
booted = true;
|
|
4871
|
+
break;
|
|
4872
|
+
}
|
|
4751
4873
|
} else {
|
|
4752
4874
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4753
4875
|
booted = true;
|
|
@@ -4759,9 +4881,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4759
4881
|
}
|
|
4760
4882
|
if (!booted) {
|
|
4761
4883
|
releaseSpawnLock2(sessionName);
|
|
4762
|
-
|
|
4884
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4885
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4763
4886
|
}
|
|
4764
|
-
if (!useExeAgent) {
|
|
4887
|
+
if (!useExeAgent && !useCodex) {
|
|
4765
4888
|
try {
|
|
4766
4889
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4767
4890
|
} catch {
|
|
@@ -4788,17 +4911,19 @@ var init_tmux_routing = __esm({
|
|
|
4788
4911
|
init_cc_agent_support();
|
|
4789
4912
|
init_mcp_prefix();
|
|
4790
4913
|
init_provider_table();
|
|
4914
|
+
init_agent_config();
|
|
4915
|
+
init_runtime_table();
|
|
4791
4916
|
init_intercom_queue();
|
|
4792
4917
|
init_plan_limits();
|
|
4793
4918
|
init_employees();
|
|
4794
|
-
SPAWN_LOCK_DIR =
|
|
4795
|
-
SESSION_CACHE =
|
|
4919
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
4920
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4796
4921
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4797
4922
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4798
4923
|
VERIFY_PANE_LINES = 200;
|
|
4799
4924
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4800
|
-
INTERCOM_LOG2 =
|
|
4801
|
-
DEBOUNCE_FILE =
|
|
4925
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4926
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4802
4927
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4803
4928
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4804
4929
|
}
|
|
@@ -4839,14 +4964,14 @@ var init_memory = __esm({
|
|
|
4839
4964
|
|
|
4840
4965
|
// src/lib/keychain.ts
|
|
4841
4966
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4842
|
-
import { existsSync as
|
|
4843
|
-
import
|
|
4967
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4968
|
+
import path16 from "path";
|
|
4844
4969
|
import os8 from "os";
|
|
4845
4970
|
function getKeyDir() {
|
|
4846
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4971
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os8.homedir(), ".exe-os");
|
|
4847
4972
|
}
|
|
4848
4973
|
function getKeyPath() {
|
|
4849
|
-
return
|
|
4974
|
+
return path16.join(getKeyDir(), "master.key");
|
|
4850
4975
|
}
|
|
4851
4976
|
async function tryKeytar() {
|
|
4852
4977
|
try {
|
|
@@ -4867,7 +4992,7 @@ async function getMasterKey() {
|
|
|
4867
4992
|
}
|
|
4868
4993
|
}
|
|
4869
4994
|
const keyPath = getKeyPath();
|
|
4870
|
-
if (!
|
|
4995
|
+
if (!existsSync13(keyPath)) {
|
|
4871
4996
|
process.stderr.write(
|
|
4872
4997
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4873
4998
|
`
|
|
@@ -4907,13 +5032,13 @@ __export(shard_manager_exports, {
|
|
|
4907
5032
|
listShards: () => listShards,
|
|
4908
5033
|
shardExists: () => shardExists
|
|
4909
5034
|
});
|
|
4910
|
-
import
|
|
4911
|
-
import { existsSync as
|
|
5035
|
+
import path17 from "path";
|
|
5036
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
4912
5037
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4913
5038
|
function initShardManager(encryptionKey) {
|
|
4914
5039
|
_encryptionKey = encryptionKey;
|
|
4915
|
-
if (!
|
|
4916
|
-
|
|
5040
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5041
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
4917
5042
|
}
|
|
4918
5043
|
_shardingEnabled = true;
|
|
4919
5044
|
}
|
|
@@ -4933,7 +5058,7 @@ function getShardClient(projectName) {
|
|
|
4933
5058
|
}
|
|
4934
5059
|
const cached = _shards.get(safeName);
|
|
4935
5060
|
if (cached) return cached;
|
|
4936
|
-
const dbPath =
|
|
5061
|
+
const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
|
|
4937
5062
|
const client = createClient2({
|
|
4938
5063
|
url: `file:${dbPath}`,
|
|
4939
5064
|
encryptionKey: _encryptionKey
|
|
@@ -4943,10 +5068,10 @@ function getShardClient(projectName) {
|
|
|
4943
5068
|
}
|
|
4944
5069
|
function shardExists(projectName) {
|
|
4945
5070
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4946
|
-
return
|
|
5071
|
+
return existsSync14(path17.join(SHARDS_DIR, `${safeName}.db`));
|
|
4947
5072
|
}
|
|
4948
5073
|
function listShards() {
|
|
4949
|
-
if (!
|
|
5074
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
4950
5075
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4951
5076
|
}
|
|
4952
5077
|
async function ensureShardSchema(client) {
|
|
@@ -5132,7 +5257,7 @@ var init_shard_manager = __esm({
|
|
|
5132
5257
|
"src/lib/shard-manager.ts"() {
|
|
5133
5258
|
"use strict";
|
|
5134
5259
|
init_config();
|
|
5135
|
-
SHARDS_DIR =
|
|
5260
|
+
SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
|
|
5136
5261
|
_shards = /* @__PURE__ */ new Map();
|
|
5137
5262
|
_encryptionKey = null;
|
|
5138
5263
|
_shardingEnabled = false;
|