@askexenow/exe-os 0.8.83 → 0.8.86
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/backfill-conversations.js +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +154 -21
- package/dist/bin/cli.js +14678 -12676
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +100 -91
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1420 -485
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +572 -271
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +102 -3
- package/dist/bin/exe-gateway.js +796 -292
- package/dist/bin/exe-healthcheck.js +134 -1
- package/dist/bin/exe-heartbeat.js +172 -36
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +927 -82
- package/dist/bin/exe-new-employee.js +60 -8
- package/dist/bin/exe-pending-messages.js +151 -19
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +155 -22
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +995 -228
- package/dist/bin/exe-session-cleanup.js +4930 -1664
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +154 -21
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +1180 -363
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +60 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +1185 -367
- package/dist/bin/setup.js +914 -270
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +792 -285
- package/dist/hooks/bug-report-worker.js +445 -135
- package/dist/hooks/commit-complete.js +1178 -361
- package/dist/hooks/error-recall.js +994 -228
- package/dist/hooks/ingest-worker.js +1799 -1234
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +757 -109
- package/dist/hooks/pre-compact.js +1061 -244
- package/dist/hooks/pre-tool-use.js +787 -130
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +1121 -299
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +4063 -397
- package/dist/hooks/session-start.js +1071 -254
- package/dist/hooks/stop.js +768 -120
- package/dist/hooks/subagent-stop.js +757 -109
- package/dist/hooks/summary-worker.js +1706 -1011
- package/dist/index.js +1821 -1098
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +932 -88
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +2733 -1575
- package/dist/lib/hybrid-search.js +995 -228
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +103 -40
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +348 -134
- package/dist/lib/tmux-routing.js +422 -208
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5742 -696
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +375 -152
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +99 -31
- package/dist/mcp/tools/send-message.js +108 -45
- package/dist/mcp/tools/update-task.js +162 -77
- package/dist/runtime/index.js +1075 -258
- package/dist/tui/App.js +1333 -506
- package/package.json +6 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/tui/App.js
CHANGED
|
@@ -322,110 +322,6 @@ var init_provider_table = __esm({
|
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
|
|
325
|
-
// src/lib/intercom-queue.ts
|
|
326
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
327
|
-
import path2 from "path";
|
|
328
|
-
import os2 from "os";
|
|
329
|
-
function ensureDir() {
|
|
330
|
-
const dir = path2.dirname(QUEUE_PATH);
|
|
331
|
-
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
|
|
332
|
-
}
|
|
333
|
-
function readQueue() {
|
|
334
|
-
try {
|
|
335
|
-
if (!existsSync3(QUEUE_PATH)) return [];
|
|
336
|
-
return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
|
|
337
|
-
} catch {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function writeQueue(queue) {
|
|
342
|
-
ensureDir();
|
|
343
|
-
const tmp = `${QUEUE_PATH}.tmp`;
|
|
344
|
-
writeFileSync2(tmp, JSON.stringify(queue, null, 2));
|
|
345
|
-
renameSync(tmp, QUEUE_PATH);
|
|
346
|
-
}
|
|
347
|
-
function queueIntercom(targetSession, reason) {
|
|
348
|
-
const queue = readQueue();
|
|
349
|
-
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
350
|
-
if (existing) {
|
|
351
|
-
existing.attempts++;
|
|
352
|
-
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
353
|
-
existing.reason = reason;
|
|
354
|
-
} else {
|
|
355
|
-
queue.push({
|
|
356
|
-
targetSession,
|
|
357
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
358
|
-
attempts: 0,
|
|
359
|
-
reason
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
writeQueue(queue);
|
|
363
|
-
}
|
|
364
|
-
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
365
|
-
var init_intercom_queue = __esm({
|
|
366
|
-
"src/lib/intercom-queue.ts"() {
|
|
367
|
-
"use strict";
|
|
368
|
-
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
369
|
-
TTL_MS = 60 * 60 * 1e3;
|
|
370
|
-
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// src/lib/db-retry.ts
|
|
375
|
-
function isBusyError(err) {
|
|
376
|
-
if (err instanceof Error) {
|
|
377
|
-
const msg = err.message.toLowerCase();
|
|
378
|
-
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
function delay(ms) {
|
|
383
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
384
|
-
}
|
|
385
|
-
async function retryOnBusy(fn, label) {
|
|
386
|
-
let lastError;
|
|
387
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
388
|
-
try {
|
|
389
|
-
return await fn();
|
|
390
|
-
} catch (err) {
|
|
391
|
-
lastError = err;
|
|
392
|
-
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
393
|
-
throw err;
|
|
394
|
-
}
|
|
395
|
-
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
396
|
-
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
397
|
-
process.stderr.write(
|
|
398
|
-
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
399
|
-
`
|
|
400
|
-
);
|
|
401
|
-
await delay(backoff + jitter);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
throw lastError;
|
|
405
|
-
}
|
|
406
|
-
function wrapWithRetry(client) {
|
|
407
|
-
return new Proxy(client, {
|
|
408
|
-
get(target, prop, receiver) {
|
|
409
|
-
if (prop === "execute") {
|
|
410
|
-
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
411
|
-
}
|
|
412
|
-
if (prop === "batch") {
|
|
413
|
-
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
414
|
-
}
|
|
415
|
-
return Reflect.get(target, prop, receiver);
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
420
|
-
var init_db_retry = __esm({
|
|
421
|
-
"src/lib/db-retry.ts"() {
|
|
422
|
-
"use strict";
|
|
423
|
-
MAX_RETRIES = 3;
|
|
424
|
-
BASE_DELAY_MS = 200;
|
|
425
|
-
MAX_JITTER_MS = 300;
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
325
|
// src/lib/config.ts
|
|
430
326
|
var config_exports = {};
|
|
431
327
|
__export(config_exports, {
|
|
@@ -443,17 +339,17 @@ __export(config_exports, {
|
|
|
443
339
|
saveConfig: () => saveConfig
|
|
444
340
|
});
|
|
445
341
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
446
|
-
import { readFileSync as
|
|
447
|
-
import
|
|
448
|
-
import
|
|
342
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync } from "fs";
|
|
343
|
+
import path2 from "path";
|
|
344
|
+
import os2 from "os";
|
|
449
345
|
function resolveDataDir() {
|
|
450
346
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
451
347
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
452
|
-
const newDir =
|
|
453
|
-
const legacyDir =
|
|
454
|
-
if (!
|
|
348
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
349
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
350
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
455
351
|
try {
|
|
456
|
-
|
|
352
|
+
renameSync(legacyDir, newDir);
|
|
457
353
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
458
354
|
`);
|
|
459
355
|
} catch {
|
|
@@ -515,9 +411,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
515
411
|
async function loadConfig() {
|
|
516
412
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
517
413
|
await mkdir(dir, { recursive: true });
|
|
518
|
-
const configPath =
|
|
519
|
-
if (!
|
|
520
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
414
|
+
const configPath = path2.join(dir, "config.json");
|
|
415
|
+
if (!existsSync3(configPath)) {
|
|
416
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
521
417
|
}
|
|
522
418
|
const raw = await readFile(configPath, "utf-8");
|
|
523
419
|
try {
|
|
@@ -535,38 +431,38 @@ async function loadConfig() {
|
|
|
535
431
|
normalizeScalingRoadmap(migratedCfg);
|
|
536
432
|
normalizeSessionLifecycle(migratedCfg);
|
|
537
433
|
normalizeAutoUpdate(migratedCfg);
|
|
538
|
-
const config = { ...DEFAULT_CONFIG, dbPath:
|
|
434
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
539
435
|
if (config.dbPath.startsWith("~")) {
|
|
540
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
436
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
541
437
|
}
|
|
542
438
|
return config;
|
|
543
439
|
} catch {
|
|
544
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
440
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
545
441
|
}
|
|
546
442
|
}
|
|
547
443
|
function loadConfigSync() {
|
|
548
444
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
549
|
-
const configPath =
|
|
550
|
-
if (!
|
|
551
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
445
|
+
const configPath = path2.join(dir, "config.json");
|
|
446
|
+
if (!existsSync3(configPath)) {
|
|
447
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
552
448
|
}
|
|
553
449
|
try {
|
|
554
|
-
const raw =
|
|
450
|
+
const raw = readFileSync3(configPath, "utf-8");
|
|
555
451
|
let parsed = JSON.parse(raw);
|
|
556
452
|
parsed = migrateLegacyConfig(parsed);
|
|
557
453
|
const { config: migratedCfg } = migrateConfig(parsed);
|
|
558
454
|
normalizeScalingRoadmap(migratedCfg);
|
|
559
455
|
normalizeSessionLifecycle(migratedCfg);
|
|
560
456
|
normalizeAutoUpdate(migratedCfg);
|
|
561
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
457
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
562
458
|
} catch {
|
|
563
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
459
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
564
460
|
}
|
|
565
461
|
}
|
|
566
462
|
async function saveConfig(config) {
|
|
567
463
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
568
464
|
await mkdir(dir, { recursive: true });
|
|
569
|
-
const configPath =
|
|
465
|
+
const configPath = path2.join(dir, "config.json");
|
|
570
466
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
571
467
|
if (config.cloud?.apiKey) {
|
|
572
468
|
await chmod(configPath, 384);
|
|
@@ -591,10 +487,10 @@ var init_config = __esm({
|
|
|
591
487
|
"src/lib/config.ts"() {
|
|
592
488
|
"use strict";
|
|
593
489
|
EXE_AI_DIR = resolveDataDir();
|
|
594
|
-
DB_PATH =
|
|
595
|
-
MODELS_DIR =
|
|
596
|
-
CONFIG_PATH =
|
|
597
|
-
LEGACY_LANCE_PATH =
|
|
490
|
+
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
491
|
+
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
492
|
+
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
493
|
+
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
598
494
|
CURRENT_CONFIG_VERSION = 1;
|
|
599
495
|
DEFAULT_CONFIG = {
|
|
600
496
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -666,6 +562,161 @@ var init_config = __esm({
|
|
|
666
562
|
}
|
|
667
563
|
});
|
|
668
564
|
|
|
565
|
+
// src/lib/runtime-table.ts
|
|
566
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
567
|
+
var init_runtime_table = __esm({
|
|
568
|
+
"src/lib/runtime-table.ts"() {
|
|
569
|
+
"use strict";
|
|
570
|
+
RUNTIME_TABLE = {
|
|
571
|
+
codex: {
|
|
572
|
+
binary: "codex",
|
|
573
|
+
launchMode: "exec",
|
|
574
|
+
autoApproveFlag: "--full-auto",
|
|
575
|
+
inlineFlag: "--no-alt-screen",
|
|
576
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
577
|
+
defaultModel: "gpt-5.4"
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
DEFAULT_RUNTIME = "claude";
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// src/lib/agent-config.ts
|
|
585
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
586
|
+
import path3 from "path";
|
|
587
|
+
function loadAgentConfig() {
|
|
588
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
589
|
+
try {
|
|
590
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
591
|
+
} catch {
|
|
592
|
+
return {};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function getAgentRuntime(agentId) {
|
|
596
|
+
const config = loadAgentConfig();
|
|
597
|
+
const entry = config[agentId];
|
|
598
|
+
if (entry) return entry;
|
|
599
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
600
|
+
}
|
|
601
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
602
|
+
var init_agent_config = __esm({
|
|
603
|
+
"src/lib/agent-config.ts"() {
|
|
604
|
+
"use strict";
|
|
605
|
+
init_config();
|
|
606
|
+
init_runtime_table();
|
|
607
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
608
|
+
DEFAULT_MODELS = {
|
|
609
|
+
claude: "claude-opus-4",
|
|
610
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
611
|
+
opencode: "minimax-m2.7"
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// src/lib/intercom-queue.ts
|
|
617
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
618
|
+
import path4 from "path";
|
|
619
|
+
import os3 from "os";
|
|
620
|
+
function ensureDir() {
|
|
621
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
622
|
+
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
623
|
+
}
|
|
624
|
+
function readQueue() {
|
|
625
|
+
try {
|
|
626
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
627
|
+
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
628
|
+
} catch {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function writeQueue(queue) {
|
|
633
|
+
ensureDir();
|
|
634
|
+
const tmp = `${QUEUE_PATH}.tmp`;
|
|
635
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
636
|
+
renameSync2(tmp, QUEUE_PATH);
|
|
637
|
+
}
|
|
638
|
+
function queueIntercom(targetSession, reason) {
|
|
639
|
+
const queue = readQueue();
|
|
640
|
+
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
641
|
+
if (existing) {
|
|
642
|
+
existing.attempts++;
|
|
643
|
+
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
644
|
+
existing.reason = reason;
|
|
645
|
+
} else {
|
|
646
|
+
queue.push({
|
|
647
|
+
targetSession,
|
|
648
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
649
|
+
attempts: 0,
|
|
650
|
+
reason
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
writeQueue(queue);
|
|
654
|
+
}
|
|
655
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
656
|
+
var init_intercom_queue = __esm({
|
|
657
|
+
"src/lib/intercom-queue.ts"() {
|
|
658
|
+
"use strict";
|
|
659
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
660
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
661
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// src/lib/db-retry.ts
|
|
666
|
+
function isBusyError(err) {
|
|
667
|
+
if (err instanceof Error) {
|
|
668
|
+
const msg = err.message.toLowerCase();
|
|
669
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
function delay(ms) {
|
|
674
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
675
|
+
}
|
|
676
|
+
async function retryOnBusy(fn, label) {
|
|
677
|
+
let lastError;
|
|
678
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
679
|
+
try {
|
|
680
|
+
return await fn();
|
|
681
|
+
} catch (err) {
|
|
682
|
+
lastError = err;
|
|
683
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
684
|
+
throw err;
|
|
685
|
+
}
|
|
686
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
687
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
688
|
+
process.stderr.write(
|
|
689
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
690
|
+
`
|
|
691
|
+
);
|
|
692
|
+
await delay(backoff + jitter);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
throw lastError;
|
|
696
|
+
}
|
|
697
|
+
function wrapWithRetry(client) {
|
|
698
|
+
return new Proxy(client, {
|
|
699
|
+
get(target, prop, receiver) {
|
|
700
|
+
if (prop === "execute") {
|
|
701
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
702
|
+
}
|
|
703
|
+
if (prop === "batch") {
|
|
704
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
705
|
+
}
|
|
706
|
+
return Reflect.get(target, prop, receiver);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
711
|
+
var init_db_retry = __esm({
|
|
712
|
+
"src/lib/db-retry.ts"() {
|
|
713
|
+
"use strict";
|
|
714
|
+
MAX_RETRIES = 3;
|
|
715
|
+
BASE_DELAY_MS = 200;
|
|
716
|
+
MAX_JITTER_MS = 300;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
669
720
|
// src/lib/employees.ts
|
|
670
721
|
var employees_exports = {};
|
|
671
722
|
__export(employees_exports, {
|
|
@@ -673,6 +724,7 @@ __export(employees_exports, {
|
|
|
673
724
|
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
674
725
|
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
675
726
|
addEmployee: () => addEmployee,
|
|
727
|
+
baseAgentName: () => baseAgentName,
|
|
676
728
|
canCoordinate: () => canCoordinate,
|
|
677
729
|
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
678
730
|
getCoordinatorName: () => getCoordinatorName,
|
|
@@ -692,9 +744,9 @@ __export(employees_exports, {
|
|
|
692
744
|
validateEmployeeName: () => validateEmployeeName
|
|
693
745
|
});
|
|
694
746
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
695
|
-
import { existsSync as
|
|
747
|
+
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
696
748
|
import { execSync as execSync4 } from "child_process";
|
|
697
|
-
import
|
|
749
|
+
import path5 from "path";
|
|
698
750
|
import os4 from "os";
|
|
699
751
|
function normalizeRole(role) {
|
|
700
752
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -731,7 +783,7 @@ function validateEmployeeName(name) {
|
|
|
731
783
|
return { valid: true };
|
|
732
784
|
}
|
|
733
785
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
734
|
-
if (!
|
|
786
|
+
if (!existsSync6(employeesPath)) {
|
|
735
787
|
return [];
|
|
736
788
|
}
|
|
737
789
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -742,13 +794,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
742
794
|
}
|
|
743
795
|
}
|
|
744
796
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
745
|
-
await mkdir2(
|
|
797
|
+
await mkdir2(path5.dirname(employeesPath), { recursive: true });
|
|
746
798
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
747
799
|
}
|
|
748
800
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
749
|
-
if (!
|
|
801
|
+
if (!existsSync6(employeesPath)) return [];
|
|
750
802
|
try {
|
|
751
|
-
return JSON.parse(
|
|
803
|
+
return JSON.parse(readFileSync6(employeesPath, "utf-8"));
|
|
752
804
|
} catch {
|
|
753
805
|
return [];
|
|
754
806
|
}
|
|
@@ -769,6 +821,14 @@ function hasRole(agentName, role) {
|
|
|
769
821
|
const emp = getEmployee(employees, agentName);
|
|
770
822
|
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
771
823
|
}
|
|
824
|
+
function baseAgentName(name, employees) {
|
|
825
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
826
|
+
if (!match) return name;
|
|
827
|
+
const base = match[1];
|
|
828
|
+
const roster = employees ?? loadEmployeesSync();
|
|
829
|
+
if (getEmployee(roster, base)) return base;
|
|
830
|
+
return name;
|
|
831
|
+
}
|
|
772
832
|
function isMultiInstance(agentName, employees) {
|
|
773
833
|
const roster = employees ?? loadEmployeesSync();
|
|
774
834
|
const emp = getEmployee(roster, agentName);
|
|
@@ -791,14 +851,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
791
851
|
emp.name = emp.name.toLowerCase();
|
|
792
852
|
changed = true;
|
|
793
853
|
try {
|
|
794
|
-
const identityDir =
|
|
795
|
-
const oldPath =
|
|
796
|
-
const newPath =
|
|
797
|
-
if (
|
|
854
|
+
const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
|
|
855
|
+
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
856
|
+
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
857
|
+
if (existsSync6(oldPath) && !existsSync6(newPath)) {
|
|
798
858
|
renameSync3(oldPath, newPath);
|
|
799
|
-
} else if (
|
|
800
|
-
const content =
|
|
801
|
-
|
|
859
|
+
} else if (existsSync6(oldPath) && oldPath !== newPath) {
|
|
860
|
+
const content = readFileSync6(oldPath, "utf-8");
|
|
861
|
+
writeFileSync4(newPath, content, "utf-8");
|
|
802
862
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
803
863
|
unlinkSync(oldPath);
|
|
804
864
|
}
|
|
@@ -819,48 +879,485 @@ function findExeBin() {
|
|
|
819
879
|
return null;
|
|
820
880
|
}
|
|
821
881
|
}
|
|
822
|
-
function registerBinSymlinks(name) {
|
|
823
|
-
const created = [];
|
|
824
|
-
const skipped = [];
|
|
825
|
-
const errors = [];
|
|
826
|
-
const exeBinPath = findExeBin();
|
|
827
|
-
if (!exeBinPath) {
|
|
828
|
-
errors.push("Could not find 'exe-os' in PATH");
|
|
829
|
-
return { created, skipped, errors };
|
|
882
|
+
function registerBinSymlinks(name) {
|
|
883
|
+
const created = [];
|
|
884
|
+
const skipped = [];
|
|
885
|
+
const errors = [];
|
|
886
|
+
const exeBinPath = findExeBin();
|
|
887
|
+
if (!exeBinPath) {
|
|
888
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
889
|
+
return { created, skipped, errors };
|
|
890
|
+
}
|
|
891
|
+
const binDir = path5.dirname(exeBinPath);
|
|
892
|
+
let target;
|
|
893
|
+
try {
|
|
894
|
+
target = readlinkSync(exeBinPath);
|
|
895
|
+
} catch {
|
|
896
|
+
errors.push("Could not read 'exe' symlink");
|
|
897
|
+
return { created, skipped, errors };
|
|
898
|
+
}
|
|
899
|
+
for (const suffix of ["", "-opencode"]) {
|
|
900
|
+
const linkName = `${name}${suffix}`;
|
|
901
|
+
const linkPath = path5.join(binDir, linkName);
|
|
902
|
+
if (existsSync6(linkPath)) {
|
|
903
|
+
skipped.push(linkName);
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
symlinkSync(target, linkPath);
|
|
908
|
+
created.push(linkName);
|
|
909
|
+
} catch (err) {
|
|
910
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return { created, skipped, errors };
|
|
914
|
+
}
|
|
915
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
916
|
+
var init_employees = __esm({
|
|
917
|
+
"src/lib/employees.ts"() {
|
|
918
|
+
"use strict";
|
|
919
|
+
init_config();
|
|
920
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
921
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
922
|
+
COORDINATOR_ROLE = "COO";
|
|
923
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// src/lib/exe-daemon-client.ts
|
|
928
|
+
import net from "net";
|
|
929
|
+
import { spawn } from "child_process";
|
|
930
|
+
import { randomUUID } from "crypto";
|
|
931
|
+
import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
932
|
+
import path6 from "path";
|
|
933
|
+
import { fileURLToPath } from "url";
|
|
934
|
+
function handleData(chunk) {
|
|
935
|
+
_buffer += chunk.toString();
|
|
936
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
937
|
+
_buffer = "";
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
let newlineIdx;
|
|
941
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
942
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
943
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
944
|
+
if (!line) continue;
|
|
945
|
+
try {
|
|
946
|
+
const response = JSON.parse(line);
|
|
947
|
+
const id = response.id;
|
|
948
|
+
if (!id) continue;
|
|
949
|
+
const entry = _pending.get(id);
|
|
950
|
+
if (entry) {
|
|
951
|
+
clearTimeout(entry.timer);
|
|
952
|
+
_pending.delete(id);
|
|
953
|
+
entry.resolve(response);
|
|
954
|
+
}
|
|
955
|
+
} catch {
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
function cleanupStaleFiles() {
|
|
960
|
+
if (existsSync7(PID_PATH)) {
|
|
961
|
+
try {
|
|
962
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
963
|
+
if (pid > 0) {
|
|
964
|
+
try {
|
|
965
|
+
process.kill(pid, 0);
|
|
966
|
+
return;
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
} catch {
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
unlinkSync2(PID_PATH);
|
|
974
|
+
} catch {
|
|
975
|
+
}
|
|
976
|
+
try {
|
|
977
|
+
unlinkSync2(SOCKET_PATH);
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function findPackageRoot() {
|
|
983
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
984
|
+
const { root } = path6.parse(dir);
|
|
985
|
+
while (dir !== root) {
|
|
986
|
+
if (existsSync7(path6.join(dir, "package.json"))) return dir;
|
|
987
|
+
dir = path6.dirname(dir);
|
|
988
|
+
}
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
function spawnDaemon() {
|
|
992
|
+
const pkgRoot = findPackageRoot();
|
|
993
|
+
if (!pkgRoot) {
|
|
994
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
998
|
+
if (!existsSync7(daemonPath)) {
|
|
999
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1000
|
+
`);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const resolvedPath = daemonPath;
|
|
1004
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1005
|
+
`);
|
|
1006
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1007
|
+
let stderrFd = "ignore";
|
|
1008
|
+
try {
|
|
1009
|
+
stderrFd = openSync(logPath, "a");
|
|
1010
|
+
} catch {
|
|
1011
|
+
}
|
|
1012
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
1013
|
+
detached: true,
|
|
1014
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
1015
|
+
env: {
|
|
1016
|
+
...process.env,
|
|
1017
|
+
TMUX: void 0,
|
|
1018
|
+
// Daemon is global — must not inherit session scope
|
|
1019
|
+
TMUX_PANE: void 0,
|
|
1020
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
1021
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1022
|
+
EXE_DAEMON_PID: PID_PATH
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
child.unref();
|
|
1026
|
+
if (typeof stderrFd === "number") {
|
|
1027
|
+
try {
|
|
1028
|
+
closeSync(stderrFd);
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function acquireSpawnLock() {
|
|
1034
|
+
try {
|
|
1035
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1036
|
+
closeSync(fd);
|
|
1037
|
+
return true;
|
|
1038
|
+
} catch {
|
|
1039
|
+
try {
|
|
1040
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
1041
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
1042
|
+
try {
|
|
1043
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1044
|
+
} catch {
|
|
1045
|
+
}
|
|
1046
|
+
try {
|
|
1047
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1048
|
+
closeSync(fd);
|
|
1049
|
+
return true;
|
|
1050
|
+
} catch {
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
function releaseSpawnLock() {
|
|
1059
|
+
try {
|
|
1060
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1061
|
+
} catch {
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
function connectToSocket() {
|
|
1065
|
+
return new Promise((resolve) => {
|
|
1066
|
+
if (_socket && _connected) {
|
|
1067
|
+
resolve(true);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
1071
|
+
const connectTimeout = setTimeout(() => {
|
|
1072
|
+
socket.destroy();
|
|
1073
|
+
resolve(false);
|
|
1074
|
+
}, 2e3);
|
|
1075
|
+
socket.on("connect", () => {
|
|
1076
|
+
clearTimeout(connectTimeout);
|
|
1077
|
+
_socket = socket;
|
|
1078
|
+
_connected = true;
|
|
1079
|
+
_buffer = "";
|
|
1080
|
+
socket.on("data", handleData);
|
|
1081
|
+
socket.on("close", () => {
|
|
1082
|
+
_connected = false;
|
|
1083
|
+
_socket = null;
|
|
1084
|
+
for (const [id, entry] of _pending) {
|
|
1085
|
+
clearTimeout(entry.timer);
|
|
1086
|
+
_pending.delete(id);
|
|
1087
|
+
entry.resolve({ error: "Connection closed" });
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
socket.on("error", () => {
|
|
1091
|
+
_connected = false;
|
|
1092
|
+
_socket = null;
|
|
1093
|
+
});
|
|
1094
|
+
resolve(true);
|
|
1095
|
+
});
|
|
1096
|
+
socket.on("error", () => {
|
|
1097
|
+
clearTimeout(connectTimeout);
|
|
1098
|
+
resolve(false);
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
async function connectEmbedDaemon() {
|
|
1103
|
+
if (_socket && _connected) return true;
|
|
1104
|
+
if (await connectToSocket()) return true;
|
|
1105
|
+
if (acquireSpawnLock()) {
|
|
1106
|
+
try {
|
|
1107
|
+
cleanupStaleFiles();
|
|
1108
|
+
spawnDaemon();
|
|
1109
|
+
} finally {
|
|
1110
|
+
releaseSpawnLock();
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
const start = Date.now();
|
|
1114
|
+
let delay2 = 100;
|
|
1115
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1116
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1117
|
+
if (await connectToSocket()) return true;
|
|
1118
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1119
|
+
}
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1123
|
+
return new Promise((resolve) => {
|
|
1124
|
+
if (!_socket || !_connected) {
|
|
1125
|
+
resolve({ error: "Not connected" });
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
const id = randomUUID();
|
|
1129
|
+
const timer = setTimeout(() => {
|
|
1130
|
+
_pending.delete(id);
|
|
1131
|
+
resolve({ error: "Request timeout" });
|
|
1132
|
+
}, timeoutMs);
|
|
1133
|
+
_pending.set(id, { resolve, timer });
|
|
1134
|
+
try {
|
|
1135
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1136
|
+
} catch {
|
|
1137
|
+
clearTimeout(timer);
|
|
1138
|
+
_pending.delete(id);
|
|
1139
|
+
resolve({ error: "Write failed" });
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
function isClientConnected() {
|
|
1144
|
+
return _connected;
|
|
1145
|
+
}
|
|
1146
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1147
|
+
var init_exe_daemon_client = __esm({
|
|
1148
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
1149
|
+
"use strict";
|
|
1150
|
+
init_config();
|
|
1151
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1152
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1153
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1154
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1155
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
1156
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
1157
|
+
_socket = null;
|
|
1158
|
+
_connected = false;
|
|
1159
|
+
_buffer = "";
|
|
1160
|
+
_pending = /* @__PURE__ */ new Map();
|
|
1161
|
+
MAX_BUFFER = 1e7;
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
// src/lib/daemon-protocol.ts
|
|
1166
|
+
function serializeValue(v) {
|
|
1167
|
+
if (v === null || v === void 0) return null;
|
|
1168
|
+
if (typeof v === "bigint") return Number(v);
|
|
1169
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
1170
|
+
if (v instanceof Uint8Array) {
|
|
1171
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1172
|
+
}
|
|
1173
|
+
if (ArrayBuffer.isView(v)) {
|
|
1174
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
1175
|
+
}
|
|
1176
|
+
if (v instanceof ArrayBuffer) {
|
|
1177
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1178
|
+
}
|
|
1179
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1180
|
+
return String(v);
|
|
1181
|
+
}
|
|
1182
|
+
function deserializeValue(v) {
|
|
1183
|
+
if (v === null) return null;
|
|
1184
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
1185
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
1186
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
830
1187
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
1188
|
+
return v;
|
|
1189
|
+
}
|
|
1190
|
+
function deserializeResultSet(srs) {
|
|
1191
|
+
const rows = srs.rows.map((obj) => {
|
|
1192
|
+
const values = srs.columns.map(
|
|
1193
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
1194
|
+
);
|
|
1195
|
+
const row = values;
|
|
1196
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
1197
|
+
const col = srs.columns[i];
|
|
1198
|
+
if (col !== void 0) {
|
|
1199
|
+
row[col] = values[i] ?? null;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
Object.defineProperty(row, "length", {
|
|
1203
|
+
value: values.length,
|
|
1204
|
+
enumerable: false
|
|
1205
|
+
});
|
|
1206
|
+
return row;
|
|
1207
|
+
});
|
|
1208
|
+
return {
|
|
1209
|
+
columns: srs.columns,
|
|
1210
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1211
|
+
rows,
|
|
1212
|
+
rowsAffected: srs.rowsAffected,
|
|
1213
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
1214
|
+
toJSON: () => ({
|
|
1215
|
+
columns: srs.columns,
|
|
1216
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1217
|
+
rows: srs.rows,
|
|
1218
|
+
rowsAffected: srs.rowsAffected,
|
|
1219
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
1220
|
+
})
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
var init_daemon_protocol = __esm({
|
|
1224
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1225
|
+
"use strict";
|
|
838
1226
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
// src/lib/db-daemon-client.ts
|
|
1230
|
+
var db_daemon_client_exports = {};
|
|
1231
|
+
__export(db_daemon_client_exports, {
|
|
1232
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1233
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1234
|
+
});
|
|
1235
|
+
function normalizeStatement(stmt) {
|
|
1236
|
+
if (typeof stmt === "string") {
|
|
1237
|
+
return { sql: stmt, args: [] };
|
|
1238
|
+
}
|
|
1239
|
+
const sql = stmt.sql;
|
|
1240
|
+
let args = [];
|
|
1241
|
+
if (Array.isArray(stmt.args)) {
|
|
1242
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
1243
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1244
|
+
const named = {};
|
|
1245
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1246
|
+
named[key] = serializeValue(val);
|
|
1247
|
+
}
|
|
1248
|
+
return { sql, args: named };
|
|
1249
|
+
}
|
|
1250
|
+
return { sql, args };
|
|
1251
|
+
}
|
|
1252
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1253
|
+
let _useDaemon = false;
|
|
1254
|
+
const client = {
|
|
1255
|
+
async execute(stmt) {
|
|
1256
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1257
|
+
return fallbackClient.execute(stmt);
|
|
1258
|
+
}
|
|
1259
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
1260
|
+
const response = await sendDaemonRequest({
|
|
1261
|
+
type: "db-execute",
|
|
1262
|
+
sql,
|
|
1263
|
+
args
|
|
1264
|
+
});
|
|
1265
|
+
if (response.error) {
|
|
1266
|
+
const errMsg = String(response.error);
|
|
1267
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1268
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1269
|
+
`);
|
|
1270
|
+
return fallbackClient.execute(stmt);
|
|
1271
|
+
}
|
|
1272
|
+
throw new Error(errMsg);
|
|
1273
|
+
}
|
|
1274
|
+
if (response.db) {
|
|
1275
|
+
return deserializeResultSet(response.db);
|
|
1276
|
+
}
|
|
1277
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1278
|
+
return fallbackClient.execute(stmt);
|
|
1279
|
+
},
|
|
1280
|
+
async batch(stmts, mode) {
|
|
1281
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1282
|
+
return fallbackClient.batch(stmts, mode);
|
|
1283
|
+
}
|
|
1284
|
+
const statements = stmts.map(normalizeStatement);
|
|
1285
|
+
const response = await sendDaemonRequest({
|
|
1286
|
+
type: "db-batch",
|
|
1287
|
+
statements,
|
|
1288
|
+
mode: mode ?? "deferred"
|
|
1289
|
+
});
|
|
1290
|
+
if (response.error) {
|
|
1291
|
+
const errMsg = String(response.error);
|
|
1292
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1293
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1294
|
+
`);
|
|
1295
|
+
return fallbackClient.batch(stmts, mode);
|
|
1296
|
+
}
|
|
1297
|
+
throw new Error(errMsg);
|
|
1298
|
+
}
|
|
1299
|
+
const batchResults = response["db-batch"];
|
|
1300
|
+
if (batchResults) {
|
|
1301
|
+
return batchResults.map(deserializeResultSet);
|
|
1302
|
+
}
|
|
1303
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1304
|
+
return fallbackClient.batch(stmts, mode);
|
|
1305
|
+
},
|
|
1306
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1307
|
+
async transaction(mode) {
|
|
1308
|
+
return fallbackClient.transaction(mode);
|
|
1309
|
+
},
|
|
1310
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1311
|
+
async executeMultiple(sql) {
|
|
1312
|
+
return fallbackClient.executeMultiple(sql);
|
|
1313
|
+
},
|
|
1314
|
+
// migrate — delegate to fallback
|
|
1315
|
+
async migrate(stmts) {
|
|
1316
|
+
return fallbackClient.migrate(stmts);
|
|
1317
|
+
},
|
|
1318
|
+
// Sync mode — delegate to fallback
|
|
1319
|
+
sync() {
|
|
1320
|
+
return fallbackClient.sync();
|
|
1321
|
+
},
|
|
1322
|
+
close() {
|
|
1323
|
+
_useDaemon = false;
|
|
1324
|
+
},
|
|
1325
|
+
get closed() {
|
|
1326
|
+
return fallbackClient.closed;
|
|
1327
|
+
},
|
|
1328
|
+
get protocol() {
|
|
1329
|
+
return fallbackClient.protocol;
|
|
845
1330
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1331
|
+
};
|
|
1332
|
+
return {
|
|
1333
|
+
...client,
|
|
1334
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1335
|
+
_enableDaemon() {
|
|
1336
|
+
_useDaemon = true;
|
|
1337
|
+
},
|
|
1338
|
+
/** Check if daemon routing is active */
|
|
1339
|
+
_isDaemonActive() {
|
|
1340
|
+
return _useDaemon && isClientConnected();
|
|
851
1341
|
}
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1345
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1346
|
+
const connected = await connectEmbedDaemon();
|
|
1347
|
+
if (!connected) {
|
|
1348
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1349
|
+
return null;
|
|
852
1350
|
}
|
|
853
|
-
|
|
1351
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1352
|
+
client._enableDaemon();
|
|
1353
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1354
|
+
return client;
|
|
854
1355
|
}
|
|
855
|
-
var
|
|
856
|
-
|
|
857
|
-
"src/lib/employees.ts"() {
|
|
1356
|
+
var init_db_daemon_client = __esm({
|
|
1357
|
+
"src/lib/db-daemon-client.ts"() {
|
|
858
1358
|
"use strict";
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
862
|
-
COORDINATOR_ROLE = "COO";
|
|
863
|
-
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1359
|
+
init_exe_daemon_client();
|
|
1360
|
+
init_daemon_protocol();
|
|
864
1361
|
}
|
|
865
1362
|
});
|
|
866
1363
|
|
|
@@ -872,6 +1369,7 @@ __export(database_exports, {
|
|
|
872
1369
|
ensureSchema: () => ensureSchema,
|
|
873
1370
|
getClient: () => getClient,
|
|
874
1371
|
getRawClient: () => getRawClient,
|
|
1372
|
+
initDaemonClient: () => initDaemonClient,
|
|
875
1373
|
initDatabase: () => initDatabase,
|
|
876
1374
|
initTurso: () => initTurso,
|
|
877
1375
|
isInitialized: () => isInitialized
|
|
@@ -899,8 +1397,27 @@ function getClient() {
|
|
|
899
1397
|
if (!_resilientClient) {
|
|
900
1398
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
901
1399
|
}
|
|
1400
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1401
|
+
return _resilientClient;
|
|
1402
|
+
}
|
|
1403
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1404
|
+
return _daemonClient;
|
|
1405
|
+
}
|
|
902
1406
|
return _resilientClient;
|
|
903
1407
|
}
|
|
1408
|
+
async function initDaemonClient() {
|
|
1409
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1410
|
+
if (!_resilientClient) return;
|
|
1411
|
+
try {
|
|
1412
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1413
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
process.stderr.write(
|
|
1416
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1417
|
+
`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
904
1421
|
function getRawClient() {
|
|
905
1422
|
if (!_client) {
|
|
906
1423
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1387,6 +1904,12 @@ async function ensureSchema() {
|
|
|
1387
1904
|
} catch {
|
|
1388
1905
|
}
|
|
1389
1906
|
}
|
|
1907
|
+
try {
|
|
1908
|
+
await client.execute(
|
|
1909
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1910
|
+
);
|
|
1911
|
+
} catch {
|
|
1912
|
+
}
|
|
1390
1913
|
await client.executeMultiple(`
|
|
1391
1914
|
CREATE TABLE IF NOT EXISTS entities (
|
|
1392
1915
|
id TEXT PRIMARY KEY,
|
|
@@ -1439,7 +1962,30 @@ async function ensureSchema() {
|
|
|
1439
1962
|
entity_id TEXT NOT NULL,
|
|
1440
1963
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1441
1964
|
);
|
|
1965
|
+
|
|
1966
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1967
|
+
name,
|
|
1968
|
+
content=entities,
|
|
1969
|
+
content_rowid=rowid
|
|
1970
|
+
);
|
|
1971
|
+
|
|
1972
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1973
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1974
|
+
END;
|
|
1975
|
+
|
|
1976
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1977
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1978
|
+
END;
|
|
1979
|
+
|
|
1980
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1981
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1982
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1983
|
+
END;
|
|
1442
1984
|
`);
|
|
1985
|
+
try {
|
|
1986
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1987
|
+
} catch {
|
|
1988
|
+
}
|
|
1443
1989
|
await client.executeMultiple(`
|
|
1444
1990
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1445
1991
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1620,6 +2166,33 @@ async function ensureSchema() {
|
|
|
1620
2166
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1621
2167
|
ON conversations(channel_id);
|
|
1622
2168
|
`);
|
|
2169
|
+
await client.executeMultiple(`
|
|
2170
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
2171
|
+
session_uuid TEXT PRIMARY KEY,
|
|
2172
|
+
agent_id TEXT NOT NULL,
|
|
2173
|
+
session_name TEXT,
|
|
2174
|
+
task_id TEXT,
|
|
2175
|
+
project_name TEXT,
|
|
2176
|
+
started_at TEXT NOT NULL
|
|
2177
|
+
);
|
|
2178
|
+
|
|
2179
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
2180
|
+
ON session_agent_map(agent_id);
|
|
2181
|
+
`);
|
|
2182
|
+
try {
|
|
2183
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
2184
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
2185
|
+
await client.execute({
|
|
2186
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
2187
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
2188
|
+
FROM memories
|
|
2189
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
2190
|
+
GROUP BY session_id, agent_id`,
|
|
2191
|
+
args: []
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
} catch {
|
|
2195
|
+
}
|
|
1623
2196
|
try {
|
|
1624
2197
|
await client.execute({
|
|
1625
2198
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1753,15 +2326,41 @@ async function ensureSchema() {
|
|
|
1753
2326
|
});
|
|
1754
2327
|
} catch {
|
|
1755
2328
|
}
|
|
2329
|
+
for (const col of [
|
|
2330
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2331
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2332
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2333
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2334
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2335
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2336
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2337
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2338
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2339
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2340
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2341
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2342
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2343
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2344
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2345
|
+
]) {
|
|
2346
|
+
try {
|
|
2347
|
+
await client.execute(col);
|
|
2348
|
+
} catch {
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
1756
2351
|
}
|
|
1757
2352
|
async function disposeDatabase() {
|
|
2353
|
+
if (_daemonClient) {
|
|
2354
|
+
_daemonClient.close();
|
|
2355
|
+
_daemonClient = null;
|
|
2356
|
+
}
|
|
1758
2357
|
if (_client) {
|
|
1759
2358
|
_client.close();
|
|
1760
2359
|
_client = null;
|
|
1761
2360
|
_resilientClient = null;
|
|
1762
2361
|
}
|
|
1763
2362
|
}
|
|
1764
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
2363
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1765
2364
|
var init_database = __esm({
|
|
1766
2365
|
"src/lib/database.ts"() {
|
|
1767
2366
|
"use strict";
|
|
@@ -1769,6 +2368,7 @@ var init_database = __esm({
|
|
|
1769
2368
|
init_employees();
|
|
1770
2369
|
_client = null;
|
|
1771
2370
|
_resilientClient = null;
|
|
2371
|
+
_daemonClient = null;
|
|
1772
2372
|
initTurso = initDatabase;
|
|
1773
2373
|
disposeTurso = disposeDatabase;
|
|
1774
2374
|
}
|
|
@@ -1791,9 +2391,9 @@ __export(license_exports, {
|
|
|
1791
2391
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
1792
2392
|
validateLicense: () => validateLicense
|
|
1793
2393
|
});
|
|
1794
|
-
import { readFileSync as
|
|
1795
|
-
import { randomUUID } from "crypto";
|
|
1796
|
-
import
|
|
2394
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2395
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2396
|
+
import path7 from "path";
|
|
1797
2397
|
import { jwtVerify, importSPKI } from "jose";
|
|
1798
2398
|
async function fetchRetry(url, init) {
|
|
1799
2399
|
try {
|
|
@@ -1804,37 +2404,37 @@ async function fetchRetry(url, init) {
|
|
|
1804
2404
|
}
|
|
1805
2405
|
}
|
|
1806
2406
|
function loadDeviceId() {
|
|
1807
|
-
const deviceJsonPath =
|
|
2407
|
+
const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
|
|
1808
2408
|
try {
|
|
1809
|
-
if (
|
|
1810
|
-
const data = JSON.parse(
|
|
2409
|
+
if (existsSync8(deviceJsonPath)) {
|
|
2410
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
1811
2411
|
if (data.deviceId) return data.deviceId;
|
|
1812
2412
|
}
|
|
1813
2413
|
} catch {
|
|
1814
2414
|
}
|
|
1815
2415
|
try {
|
|
1816
|
-
if (
|
|
1817
|
-
const id2 =
|
|
2416
|
+
if (existsSync8(DEVICE_ID_PATH)) {
|
|
2417
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
1818
2418
|
if (id2) return id2;
|
|
1819
2419
|
}
|
|
1820
2420
|
} catch {
|
|
1821
2421
|
}
|
|
1822
|
-
const id =
|
|
1823
|
-
|
|
1824
|
-
|
|
2422
|
+
const id = randomUUID2();
|
|
2423
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
2424
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
1825
2425
|
return id;
|
|
1826
2426
|
}
|
|
1827
2427
|
function loadLicense() {
|
|
1828
2428
|
try {
|
|
1829
|
-
if (!
|
|
1830
|
-
return
|
|
2429
|
+
if (!existsSync8(LICENSE_PATH)) return null;
|
|
2430
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
1831
2431
|
} catch {
|
|
1832
2432
|
return null;
|
|
1833
2433
|
}
|
|
1834
2434
|
}
|
|
1835
2435
|
function saveLicense(apiKey) {
|
|
1836
|
-
|
|
1837
|
-
|
|
2436
|
+
mkdirSync4(EXE_AI_DIR, { recursive: true });
|
|
2437
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
1838
2438
|
}
|
|
1839
2439
|
async function verifyLicenseJwt(token) {
|
|
1840
2440
|
try {
|
|
@@ -1860,8 +2460,8 @@ async function verifyLicenseJwt(token) {
|
|
|
1860
2460
|
}
|
|
1861
2461
|
async function getCachedLicense() {
|
|
1862
2462
|
try {
|
|
1863
|
-
if (!
|
|
1864
|
-
const raw = JSON.parse(
|
|
2463
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
2464
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
1865
2465
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
1866
2466
|
return await verifyLicenseJwt(raw.token);
|
|
1867
2467
|
} catch {
|
|
@@ -1870,8 +2470,8 @@ async function getCachedLicense() {
|
|
|
1870
2470
|
}
|
|
1871
2471
|
function readCachedToken() {
|
|
1872
2472
|
try {
|
|
1873
|
-
if (!
|
|
1874
|
-
const raw = JSON.parse(
|
|
2473
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
2474
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
1875
2475
|
return typeof raw.token === "string" ? raw.token : null;
|
|
1876
2476
|
} catch {
|
|
1877
2477
|
return null;
|
|
@@ -1905,7 +2505,7 @@ function getRawCachedPlan() {
|
|
|
1905
2505
|
}
|
|
1906
2506
|
function cacheResponse(token) {
|
|
1907
2507
|
try {
|
|
1908
|
-
|
|
2508
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
1909
2509
|
} catch {
|
|
1910
2510
|
}
|
|
1911
2511
|
}
|
|
@@ -1958,8 +2558,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
1958
2558
|
}
|
|
1959
2559
|
function getCacheAgeMs() {
|
|
1960
2560
|
try {
|
|
1961
|
-
const { statSync } = __require("fs");
|
|
1962
|
-
const s =
|
|
2561
|
+
const { statSync: statSync2 } = __require("fs");
|
|
2562
|
+
const s = statSync2(CACHE_PATH);
|
|
1963
2563
|
return Date.now() - s.mtimeMs;
|
|
1964
2564
|
} catch {
|
|
1965
2565
|
return Infinity;
|
|
@@ -1969,9 +2569,9 @@ async function checkLicense() {
|
|
|
1969
2569
|
let key = loadLicense();
|
|
1970
2570
|
if (!key) {
|
|
1971
2571
|
try {
|
|
1972
|
-
const configPath =
|
|
1973
|
-
if (
|
|
1974
|
-
const raw = JSON.parse(
|
|
2572
|
+
const configPath = path7.join(EXE_AI_DIR, "config.json");
|
|
2573
|
+
if (existsSync8(configPath)) {
|
|
2574
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
1975
2575
|
const cloud = raw.cloud;
|
|
1976
2576
|
if (cloud?.apiKey) {
|
|
1977
2577
|
key = cloud.apiKey;
|
|
@@ -2130,9 +2730,9 @@ var init_license = __esm({
|
|
|
2130
2730
|
"src/lib/license.ts"() {
|
|
2131
2731
|
"use strict";
|
|
2132
2732
|
init_config();
|
|
2133
|
-
LICENSE_PATH =
|
|
2134
|
-
CACHE_PATH =
|
|
2135
|
-
DEVICE_ID_PATH =
|
|
2733
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2734
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2735
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
2136
2736
|
API_BASE = "https://askexe.com/cloud";
|
|
2137
2737
|
RETRY_DELAY_MS = 500;
|
|
2138
2738
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -2162,12 +2762,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2162
2762
|
});
|
|
2163
2763
|
|
|
2164
2764
|
// src/lib/plan-limits.ts
|
|
2165
|
-
import { readFileSync as
|
|
2166
|
-
import
|
|
2765
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2766
|
+
import path8 from "path";
|
|
2167
2767
|
function getLicenseSync() {
|
|
2168
2768
|
try {
|
|
2169
|
-
if (!
|
|
2170
|
-
const raw = JSON.parse(
|
|
2769
|
+
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
2770
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
2171
2771
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2172
2772
|
const parts = raw.token.split(".");
|
|
2173
2773
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2205,8 +2805,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2205
2805
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2206
2806
|
let count = 0;
|
|
2207
2807
|
try {
|
|
2208
|
-
if (
|
|
2209
|
-
const raw =
|
|
2808
|
+
if (existsSync9(filePath)) {
|
|
2809
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
2210
2810
|
const employees = JSON.parse(raw);
|
|
2211
2811
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2212
2812
|
}
|
|
@@ -2235,19 +2835,19 @@ var init_plan_limits = __esm({
|
|
|
2235
2835
|
this.name = "PlanLimitError";
|
|
2236
2836
|
}
|
|
2237
2837
|
};
|
|
2238
|
-
CACHE_PATH2 =
|
|
2838
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2239
2839
|
}
|
|
2240
2840
|
});
|
|
2241
2841
|
|
|
2242
2842
|
// src/lib/notifications.ts
|
|
2243
2843
|
import crypto from "crypto";
|
|
2244
|
-
import
|
|
2844
|
+
import path9 from "path";
|
|
2245
2845
|
import os5 from "os";
|
|
2246
2846
|
import {
|
|
2247
|
-
readFileSync as
|
|
2847
|
+
readFileSync as readFileSync10,
|
|
2248
2848
|
readdirSync,
|
|
2249
|
-
unlinkSync as
|
|
2250
|
-
existsSync as
|
|
2849
|
+
unlinkSync as unlinkSync3,
|
|
2850
|
+
existsSync as existsSync10,
|
|
2251
2851
|
rmdirSync
|
|
2252
2852
|
} from "fs";
|
|
2253
2853
|
async function writeNotification(notification) {
|
|
@@ -2398,10 +2998,11 @@ __export(tasks_crud_exports, {
|
|
|
2398
2998
|
writeCheckpoint: () => writeCheckpoint
|
|
2399
2999
|
});
|
|
2400
3000
|
import crypto3 from "crypto";
|
|
2401
|
-
import
|
|
3001
|
+
import path10 from "path";
|
|
3002
|
+
import os6 from "os";
|
|
2402
3003
|
import { execSync as execSync5 } from "child_process";
|
|
2403
3004
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2404
|
-
import { existsSync as
|
|
3005
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
2405
3006
|
async function writeCheckpoint(input) {
|
|
2406
3007
|
const client = getClient();
|
|
2407
3008
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2442,6 +3043,35 @@ function extractParentFromContext(contextBody) {
|
|
|
2442
3043
|
function slugify(title) {
|
|
2443
3044
|
return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2444
3045
|
}
|
|
3046
|
+
function buildKeywordIndex() {
|
|
3047
|
+
const idx = /* @__PURE__ */ new Map();
|
|
3048
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
3049
|
+
for (const kw of keywords) {
|
|
3050
|
+
const existing = idx.get(kw) ?? [];
|
|
3051
|
+
existing.push(role);
|
|
3052
|
+
idx.set(kw, existing);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
return idx;
|
|
3056
|
+
}
|
|
3057
|
+
function checkLaneAffinity(title, context, assigneeName) {
|
|
3058
|
+
const employees = loadEmployeesSync();
|
|
3059
|
+
const employee = employees.find((e) => e.name === assigneeName);
|
|
3060
|
+
if (!employee) return void 0;
|
|
3061
|
+
const assigneeRole = employee.role;
|
|
3062
|
+
const text = `${title} ${context}`.toLowerCase();
|
|
3063
|
+
const matchedRoles = /* @__PURE__ */ new Set();
|
|
3064
|
+
for (const [keyword, roles] of KEYWORD_INDEX) {
|
|
3065
|
+
if (text.includes(keyword)) {
|
|
3066
|
+
for (const role of roles) matchedRoles.add(role);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
if (matchedRoles.size === 0) return void 0;
|
|
3070
|
+
if (matchedRoles.has(assigneeRole)) return void 0;
|
|
3071
|
+
if (assigneeRole === "COO") return void 0;
|
|
3072
|
+
const expectedRoles = Array.from(matchedRoles).join(" or ");
|
|
3073
|
+
return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
|
|
3074
|
+
}
|
|
2445
3075
|
async function resolveTask(client, identifier, scopeSession) {
|
|
2446
3076
|
const scope = sessionScopeFilter(scopeSession);
|
|
2447
3077
|
let result = await client.execute({
|
|
@@ -2491,7 +3121,14 @@ async function createTaskCore(input) {
|
|
|
2491
3121
|
const id = crypto3.randomUUID();
|
|
2492
3122
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2493
3123
|
const slug = slugify(input.title);
|
|
2494
|
-
|
|
3124
|
+
let earlySessionScope = null;
|
|
3125
|
+
try {
|
|
3126
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3127
|
+
earlySessionScope = resolveExeSession2();
|
|
3128
|
+
} catch {
|
|
3129
|
+
}
|
|
3130
|
+
const scope = earlySessionScope ?? "default";
|
|
3131
|
+
const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
|
|
2495
3132
|
let blockedById = null;
|
|
2496
3133
|
const initialStatus = input.blockedBy ? "blocked" : "open";
|
|
2497
3134
|
if (input.blockedBy) {
|
|
@@ -2531,22 +3168,24 @@ async function createTaskCore(input) {
|
|
|
2531
3168
|
if (dupCheck.rows.length > 0) {
|
|
2532
3169
|
warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
|
|
2533
3170
|
}
|
|
3171
|
+
if (!process.env.DISABLE_LANE_AFFINITY) {
|
|
3172
|
+
const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
|
|
3173
|
+
if (laneWarning) {
|
|
3174
|
+
warning = warning ? `${warning}
|
|
3175
|
+
${laneWarning}` : laneWarning;
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
2534
3178
|
if (input.baseDir) {
|
|
2535
3179
|
try {
|
|
2536
|
-
await mkdir3(
|
|
2537
|
-
await mkdir3(
|
|
3180
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3181
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2538
3182
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2539
3183
|
await ensureGitignoreExe(input.baseDir);
|
|
2540
3184
|
} catch {
|
|
2541
3185
|
}
|
|
2542
3186
|
}
|
|
2543
3187
|
const complexity = input.complexity ?? "standard";
|
|
2544
|
-
|
|
2545
|
-
try {
|
|
2546
|
-
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
2547
|
-
sessionScope = resolveExeSession2();
|
|
2548
|
-
} catch {
|
|
2549
|
-
}
|
|
3188
|
+
const sessionScope = earlySessionScope;
|
|
2550
3189
|
await client.execute({
|
|
2551
3190
|
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
|
|
2552
3191
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -2573,6 +3212,43 @@ async function createTaskCore(input) {
|
|
|
2573
3212
|
now
|
|
2574
3213
|
]
|
|
2575
3214
|
});
|
|
3215
|
+
if (input.baseDir) {
|
|
3216
|
+
try {
|
|
3217
|
+
const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
|
|
3218
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
3219
|
+
const mdDir = path10.dirname(mdPath);
|
|
3220
|
+
if (!existsSync11(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3221
|
+
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3222
|
+
const mdContent = `# ${input.title}
|
|
3223
|
+
|
|
3224
|
+
**ID:** ${id}
|
|
3225
|
+
**Status:** ${initialStatus}
|
|
3226
|
+
**Priority:** ${input.priority}
|
|
3227
|
+
**Assigned by:** ${input.assignedBy}
|
|
3228
|
+
**Assigned to:** ${input.assignedTo}
|
|
3229
|
+
**Project:** ${input.projectName}
|
|
3230
|
+
**Created:** ${now.split("T")[0]}${parentTaskId ? `
|
|
3231
|
+
**Parent task:** ${parentTaskId}` : ""}
|
|
3232
|
+
**Reviewer:** ${reviewer}
|
|
3233
|
+
|
|
3234
|
+
## Context
|
|
3235
|
+
|
|
3236
|
+
${input.context}
|
|
3237
|
+
|
|
3238
|
+
## MANDATORY: When done
|
|
3239
|
+
|
|
3240
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
3241
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3242
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3243
|
+
`;
|
|
3244
|
+
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3245
|
+
} catch (err) {
|
|
3246
|
+
process.stderr.write(
|
|
3247
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3248
|
+
`
|
|
3249
|
+
);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
2576
3252
|
return {
|
|
2577
3253
|
id,
|
|
2578
3254
|
title: input.title,
|
|
@@ -2765,7 +3441,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
2765
3441
|
return { row, taskFile, now, taskId };
|
|
2766
3442
|
}
|
|
2767
3443
|
}
|
|
2768
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
3444
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
2769
3445
|
process.stderr.write(
|
|
2770
3446
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
2771
3447
|
`
|
|
@@ -2830,9 +3506,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2830
3506
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2831
3507
|
}
|
|
2832
3508
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2833
|
-
const archPath =
|
|
3509
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2834
3510
|
try {
|
|
2835
|
-
if (
|
|
3511
|
+
if (existsSync11(archPath)) return;
|
|
2836
3512
|
const template = [
|
|
2837
3513
|
`# ${projectName} \u2014 System Architecture`,
|
|
2838
3514
|
"",
|
|
@@ -2865,10 +3541,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2865
3541
|
}
|
|
2866
3542
|
}
|
|
2867
3543
|
async function ensureGitignoreExe(baseDir) {
|
|
2868
|
-
const gitignorePath =
|
|
3544
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2869
3545
|
try {
|
|
2870
|
-
if (
|
|
2871
|
-
const content =
|
|
3546
|
+
if (existsSync11(gitignorePath)) {
|
|
3547
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
2872
3548
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2873
3549
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2874
3550
|
} else {
|
|
@@ -2877,20 +3553,30 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
2877
3553
|
} catch {
|
|
2878
3554
|
}
|
|
2879
3555
|
}
|
|
2880
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
3556
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
2881
3557
|
var init_tasks_crud = __esm({
|
|
2882
3558
|
"src/lib/tasks-crud.ts"() {
|
|
2883
3559
|
"use strict";
|
|
2884
3560
|
init_database();
|
|
2885
3561
|
init_task_scope();
|
|
3562
|
+
init_employees();
|
|
3563
|
+
LANE_KEYWORDS = {
|
|
3564
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
3565
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
3566
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
3567
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
3568
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
3569
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
3570
|
+
};
|
|
3571
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
2886
3572
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
2887
3573
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
2888
3574
|
}
|
|
2889
3575
|
});
|
|
2890
3576
|
|
|
2891
3577
|
// src/lib/tasks-review.ts
|
|
2892
|
-
import
|
|
2893
|
-
import { existsSync as
|
|
3578
|
+
import path11 from "path";
|
|
3579
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2894
3580
|
async function countPendingReviews(sessionScope) {
|
|
2895
3581
|
const client = getClient();
|
|
2896
3582
|
if (sessionScope) {
|
|
@@ -2912,7 +3598,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
|
2912
3598
|
const result2 = await client.execute({
|
|
2913
3599
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
2914
3600
|
WHERE status = 'needs_review' AND updated_at > ?
|
|
2915
|
-
AND
|
|
3601
|
+
AND session_scope = ?`,
|
|
2916
3602
|
args: [sinceIso, sessionScope]
|
|
2917
3603
|
});
|
|
2918
3604
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -2930,7 +3616,7 @@ async function listPendingReviews(limit, sessionScope) {
|
|
|
2930
3616
|
const result2 = await client.execute({
|
|
2931
3617
|
sql: `SELECT title, assigned_to, project_name FROM tasks
|
|
2932
3618
|
WHERE status = 'needs_review'
|
|
2933
|
-
AND
|
|
3619
|
+
AND session_scope = ?
|
|
2934
3620
|
ORDER BY priority ASC, created_at DESC LIMIT ?`,
|
|
2935
3621
|
args: [sessionScope, limit]
|
|
2936
3622
|
});
|
|
@@ -3051,14 +3737,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3051
3737
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
3052
3738
|
const agent = parts[1];
|
|
3053
3739
|
const slug = parts.slice(2).join("-");
|
|
3054
|
-
const
|
|
3740
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
3055
3741
|
const result = await client.execute({
|
|
3056
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
3057
|
-
args: [now,
|
|
3742
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
3743
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
3058
3744
|
});
|
|
3059
3745
|
if (result.rowsAffected > 0) {
|
|
3060
3746
|
process.stderr.write(
|
|
3061
|
-
`[review-cleanup] Cascaded original task to done
|
|
3747
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
3062
3748
|
`
|
|
3063
3749
|
);
|
|
3064
3750
|
}
|
|
@@ -3071,11 +3757,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3071
3757
|
);
|
|
3072
3758
|
}
|
|
3073
3759
|
try {
|
|
3074
|
-
const cacheDir =
|
|
3075
|
-
if (
|
|
3760
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
3761
|
+
if (existsSync12(cacheDir)) {
|
|
3076
3762
|
for (const f of readdirSync2(cacheDir)) {
|
|
3077
3763
|
if (f.startsWith("review-notified-")) {
|
|
3078
|
-
|
|
3764
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
3079
3765
|
}
|
|
3080
3766
|
}
|
|
3081
3767
|
}
|
|
@@ -3096,7 +3782,7 @@ var init_tasks_review = __esm({
|
|
|
3096
3782
|
});
|
|
3097
3783
|
|
|
3098
3784
|
// src/lib/tasks-chain.ts
|
|
3099
|
-
import
|
|
3785
|
+
import path12 from "path";
|
|
3100
3786
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3101
3787
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3102
3788
|
const client = getClient();
|
|
@@ -3113,7 +3799,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3113
3799
|
});
|
|
3114
3800
|
for (const ur of unblockedRows.rows) {
|
|
3115
3801
|
try {
|
|
3116
|
-
const ubFile =
|
|
3802
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
3117
3803
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3118
3804
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3119
3805
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3182,7 +3868,7 @@ var init_tasks_chain = __esm({
|
|
|
3182
3868
|
|
|
3183
3869
|
// src/lib/project-name.ts
|
|
3184
3870
|
import { execSync as execSync6 } from "child_process";
|
|
3185
|
-
import
|
|
3871
|
+
import path13 from "path";
|
|
3186
3872
|
function getProjectName(cwd2) {
|
|
3187
3873
|
const dir = cwd2 ?? process.cwd();
|
|
3188
3874
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3195,7 +3881,7 @@ function getProjectName(cwd2) {
|
|
|
3195
3881
|
timeout: 2e3,
|
|
3196
3882
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3197
3883
|
}).trim();
|
|
3198
|
-
repoRoot =
|
|
3884
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
3199
3885
|
} catch {
|
|
3200
3886
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3201
3887
|
cwd: dir,
|
|
@@ -3204,11 +3890,11 @@ function getProjectName(cwd2) {
|
|
|
3204
3890
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3205
3891
|
}).trim();
|
|
3206
3892
|
}
|
|
3207
|
-
_cached2 =
|
|
3893
|
+
_cached2 = path13.basename(repoRoot);
|
|
3208
3894
|
_cachedCwd = dir;
|
|
3209
3895
|
return _cached2;
|
|
3210
3896
|
} catch {
|
|
3211
|
-
_cached2 =
|
|
3897
|
+
_cached2 = path13.basename(dir);
|
|
3212
3898
|
_cachedCwd = dir;
|
|
3213
3899
|
return _cached2;
|
|
3214
3900
|
}
|
|
@@ -3240,7 +3926,7 @@ function findSessionForProject(projectName) {
|
|
|
3240
3926
|
const sessions = listSessions();
|
|
3241
3927
|
for (const s of sessions) {
|
|
3242
3928
|
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
3243
|
-
if (proj === projectName &&
|
|
3929
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
3244
3930
|
}
|
|
3245
3931
|
return null;
|
|
3246
3932
|
}
|
|
@@ -3286,7 +3972,7 @@ var init_session_scope = __esm({
|
|
|
3286
3972
|
|
|
3287
3973
|
// src/lib/tasks-notify.ts
|
|
3288
3974
|
async function dispatchTaskToEmployee(input) {
|
|
3289
|
-
if (
|
|
3975
|
+
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
3290
3976
|
let crossProject = false;
|
|
3291
3977
|
if (input.projectName) {
|
|
3292
3978
|
try {
|
|
@@ -3681,8 +4367,8 @@ __export(tasks_exports, {
|
|
|
3681
4367
|
updateTaskStatus: () => updateTaskStatus,
|
|
3682
4368
|
writeCheckpoint: () => writeCheckpoint
|
|
3683
4369
|
});
|
|
3684
|
-
import
|
|
3685
|
-
import { writeFileSync as
|
|
4370
|
+
import path14 from "path";
|
|
4371
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3686
4372
|
async function createTask(input) {
|
|
3687
4373
|
const result = await createTaskCore(input);
|
|
3688
4374
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3701,14 +4387,14 @@ async function updateTask(input) {
|
|
|
3701
4387
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3702
4388
|
try {
|
|
3703
4389
|
const agent = String(row.assigned_to);
|
|
3704
|
-
const cacheDir =
|
|
3705
|
-
const cachePath =
|
|
4390
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4391
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3706
4392
|
if (input.status === "in_progress") {
|
|
3707
|
-
|
|
3708
|
-
|
|
4393
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
4394
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3709
4395
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3710
4396
|
try {
|
|
3711
|
-
|
|
4397
|
+
unlinkSync5(cachePath);
|
|
3712
4398
|
} catch {
|
|
3713
4399
|
}
|
|
3714
4400
|
}
|
|
@@ -3765,7 +4451,7 @@ async function updateTask(input) {
|
|
|
3765
4451
|
}
|
|
3766
4452
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
3767
4453
|
if (isTerminal) {
|
|
3768
|
-
const isCoordinator =
|
|
4454
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
3769
4455
|
if (!isCoordinator) {
|
|
3770
4456
|
notifyTaskDone();
|
|
3771
4457
|
}
|
|
@@ -3790,7 +4476,7 @@ async function updateTask(input) {
|
|
|
3790
4476
|
}
|
|
3791
4477
|
}
|
|
3792
4478
|
}
|
|
3793
|
-
if (input.status === "done" &&
|
|
4479
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
3794
4480
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
3795
4481
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
3796
4482
|
taskId,
|
|
@@ -3806,7 +4492,7 @@ async function updateTask(input) {
|
|
|
3806
4492
|
});
|
|
3807
4493
|
}
|
|
3808
4494
|
let nextTask;
|
|
3809
|
-
if (isTerminal &&
|
|
4495
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
3810
4496
|
try {
|
|
3811
4497
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
3812
4498
|
} catch {
|
|
@@ -4150,7 +4836,7 @@ var init_capacity_monitor = __esm({
|
|
|
4150
4836
|
// src/lib/tmux-routing.ts
|
|
4151
4837
|
var tmux_routing_exports = {};
|
|
4152
4838
|
__export(tmux_routing_exports, {
|
|
4153
|
-
acquireSpawnLock: () =>
|
|
4839
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
4154
4840
|
employeeSessionName: () => employeeSessionName,
|
|
4155
4841
|
ensureEmployee: () => ensureEmployee,
|
|
4156
4842
|
extractRootExe: () => extractRootExe,
|
|
@@ -4165,20 +4851,20 @@ __export(tmux_routing_exports, {
|
|
|
4165
4851
|
notifyParentExe: () => notifyParentExe,
|
|
4166
4852
|
parseParentExe: () => parseParentExe,
|
|
4167
4853
|
registerParentExe: () => registerParentExe,
|
|
4168
|
-
releaseSpawnLock: () =>
|
|
4854
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
4169
4855
|
resolveExeSession: () => resolveExeSession,
|
|
4170
4856
|
sendIntercom: () => sendIntercom,
|
|
4171
4857
|
spawnEmployee: () => spawnEmployee,
|
|
4172
4858
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4173
4859
|
});
|
|
4174
4860
|
import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
|
|
4175
|
-
import { readFileSync as
|
|
4176
|
-
import
|
|
4177
|
-
import
|
|
4178
|
-
import { fileURLToPath } from "url";
|
|
4179
|
-
import { unlinkSync as
|
|
4861
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync } from "fs";
|
|
4862
|
+
import path15 from "path";
|
|
4863
|
+
import os7 from "os";
|
|
4864
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4865
|
+
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4180
4866
|
function spawnLockPath(sessionName) {
|
|
4181
|
-
return
|
|
4867
|
+
return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4182
4868
|
}
|
|
4183
4869
|
function isProcessAlive(pid) {
|
|
4184
4870
|
try {
|
|
@@ -4188,14 +4874,14 @@ function isProcessAlive(pid) {
|
|
|
4188
4874
|
return false;
|
|
4189
4875
|
}
|
|
4190
4876
|
}
|
|
4191
|
-
function
|
|
4192
|
-
if (!
|
|
4193
|
-
|
|
4877
|
+
function acquireSpawnLock2(sessionName) {
|
|
4878
|
+
if (!existsSync13(SPAWN_LOCK_DIR)) {
|
|
4879
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4194
4880
|
}
|
|
4195
4881
|
const lockFile = spawnLockPath(sessionName);
|
|
4196
|
-
if (
|
|
4882
|
+
if (existsSync13(lockFile)) {
|
|
4197
4883
|
try {
|
|
4198
|
-
const lock = JSON.parse(
|
|
4884
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
4199
4885
|
const age = Date.now() - lock.timestamp;
|
|
4200
4886
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4201
4887
|
return false;
|
|
@@ -4203,25 +4889,25 @@ function acquireSpawnLock(sessionName) {
|
|
|
4203
4889
|
} catch {
|
|
4204
4890
|
}
|
|
4205
4891
|
}
|
|
4206
|
-
|
|
4892
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4207
4893
|
return true;
|
|
4208
4894
|
}
|
|
4209
|
-
function
|
|
4895
|
+
function releaseSpawnLock2(sessionName) {
|
|
4210
4896
|
try {
|
|
4211
|
-
|
|
4897
|
+
unlinkSync6(spawnLockPath(sessionName));
|
|
4212
4898
|
} catch {
|
|
4213
4899
|
}
|
|
4214
4900
|
}
|
|
4215
4901
|
function resolveBehaviorsExporterScript() {
|
|
4216
4902
|
try {
|
|
4217
|
-
const thisFile =
|
|
4218
|
-
const scriptPath =
|
|
4219
|
-
|
|
4903
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
4904
|
+
const scriptPath = path15.join(
|
|
4905
|
+
path15.dirname(thisFile),
|
|
4220
4906
|
"..",
|
|
4221
4907
|
"bin",
|
|
4222
4908
|
"exe-export-behaviors.js"
|
|
4223
4909
|
);
|
|
4224
|
-
return
|
|
4910
|
+
return existsSync13(scriptPath) ? scriptPath : null;
|
|
4225
4911
|
} catch {
|
|
4226
4912
|
return null;
|
|
4227
4913
|
}
|
|
@@ -4287,12 +4973,12 @@ function extractRootExe(name) {
|
|
|
4287
4973
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4288
4974
|
}
|
|
4289
4975
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4290
|
-
if (!
|
|
4291
|
-
|
|
4976
|
+
if (!existsSync13(SESSION_CACHE)) {
|
|
4977
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4292
4978
|
}
|
|
4293
4979
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4294
|
-
const filePath =
|
|
4295
|
-
|
|
4980
|
+
const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4981
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4296
4982
|
parentExe: rootExe,
|
|
4297
4983
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4298
4984
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4300,7 +4986,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4300
4986
|
}
|
|
4301
4987
|
function getParentExe(sessionKey) {
|
|
4302
4988
|
try {
|
|
4303
|
-
const data = JSON.parse(
|
|
4989
|
+
const data = JSON.parse(readFileSync12(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4304
4990
|
return data.parentExe || null;
|
|
4305
4991
|
} catch {
|
|
4306
4992
|
return null;
|
|
@@ -4308,8 +4994,8 @@ function getParentExe(sessionKey) {
|
|
|
4308
4994
|
}
|
|
4309
4995
|
function getDispatchedBy(sessionKey) {
|
|
4310
4996
|
try {
|
|
4311
|
-
const data = JSON.parse(
|
|
4312
|
-
|
|
4997
|
+
const data = JSON.parse(readFileSync12(
|
|
4998
|
+
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4313
4999
|
"utf8"
|
|
4314
5000
|
));
|
|
4315
5001
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4335,10 +5021,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
4335
5021
|
}
|
|
4336
5022
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
4337
5023
|
const base = employeeSessionName(employeeName, exeSession);
|
|
4338
|
-
if (!isAlive(base) &&
|
|
5024
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
4339
5025
|
for (let i = 2; i <= maxInstances; i++) {
|
|
4340
5026
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
4341
|
-
if (!isAlive(candidate) &&
|
|
5027
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
4342
5028
|
}
|
|
4343
5029
|
return null;
|
|
4344
5030
|
}
|
|
@@ -4370,32 +5056,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4370
5056
|
}
|
|
4371
5057
|
function readDebounceState() {
|
|
4372
5058
|
try {
|
|
4373
|
-
if (!
|
|
4374
|
-
|
|
5059
|
+
if (!existsSync13(DEBOUNCE_FILE)) return {};
|
|
5060
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
5061
|
+
const state = {};
|
|
5062
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5063
|
+
if (typeof val === "number") {
|
|
5064
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5065
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5066
|
+
state[key] = val;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
return state;
|
|
4375
5070
|
} catch {
|
|
4376
5071
|
return {};
|
|
4377
5072
|
}
|
|
4378
5073
|
}
|
|
4379
5074
|
function writeDebounceState(state) {
|
|
4380
5075
|
try {
|
|
4381
|
-
if (!
|
|
4382
|
-
|
|
5076
|
+
if (!existsSync13(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5077
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4383
5078
|
} catch {
|
|
4384
5079
|
}
|
|
4385
5080
|
}
|
|
4386
5081
|
function isDebounced(targetSession) {
|
|
4387
5082
|
const state = readDebounceState();
|
|
4388
|
-
const
|
|
4389
|
-
|
|
5083
|
+
const entry = state[targetSession];
|
|
5084
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5085
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5086
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5087
|
+
state[targetSession].pending++;
|
|
5088
|
+
writeDebounceState(state);
|
|
5089
|
+
return true;
|
|
5090
|
+
}
|
|
5091
|
+
return false;
|
|
4390
5092
|
}
|
|
4391
5093
|
function recordDebounce(targetSession) {
|
|
4392
5094
|
const state = readDebounceState();
|
|
4393
|
-
state[targetSession]
|
|
5095
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5096
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4394
5097
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4395
5098
|
for (const key of Object.keys(state)) {
|
|
4396
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5099
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4397
5100
|
}
|
|
4398
5101
|
writeDebounceState(state);
|
|
5102
|
+
return batched;
|
|
4399
5103
|
}
|
|
4400
5104
|
function logIntercom(msg) {
|
|
4401
5105
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4440,7 +5144,7 @@ function sendIntercom(targetSession) {
|
|
|
4440
5144
|
return "skipped_exe";
|
|
4441
5145
|
}
|
|
4442
5146
|
if (isDebounced(targetSession)) {
|
|
4443
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5147
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4444
5148
|
return "debounced";
|
|
4445
5149
|
}
|
|
4446
5150
|
try {
|
|
@@ -4452,14 +5156,14 @@ function sendIntercom(targetSession) {
|
|
|
4452
5156
|
const sessionState = getSessionState(targetSession);
|
|
4453
5157
|
if (sessionState === "no_claude") {
|
|
4454
5158
|
queueIntercom(targetSession, "claude not running in session");
|
|
4455
|
-
recordDebounce(targetSession);
|
|
4456
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5159
|
+
const batched2 = recordDebounce(targetSession);
|
|
5160
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4457
5161
|
return "queued";
|
|
4458
5162
|
}
|
|
4459
5163
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4460
5164
|
queueIntercom(targetSession, "session busy at send time");
|
|
4461
|
-
recordDebounce(targetSession);
|
|
4462
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5165
|
+
const batched2 = recordDebounce(targetSession);
|
|
5166
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4463
5167
|
return "queued";
|
|
4464
5168
|
}
|
|
4465
5169
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4467,8 +5171,8 @@ function sendIntercom(targetSession) {
|
|
|
4467
5171
|
transport.sendKeys(targetSession, "q");
|
|
4468
5172
|
}
|
|
4469
5173
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4470
|
-
recordDebounce(targetSession);
|
|
4471
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5174
|
+
const batched = recordDebounce(targetSession);
|
|
5175
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4472
5176
|
return "delivered";
|
|
4473
5177
|
} catch {
|
|
4474
5178
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4498,7 +5202,7 @@ function notifyParentExe(sessionKey) {
|
|
|
4498
5202
|
return true;
|
|
4499
5203
|
}
|
|
4500
5204
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
4501
|
-
if (
|
|
5205
|
+
if (isCoordinatorName(employeeName)) {
|
|
4502
5206
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
4503
5207
|
}
|
|
4504
5208
|
try {
|
|
@@ -4570,26 +5274,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4570
5274
|
const transport = getTransport();
|
|
4571
5275
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4572
5276
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4573
|
-
const logDir =
|
|
4574
|
-
const logFile =
|
|
4575
|
-
if (!
|
|
4576
|
-
|
|
5277
|
+
const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
|
|
5278
|
+
const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5279
|
+
if (!existsSync13(logDir)) {
|
|
5280
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4577
5281
|
}
|
|
4578
5282
|
transport.kill(sessionName);
|
|
4579
5283
|
let cleanupSuffix = "";
|
|
4580
5284
|
try {
|
|
4581
|
-
const thisFile =
|
|
4582
|
-
const cleanupScript =
|
|
4583
|
-
if (
|
|
5285
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
5286
|
+
const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5287
|
+
if (existsSync13(cleanupScript)) {
|
|
4584
5288
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4585
5289
|
}
|
|
4586
5290
|
} catch {
|
|
4587
5291
|
}
|
|
4588
5292
|
try {
|
|
4589
|
-
const claudeJsonPath =
|
|
5293
|
+
const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
|
|
4590
5294
|
let claudeJson = {};
|
|
4591
5295
|
try {
|
|
4592
|
-
claudeJson = JSON.parse(
|
|
5296
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
4593
5297
|
} catch {
|
|
4594
5298
|
}
|
|
4595
5299
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4597,17 +5301,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4597
5301
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4598
5302
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4599
5303
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4600
|
-
|
|
5304
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4601
5305
|
} catch {
|
|
4602
5306
|
}
|
|
4603
5307
|
try {
|
|
4604
|
-
const settingsDir =
|
|
5308
|
+
const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
|
|
4605
5309
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4606
|
-
const projSettingsDir =
|
|
4607
|
-
const settingsPath =
|
|
5310
|
+
const projSettingsDir = path15.join(settingsDir, normalizedKey);
|
|
5311
|
+
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4608
5312
|
let settings = {};
|
|
4609
5313
|
try {
|
|
4610
|
-
settings = JSON.parse(
|
|
5314
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
4611
5315
|
} catch {
|
|
4612
5316
|
}
|
|
4613
5317
|
const perms = settings.permissions ?? {};
|
|
@@ -4635,21 +5339,24 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4635
5339
|
if (changed) {
|
|
4636
5340
|
perms.allow = allow;
|
|
4637
5341
|
settings.permissions = perms;
|
|
4638
|
-
|
|
4639
|
-
|
|
5342
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
5343
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4640
5344
|
}
|
|
4641
5345
|
} catch {
|
|
4642
5346
|
}
|
|
4643
5347
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4644
5348
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4645
|
-
const
|
|
5349
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
5350
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
5351
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
5352
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4646
5353
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4647
5354
|
let identityFlag = "";
|
|
4648
5355
|
let behaviorsFlag = "";
|
|
4649
5356
|
let legacyFallbackWarned = false;
|
|
4650
5357
|
if (!useExeAgent && !useBinSymlink) {
|
|
4651
|
-
const identityPath =
|
|
4652
|
-
|
|
5358
|
+
const identityPath = path15.join(
|
|
5359
|
+
os7.homedir(),
|
|
4653
5360
|
".exe-os",
|
|
4654
5361
|
"identity",
|
|
4655
5362
|
`${employeeName}.md`
|
|
@@ -4658,13 +5365,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4658
5365
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4659
5366
|
if (hasAgentFlag) {
|
|
4660
5367
|
identityFlag = ` --agent ${employeeName}`;
|
|
4661
|
-
} else if (
|
|
5368
|
+
} else if (existsSync13(identityPath)) {
|
|
4662
5369
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4663
5370
|
legacyFallbackWarned = true;
|
|
4664
5371
|
}
|
|
4665
5372
|
const behaviorsFile = exportBehaviorsSync(
|
|
4666
5373
|
employeeName,
|
|
4667
|
-
|
|
5374
|
+
path15.basename(spawnCwd),
|
|
4668
5375
|
sessionName
|
|
4669
5376
|
);
|
|
4670
5377
|
if (behaviorsFile) {
|
|
@@ -4679,16 +5386,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4679
5386
|
}
|
|
4680
5387
|
let sessionContextFlag = "";
|
|
4681
5388
|
try {
|
|
4682
|
-
const ctxDir =
|
|
4683
|
-
|
|
4684
|
-
const ctxFile =
|
|
5389
|
+
const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
5390
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
5391
|
+
const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4685
5392
|
const ctxContent = [
|
|
4686
5393
|
`## Session Context`,
|
|
4687
5394
|
`You are running in tmux session: ${sessionName}.`,
|
|
4688
5395
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4689
5396
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4690
5397
|
].join("\n");
|
|
4691
|
-
|
|
5398
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4692
5399
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4693
5400
|
} catch {
|
|
4694
5401
|
}
|
|
@@ -4702,9 +5409,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4702
5409
|
}
|
|
4703
5410
|
}
|
|
4704
5411
|
}
|
|
5412
|
+
if (useCodex) {
|
|
5413
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
5414
|
+
if (codexCfg?.apiKeyEnv) {
|
|
5415
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
5416
|
+
if (keyVal) {
|
|
5417
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
5421
|
+
}
|
|
5422
|
+
if (useOpencode) {
|
|
5423
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
5424
|
+
if (ocCfg?.apiKeyEnv) {
|
|
5425
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
5426
|
+
if (keyVal) {
|
|
5427
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5431
|
+
}
|
|
5432
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
5433
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
5434
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
5435
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
4705
5438
|
let spawnCommand;
|
|
4706
5439
|
if (useExeAgent) {
|
|
4707
5440
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
5441
|
+
} else if (useCodex) {
|
|
5442
|
+
process.stderr.write(
|
|
5443
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
5444
|
+
`
|
|
5445
|
+
);
|
|
5446
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
5447
|
+
} else if (useOpencode) {
|
|
5448
|
+
const binName = `${employeeName}-opencode`;
|
|
5449
|
+
process.stderr.write(
|
|
5450
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
5451
|
+
`
|
|
5452
|
+
);
|
|
5453
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4708
5454
|
} else if (useBinSymlink) {
|
|
4709
5455
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4710
5456
|
process.stderr.write(
|
|
@@ -4720,17 +5466,19 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4720
5466
|
command: spawnCommand
|
|
4721
5467
|
});
|
|
4722
5468
|
if (spawnResult.error) {
|
|
4723
|
-
|
|
5469
|
+
releaseSpawnLock2(sessionName);
|
|
4724
5470
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
4725
5471
|
}
|
|
4726
5472
|
transport.pipeLog(sessionName, logFile);
|
|
4727
5473
|
try {
|
|
4728
5474
|
const mySession = getMySession();
|
|
4729
|
-
const dispatchInfo =
|
|
4730
|
-
|
|
5475
|
+
const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5476
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4731
5477
|
dispatchedBy: mySession,
|
|
4732
5478
|
rootExe: exeSession,
|
|
4733
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
5479
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
5480
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
5481
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4734
5482
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4735
5483
|
}));
|
|
4736
5484
|
} catch {
|
|
@@ -4748,6 +5496,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4748
5496
|
booted = true;
|
|
4749
5497
|
break;
|
|
4750
5498
|
}
|
|
5499
|
+
} else if (useCodex) {
|
|
5500
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
5501
|
+
booted = true;
|
|
5502
|
+
break;
|
|
5503
|
+
}
|
|
4751
5504
|
} else {
|
|
4752
5505
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4753
5506
|
booted = true;
|
|
@@ -4758,10 +5511,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4758
5511
|
}
|
|
4759
5512
|
}
|
|
4760
5513
|
if (!booted) {
|
|
4761
|
-
|
|
4762
|
-
|
|
5514
|
+
releaseSpawnLock2(sessionName);
|
|
5515
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5516
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4763
5517
|
}
|
|
4764
|
-
if (!useExeAgent) {
|
|
5518
|
+
if (!useExeAgent && !useCodex) {
|
|
4765
5519
|
try {
|
|
4766
5520
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4767
5521
|
} catch {
|
|
@@ -4775,7 +5529,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4775
5529
|
pid: 0,
|
|
4776
5530
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4777
5531
|
});
|
|
4778
|
-
|
|
5532
|
+
releaseSpawnLock2(sessionName);
|
|
4779
5533
|
return { sessionName };
|
|
4780
5534
|
}
|
|
4781
5535
|
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
@@ -4788,17 +5542,19 @@ var init_tmux_routing = __esm({
|
|
|
4788
5542
|
init_cc_agent_support();
|
|
4789
5543
|
init_mcp_prefix();
|
|
4790
5544
|
init_provider_table();
|
|
5545
|
+
init_agent_config();
|
|
5546
|
+
init_runtime_table();
|
|
4791
5547
|
init_intercom_queue();
|
|
4792
5548
|
init_plan_limits();
|
|
4793
5549
|
init_employees();
|
|
4794
|
-
SPAWN_LOCK_DIR =
|
|
4795
|
-
SESSION_CACHE =
|
|
5550
|
+
SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
5551
|
+
SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4796
5552
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4797
5553
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4798
5554
|
VERIFY_PANE_LINES = 200;
|
|
4799
5555
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4800
|
-
INTERCOM_LOG2 =
|
|
4801
|
-
DEBOUNCE_FILE =
|
|
5556
|
+
INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
5557
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4802
5558
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4803
5559
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4804
5560
|
}
|
|
@@ -6191,7 +6947,7 @@ __export(global_procedures_exports, {
|
|
|
6191
6947
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
6192
6948
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
6193
6949
|
});
|
|
6194
|
-
import { randomUUID as
|
|
6950
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
6195
6951
|
async function loadGlobalProcedures() {
|
|
6196
6952
|
const client = getClient();
|
|
6197
6953
|
const result = await client.execute({
|
|
@@ -6220,7 +6976,7 @@ ${sections.join("\n\n")}
|
|
|
6220
6976
|
`;
|
|
6221
6977
|
}
|
|
6222
6978
|
async function storeGlobalProcedure(input) {
|
|
6223
|
-
const id =
|
|
6979
|
+
const id = randomUUID3();
|
|
6224
6980
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6225
6981
|
const client = getClient();
|
|
6226
6982
|
await client.execute({
|
|
@@ -6358,7 +7114,7 @@ var init_anthropic = __esm({
|
|
|
6358
7114
|
|
|
6359
7115
|
// src/gateway/providers/openai-compat.ts
|
|
6360
7116
|
import OpenAI from "openai";
|
|
6361
|
-
import { randomUUID as
|
|
7117
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6362
7118
|
var OpenAICompatProvider;
|
|
6363
7119
|
var init_openai_compat = __esm({
|
|
6364
7120
|
"src/gateway/providers/openai-compat.ts"() {
|
|
@@ -6475,7 +7231,7 @@ var init_openai_compat = __esm({
|
|
|
6475
7231
|
}
|
|
6476
7232
|
content.push({
|
|
6477
7233
|
type: "tool_use",
|
|
6478
|
-
id: call.id ??
|
|
7234
|
+
id: call.id ?? randomUUID4(),
|
|
6479
7235
|
name: fn.name,
|
|
6480
7236
|
input
|
|
6481
7237
|
});
|
|
@@ -6667,10 +7423,10 @@ var init_hooks = __esm({
|
|
|
6667
7423
|
});
|
|
6668
7424
|
|
|
6669
7425
|
// src/runtime/safety-checks.ts
|
|
6670
|
-
import
|
|
6671
|
-
import
|
|
7426
|
+
import path16 from "path";
|
|
7427
|
+
import os8 from "os";
|
|
6672
7428
|
function checkPathSafety(filePath) {
|
|
6673
|
-
const resolved =
|
|
7429
|
+
const resolved = path16.resolve(filePath);
|
|
6674
7430
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
6675
7431
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
6676
7432
|
if (matches) {
|
|
@@ -6680,7 +7436,7 @@ function checkPathSafety(filePath) {
|
|
|
6680
7436
|
return { safe: true, bypassImmune: true };
|
|
6681
7437
|
}
|
|
6682
7438
|
function checkReadPathSafety(filePath) {
|
|
6683
|
-
const resolved =
|
|
7439
|
+
const resolved = path16.resolve(filePath);
|
|
6684
7440
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
6685
7441
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
6686
7442
|
);
|
|
@@ -6695,7 +7451,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
|
|
|
6695
7451
|
var init_safety_checks = __esm({
|
|
6696
7452
|
"src/runtime/safety-checks.ts"() {
|
|
6697
7453
|
"use strict";
|
|
6698
|
-
HOME =
|
|
7454
|
+
HOME = os8.homedir();
|
|
6699
7455
|
BYPASS_IMMUNE_PATTERNS = [
|
|
6700
7456
|
{
|
|
6701
7457
|
pattern: /\/\.git\/hooks\//,
|
|
@@ -6706,11 +7462,11 @@ var init_safety_checks = __esm({
|
|
|
6706
7462
|
reason: "Git config can set hooks and command execution"
|
|
6707
7463
|
},
|
|
6708
7464
|
{
|
|
6709
|
-
pattern: (p) => p.startsWith(
|
|
7465
|
+
pattern: (p) => p.startsWith(path16.join(HOME, ".claude")),
|
|
6710
7466
|
reason: "Claude configuration files are protected"
|
|
6711
7467
|
},
|
|
6712
7468
|
{
|
|
6713
|
-
pattern: (p) => p.startsWith(
|
|
7469
|
+
pattern: (p) => p.startsWith(path16.join(HOME, ".exe-os")),
|
|
6714
7470
|
reason: "exe-os configuration files are protected"
|
|
6715
7471
|
},
|
|
6716
7472
|
{
|
|
@@ -6727,7 +7483,7 @@ var init_safety_checks = __esm({
|
|
|
6727
7483
|
},
|
|
6728
7484
|
{
|
|
6729
7485
|
pattern: (p) => {
|
|
6730
|
-
const name =
|
|
7486
|
+
const name = path16.basename(p);
|
|
6731
7487
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
6732
7488
|
},
|
|
6733
7489
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -6754,7 +7510,7 @@ __export(file_read_exports, {
|
|
|
6754
7510
|
FileReadTool: () => FileReadTool
|
|
6755
7511
|
});
|
|
6756
7512
|
import fs3 from "fs/promises";
|
|
6757
|
-
import
|
|
7513
|
+
import path17 from "path";
|
|
6758
7514
|
import { z } from "zod";
|
|
6759
7515
|
function isBinary(buf) {
|
|
6760
7516
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -6790,7 +7546,7 @@ var init_file_read = __esm({
|
|
|
6790
7546
|
return { behavior: "allow" };
|
|
6791
7547
|
},
|
|
6792
7548
|
async call(input, context) {
|
|
6793
|
-
const filePath =
|
|
7549
|
+
const filePath = path17.isAbsolute(input.file_path) ? input.file_path : path17.resolve(context.cwd, input.file_path);
|
|
6794
7550
|
let stat;
|
|
6795
7551
|
try {
|
|
6796
7552
|
stat = await fs3.stat(filePath);
|
|
@@ -6830,7 +7586,7 @@ __export(glob_exports, {
|
|
|
6830
7586
|
GlobTool: () => GlobTool
|
|
6831
7587
|
});
|
|
6832
7588
|
import fs4 from "fs/promises";
|
|
6833
|
-
import
|
|
7589
|
+
import path18 from "path";
|
|
6834
7590
|
import { z as z2 } from "zod";
|
|
6835
7591
|
async function walkDir(dir, maxDepth = 10) {
|
|
6836
7592
|
const results = [];
|
|
@@ -6846,7 +7602,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
6846
7602
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
6847
7603
|
continue;
|
|
6848
7604
|
}
|
|
6849
|
-
const fullPath =
|
|
7605
|
+
const fullPath = path18.join(current, entry.name);
|
|
6850
7606
|
if (entry.isDirectory()) {
|
|
6851
7607
|
await walk(fullPath, depth + 1);
|
|
6852
7608
|
} else {
|
|
@@ -6880,11 +7636,11 @@ var init_glob = __esm({
|
|
|
6880
7636
|
inputSchema: inputSchema2,
|
|
6881
7637
|
isReadOnly: true,
|
|
6882
7638
|
async call(input, context) {
|
|
6883
|
-
const baseDir = input.path ?
|
|
7639
|
+
const baseDir = input.path ? path18.isAbsolute(input.path) ? input.path : path18.resolve(context.cwd, input.path) : context.cwd;
|
|
6884
7640
|
try {
|
|
6885
7641
|
const entries = await walkDir(baseDir);
|
|
6886
7642
|
const matched = entries.filter(
|
|
6887
|
-
(e) => simpleGlobMatch(
|
|
7643
|
+
(e) => simpleGlobMatch(path18.relative(baseDir, e.path), input.pattern)
|
|
6888
7644
|
);
|
|
6889
7645
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
6890
7646
|
if (matched.length === 0) {
|
|
@@ -6908,9 +7664,9 @@ var grep_exports = {};
|
|
|
6908
7664
|
__export(grep_exports, {
|
|
6909
7665
|
GrepTool: () => GrepTool
|
|
6910
7666
|
});
|
|
6911
|
-
import { spawn } from "child_process";
|
|
7667
|
+
import { spawn as spawn2 } from "child_process";
|
|
6912
7668
|
import fs5 from "fs/promises";
|
|
6913
|
-
import
|
|
7669
|
+
import path19 from "path";
|
|
6914
7670
|
import { z as z3 } from "zod";
|
|
6915
7671
|
function runRipgrep(input, searchPath, context) {
|
|
6916
7672
|
return new Promise((resolve, reject) => {
|
|
@@ -6922,7 +7678,7 @@ function runRipgrep(input, searchPath, context) {
|
|
|
6922
7678
|
args.push("--glob", input.include);
|
|
6923
7679
|
}
|
|
6924
7680
|
args.push(input.pattern, searchPath);
|
|
6925
|
-
const child =
|
|
7681
|
+
const child = spawn2("rg", args, {
|
|
6926
7682
|
cwd: searchPath,
|
|
6927
7683
|
timeout: 3e4,
|
|
6928
7684
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6964,7 +7720,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
6964
7720
|
}
|
|
6965
7721
|
for (const entry of entries) {
|
|
6966
7722
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
6967
|
-
const fullPath =
|
|
7723
|
+
const fullPath = path19.join(dir, entry.name);
|
|
6968
7724
|
if (entry.isDirectory()) {
|
|
6969
7725
|
await walk(fullPath);
|
|
6970
7726
|
} else {
|
|
@@ -7010,7 +7766,7 @@ var init_grep = __esm({
|
|
|
7010
7766
|
inputSchema: inputSchema3,
|
|
7011
7767
|
isReadOnly: true,
|
|
7012
7768
|
async call(input, context) {
|
|
7013
|
-
const searchPath = input.path ?
|
|
7769
|
+
const searchPath = input.path ? path19.isAbsolute(input.path) ? input.path : path19.resolve(context.cwd, input.path) : context.cwd;
|
|
7014
7770
|
try {
|
|
7015
7771
|
const result = await runRipgrep(input, searchPath, context);
|
|
7016
7772
|
return result;
|
|
@@ -7035,7 +7791,7 @@ __export(file_write_exports, {
|
|
|
7035
7791
|
FileWriteTool: () => FileWriteTool
|
|
7036
7792
|
});
|
|
7037
7793
|
import fs6 from "fs/promises";
|
|
7038
|
-
import
|
|
7794
|
+
import path20 from "path";
|
|
7039
7795
|
import { z as z4 } from "zod";
|
|
7040
7796
|
var inputSchema4, FileWriteTool;
|
|
7041
7797
|
var init_file_write = __esm({
|
|
@@ -7063,8 +7819,8 @@ var init_file_write = __esm({
|
|
|
7063
7819
|
return { behavior: "allow" };
|
|
7064
7820
|
},
|
|
7065
7821
|
async call(input, context) {
|
|
7066
|
-
const filePath =
|
|
7067
|
-
const dir =
|
|
7822
|
+
const filePath = path20.isAbsolute(input.file_path) ? input.file_path : path20.resolve(context.cwd, input.file_path);
|
|
7823
|
+
const dir = path20.dirname(filePath);
|
|
7068
7824
|
await fs6.mkdir(dir, { recursive: true });
|
|
7069
7825
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
7070
7826
|
return {
|
|
@@ -7082,7 +7838,7 @@ __export(file_edit_exports, {
|
|
|
7082
7838
|
FileEditTool: () => FileEditTool
|
|
7083
7839
|
});
|
|
7084
7840
|
import fs7 from "fs/promises";
|
|
7085
|
-
import
|
|
7841
|
+
import path21 from "path";
|
|
7086
7842
|
import { z as z5 } from "zod";
|
|
7087
7843
|
function countOccurrences(haystack, needle) {
|
|
7088
7844
|
let count = 0;
|
|
@@ -7123,7 +7879,7 @@ var init_file_edit = __esm({
|
|
|
7123
7879
|
return { behavior: "allow" };
|
|
7124
7880
|
},
|
|
7125
7881
|
async call(input, context) {
|
|
7126
|
-
const filePath =
|
|
7882
|
+
const filePath = path21.isAbsolute(input.file_path) ? input.file_path : path21.resolve(context.cwd, input.file_path);
|
|
7127
7883
|
let content;
|
|
7128
7884
|
try {
|
|
7129
7885
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -7280,7 +8036,7 @@ var bash_exports = {};
|
|
|
7280
8036
|
__export(bash_exports, {
|
|
7281
8037
|
BashTool: () => BashTool
|
|
7282
8038
|
});
|
|
7283
|
-
import { spawn as
|
|
8039
|
+
import { spawn as spawn3 } from "child_process";
|
|
7284
8040
|
import { z as z6 } from "zod";
|
|
7285
8041
|
var DEFAULT_TIMEOUT_MS, inputSchema6, BashTool;
|
|
7286
8042
|
var init_bash = __esm({
|
|
@@ -7311,7 +8067,7 @@ var init_bash = __esm({
|
|
|
7311
8067
|
async call(input, context) {
|
|
7312
8068
|
const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
7313
8069
|
return new Promise((resolve) => {
|
|
7314
|
-
const child =
|
|
8070
|
+
const child = spawn3("bash", ["-c", input.command], {
|
|
7315
8071
|
cwd: context.cwd,
|
|
7316
8072
|
timeout,
|
|
7317
8073
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7363,7 +8119,7 @@ var init_bash = __esm({
|
|
|
7363
8119
|
});
|
|
7364
8120
|
|
|
7365
8121
|
// src/lib/task-router.ts
|
|
7366
|
-
import { randomUUID as
|
|
8122
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
7367
8123
|
function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
|
|
7368
8124
|
const tier = config.complexityToTier[complexity];
|
|
7369
8125
|
const rule = config.tierRules[tier];
|
|
@@ -7788,14 +8544,14 @@ __export(keychain_exports, {
|
|
|
7788
8544
|
setMasterKey: () => setMasterKey
|
|
7789
8545
|
});
|
|
7790
8546
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
7791
|
-
import { existsSync as
|
|
7792
|
-
import
|
|
7793
|
-
import
|
|
8547
|
+
import { existsSync as existsSync14 } from "fs";
|
|
8548
|
+
import path24 from "path";
|
|
8549
|
+
import os9 from "os";
|
|
7794
8550
|
function getKeyDir() {
|
|
7795
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
8551
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os9.homedir(), ".exe-os");
|
|
7796
8552
|
}
|
|
7797
8553
|
function getKeyPath() {
|
|
7798
|
-
return
|
|
8554
|
+
return path24.join(getKeyDir(), "master.key");
|
|
7799
8555
|
}
|
|
7800
8556
|
async function tryKeytar() {
|
|
7801
8557
|
try {
|
|
@@ -7816,13 +8572,21 @@ async function getMasterKey() {
|
|
|
7816
8572
|
}
|
|
7817
8573
|
}
|
|
7818
8574
|
const keyPath = getKeyPath();
|
|
7819
|
-
if (!
|
|
8575
|
+
if (!existsSync14(keyPath)) {
|
|
8576
|
+
process.stderr.write(
|
|
8577
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
8578
|
+
`
|
|
8579
|
+
);
|
|
7820
8580
|
return null;
|
|
7821
8581
|
}
|
|
7822
8582
|
try {
|
|
7823
8583
|
const content = await readFile4(keyPath, "utf-8");
|
|
7824
8584
|
return Buffer.from(content.trim(), "base64");
|
|
7825
|
-
} catch {
|
|
8585
|
+
} catch (err) {
|
|
8586
|
+
process.stderr.write(
|
|
8587
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
8588
|
+
`
|
|
8589
|
+
);
|
|
7826
8590
|
return null;
|
|
7827
8591
|
}
|
|
7828
8592
|
}
|
|
@@ -7851,7 +8615,7 @@ async function deleteMasterKey() {
|
|
|
7851
8615
|
}
|
|
7852
8616
|
}
|
|
7853
8617
|
const keyPath = getKeyPath();
|
|
7854
|
-
if (
|
|
8618
|
+
if (existsSync14(keyPath)) {
|
|
7855
8619
|
await unlink(keyPath);
|
|
7856
8620
|
}
|
|
7857
8621
|
}
|
|
@@ -8151,14 +8915,14 @@ __export(wiki_client_exports, {
|
|
|
8151
8915
|
listDocuments: () => listDocuments,
|
|
8152
8916
|
listWorkspaces: () => listWorkspaces
|
|
8153
8917
|
});
|
|
8154
|
-
async function wikiFetch(config,
|
|
8155
|
-
const url = `${config.baseUrl}/api/v1${
|
|
8918
|
+
async function wikiFetch(config, path26, method = "GET", body) {
|
|
8919
|
+
const url = `${config.baseUrl}/api/v1${path26}`;
|
|
8156
8920
|
const headers = {
|
|
8157
8921
|
Authorization: `Bearer ${config.apiKey}`,
|
|
8158
8922
|
"Content-Type": "application/json"
|
|
8159
8923
|
};
|
|
8160
8924
|
const controller = new AbortController();
|
|
8161
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
8925
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
8162
8926
|
try {
|
|
8163
8927
|
let response;
|
|
8164
8928
|
try {
|
|
@@ -8171,7 +8935,7 @@ async function wikiFetch(config, path24, method = "GET", body) {
|
|
|
8171
8935
|
} catch {
|
|
8172
8936
|
clearTimeout(timeout);
|
|
8173
8937
|
const retryController = new AbortController();
|
|
8174
|
-
const retryTimeout = setTimeout(() => retryController.abort(),
|
|
8938
|
+
const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
|
|
8175
8939
|
try {
|
|
8176
8940
|
await new Promise((r) => setTimeout(r, 500));
|
|
8177
8941
|
response = await fetch(url, {
|
|
@@ -8185,7 +8949,7 @@ async function wikiFetch(config, path24, method = "GET", body) {
|
|
|
8185
8949
|
}
|
|
8186
8950
|
}
|
|
8187
8951
|
if (!response.ok) {
|
|
8188
|
-
throw new Error(`Wiki API ${method} ${
|
|
8952
|
+
throw new Error(`Wiki API ${method} ${path26}: ${response.status} ${response.statusText}`);
|
|
8189
8953
|
}
|
|
8190
8954
|
return response.json();
|
|
8191
8955
|
} finally {
|
|
@@ -8275,12 +9039,12 @@ async function getChatHistory(client, workspaceSlug, limit = 50) {
|
|
|
8275
9039
|
sentAt: h.sentAt ?? 0
|
|
8276
9040
|
}));
|
|
8277
9041
|
}
|
|
8278
|
-
var LOCAL_WIKI_URL,
|
|
9042
|
+
var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS2;
|
|
8279
9043
|
var init_wiki_client = __esm({
|
|
8280
9044
|
"src/lib/wiki-client.ts"() {
|
|
8281
9045
|
"use strict";
|
|
8282
|
-
LOCAL_WIKI_URL = "http://localhost:3001";
|
|
8283
|
-
|
|
9046
|
+
LOCAL_WIKI_URL = process.env.EXE_WIKI_URL || "http://localhost:3001";
|
|
9047
|
+
REQUEST_TIMEOUT_MS2 = 8e3;
|
|
8284
9048
|
}
|
|
8285
9049
|
});
|
|
8286
9050
|
|
|
@@ -8306,13 +9070,13 @@ __export(shard_manager_exports, {
|
|
|
8306
9070
|
listShards: () => listShards,
|
|
8307
9071
|
shardExists: () => shardExists
|
|
8308
9072
|
});
|
|
8309
|
-
import
|
|
8310
|
-
import { existsSync as
|
|
9073
|
+
import path25 from "path";
|
|
9074
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
8311
9075
|
import { createClient as createClient2 } from "@libsql/client";
|
|
8312
9076
|
function initShardManager(encryptionKey) {
|
|
8313
9077
|
_encryptionKey = encryptionKey;
|
|
8314
|
-
if (!
|
|
8315
|
-
|
|
9078
|
+
if (!existsSync15(SHARDS_DIR)) {
|
|
9079
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
8316
9080
|
}
|
|
8317
9081
|
_shardingEnabled = true;
|
|
8318
9082
|
}
|
|
@@ -8332,7 +9096,7 @@ function getShardClient(projectName) {
|
|
|
8332
9096
|
}
|
|
8333
9097
|
const cached = _shards.get(safeName);
|
|
8334
9098
|
if (cached) return cached;
|
|
8335
|
-
const dbPath =
|
|
9099
|
+
const dbPath = path25.join(SHARDS_DIR, `${safeName}.db`);
|
|
8336
9100
|
const client = createClient2({
|
|
8337
9101
|
url: `file:${dbPath}`,
|
|
8338
9102
|
encryptionKey: _encryptionKey
|
|
@@ -8342,10 +9106,10 @@ function getShardClient(projectName) {
|
|
|
8342
9106
|
}
|
|
8343
9107
|
function shardExists(projectName) {
|
|
8344
9108
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
8345
|
-
return
|
|
9109
|
+
return existsSync15(path25.join(SHARDS_DIR, `${safeName}.db`));
|
|
8346
9110
|
}
|
|
8347
9111
|
function listShards() {
|
|
8348
|
-
if (!
|
|
9112
|
+
if (!existsSync15(SHARDS_DIR)) return [];
|
|
8349
9113
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
8350
9114
|
}
|
|
8351
9115
|
async function ensureShardSchema(client) {
|
|
@@ -8531,7 +9295,7 @@ var init_shard_manager = __esm({
|
|
|
8531
9295
|
"src/lib/shard-manager.ts"() {
|
|
8532
9296
|
"use strict";
|
|
8533
9297
|
init_config();
|
|
8534
|
-
SHARDS_DIR =
|
|
9298
|
+
SHARDS_DIR = path25.join(EXE_AI_DIR, "shards");
|
|
8535
9299
|
_shards = /* @__PURE__ */ new Map();
|
|
8536
9300
|
_encryptionKey = null;
|
|
8537
9301
|
_shardingEnabled = false;
|
|
@@ -8555,6 +9319,7 @@ __export(store_exports, {
|
|
|
8555
9319
|
vectorToBlob: () => vectorToBlob,
|
|
8556
9320
|
writeMemory: () => writeMemory
|
|
8557
9321
|
});
|
|
9322
|
+
import { createHash } from "crypto";
|
|
8558
9323
|
function isBusyError2(err) {
|
|
8559
9324
|
if (err instanceof Error) {
|
|
8560
9325
|
const msg = err.message.toLowerCase();
|
|
@@ -8628,12 +9393,52 @@ function classifyTier(record) {
|
|
|
8628
9393
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
8629
9394
|
return 3;
|
|
8630
9395
|
}
|
|
9396
|
+
function inferFilePaths(record) {
|
|
9397
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
9398
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
9399
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
9400
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
9401
|
+
}
|
|
9402
|
+
function inferCommitHash(record) {
|
|
9403
|
+
if (record.tool_name !== "Bash") return null;
|
|
9404
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
9405
|
+
return match ? match[1] : null;
|
|
9406
|
+
}
|
|
9407
|
+
function inferLanguageType(record) {
|
|
9408
|
+
const text = record.raw_text;
|
|
9409
|
+
if (!text || text.length < 10) return null;
|
|
9410
|
+
const trimmed = text.trimStart();
|
|
9411
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
9412
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
9413
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
9414
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
9415
|
+
return "mixed";
|
|
9416
|
+
}
|
|
9417
|
+
function inferDomain(record) {
|
|
9418
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
9419
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
9420
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
9421
|
+
return null;
|
|
9422
|
+
}
|
|
8631
9423
|
async function writeMemory(record) {
|
|
8632
9424
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
8633
9425
|
throw new Error(
|
|
8634
9426
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
8635
9427
|
);
|
|
8636
9428
|
}
|
|
9429
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
9430
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
9431
|
+
return;
|
|
9432
|
+
}
|
|
9433
|
+
try {
|
|
9434
|
+
const client = getClient();
|
|
9435
|
+
const existing = await client.execute({
|
|
9436
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
9437
|
+
args: [contentHash, record.agent_id]
|
|
9438
|
+
});
|
|
9439
|
+
if (existing.rows.length > 0) return;
|
|
9440
|
+
} catch {
|
|
9441
|
+
}
|
|
8637
9442
|
const dbRow = {
|
|
8638
9443
|
id: record.id,
|
|
8639
9444
|
agent_id: record.agent_id,
|
|
@@ -8663,7 +9468,23 @@ async function writeMemory(record) {
|
|
|
8663
9468
|
supersedes_id: record.supersedes_id ?? null,
|
|
8664
9469
|
draft: record.draft ? 1 : 0,
|
|
8665
9470
|
memory_type: record.memory_type ?? "raw",
|
|
8666
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
9471
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
9472
|
+
content_hash: contentHash,
|
|
9473
|
+
intent: record.intent ?? null,
|
|
9474
|
+
outcome: record.outcome ?? null,
|
|
9475
|
+
domain: record.domain ?? inferDomain(record),
|
|
9476
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
9477
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
9478
|
+
chain_position: record.chain_position ?? null,
|
|
9479
|
+
review_status: record.review_status ?? null,
|
|
9480
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
9481
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
9482
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
9483
|
+
duration_ms: record.duration_ms ?? null,
|
|
9484
|
+
token_cost: record.token_cost ?? null,
|
|
9485
|
+
audience: record.audience ?? null,
|
|
9486
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
9487
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
8667
9488
|
};
|
|
8668
9489
|
_pendingRecords.push(dbRow);
|
|
8669
9490
|
orgBus.emit({
|
|
@@ -8721,80 +9542,85 @@ async function flushBatch() {
|
|
|
8721
9542
|
const draft = row.draft ? 1 : 0;
|
|
8722
9543
|
const memoryType = row.memory_type ?? "raw";
|
|
8723
9544
|
const trajectory = row.trajectory ?? null;
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
9545
|
+
const contentHash = row.content_hash ?? null;
|
|
9546
|
+
const intent = row.intent ?? null;
|
|
9547
|
+
const outcome = row.outcome ?? null;
|
|
9548
|
+
const domain = row.domain ?? null;
|
|
9549
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
9550
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
9551
|
+
const chainPosition = row.chain_position ?? null;
|
|
9552
|
+
const reviewStatus = row.review_status ?? null;
|
|
9553
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
9554
|
+
const filePaths = row.file_paths ?? null;
|
|
9555
|
+
const commitHash = row.commit_hash ?? null;
|
|
9556
|
+
const durationMs = row.duration_ms ?? null;
|
|
9557
|
+
const tokenCost = row.token_cost ?? null;
|
|
9558
|
+
const audience = row.audience ?? null;
|
|
9559
|
+
const languageType = row.language_type ?? null;
|
|
9560
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
9561
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
8734
9562
|
tool_name, project_name,
|
|
8735
9563
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
8736
9564
|
confidence, last_accessed,
|
|
8737
9565
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
8738
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
trajectory
|
|
8797
|
-
]
|
|
9566
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
9567
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
9568
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
9569
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
9570
|
+
const metaArgs = [
|
|
9571
|
+
intent,
|
|
9572
|
+
outcome,
|
|
9573
|
+
domain,
|
|
9574
|
+
referencedEntities,
|
|
9575
|
+
retrievalCount,
|
|
9576
|
+
chainPosition,
|
|
9577
|
+
reviewStatus,
|
|
9578
|
+
contextWindowPct,
|
|
9579
|
+
filePaths,
|
|
9580
|
+
commitHash,
|
|
9581
|
+
durationMs,
|
|
9582
|
+
tokenCost,
|
|
9583
|
+
audience,
|
|
9584
|
+
languageType,
|
|
9585
|
+
parentMemoryId
|
|
9586
|
+
];
|
|
9587
|
+
const baseArgs = [
|
|
9588
|
+
row.id,
|
|
9589
|
+
row.agent_id,
|
|
9590
|
+
row.agent_role,
|
|
9591
|
+
row.session_id,
|
|
9592
|
+
row.timestamp,
|
|
9593
|
+
row.tool_name,
|
|
9594
|
+
row.project_name,
|
|
9595
|
+
row.has_error,
|
|
9596
|
+
row.raw_text
|
|
9597
|
+
];
|
|
9598
|
+
const sharedArgs = [
|
|
9599
|
+
row.version,
|
|
9600
|
+
taskId,
|
|
9601
|
+
importance,
|
|
9602
|
+
status,
|
|
9603
|
+
confidence,
|
|
9604
|
+
lastAccessed,
|
|
9605
|
+
workspaceId,
|
|
9606
|
+
documentId,
|
|
9607
|
+
userId,
|
|
9608
|
+
charOffset,
|
|
9609
|
+
pageNumber,
|
|
9610
|
+
sourcePath,
|
|
9611
|
+
sourceType,
|
|
9612
|
+
tier,
|
|
9613
|
+
supersedesId,
|
|
9614
|
+
draft,
|
|
9615
|
+
memoryType,
|
|
9616
|
+
trajectory,
|
|
9617
|
+
contentHash
|
|
9618
|
+
];
|
|
9619
|
+
return {
|
|
9620
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
9621
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
9622
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
9623
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
8798
9624
|
};
|
|
8799
9625
|
};
|
|
8800
9626
|
const globalClient = getClient();
|
|
@@ -13039,8 +13865,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
13039
13865
|
}
|
|
13040
13866
|
|
|
13041
13867
|
// src/tui/ink/components/ErrorOverview.js
|
|
13042
|
-
var cleanupPath = (
|
|
13043
|
-
return
|
|
13868
|
+
var cleanupPath = (path26) => {
|
|
13869
|
+
return path26?.replace(`file://${cwd()}/`, "");
|
|
13044
13870
|
};
|
|
13045
13871
|
var stackUtils = new StackUtils({
|
|
13046
13872
|
cwd: cwd(),
|
|
@@ -14639,7 +15465,7 @@ var useInput = (inputHandler, options = {}) => {
|
|
|
14639
15465
|
if (options.isActive === false) {
|
|
14640
15466
|
return;
|
|
14641
15467
|
}
|
|
14642
|
-
const
|
|
15468
|
+
const handleData2 = (data) => {
|
|
14643
15469
|
const keypress = parse_keypress_default(data);
|
|
14644
15470
|
const key = {
|
|
14645
15471
|
upArrow: keypress.name === "up",
|
|
@@ -14698,9 +15524,9 @@ var useInput = (inputHandler, options = {}) => {
|
|
|
14698
15524
|
});
|
|
14699
15525
|
}
|
|
14700
15526
|
};
|
|
14701
|
-
internal_eventEmitter?.on("input",
|
|
15527
|
+
internal_eventEmitter?.on("input", handleData2);
|
|
14702
15528
|
return () => {
|
|
14703
|
-
internal_eventEmitter?.removeListener("input",
|
|
15529
|
+
internal_eventEmitter?.removeListener("input", handleData2);
|
|
14704
15530
|
};
|
|
14705
15531
|
}, [options.isActive, stdin, internal_exitOnCtrlC, inputHandler]);
|
|
14706
15532
|
};
|
|
@@ -14972,11 +15798,11 @@ function Footer() {
|
|
|
14972
15798
|
} catch {
|
|
14973
15799
|
}
|
|
14974
15800
|
try {
|
|
14975
|
-
const { existsSync:
|
|
15801
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
14976
15802
|
const { join } = await import("path");
|
|
14977
15803
|
const home = process.env.HOME ?? "";
|
|
14978
15804
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
14979
|
-
setDaemon(
|
|
15805
|
+
setDaemon(existsSync16(pidPath) ? "running" : "stopped");
|
|
14980
15806
|
} catch {
|
|
14981
15807
|
setDaemon("unknown");
|
|
14982
15808
|
}
|
|
@@ -15058,7 +15884,7 @@ function Footer() {
|
|
|
15058
15884
|
// src/tui/views/CommandCenter.tsx
|
|
15059
15885
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
15060
15886
|
import TextInput from "ink-text-input";
|
|
15061
|
-
import
|
|
15887
|
+
import path22 from "path";
|
|
15062
15888
|
import { homedir } from "os";
|
|
15063
15889
|
|
|
15064
15890
|
// src/tui/components/StatusDot.tsx
|
|
@@ -15845,15 +16671,15 @@ function CommandCenterView({
|
|
|
15845
16671
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
15846
16672
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
15847
16673
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
15848
|
-
const { readFileSync:
|
|
16674
|
+
const { readFileSync: readFileSync13, existsSync: existsSync16 } = await import("fs");
|
|
15849
16675
|
const { join } = await import("path");
|
|
15850
16676
|
const { homedir: homedir3 } = await import("os");
|
|
15851
16677
|
const configPath = join(homedir3(), ".exe-os", "config.json");
|
|
15852
16678
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
15853
16679
|
let providerConfigs = {};
|
|
15854
|
-
if (
|
|
16680
|
+
if (existsSync16(configPath)) {
|
|
15855
16681
|
try {
|
|
15856
|
-
const raw = JSON.parse(
|
|
16682
|
+
const raw = JSON.parse(readFileSync13(configPath, "utf8"));
|
|
15857
16683
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
15858
16684
|
if (raw.providers && typeof raw.providers === "object") {
|
|
15859
16685
|
providerConfigs = raw.providers;
|
|
@@ -15914,7 +16740,7 @@ function CommandCenterView({
|
|
|
15914
16740
|
const markerDir = join(homedir3(), ".exe-os", "session-cache");
|
|
15915
16741
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
15916
16742
|
for (const f of agentFiles) {
|
|
15917
|
-
const data = JSON.parse(
|
|
16743
|
+
const data = JSON.parse(readFileSync13(join(markerDir, f), "utf8"));
|
|
15918
16744
|
if (data.agentRole) {
|
|
15919
16745
|
agentRole = data.agentRole;
|
|
15920
16746
|
break;
|
|
@@ -16059,7 +16885,7 @@ function CommandCenterView({
|
|
|
16059
16885
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
16060
16886
|
projectName: p.projectName,
|
|
16061
16887
|
exeSession: p.exeSession,
|
|
16062
|
-
projectDir:
|
|
16888
|
+
projectDir: path22.join(homedir(), p.projectName),
|
|
16063
16889
|
employeeCount: p.employees.length,
|
|
16064
16890
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
16065
16891
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -16097,7 +16923,7 @@ function CommandCenterView({
|
|
|
16097
16923
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
16098
16924
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
16099
16925
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
16100
|
-
const { existsSync:
|
|
16926
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
16101
16927
|
const { join } = await import("path");
|
|
16102
16928
|
const client = getClient2();
|
|
16103
16929
|
if (!client) {
|
|
@@ -16142,12 +16968,12 @@ function CommandCenterView({
|
|
|
16142
16968
|
const registry = listSessions2();
|
|
16143
16969
|
const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
|
|
16144
16970
|
const roster = await loadEmployees2();
|
|
16145
|
-
const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
16971
|
+
const { getCoordinatorName: getCoordinatorName2, isCoordinatorName: isCoordinatorName2, isCoordinatorRole: isCoordinatorRole2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
16146
16972
|
const coordinatorName = getCoordinatorName2(roster);
|
|
16147
16973
|
const employeeNames = roster.filter((e) => !isCoordinatorRole2(e.role)).map((e) => e.name);
|
|
16148
16974
|
const projectSessions = /* @__PURE__ */ new Map();
|
|
16149
16975
|
for (const entry of registry) {
|
|
16150
|
-
if ((entry.agentId === coordinatorName || entry.agentId
|
|
16976
|
+
if ((entry.agentId === coordinatorName || isCoordinatorName2(entry.agentId)) && tmuxSessions.has(entry.windowName)) {
|
|
16151
16977
|
const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
|
|
16152
16978
|
if (projName) {
|
|
16153
16979
|
projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
|
|
@@ -16168,7 +16994,7 @@ function CommandCenterView({
|
|
|
16168
16994
|
}
|
|
16169
16995
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
16170
16996
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
16171
|
-
const hasGit = projectDir ?
|
|
16997
|
+
const hasGit = projectDir ? existsSync16(join(projectDir, ".git")) : false;
|
|
16172
16998
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
16173
16999
|
projectList.push({
|
|
16174
17000
|
projectName: name,
|
|
@@ -16193,7 +17019,7 @@ function CommandCenterView({
|
|
|
16193
17019
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
16194
17020
|
try {
|
|
16195
17021
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
16196
|
-
setHealth((h) => ({ ...h, daemon:
|
|
17022
|
+
setHealth((h) => ({ ...h, daemon: existsSync16(pidPath) ? "running" : "stopped" }));
|
|
16197
17023
|
} catch {
|
|
16198
17024
|
}
|
|
16199
17025
|
const activityResult = await client.execute(
|
|
@@ -16408,7 +17234,7 @@ function ChatMessageRow({ msg }) {
|
|
|
16408
17234
|
|
|
16409
17235
|
// src/tui/views/Sessions.tsx
|
|
16410
17236
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
16411
|
-
import
|
|
17237
|
+
import path23 from "path";
|
|
16412
17238
|
import { homedir as homedir2 } from "os";
|
|
16413
17239
|
|
|
16414
17240
|
// src/tui/components/TmuxPane.tsx
|
|
@@ -16671,12 +17497,13 @@ function useOrchestrator(enabled = true) {
|
|
|
16671
17497
|
}
|
|
16672
17498
|
|
|
16673
17499
|
// src/tui/views/Sessions.tsx
|
|
17500
|
+
init_employees();
|
|
16674
17501
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
16675
17502
|
var SESSION_REFRESH_MS = 5e3;
|
|
16676
17503
|
var ACTIVE_PANE_PATTERN = /█|░|Calculating|tokens|writing|Reading/;
|
|
16677
17504
|
var ACTIVITY_PREVIEW_MAX = 50;
|
|
16678
17505
|
function isCoordinatorEntry(entry) {
|
|
16679
|
-
return entry.role.toLowerCase() === "coo" || entry.name
|
|
17506
|
+
return entry.role.toLowerCase() === "coo" || isCoordinatorName(entry.name);
|
|
16680
17507
|
}
|
|
16681
17508
|
function SessionsView({
|
|
16682
17509
|
initialProject,
|
|
@@ -16710,7 +17537,7 @@ function SessionsView({
|
|
|
16710
17537
|
if (demo) {
|
|
16711
17538
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
16712
17539
|
...p,
|
|
16713
|
-
projectDir:
|
|
17540
|
+
projectDir: path23.join(homedir2(), p.projectName),
|
|
16714
17541
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
16715
17542
|
})));
|
|
16716
17543
|
return;
|
|
@@ -16900,7 +17727,7 @@ function SessionsView({
|
|
|
16900
17727
|
const coordinatorName = getCoordinatorName2(roster);
|
|
16901
17728
|
const exeSessions = /* @__PURE__ */ new Map();
|
|
16902
17729
|
for (const entry of registry) {
|
|
16903
|
-
if ((entry.agentId === coordinatorName || entry.agentId
|
|
17730
|
+
if ((entry.agentId === coordinatorName || isCoordinatorName(entry.agentId)) && tmuxSessions.has(entry.windowName)) {
|
|
16904
17731
|
exeSessions.set(entry.windowName, entry.projectDir);
|
|
16905
17732
|
}
|
|
16906
17733
|
}
|
|
@@ -17607,12 +18434,12 @@ async function loadGatewayConfig() {
|
|
|
17607
18434
|
state.running = false;
|
|
17608
18435
|
}
|
|
17609
18436
|
try {
|
|
17610
|
-
const { existsSync:
|
|
18437
|
+
const { existsSync: existsSync16, readFileSync: readFileSync13 } = await import("fs");
|
|
17611
18438
|
const { join } = await import("path");
|
|
17612
18439
|
const home = process.env.HOME ?? "";
|
|
17613
18440
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
17614
|
-
if (
|
|
17615
|
-
const raw = JSON.parse(
|
|
18441
|
+
if (existsSync16(configPath)) {
|
|
18442
|
+
const raw = JSON.parse(readFileSync13(configPath, "utf8"));
|
|
17616
18443
|
state.port = raw.port ?? 3100;
|
|
17617
18444
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
17618
18445
|
if (raw.adapters) {
|
|
@@ -18138,12 +18965,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
18138
18965
|
setMembers(teamData);
|
|
18139
18966
|
setDbError(null);
|
|
18140
18967
|
try {
|
|
18141
|
-
const { existsSync:
|
|
18968
|
+
const { existsSync: existsSync16, readFileSync: readFileSync13 } = await import("fs");
|
|
18142
18969
|
const { join } = await import("path");
|
|
18143
18970
|
const home = process.env.HOME ?? "";
|
|
18144
18971
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
18145
|
-
if (
|
|
18146
|
-
const raw = JSON.parse(
|
|
18972
|
+
if (existsSync16(gatewayConfig)) {
|
|
18973
|
+
const raw = JSON.parse(readFileSync13(gatewayConfig, "utf8"));
|
|
18147
18974
|
if (raw.agents && raw.agents.length > 0) {
|
|
18148
18975
|
setExternals(raw.agents.map((a) => ({
|
|
18149
18976
|
name: a.name,
|
|
@@ -18807,12 +19634,12 @@ function SettingsView({ onBack }) {
|
|
|
18807
19634
|
}
|
|
18808
19635
|
setProviders(providerList);
|
|
18809
19636
|
try {
|
|
18810
|
-
const { existsSync:
|
|
19637
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
18811
19638
|
const { join } = await import("path");
|
|
18812
19639
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
18813
19640
|
const cfg = await loadConfig2();
|
|
18814
19641
|
const home = process.env.HOME ?? "";
|
|
18815
|
-
const hasKey =
|
|
19642
|
+
const hasKey = existsSync16(join(home, ".exe-os", "master.key"));
|
|
18816
19643
|
if (cfg.cloud) {
|
|
18817
19644
|
setCloud({
|
|
18818
19645
|
configured: true,
|
|
@@ -18825,22 +19652,22 @@ function SettingsView({ onBack }) {
|
|
|
18825
19652
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
18826
19653
|
let daemon = "unknown";
|
|
18827
19654
|
try {
|
|
18828
|
-
daemon =
|
|
19655
|
+
daemon = existsSync16(pidPath) ? "running" : "stopped";
|
|
18829
19656
|
} catch {
|
|
18830
19657
|
}
|
|
18831
19658
|
let version = "unknown";
|
|
18832
19659
|
try {
|
|
18833
|
-
const { readFileSync:
|
|
19660
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
18834
19661
|
const { createRequire } = await import("module");
|
|
18835
19662
|
const require2 = createRequire(import.meta.url);
|
|
18836
19663
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
18837
|
-
const pkg = JSON.parse(
|
|
19664
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
18838
19665
|
version = pkg.version;
|
|
18839
19666
|
} catch {
|
|
18840
19667
|
try {
|
|
18841
|
-
const { readFileSync:
|
|
19668
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
18842
19669
|
const { join: joinPath } = await import("path");
|
|
18843
|
-
const pkg = JSON.parse(
|
|
19670
|
+
const pkg = JSON.parse(readFileSync13(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
18844
19671
|
version = pkg.version;
|
|
18845
19672
|
} catch {
|
|
18846
19673
|
}
|