@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/git-sweep.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 } 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(fileURLToPath(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) {
|
|
@@ -2317,11 +2368,11 @@ __export(tasks_crud_exports, {
|
|
|
2317
2368
|
writeCheckpoint: () => writeCheckpoint
|
|
2318
2369
|
});
|
|
2319
2370
|
import crypto3 from "crypto";
|
|
2320
|
-
import
|
|
2371
|
+
import path10 from "path";
|
|
2321
2372
|
import os6 from "os";
|
|
2322
2373
|
import { execSync as execSync4 } from "child_process";
|
|
2323
2374
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2324
|
-
import { existsSync as
|
|
2375
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2325
2376
|
async function writeCheckpoint(input) {
|
|
2326
2377
|
const client = getClient();
|
|
2327
2378
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2496,8 +2547,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2496
2547
|
}
|
|
2497
2548
|
if (input.baseDir) {
|
|
2498
2549
|
try {
|
|
2499
|
-
await mkdir3(
|
|
2500
|
-
await mkdir3(
|
|
2550
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2551
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2501
2552
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2502
2553
|
await ensureGitignoreExe(input.baseDir);
|
|
2503
2554
|
} catch {
|
|
@@ -2533,10 +2584,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2533
2584
|
});
|
|
2534
2585
|
if (input.baseDir) {
|
|
2535
2586
|
try {
|
|
2536
|
-
const EXE_OS_DIR =
|
|
2537
|
-
const mdPath =
|
|
2538
|
-
const mdDir =
|
|
2539
|
-
if (!
|
|
2587
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
2588
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2589
|
+
const mdDir = path10.dirname(mdPath);
|
|
2590
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2540
2591
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2541
2592
|
const mdContent = `# ${input.title}
|
|
2542
2593
|
|
|
@@ -2561,7 +2612,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2561
2612
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2562
2613
|
`;
|
|
2563
2614
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2564
|
-
} catch {
|
|
2615
|
+
} catch (err) {
|
|
2616
|
+
process.stderr.write(
|
|
2617
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2618
|
+
`
|
|
2619
|
+
);
|
|
2565
2620
|
}
|
|
2566
2621
|
}
|
|
2567
2622
|
return {
|
|
@@ -2821,9 +2876,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2821
2876
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2822
2877
|
}
|
|
2823
2878
|
async function ensureArchitectureDoc(baseDir, projectName2) {
|
|
2824
|
-
const archPath =
|
|
2879
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2825
2880
|
try {
|
|
2826
|
-
if (
|
|
2881
|
+
if (existsSync10(archPath)) return;
|
|
2827
2882
|
const template = [
|
|
2828
2883
|
`# ${projectName2} \u2014 System Architecture`,
|
|
2829
2884
|
"",
|
|
@@ -2856,10 +2911,10 @@ async function ensureArchitectureDoc(baseDir, projectName2) {
|
|
|
2856
2911
|
}
|
|
2857
2912
|
}
|
|
2858
2913
|
async function ensureGitignoreExe(baseDir) {
|
|
2859
|
-
const gitignorePath =
|
|
2914
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2860
2915
|
try {
|
|
2861
|
-
if (
|
|
2862
|
-
const content =
|
|
2916
|
+
if (existsSync10(gitignorePath)) {
|
|
2917
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2863
2918
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2864
2919
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2865
2920
|
} else {
|
|
@@ -2890,8 +2945,8 @@ var init_tasks_crud = __esm({
|
|
|
2890
2945
|
});
|
|
2891
2946
|
|
|
2892
2947
|
// src/lib/tasks-review.ts
|
|
2893
|
-
import
|
|
2894
|
-
import { existsSync as
|
|
2948
|
+
import path11 from "path";
|
|
2949
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2895
2950
|
async function countPendingReviews(sessionScope) {
|
|
2896
2951
|
const client = getClient();
|
|
2897
2952
|
if (sessionScope) {
|
|
@@ -3072,11 +3127,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3072
3127
|
);
|
|
3073
3128
|
}
|
|
3074
3129
|
try {
|
|
3075
|
-
const cacheDir =
|
|
3076
|
-
if (
|
|
3130
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3131
|
+
if (existsSync11(cacheDir)) {
|
|
3077
3132
|
for (const f of readdirSync2(cacheDir)) {
|
|
3078
3133
|
if (f.startsWith("review-notified-")) {
|
|
3079
|
-
unlinkSync4(
|
|
3134
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3080
3135
|
}
|
|
3081
3136
|
}
|
|
3082
3137
|
}
|
|
@@ -3097,7 +3152,7 @@ var init_tasks_review = __esm({
|
|
|
3097
3152
|
});
|
|
3098
3153
|
|
|
3099
3154
|
// src/lib/tasks-chain.ts
|
|
3100
|
-
import
|
|
3155
|
+
import path12 from "path";
|
|
3101
3156
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3102
3157
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3103
3158
|
const client = getClient();
|
|
@@ -3114,7 +3169,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3114
3169
|
});
|
|
3115
3170
|
for (const ur of unblockedRows.rows) {
|
|
3116
3171
|
try {
|
|
3117
|
-
const ubFile =
|
|
3172
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3118
3173
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3119
3174
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3120
3175
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3183,7 +3238,7 @@ var init_tasks_chain = __esm({
|
|
|
3183
3238
|
|
|
3184
3239
|
// src/lib/project-name.ts
|
|
3185
3240
|
import { execSync as execSync5 } from "child_process";
|
|
3186
|
-
import
|
|
3241
|
+
import path13 from "path";
|
|
3187
3242
|
function getProjectName(cwd) {
|
|
3188
3243
|
const dir = cwd ?? process.cwd();
|
|
3189
3244
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3196,7 +3251,7 @@ function getProjectName(cwd) {
|
|
|
3196
3251
|
timeout: 2e3,
|
|
3197
3252
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3198
3253
|
}).trim();
|
|
3199
|
-
repoRoot =
|
|
3254
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3200
3255
|
} catch {
|
|
3201
3256
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
3202
3257
|
cwd: dir,
|
|
@@ -3205,11 +3260,11 @@ function getProjectName(cwd) {
|
|
|
3205
3260
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3206
3261
|
}).trim();
|
|
3207
3262
|
}
|
|
3208
|
-
_cached2 =
|
|
3263
|
+
_cached2 = path13.basename(repoRoot);
|
|
3209
3264
|
_cachedCwd = dir;
|
|
3210
3265
|
return _cached2;
|
|
3211
3266
|
} catch {
|
|
3212
|
-
_cached2 =
|
|
3267
|
+
_cached2 = path13.basename(dir);
|
|
3213
3268
|
_cachedCwd = dir;
|
|
3214
3269
|
return _cached2;
|
|
3215
3270
|
}
|
|
@@ -3682,8 +3737,8 @@ __export(tasks_exports, {
|
|
|
3682
3737
|
updateTaskStatus: () => updateTaskStatus,
|
|
3683
3738
|
writeCheckpoint: () => writeCheckpoint
|
|
3684
3739
|
});
|
|
3685
|
-
import
|
|
3686
|
-
import { writeFileSync as
|
|
3740
|
+
import path14 from "path";
|
|
3741
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3687
3742
|
async function createTask(input) {
|
|
3688
3743
|
const result = await createTaskCore(input);
|
|
3689
3744
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3702,11 +3757,11 @@ async function updateTask(input) {
|
|
|
3702
3757
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3703
3758
|
try {
|
|
3704
3759
|
const agent = String(row.assigned_to);
|
|
3705
|
-
const cacheDir =
|
|
3706
|
-
const cachePath =
|
|
3760
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
3761
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3707
3762
|
if (input.status === "in_progress") {
|
|
3708
|
-
|
|
3709
|
-
|
|
3763
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3764
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3710
3765
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3711
3766
|
try {
|
|
3712
3767
|
unlinkSync5(cachePath);
|
|
@@ -4173,13 +4228,13 @@ __export(tmux_routing_exports, {
|
|
|
4173
4228
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4174
4229
|
});
|
|
4175
4230
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
4176
|
-
import { readFileSync as
|
|
4177
|
-
import
|
|
4231
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4232
|
+
import path15 from "path";
|
|
4178
4233
|
import os7 from "os";
|
|
4179
4234
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4180
4235
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4181
4236
|
function spawnLockPath(sessionName) {
|
|
4182
|
-
return
|
|
4237
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4183
4238
|
}
|
|
4184
4239
|
function isProcessAlive(pid) {
|
|
4185
4240
|
try {
|
|
@@ -4190,13 +4245,13 @@ function isProcessAlive(pid) {
|
|
|
4190
4245
|
}
|
|
4191
4246
|
}
|
|
4192
4247
|
function acquireSpawnLock2(sessionName) {
|
|
4193
|
-
if (!
|
|
4194
|
-
|
|
4248
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4249
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4195
4250
|
}
|
|
4196
4251
|
const lockFile = spawnLockPath(sessionName);
|
|
4197
|
-
if (
|
|
4252
|
+
if (existsSync12(lockFile)) {
|
|
4198
4253
|
try {
|
|
4199
|
-
const lock = JSON.parse(
|
|
4254
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4200
4255
|
const age = Date.now() - lock.timestamp;
|
|
4201
4256
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4202
4257
|
return false;
|
|
@@ -4204,7 +4259,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4204
4259
|
} catch {
|
|
4205
4260
|
}
|
|
4206
4261
|
}
|
|
4207
|
-
|
|
4262
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4208
4263
|
return true;
|
|
4209
4264
|
}
|
|
4210
4265
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4216,13 +4271,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4216
4271
|
function resolveBehaviorsExporterScript() {
|
|
4217
4272
|
try {
|
|
4218
4273
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4219
|
-
const scriptPath =
|
|
4220
|
-
|
|
4274
|
+
const scriptPath = path15.join(
|
|
4275
|
+
path15.dirname(thisFile),
|
|
4221
4276
|
"..",
|
|
4222
4277
|
"bin",
|
|
4223
4278
|
"exe-export-behaviors.js"
|
|
4224
4279
|
);
|
|
4225
|
-
return
|
|
4280
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4226
4281
|
} catch {
|
|
4227
4282
|
return null;
|
|
4228
4283
|
}
|
|
@@ -4288,12 +4343,12 @@ function extractRootExe(name) {
|
|
|
4288
4343
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4289
4344
|
}
|
|
4290
4345
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4291
|
-
if (!
|
|
4292
|
-
|
|
4346
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4347
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4293
4348
|
}
|
|
4294
4349
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4295
|
-
const filePath =
|
|
4296
|
-
|
|
4350
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4351
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4297
4352
|
parentExe: rootExe,
|
|
4298
4353
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4299
4354
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4301,7 +4356,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4301
4356
|
}
|
|
4302
4357
|
function getParentExe(sessionKey) {
|
|
4303
4358
|
try {
|
|
4304
|
-
const data = JSON.parse(
|
|
4359
|
+
const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4305
4360
|
return data.parentExe || null;
|
|
4306
4361
|
} catch {
|
|
4307
4362
|
return null;
|
|
@@ -4309,8 +4364,8 @@ function getParentExe(sessionKey) {
|
|
|
4309
4364
|
}
|
|
4310
4365
|
function getDispatchedBy(sessionKey) {
|
|
4311
4366
|
try {
|
|
4312
|
-
const data = JSON.parse(
|
|
4313
|
-
|
|
4367
|
+
const data = JSON.parse(readFileSync11(
|
|
4368
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4314
4369
|
"utf8"
|
|
4315
4370
|
));
|
|
4316
4371
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4371,32 +4426,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4371
4426
|
}
|
|
4372
4427
|
function readDebounceState() {
|
|
4373
4428
|
try {
|
|
4374
|
-
if (!
|
|
4375
|
-
|
|
4429
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4430
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
4431
|
+
const state = {};
|
|
4432
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4433
|
+
if (typeof val === "number") {
|
|
4434
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4435
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4436
|
+
state[key] = val;
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
return state;
|
|
4376
4440
|
} catch {
|
|
4377
4441
|
return {};
|
|
4378
4442
|
}
|
|
4379
4443
|
}
|
|
4380
4444
|
function writeDebounceState(state) {
|
|
4381
4445
|
try {
|
|
4382
|
-
if (!
|
|
4383
|
-
|
|
4446
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4447
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4384
4448
|
} catch {
|
|
4385
4449
|
}
|
|
4386
4450
|
}
|
|
4387
4451
|
function isDebounced(targetSession) {
|
|
4388
4452
|
const state = readDebounceState();
|
|
4389
|
-
const
|
|
4390
|
-
|
|
4453
|
+
const entry = state[targetSession];
|
|
4454
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4455
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4456
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4457
|
+
state[targetSession].pending++;
|
|
4458
|
+
writeDebounceState(state);
|
|
4459
|
+
return true;
|
|
4460
|
+
}
|
|
4461
|
+
return false;
|
|
4391
4462
|
}
|
|
4392
4463
|
function recordDebounce(targetSession) {
|
|
4393
4464
|
const state = readDebounceState();
|
|
4394
|
-
state[targetSession]
|
|
4465
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4466
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4395
4467
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4396
4468
|
for (const key of Object.keys(state)) {
|
|
4397
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4469
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4398
4470
|
}
|
|
4399
4471
|
writeDebounceState(state);
|
|
4472
|
+
return batched;
|
|
4400
4473
|
}
|
|
4401
4474
|
function logIntercom(msg) {
|
|
4402
4475
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4441,7 +4514,7 @@ function sendIntercom(targetSession) {
|
|
|
4441
4514
|
return "skipped_exe";
|
|
4442
4515
|
}
|
|
4443
4516
|
if (isDebounced(targetSession)) {
|
|
4444
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4517
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4445
4518
|
return "debounced";
|
|
4446
4519
|
}
|
|
4447
4520
|
try {
|
|
@@ -4453,14 +4526,14 @@ function sendIntercom(targetSession) {
|
|
|
4453
4526
|
const sessionState = getSessionState(targetSession);
|
|
4454
4527
|
if (sessionState === "no_claude") {
|
|
4455
4528
|
queueIntercom(targetSession, "claude not running in session");
|
|
4456
|
-
recordDebounce(targetSession);
|
|
4457
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4529
|
+
const batched2 = recordDebounce(targetSession);
|
|
4530
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4458
4531
|
return "queued";
|
|
4459
4532
|
}
|
|
4460
4533
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4461
4534
|
queueIntercom(targetSession, "session busy at send time");
|
|
4462
|
-
recordDebounce(targetSession);
|
|
4463
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4535
|
+
const batched2 = recordDebounce(targetSession);
|
|
4536
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4464
4537
|
return "queued";
|
|
4465
4538
|
}
|
|
4466
4539
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4468,8 +4541,8 @@ function sendIntercom(targetSession) {
|
|
|
4468
4541
|
transport.sendKeys(targetSession, "q");
|
|
4469
4542
|
}
|
|
4470
4543
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4471
|
-
recordDebounce(targetSession);
|
|
4472
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4544
|
+
const batched = recordDebounce(targetSession);
|
|
4545
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4473
4546
|
return "delivered";
|
|
4474
4547
|
} catch {
|
|
4475
4548
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4571,26 +4644,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4571
4644
|
const transport = getTransport();
|
|
4572
4645
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4573
4646
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4574
|
-
const logDir =
|
|
4575
|
-
const logFile =
|
|
4576
|
-
if (!
|
|
4577
|
-
|
|
4647
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4648
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4649
|
+
if (!existsSync12(logDir)) {
|
|
4650
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4578
4651
|
}
|
|
4579
4652
|
transport.kill(sessionName);
|
|
4580
4653
|
let cleanupSuffix = "";
|
|
4581
4654
|
try {
|
|
4582
4655
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4583
|
-
const cleanupScript =
|
|
4584
|
-
if (
|
|
4656
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4657
|
+
if (existsSync12(cleanupScript)) {
|
|
4585
4658
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4586
4659
|
}
|
|
4587
4660
|
} catch {
|
|
4588
4661
|
}
|
|
4589
4662
|
try {
|
|
4590
|
-
const claudeJsonPath =
|
|
4663
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
4591
4664
|
let claudeJson = {};
|
|
4592
4665
|
try {
|
|
4593
|
-
claudeJson = JSON.parse(
|
|
4666
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
4594
4667
|
} catch {
|
|
4595
4668
|
}
|
|
4596
4669
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4598,17 +4671,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4598
4671
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4599
4672
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4600
4673
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4601
|
-
|
|
4674
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4602
4675
|
} catch {
|
|
4603
4676
|
}
|
|
4604
4677
|
try {
|
|
4605
|
-
const settingsDir =
|
|
4678
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
4606
4679
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4607
|
-
const projSettingsDir =
|
|
4608
|
-
const settingsPath =
|
|
4680
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
4681
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4609
4682
|
let settings = {};
|
|
4610
4683
|
try {
|
|
4611
|
-
settings = JSON.parse(
|
|
4684
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
4612
4685
|
} catch {
|
|
4613
4686
|
}
|
|
4614
4687
|
const perms = settings.permissions ?? {};
|
|
@@ -4636,20 +4709,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4636
4709
|
if (changed) {
|
|
4637
4710
|
perms.allow = allow;
|
|
4638
4711
|
settings.permissions = perms;
|
|
4639
|
-
|
|
4640
|
-
|
|
4712
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4713
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4641
4714
|
}
|
|
4642
4715
|
} catch {
|
|
4643
4716
|
}
|
|
4644
4717
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4645
4718
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4646
|
-
const
|
|
4719
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4720
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4721
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4722
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4647
4723
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4648
4724
|
let identityFlag = "";
|
|
4649
4725
|
let behaviorsFlag = "";
|
|
4650
4726
|
let legacyFallbackWarned = false;
|
|
4651
4727
|
if (!useExeAgent && !useBinSymlink) {
|
|
4652
|
-
const identityPath =
|
|
4728
|
+
const identityPath = path15.join(
|
|
4653
4729
|
os7.homedir(),
|
|
4654
4730
|
".exe-os",
|
|
4655
4731
|
"identity",
|
|
@@ -4659,13 +4735,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4659
4735
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4660
4736
|
if (hasAgentFlag) {
|
|
4661
4737
|
identityFlag = ` --agent ${employeeName}`;
|
|
4662
|
-
} else if (
|
|
4738
|
+
} else if (existsSync12(identityPath)) {
|
|
4663
4739
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4664
4740
|
legacyFallbackWarned = true;
|
|
4665
4741
|
}
|
|
4666
4742
|
const behaviorsFile = exportBehaviorsSync(
|
|
4667
4743
|
employeeName,
|
|
4668
|
-
|
|
4744
|
+
path15.basename(spawnCwd),
|
|
4669
4745
|
sessionName
|
|
4670
4746
|
);
|
|
4671
4747
|
if (behaviorsFile) {
|
|
@@ -4680,16 +4756,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4680
4756
|
}
|
|
4681
4757
|
let sessionContextFlag = "";
|
|
4682
4758
|
try {
|
|
4683
|
-
const ctxDir =
|
|
4684
|
-
|
|
4685
|
-
const ctxFile =
|
|
4759
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4760
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4761
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4686
4762
|
const ctxContent = [
|
|
4687
4763
|
`## Session Context`,
|
|
4688
4764
|
`You are running in tmux session: ${sessionName}.`,
|
|
4689
4765
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4690
4766
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4691
4767
|
].join("\n");
|
|
4692
|
-
|
|
4768
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4693
4769
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4694
4770
|
} catch {
|
|
4695
4771
|
}
|
|
@@ -4703,9 +4779,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4703
4779
|
}
|
|
4704
4780
|
}
|
|
4705
4781
|
}
|
|
4782
|
+
if (useCodex) {
|
|
4783
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4784
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4785
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4786
|
+
if (keyVal) {
|
|
4787
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4791
|
+
}
|
|
4792
|
+
if (useOpencode) {
|
|
4793
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4794
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4795
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4796
|
+
if (keyVal) {
|
|
4797
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4801
|
+
}
|
|
4802
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4803
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4804
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4805
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4706
4808
|
let spawnCommand;
|
|
4707
4809
|
if (useExeAgent) {
|
|
4708
4810
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4811
|
+
} else if (useCodex) {
|
|
4812
|
+
process.stderr.write(
|
|
4813
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4814
|
+
`
|
|
4815
|
+
);
|
|
4816
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4817
|
+
} else if (useOpencode) {
|
|
4818
|
+
const binName = `${employeeName}-opencode`;
|
|
4819
|
+
process.stderr.write(
|
|
4820
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4821
|
+
`
|
|
4822
|
+
);
|
|
4823
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4709
4824
|
} else if (useBinSymlink) {
|
|
4710
4825
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4711
4826
|
process.stderr.write(
|
|
@@ -4727,11 +4842,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4727
4842
|
transport.pipeLog(sessionName, logFile);
|
|
4728
4843
|
try {
|
|
4729
4844
|
const mySession = getMySession();
|
|
4730
|
-
const dispatchInfo =
|
|
4731
|
-
|
|
4845
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4846
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4732
4847
|
dispatchedBy: mySession,
|
|
4733
4848
|
rootExe: exeSession,
|
|
4734
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4849
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4850
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4851
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4735
4852
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4736
4853
|
}));
|
|
4737
4854
|
} catch {
|
|
@@ -4749,6 +4866,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4749
4866
|
booted = true;
|
|
4750
4867
|
break;
|
|
4751
4868
|
}
|
|
4869
|
+
} else if (useCodex) {
|
|
4870
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4871
|
+
booted = true;
|
|
4872
|
+
break;
|
|
4873
|
+
}
|
|
4752
4874
|
} else {
|
|
4753
4875
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4754
4876
|
booted = true;
|
|
@@ -4760,9 +4882,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4760
4882
|
}
|
|
4761
4883
|
if (!booted) {
|
|
4762
4884
|
releaseSpawnLock2(sessionName);
|
|
4763
|
-
|
|
4885
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4886
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4764
4887
|
}
|
|
4765
|
-
if (!useExeAgent) {
|
|
4888
|
+
if (!useExeAgent && !useCodex) {
|
|
4766
4889
|
try {
|
|
4767
4890
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4768
4891
|
} catch {
|
|
@@ -4789,17 +4912,19 @@ var init_tmux_routing = __esm({
|
|
|
4789
4912
|
init_cc_agent_support();
|
|
4790
4913
|
init_mcp_prefix();
|
|
4791
4914
|
init_provider_table();
|
|
4915
|
+
init_agent_config();
|
|
4916
|
+
init_runtime_table();
|
|
4792
4917
|
init_intercom_queue();
|
|
4793
4918
|
init_plan_limits();
|
|
4794
4919
|
init_employees();
|
|
4795
|
-
SPAWN_LOCK_DIR =
|
|
4796
|
-
SESSION_CACHE =
|
|
4920
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
4921
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4797
4922
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4798
4923
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4799
4924
|
VERIFY_PANE_LINES = 200;
|
|
4800
4925
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4801
|
-
INTERCOM_LOG2 =
|
|
4802
|
-
DEBOUNCE_FILE =
|
|
4926
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4927
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4803
4928
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4804
4929
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4805
4930
|
}
|
|
@@ -4840,14 +4965,14 @@ var init_memory = __esm({
|
|
|
4840
4965
|
|
|
4841
4966
|
// src/lib/keychain.ts
|
|
4842
4967
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4843
|
-
import { existsSync as
|
|
4844
|
-
import
|
|
4968
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4969
|
+
import path16 from "path";
|
|
4845
4970
|
import os8 from "os";
|
|
4846
4971
|
function getKeyDir() {
|
|
4847
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4972
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os8.homedir(), ".exe-os");
|
|
4848
4973
|
}
|
|
4849
4974
|
function getKeyPath() {
|
|
4850
|
-
return
|
|
4975
|
+
return path16.join(getKeyDir(), "master.key");
|
|
4851
4976
|
}
|
|
4852
4977
|
async function tryKeytar() {
|
|
4853
4978
|
try {
|
|
@@ -4868,7 +4993,7 @@ async function getMasterKey() {
|
|
|
4868
4993
|
}
|
|
4869
4994
|
}
|
|
4870
4995
|
const keyPath = getKeyPath();
|
|
4871
|
-
if (!
|
|
4996
|
+
if (!existsSync13(keyPath)) {
|
|
4872
4997
|
process.stderr.write(
|
|
4873
4998
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4874
4999
|
`
|
|
@@ -4908,13 +5033,13 @@ __export(shard_manager_exports, {
|
|
|
4908
5033
|
listShards: () => listShards,
|
|
4909
5034
|
shardExists: () => shardExists
|
|
4910
5035
|
});
|
|
4911
|
-
import
|
|
4912
|
-
import { existsSync as
|
|
5036
|
+
import path17 from "path";
|
|
5037
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
4913
5038
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4914
5039
|
function initShardManager(encryptionKey) {
|
|
4915
5040
|
_encryptionKey = encryptionKey;
|
|
4916
|
-
if (!
|
|
4917
|
-
|
|
5041
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5042
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
4918
5043
|
}
|
|
4919
5044
|
_shardingEnabled = true;
|
|
4920
5045
|
}
|
|
@@ -4934,7 +5059,7 @@ function getShardClient(projectName2) {
|
|
|
4934
5059
|
}
|
|
4935
5060
|
const cached = _shards.get(safeName);
|
|
4936
5061
|
if (cached) return cached;
|
|
4937
|
-
const dbPath =
|
|
5062
|
+
const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
|
|
4938
5063
|
const client = createClient2({
|
|
4939
5064
|
url: `file:${dbPath}`,
|
|
4940
5065
|
encryptionKey: _encryptionKey
|
|
@@ -4944,10 +5069,10 @@ function getShardClient(projectName2) {
|
|
|
4944
5069
|
}
|
|
4945
5070
|
function shardExists(projectName2) {
|
|
4946
5071
|
const safeName = projectName2.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4947
|
-
return
|
|
5072
|
+
return existsSync14(path17.join(SHARDS_DIR, `${safeName}.db`));
|
|
4948
5073
|
}
|
|
4949
5074
|
function listShards() {
|
|
4950
|
-
if (!
|
|
5075
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
4951
5076
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4952
5077
|
}
|
|
4953
5078
|
async function ensureShardSchema(client) {
|
|
@@ -5133,7 +5258,7 @@ var init_shard_manager = __esm({
|
|
|
5133
5258
|
"src/lib/shard-manager.ts"() {
|
|
5134
5259
|
"use strict";
|
|
5135
5260
|
init_config();
|
|
5136
|
-
SHARDS_DIR =
|
|
5261
|
+
SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
|
|
5137
5262
|
_shards = /* @__PURE__ */ new Map();
|
|
5138
5263
|
_encryptionKey = null;
|
|
5139
5264
|
_shardingEnabled = false;
|
|
@@ -6094,13 +6219,13 @@ async function sweepTasks(projectName2, options = {}) {
|
|
|
6094
6219
|
}
|
|
6095
6220
|
|
|
6096
6221
|
// src/bin/git-sweep.ts
|
|
6097
|
-
import
|
|
6222
|
+
import path18 from "path";
|
|
6098
6223
|
var args = process.argv.slice(2);
|
|
6099
6224
|
var dryRun = args.includes("--dry-run");
|
|
6100
6225
|
var projectIdx = args.indexOf("--project");
|
|
6101
6226
|
var limitIdx = args.indexOf("--limit");
|
|
6102
6227
|
var staleIdx = args.indexOf("--stale");
|
|
6103
|
-
var projectName = projectIdx >= 0 ? args[projectIdx + 1] : process.cwd().split(
|
|
6228
|
+
var projectName = projectIdx >= 0 ? args[projectIdx + 1] : process.cwd().split(path18.sep).pop();
|
|
6104
6229
|
var commitLimit = limitIdx >= 0 ? Number(args[limitIdx + 1]) : void 0;
|
|
6105
6230
|
var staleMinutes = staleIdx >= 0 ? Number(args[staleIdx + 1]) : void 0;
|
|
6106
6231
|
async function main() {
|