@askexenow/exe-os 0.8.85 → 0.8.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cleanup-stale-review-tasks.js +57 -19
- package/dist/bin/cli.js +510 -340
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-boot.js +344 -346
- package/dist/bin/exe-dispatch.js +375 -250
- package/dist/bin/exe-forget.js +5 -1
- package/dist/bin/exe-gateway.js +260 -135
- package/dist/bin/exe-healthcheck.js +133 -1
- package/dist/bin/exe-heartbeat.js +72 -31
- package/dist/bin/exe-link.js +25 -2
- package/dist/bin/exe-new-employee.js +22 -0
- package/dist/bin/exe-pending-messages.js +55 -17
- package/dist/bin/exe-pending-reviews.js +57 -19
- package/dist/bin/exe-search.js +6 -2
- package/dist/bin/exe-session-cleanup.js +260 -135
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +57 -19
- package/dist/bin/git-sweep.js +391 -266
- package/dist/bin/install.js +22 -0
- package/dist/bin/scan-tasks.js +394 -269
- package/dist/bin/setup.js +50 -5
- package/dist/gateway/index.js +257 -132
- package/dist/hooks/bug-report-worker.js +242 -117
- package/dist/hooks/commit-complete.js +389 -264
- package/dist/hooks/error-recall.js +6 -2
- package/dist/hooks/ingest-worker.js +314 -193
- package/dist/hooks/post-compact.js +84 -46
- package/dist/hooks/pre-compact.js +272 -147
- package/dist/hooks/pre-tool-use.js +104 -66
- package/dist/hooks/prompt-submit.js +126 -66
- package/dist/hooks/session-end.js +277 -152
- package/dist/hooks/session-start.js +70 -28
- package/dist/hooks/stop.js +90 -52
- package/dist/hooks/subagent-stop.js +84 -46
- package/dist/hooks/summary-worker.js +175 -114
- package/dist/index.js +296 -171
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +25 -2
- package/dist/lib/exe-daemon.js +338 -213
- package/dist/lib/hybrid-search.js +7 -2
- package/dist/lib/messaging.js +95 -39
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/tasks.js +242 -117
- package/dist/lib/tmux-routing.js +314 -189
- package/dist/mcp/server.js +573 -274
- package/dist/mcp/tools/create-task.js +260 -135
- package/dist/mcp/tools/list-tasks.js +68 -30
- package/dist/mcp/tools/send-message.js +100 -44
- package/dist/mcp/tools/update-task.js +123 -67
- package/dist/runtime/index.js +276 -151
- package/dist/tui/App.js +479 -354
- package/package.json +1 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/bin/exe-dispatch.js
CHANGED
|
@@ -269,123 +269,19 @@ var init_provider_table = __esm({
|
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
// src/lib/intercom-queue.ts
|
|
273
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
274
|
-
import path2 from "path";
|
|
275
|
-
import os2 from "os";
|
|
276
|
-
function ensureDir() {
|
|
277
|
-
const dir = path2.dirname(QUEUE_PATH);
|
|
278
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
279
|
-
}
|
|
280
|
-
function readQueue() {
|
|
281
|
-
try {
|
|
282
|
-
if (!existsSync2(QUEUE_PATH)) return [];
|
|
283
|
-
return JSON.parse(readFileSync2(QUEUE_PATH, "utf8"));
|
|
284
|
-
} catch {
|
|
285
|
-
return [];
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function writeQueue(queue) {
|
|
289
|
-
ensureDir();
|
|
290
|
-
const tmp = `${QUEUE_PATH}.tmp`;
|
|
291
|
-
writeFileSync2(tmp, JSON.stringify(queue, null, 2));
|
|
292
|
-
renameSync(tmp, QUEUE_PATH);
|
|
293
|
-
}
|
|
294
|
-
function queueIntercom(targetSession, reason) {
|
|
295
|
-
const queue = readQueue();
|
|
296
|
-
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
297
|
-
if (existing) {
|
|
298
|
-
existing.attempts++;
|
|
299
|
-
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
300
|
-
existing.reason = reason;
|
|
301
|
-
} else {
|
|
302
|
-
queue.push({
|
|
303
|
-
targetSession,
|
|
304
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
305
|
-
attempts: 0,
|
|
306
|
-
reason
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
writeQueue(queue);
|
|
310
|
-
}
|
|
311
|
-
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
312
|
-
var init_intercom_queue = __esm({
|
|
313
|
-
"src/lib/intercom-queue.ts"() {
|
|
314
|
-
"use strict";
|
|
315
|
-
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
316
|
-
TTL_MS = 60 * 60 * 1e3;
|
|
317
|
-
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// src/lib/db-retry.ts
|
|
322
|
-
function isBusyError(err) {
|
|
323
|
-
if (err instanceof Error) {
|
|
324
|
-
const msg = err.message.toLowerCase();
|
|
325
|
-
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
326
|
-
}
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
function delay(ms) {
|
|
330
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
331
|
-
}
|
|
332
|
-
async function retryOnBusy(fn, label) {
|
|
333
|
-
let lastError;
|
|
334
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
335
|
-
try {
|
|
336
|
-
return await fn();
|
|
337
|
-
} catch (err) {
|
|
338
|
-
lastError = err;
|
|
339
|
-
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
340
|
-
throw err;
|
|
341
|
-
}
|
|
342
|
-
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
343
|
-
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
344
|
-
process.stderr.write(
|
|
345
|
-
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
346
|
-
`
|
|
347
|
-
);
|
|
348
|
-
await delay(backoff + jitter);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
throw lastError;
|
|
352
|
-
}
|
|
353
|
-
function wrapWithRetry(client) {
|
|
354
|
-
return new Proxy(client, {
|
|
355
|
-
get(target, prop, receiver) {
|
|
356
|
-
if (prop === "execute") {
|
|
357
|
-
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
358
|
-
}
|
|
359
|
-
if (prop === "batch") {
|
|
360
|
-
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
361
|
-
}
|
|
362
|
-
return Reflect.get(target, prop, receiver);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
367
|
-
var init_db_retry = __esm({
|
|
368
|
-
"src/lib/db-retry.ts"() {
|
|
369
|
-
"use strict";
|
|
370
|
-
MAX_RETRIES = 3;
|
|
371
|
-
BASE_DELAY_MS = 200;
|
|
372
|
-
MAX_JITTER_MS = 300;
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
|
|
376
272
|
// src/lib/config.ts
|
|
377
273
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
378
|
-
import { readFileSync as
|
|
379
|
-
import
|
|
380
|
-
import
|
|
274
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, renameSync } from "fs";
|
|
275
|
+
import path2 from "path";
|
|
276
|
+
import os2 from "os";
|
|
381
277
|
function resolveDataDir() {
|
|
382
278
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
383
279
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
384
|
-
const newDir =
|
|
385
|
-
const legacyDir =
|
|
386
|
-
if (!
|
|
280
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
281
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
282
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
387
283
|
try {
|
|
388
|
-
|
|
284
|
+
renameSync(legacyDir, newDir);
|
|
389
285
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
390
286
|
`);
|
|
391
287
|
} catch {
|
|
@@ -447,9 +343,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
447
343
|
async function loadConfig() {
|
|
448
344
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
449
345
|
await mkdir(dir, { recursive: true });
|
|
450
|
-
const configPath =
|
|
451
|
-
if (!
|
|
452
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
346
|
+
const configPath = path2.join(dir, "config.json");
|
|
347
|
+
if (!existsSync2(configPath)) {
|
|
348
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
453
349
|
}
|
|
454
350
|
const raw = await readFile(configPath, "utf-8");
|
|
455
351
|
try {
|
|
@@ -467,13 +363,13 @@ async function loadConfig() {
|
|
|
467
363
|
normalizeScalingRoadmap(migratedCfg);
|
|
468
364
|
normalizeSessionLifecycle(migratedCfg);
|
|
469
365
|
normalizeAutoUpdate(migratedCfg);
|
|
470
|
-
const config = { ...DEFAULT_CONFIG, dbPath:
|
|
366
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
471
367
|
if (config.dbPath.startsWith("~")) {
|
|
472
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
368
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
473
369
|
}
|
|
474
370
|
return config;
|
|
475
371
|
} catch {
|
|
476
|
-
return { ...DEFAULT_CONFIG, dbPath:
|
|
372
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
477
373
|
}
|
|
478
374
|
}
|
|
479
375
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
@@ -481,10 +377,10 @@ var init_config = __esm({
|
|
|
481
377
|
"src/lib/config.ts"() {
|
|
482
378
|
"use strict";
|
|
483
379
|
EXE_AI_DIR = resolveDataDir();
|
|
484
|
-
DB_PATH =
|
|
485
|
-
MODELS_DIR =
|
|
486
|
-
CONFIG_PATH =
|
|
487
|
-
LEGACY_LANCE_PATH =
|
|
380
|
+
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
381
|
+
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
382
|
+
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
383
|
+
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
488
384
|
CURRENT_CONFIG_VERSION = 1;
|
|
489
385
|
DEFAULT_CONFIG = {
|
|
490
386
|
config_version: CURRENT_CONFIG_VERSION,
|
|
@@ -556,11 +452,166 @@ var init_config = __esm({
|
|
|
556
452
|
}
|
|
557
453
|
});
|
|
558
454
|
|
|
455
|
+
// src/lib/runtime-table.ts
|
|
456
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
457
|
+
var init_runtime_table = __esm({
|
|
458
|
+
"src/lib/runtime-table.ts"() {
|
|
459
|
+
"use strict";
|
|
460
|
+
RUNTIME_TABLE = {
|
|
461
|
+
codex: {
|
|
462
|
+
binary: "codex",
|
|
463
|
+
launchMode: "exec",
|
|
464
|
+
autoApproveFlag: "--full-auto",
|
|
465
|
+
inlineFlag: "--no-alt-screen",
|
|
466
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
467
|
+
defaultModel: "gpt-5.4"
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
DEFAULT_RUNTIME = "claude";
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// src/lib/agent-config.ts
|
|
475
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
476
|
+
import path3 from "path";
|
|
477
|
+
function loadAgentConfig() {
|
|
478
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
479
|
+
try {
|
|
480
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
481
|
+
} catch {
|
|
482
|
+
return {};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function getAgentRuntime(agentId) {
|
|
486
|
+
const config = loadAgentConfig();
|
|
487
|
+
const entry = config[agentId];
|
|
488
|
+
if (entry) return entry;
|
|
489
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
490
|
+
}
|
|
491
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
492
|
+
var init_agent_config = __esm({
|
|
493
|
+
"src/lib/agent-config.ts"() {
|
|
494
|
+
"use strict";
|
|
495
|
+
init_config();
|
|
496
|
+
init_runtime_table();
|
|
497
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
498
|
+
DEFAULT_MODELS = {
|
|
499
|
+
claude: "claude-opus-4",
|
|
500
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
501
|
+
opencode: "minimax-m2.7"
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// src/lib/intercom-queue.ts
|
|
507
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
508
|
+
import path4 from "path";
|
|
509
|
+
import os3 from "os";
|
|
510
|
+
function ensureDir() {
|
|
511
|
+
const dir = path4.dirname(QUEUE_PATH);
|
|
512
|
+
if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
|
|
513
|
+
}
|
|
514
|
+
function readQueue() {
|
|
515
|
+
try {
|
|
516
|
+
if (!existsSync4(QUEUE_PATH)) return [];
|
|
517
|
+
return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
|
|
518
|
+
} catch {
|
|
519
|
+
return [];
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function writeQueue(queue) {
|
|
523
|
+
ensureDir();
|
|
524
|
+
const tmp = `${QUEUE_PATH}.tmp`;
|
|
525
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
526
|
+
renameSync2(tmp, QUEUE_PATH);
|
|
527
|
+
}
|
|
528
|
+
function queueIntercom(targetSession, reason) {
|
|
529
|
+
const queue = readQueue();
|
|
530
|
+
const existing = queue.find((q) => q.targetSession === targetSession);
|
|
531
|
+
if (existing) {
|
|
532
|
+
existing.attempts++;
|
|
533
|
+
existing.queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
534
|
+
existing.reason = reason;
|
|
535
|
+
} else {
|
|
536
|
+
queue.push({
|
|
537
|
+
targetSession,
|
|
538
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
539
|
+
attempts: 0,
|
|
540
|
+
reason
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
writeQueue(queue);
|
|
544
|
+
}
|
|
545
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
546
|
+
var init_intercom_queue = __esm({
|
|
547
|
+
"src/lib/intercom-queue.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
550
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
551
|
+
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// src/lib/db-retry.ts
|
|
556
|
+
function isBusyError(err) {
|
|
557
|
+
if (err instanceof Error) {
|
|
558
|
+
const msg = err.message.toLowerCase();
|
|
559
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
function delay(ms) {
|
|
564
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
565
|
+
}
|
|
566
|
+
async function retryOnBusy(fn, label) {
|
|
567
|
+
let lastError;
|
|
568
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
569
|
+
try {
|
|
570
|
+
return await fn();
|
|
571
|
+
} catch (err) {
|
|
572
|
+
lastError = err;
|
|
573
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
577
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
578
|
+
process.stderr.write(
|
|
579
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
580
|
+
`
|
|
581
|
+
);
|
|
582
|
+
await delay(backoff + jitter);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
throw lastError;
|
|
586
|
+
}
|
|
587
|
+
function wrapWithRetry(client) {
|
|
588
|
+
return new Proxy(client, {
|
|
589
|
+
get(target, prop, receiver) {
|
|
590
|
+
if (prop === "execute") {
|
|
591
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
592
|
+
}
|
|
593
|
+
if (prop === "batch") {
|
|
594
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
595
|
+
}
|
|
596
|
+
return Reflect.get(target, prop, receiver);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
601
|
+
var init_db_retry = __esm({
|
|
602
|
+
"src/lib/db-retry.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
MAX_RETRIES = 3;
|
|
605
|
+
BASE_DELAY_MS = 200;
|
|
606
|
+
MAX_JITTER_MS = 300;
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
559
610
|
// src/lib/employees.ts
|
|
560
611
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
561
|
-
import { existsSync as
|
|
612
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
562
613
|
import { execSync as execSync3 } from "child_process";
|
|
563
|
-
import
|
|
614
|
+
import path5 from "path";
|
|
564
615
|
import os4 from "os";
|
|
565
616
|
function normalizeRole(role) {
|
|
566
617
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -579,9 +630,9 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
579
630
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
580
631
|
}
|
|
581
632
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
582
|
-
if (!
|
|
633
|
+
if (!existsSync5(employeesPath)) return [];
|
|
583
634
|
try {
|
|
584
|
-
return JSON.parse(
|
|
635
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
585
636
|
} catch {
|
|
586
637
|
return [];
|
|
587
638
|
}
|
|
@@ -600,7 +651,7 @@ var init_employees = __esm({
|
|
|
600
651
|
"src/lib/employees.ts"() {
|
|
601
652
|
"use strict";
|
|
602
653
|
init_config();
|
|
603
|
-
EMPLOYEES_PATH =
|
|
654
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
604
655
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
605
656
|
COORDINATOR_ROLE = "COO";
|
|
606
657
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
@@ -1581,18 +1632,18 @@ var init_database = __esm({
|
|
|
1581
1632
|
});
|
|
1582
1633
|
|
|
1583
1634
|
// src/lib/license.ts
|
|
1584
|
-
import { readFileSync as
|
|
1635
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
1585
1636
|
import { randomUUID } from "crypto";
|
|
1586
|
-
import
|
|
1637
|
+
import path6 from "path";
|
|
1587
1638
|
import { jwtVerify, importSPKI } from "jose";
|
|
1588
1639
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
1589
1640
|
var init_license = __esm({
|
|
1590
1641
|
"src/lib/license.ts"() {
|
|
1591
1642
|
"use strict";
|
|
1592
1643
|
init_config();
|
|
1593
|
-
LICENSE_PATH =
|
|
1594
|
-
CACHE_PATH =
|
|
1595
|
-
DEVICE_ID_PATH =
|
|
1644
|
+
LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
|
|
1645
|
+
CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
|
|
1646
|
+
DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
|
|
1596
1647
|
PLAN_LIMITS = {
|
|
1597
1648
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
1598
1649
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -1604,12 +1655,12 @@ var init_license = __esm({
|
|
|
1604
1655
|
});
|
|
1605
1656
|
|
|
1606
1657
|
// src/lib/plan-limits.ts
|
|
1607
|
-
import { readFileSync as
|
|
1608
|
-
import
|
|
1658
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
1659
|
+
import path7 from "path";
|
|
1609
1660
|
function getLicenseSync() {
|
|
1610
1661
|
try {
|
|
1611
|
-
if (!
|
|
1612
|
-
const raw = JSON.parse(
|
|
1662
|
+
if (!existsSync7(CACHE_PATH2)) return freeLicense();
|
|
1663
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
|
|
1613
1664
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
1614
1665
|
const parts = raw.token.split(".");
|
|
1615
1666
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -1647,8 +1698,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
1647
1698
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
1648
1699
|
let count = 0;
|
|
1649
1700
|
try {
|
|
1650
|
-
if (
|
|
1651
|
-
const raw =
|
|
1701
|
+
if (existsSync7(filePath)) {
|
|
1702
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
1652
1703
|
const employees = JSON.parse(raw);
|
|
1653
1704
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
1654
1705
|
}
|
|
@@ -1677,19 +1728,19 @@ var init_plan_limits = __esm({
|
|
|
1677
1728
|
this.name = "PlanLimitError";
|
|
1678
1729
|
}
|
|
1679
1730
|
};
|
|
1680
|
-
CACHE_PATH2 =
|
|
1731
|
+
CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
1681
1732
|
}
|
|
1682
1733
|
});
|
|
1683
1734
|
|
|
1684
1735
|
// src/lib/notifications.ts
|
|
1685
1736
|
import crypto from "crypto";
|
|
1686
|
-
import
|
|
1737
|
+
import path8 from "path";
|
|
1687
1738
|
import os5 from "os";
|
|
1688
1739
|
import {
|
|
1689
|
-
readFileSync as
|
|
1740
|
+
readFileSync as readFileSync8,
|
|
1690
1741
|
readdirSync,
|
|
1691
1742
|
unlinkSync as unlinkSync2,
|
|
1692
|
-
existsSync as
|
|
1743
|
+
existsSync as existsSync8,
|
|
1693
1744
|
rmdirSync
|
|
1694
1745
|
} from "fs";
|
|
1695
1746
|
async function writeNotification(notification) {
|
|
@@ -1848,11 +1899,11 @@ var init_state_bus = __esm({
|
|
|
1848
1899
|
|
|
1849
1900
|
// src/lib/tasks-crud.ts
|
|
1850
1901
|
import crypto3 from "crypto";
|
|
1851
|
-
import
|
|
1902
|
+
import path9 from "path";
|
|
1852
1903
|
import os6 from "os";
|
|
1853
1904
|
import { execSync as execSync4 } from "child_process";
|
|
1854
1905
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
1855
|
-
import { existsSync as
|
|
1906
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
1856
1907
|
async function writeCheckpoint(input) {
|
|
1857
1908
|
const client = getClient();
|
|
1858
1909
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2027,8 +2078,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2027
2078
|
}
|
|
2028
2079
|
if (input.baseDir) {
|
|
2029
2080
|
try {
|
|
2030
|
-
await mkdir3(
|
|
2031
|
-
await mkdir3(
|
|
2081
|
+
await mkdir3(path9.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2082
|
+
await mkdir3(path9.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2032
2083
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2033
2084
|
await ensureGitignoreExe(input.baseDir);
|
|
2034
2085
|
} catch {
|
|
@@ -2064,10 +2115,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2064
2115
|
});
|
|
2065
2116
|
if (input.baseDir) {
|
|
2066
2117
|
try {
|
|
2067
|
-
const EXE_OS_DIR =
|
|
2068
|
-
const mdPath =
|
|
2069
|
-
const mdDir =
|
|
2070
|
-
if (!
|
|
2118
|
+
const EXE_OS_DIR = path9.join(os6.homedir(), ".exe-os");
|
|
2119
|
+
const mdPath = path9.join(EXE_OS_DIR, taskFile);
|
|
2120
|
+
const mdDir = path9.dirname(mdPath);
|
|
2121
|
+
if (!existsSync9(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2071
2122
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2072
2123
|
const mdContent = `# ${input.title}
|
|
2073
2124
|
|
|
@@ -2092,7 +2143,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2092
2143
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2093
2144
|
`;
|
|
2094
2145
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2095
|
-
} catch {
|
|
2146
|
+
} catch (err) {
|
|
2147
|
+
process.stderr.write(
|
|
2148
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2149
|
+
`
|
|
2150
|
+
);
|
|
2096
2151
|
}
|
|
2097
2152
|
}
|
|
2098
2153
|
return {
|
|
@@ -2352,9 +2407,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2352
2407
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2353
2408
|
}
|
|
2354
2409
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2355
|
-
const archPath =
|
|
2410
|
+
const archPath = path9.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2356
2411
|
try {
|
|
2357
|
-
if (
|
|
2412
|
+
if (existsSync9(archPath)) return;
|
|
2358
2413
|
const template = [
|
|
2359
2414
|
`# ${projectName} \u2014 System Architecture`,
|
|
2360
2415
|
"",
|
|
@@ -2387,10 +2442,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2387
2442
|
}
|
|
2388
2443
|
}
|
|
2389
2444
|
async function ensureGitignoreExe(baseDir) {
|
|
2390
|
-
const gitignorePath =
|
|
2445
|
+
const gitignorePath = path9.join(baseDir, ".gitignore");
|
|
2391
2446
|
try {
|
|
2392
|
-
if (
|
|
2393
|
-
const content =
|
|
2447
|
+
if (existsSync9(gitignorePath)) {
|
|
2448
|
+
const content = readFileSync9(gitignorePath, "utf-8");
|
|
2394
2449
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2395
2450
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2396
2451
|
} else {
|
|
@@ -2421,8 +2476,8 @@ var init_tasks_crud = __esm({
|
|
|
2421
2476
|
});
|
|
2422
2477
|
|
|
2423
2478
|
// src/lib/tasks-review.ts
|
|
2424
|
-
import
|
|
2425
|
-
import { existsSync as
|
|
2479
|
+
import path10 from "path";
|
|
2480
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2426
2481
|
async function countPendingReviews(sessionScope) {
|
|
2427
2482
|
const client = getClient();
|
|
2428
2483
|
if (sessionScope) {
|
|
@@ -2603,11 +2658,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2603
2658
|
);
|
|
2604
2659
|
}
|
|
2605
2660
|
try {
|
|
2606
|
-
const cacheDir =
|
|
2607
|
-
if (
|
|
2661
|
+
const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
|
|
2662
|
+
if (existsSync10(cacheDir)) {
|
|
2608
2663
|
for (const f of readdirSync2(cacheDir)) {
|
|
2609
2664
|
if (f.startsWith("review-notified-")) {
|
|
2610
|
-
unlinkSync3(
|
|
2665
|
+
unlinkSync3(path10.join(cacheDir, f));
|
|
2611
2666
|
}
|
|
2612
2667
|
}
|
|
2613
2668
|
}
|
|
@@ -2628,7 +2683,7 @@ var init_tasks_review = __esm({
|
|
|
2628
2683
|
});
|
|
2629
2684
|
|
|
2630
2685
|
// src/lib/tasks-chain.ts
|
|
2631
|
-
import
|
|
2686
|
+
import path11 from "path";
|
|
2632
2687
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2633
2688
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
2634
2689
|
const client = getClient();
|
|
@@ -2645,7 +2700,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
2645
2700
|
});
|
|
2646
2701
|
for (const ur of unblockedRows.rows) {
|
|
2647
2702
|
try {
|
|
2648
|
-
const ubFile =
|
|
2703
|
+
const ubFile = path11.join(baseDir, String(ur.task_file));
|
|
2649
2704
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
2650
2705
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
2651
2706
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -2714,7 +2769,7 @@ var init_tasks_chain = __esm({
|
|
|
2714
2769
|
|
|
2715
2770
|
// src/lib/project-name.ts
|
|
2716
2771
|
import { execSync as execSync5 } from "child_process";
|
|
2717
|
-
import
|
|
2772
|
+
import path12 from "path";
|
|
2718
2773
|
function getProjectName(cwd) {
|
|
2719
2774
|
const dir = cwd ?? process.cwd();
|
|
2720
2775
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -2727,7 +2782,7 @@ function getProjectName(cwd) {
|
|
|
2727
2782
|
timeout: 2e3,
|
|
2728
2783
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2729
2784
|
}).trim();
|
|
2730
|
-
repoRoot =
|
|
2785
|
+
repoRoot = path12.dirname(gitCommonDir);
|
|
2731
2786
|
} catch {
|
|
2732
2787
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
2733
2788
|
cwd: dir,
|
|
@@ -2736,11 +2791,11 @@ function getProjectName(cwd) {
|
|
|
2736
2791
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2737
2792
|
}).trim();
|
|
2738
2793
|
}
|
|
2739
|
-
_cached2 =
|
|
2794
|
+
_cached2 = path12.basename(repoRoot);
|
|
2740
2795
|
_cachedCwd = dir;
|
|
2741
2796
|
return _cached2;
|
|
2742
2797
|
} catch {
|
|
2743
|
-
_cached2 =
|
|
2798
|
+
_cached2 = path12.basename(dir);
|
|
2744
2799
|
_cachedCwd = dir;
|
|
2745
2800
|
return _cached2;
|
|
2746
2801
|
}
|
|
@@ -3213,8 +3268,8 @@ __export(tasks_exports, {
|
|
|
3213
3268
|
updateTaskStatus: () => updateTaskStatus,
|
|
3214
3269
|
writeCheckpoint: () => writeCheckpoint
|
|
3215
3270
|
});
|
|
3216
|
-
import
|
|
3217
|
-
import { writeFileSync as
|
|
3271
|
+
import path13 from "path";
|
|
3272
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
3218
3273
|
async function createTask(input) {
|
|
3219
3274
|
const result2 = await createTaskCore(input);
|
|
3220
3275
|
if (!input.skipDispatch && result2.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3233,11 +3288,11 @@ async function updateTask(input) {
|
|
|
3233
3288
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3234
3289
|
try {
|
|
3235
3290
|
const agent = String(row.assigned_to);
|
|
3236
|
-
const cacheDir =
|
|
3237
|
-
const cachePath =
|
|
3291
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
3292
|
+
const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
|
|
3238
3293
|
if (input.status === "in_progress") {
|
|
3239
|
-
|
|
3240
|
-
|
|
3294
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3295
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3241
3296
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3242
3297
|
try {
|
|
3243
3298
|
unlinkSync4(cachePath);
|
|
@@ -3704,13 +3759,13 @@ __export(tmux_routing_exports, {
|
|
|
3704
3759
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
3705
3760
|
});
|
|
3706
3761
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
3707
|
-
import { readFileSync as
|
|
3708
|
-
import
|
|
3762
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync } from "fs";
|
|
3763
|
+
import path14 from "path";
|
|
3709
3764
|
import os7 from "os";
|
|
3710
3765
|
import { fileURLToPath } from "url";
|
|
3711
3766
|
import { unlinkSync as unlinkSync5 } from "fs";
|
|
3712
3767
|
function spawnLockPath(sessionName) {
|
|
3713
|
-
return
|
|
3768
|
+
return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3714
3769
|
}
|
|
3715
3770
|
function isProcessAlive(pid) {
|
|
3716
3771
|
try {
|
|
@@ -3721,13 +3776,13 @@ function isProcessAlive(pid) {
|
|
|
3721
3776
|
}
|
|
3722
3777
|
}
|
|
3723
3778
|
function acquireSpawnLock(sessionName) {
|
|
3724
|
-
if (!
|
|
3725
|
-
|
|
3779
|
+
if (!existsSync11(SPAWN_LOCK_DIR)) {
|
|
3780
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
3726
3781
|
}
|
|
3727
3782
|
const lockFile = spawnLockPath(sessionName);
|
|
3728
|
-
if (
|
|
3783
|
+
if (existsSync11(lockFile)) {
|
|
3729
3784
|
try {
|
|
3730
|
-
const lock = JSON.parse(
|
|
3785
|
+
const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
|
|
3731
3786
|
const age = Date.now() - lock.timestamp;
|
|
3732
3787
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
3733
3788
|
return false;
|
|
@@ -3735,7 +3790,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
3735
3790
|
} catch {
|
|
3736
3791
|
}
|
|
3737
3792
|
}
|
|
3738
|
-
|
|
3793
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
3739
3794
|
return true;
|
|
3740
3795
|
}
|
|
3741
3796
|
function releaseSpawnLock(sessionName) {
|
|
@@ -3747,13 +3802,13 @@ function releaseSpawnLock(sessionName) {
|
|
|
3747
3802
|
function resolveBehaviorsExporterScript() {
|
|
3748
3803
|
try {
|
|
3749
3804
|
const thisFile = fileURLToPath(import.meta.url);
|
|
3750
|
-
const scriptPath =
|
|
3751
|
-
|
|
3805
|
+
const scriptPath = path14.join(
|
|
3806
|
+
path14.dirname(thisFile),
|
|
3752
3807
|
"..",
|
|
3753
3808
|
"bin",
|
|
3754
3809
|
"exe-export-behaviors.js"
|
|
3755
3810
|
);
|
|
3756
|
-
return
|
|
3811
|
+
return existsSync11(scriptPath) ? scriptPath : null;
|
|
3757
3812
|
} catch {
|
|
3758
3813
|
return null;
|
|
3759
3814
|
}
|
|
@@ -3819,12 +3874,12 @@ function extractRootExe(name) {
|
|
|
3819
3874
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3820
3875
|
}
|
|
3821
3876
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3822
|
-
if (!
|
|
3823
|
-
|
|
3877
|
+
if (!existsSync11(SESSION_CACHE)) {
|
|
3878
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3824
3879
|
}
|
|
3825
3880
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3826
|
-
const filePath =
|
|
3827
|
-
|
|
3881
|
+
const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3882
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
3828
3883
|
parentExe: rootExe,
|
|
3829
3884
|
dispatchedBy: dispatchedBy || rootExe,
|
|
3830
3885
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -3832,7 +3887,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3832
3887
|
}
|
|
3833
3888
|
function getParentExe(sessionKey) {
|
|
3834
3889
|
try {
|
|
3835
|
-
const data = JSON.parse(
|
|
3890
|
+
const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3836
3891
|
return data.parentExe || null;
|
|
3837
3892
|
} catch {
|
|
3838
3893
|
return null;
|
|
@@ -3840,8 +3895,8 @@ function getParentExe(sessionKey) {
|
|
|
3840
3895
|
}
|
|
3841
3896
|
function getDispatchedBy(sessionKey) {
|
|
3842
3897
|
try {
|
|
3843
|
-
const data = JSON.parse(
|
|
3844
|
-
|
|
3898
|
+
const data = JSON.parse(readFileSync10(
|
|
3899
|
+
path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
3845
3900
|
"utf8"
|
|
3846
3901
|
));
|
|
3847
3902
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -3902,32 +3957,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
3902
3957
|
}
|
|
3903
3958
|
function readDebounceState() {
|
|
3904
3959
|
try {
|
|
3905
|
-
if (!
|
|
3906
|
-
|
|
3960
|
+
if (!existsSync11(DEBOUNCE_FILE)) return {};
|
|
3961
|
+
const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
|
|
3962
|
+
const state = {};
|
|
3963
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
3964
|
+
if (typeof val === "number") {
|
|
3965
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
3966
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
3967
|
+
state[key] = val;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
return state;
|
|
3907
3971
|
} catch {
|
|
3908
3972
|
return {};
|
|
3909
3973
|
}
|
|
3910
3974
|
}
|
|
3911
3975
|
function writeDebounceState(state) {
|
|
3912
3976
|
try {
|
|
3913
|
-
if (!
|
|
3914
|
-
|
|
3977
|
+
if (!existsSync11(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3978
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
3915
3979
|
} catch {
|
|
3916
3980
|
}
|
|
3917
3981
|
}
|
|
3918
3982
|
function isDebounced(targetSession) {
|
|
3919
3983
|
const state = readDebounceState();
|
|
3920
|
-
const
|
|
3921
|
-
|
|
3984
|
+
const entry = state[targetSession];
|
|
3985
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
3986
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
3987
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
3988
|
+
state[targetSession].pending++;
|
|
3989
|
+
writeDebounceState(state);
|
|
3990
|
+
return true;
|
|
3991
|
+
}
|
|
3992
|
+
return false;
|
|
3922
3993
|
}
|
|
3923
3994
|
function recordDebounce(targetSession) {
|
|
3924
3995
|
const state = readDebounceState();
|
|
3925
|
-
state[targetSession]
|
|
3996
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
3997
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
3926
3998
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
3927
3999
|
for (const key of Object.keys(state)) {
|
|
3928
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4000
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
3929
4001
|
}
|
|
3930
4002
|
writeDebounceState(state);
|
|
4003
|
+
return batched;
|
|
3931
4004
|
}
|
|
3932
4005
|
function logIntercom(msg) {
|
|
3933
4006
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -3972,7 +4045,7 @@ function sendIntercom(targetSession) {
|
|
|
3972
4045
|
return "skipped_exe";
|
|
3973
4046
|
}
|
|
3974
4047
|
if (isDebounced(targetSession)) {
|
|
3975
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4048
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
3976
4049
|
return "debounced";
|
|
3977
4050
|
}
|
|
3978
4051
|
try {
|
|
@@ -3984,14 +4057,14 @@ function sendIntercom(targetSession) {
|
|
|
3984
4057
|
const sessionState = getSessionState(targetSession);
|
|
3985
4058
|
if (sessionState === "no_claude") {
|
|
3986
4059
|
queueIntercom(targetSession, "claude not running in session");
|
|
3987
|
-
recordDebounce(targetSession);
|
|
3988
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4060
|
+
const batched2 = recordDebounce(targetSession);
|
|
4061
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3989
4062
|
return "queued";
|
|
3990
4063
|
}
|
|
3991
4064
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
3992
4065
|
queueIntercom(targetSession, "session busy at send time");
|
|
3993
|
-
recordDebounce(targetSession);
|
|
3994
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4066
|
+
const batched2 = recordDebounce(targetSession);
|
|
4067
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3995
4068
|
return "queued";
|
|
3996
4069
|
}
|
|
3997
4070
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -3999,8 +4072,8 @@ function sendIntercom(targetSession) {
|
|
|
3999
4072
|
transport.sendKeys(targetSession, "q");
|
|
4000
4073
|
}
|
|
4001
4074
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4002
|
-
recordDebounce(targetSession);
|
|
4003
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4075
|
+
const batched = recordDebounce(targetSession);
|
|
4076
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4004
4077
|
return "delivered";
|
|
4005
4078
|
} catch {
|
|
4006
4079
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4102,26 +4175,26 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4102
4175
|
const transport = getTransport();
|
|
4103
4176
|
const sessionName = employeeSessionName(employeeName2, exeSession2, opts?.instance);
|
|
4104
4177
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName2}${opts.instance}` : employeeName2;
|
|
4105
|
-
const logDir =
|
|
4106
|
-
const logFile =
|
|
4107
|
-
if (!
|
|
4108
|
-
|
|
4178
|
+
const logDir = path14.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4179
|
+
const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4180
|
+
if (!existsSync11(logDir)) {
|
|
4181
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4109
4182
|
}
|
|
4110
4183
|
transport.kill(sessionName);
|
|
4111
4184
|
let cleanupSuffix = "";
|
|
4112
4185
|
try {
|
|
4113
4186
|
const thisFile = fileURLToPath(import.meta.url);
|
|
4114
|
-
const cleanupScript =
|
|
4115
|
-
if (
|
|
4187
|
+
const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4188
|
+
if (existsSync11(cleanupScript)) {
|
|
4116
4189
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName2}" "${exeSession2}"`;
|
|
4117
4190
|
}
|
|
4118
4191
|
} catch {
|
|
4119
4192
|
}
|
|
4120
4193
|
try {
|
|
4121
|
-
const claudeJsonPath =
|
|
4194
|
+
const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
|
|
4122
4195
|
let claudeJson = {};
|
|
4123
4196
|
try {
|
|
4124
|
-
claudeJson = JSON.parse(
|
|
4197
|
+
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
4125
4198
|
} catch {
|
|
4126
4199
|
}
|
|
4127
4200
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4129,17 +4202,17 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4129
4202
|
const trustDir = opts?.cwd ?? projectDir2;
|
|
4130
4203
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4131
4204
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4132
|
-
|
|
4205
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4133
4206
|
} catch {
|
|
4134
4207
|
}
|
|
4135
4208
|
try {
|
|
4136
|
-
const settingsDir =
|
|
4209
|
+
const settingsDir = path14.join(os7.homedir(), ".claude", "projects");
|
|
4137
4210
|
const normalizedKey = (opts?.cwd ?? projectDir2).replace(/\//g, "-").replace(/^-/, "");
|
|
4138
|
-
const projSettingsDir =
|
|
4139
|
-
const settingsPath =
|
|
4211
|
+
const projSettingsDir = path14.join(settingsDir, normalizedKey);
|
|
4212
|
+
const settingsPath = path14.join(projSettingsDir, "settings.json");
|
|
4140
4213
|
let settings = {};
|
|
4141
4214
|
try {
|
|
4142
|
-
settings = JSON.parse(
|
|
4215
|
+
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
4143
4216
|
} catch {
|
|
4144
4217
|
}
|
|
4145
4218
|
const perms = settings.permissions ?? {};
|
|
@@ -4167,20 +4240,23 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4167
4240
|
if (changed) {
|
|
4168
4241
|
perms.allow = allow;
|
|
4169
4242
|
settings.permissions = perms;
|
|
4170
|
-
|
|
4171
|
-
|
|
4243
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4244
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4172
4245
|
}
|
|
4173
4246
|
} catch {
|
|
4174
4247
|
}
|
|
4175
4248
|
const spawnCwd = opts?.cwd ?? projectDir2;
|
|
4176
4249
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4177
|
-
const
|
|
4250
|
+
const agentRtConfig = getAgentRuntime(employeeName2);
|
|
4251
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4252
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4253
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4178
4254
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4179
4255
|
let identityFlag = "";
|
|
4180
4256
|
let behaviorsFlag = "";
|
|
4181
4257
|
let legacyFallbackWarned = false;
|
|
4182
4258
|
if (!useExeAgent && !useBinSymlink) {
|
|
4183
|
-
const identityPath =
|
|
4259
|
+
const identityPath = path14.join(
|
|
4184
4260
|
os7.homedir(),
|
|
4185
4261
|
".exe-os",
|
|
4186
4262
|
"identity",
|
|
@@ -4190,13 +4266,13 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4190
4266
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4191
4267
|
if (hasAgentFlag) {
|
|
4192
4268
|
identityFlag = ` --agent ${employeeName2}`;
|
|
4193
|
-
} else if (
|
|
4269
|
+
} else if (existsSync11(identityPath)) {
|
|
4194
4270
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4195
4271
|
legacyFallbackWarned = true;
|
|
4196
4272
|
}
|
|
4197
4273
|
const behaviorsFile = exportBehaviorsSync(
|
|
4198
4274
|
employeeName2,
|
|
4199
|
-
|
|
4275
|
+
path14.basename(spawnCwd),
|
|
4200
4276
|
sessionName
|
|
4201
4277
|
);
|
|
4202
4278
|
if (behaviorsFile) {
|
|
@@ -4211,16 +4287,16 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4211
4287
|
}
|
|
4212
4288
|
let sessionContextFlag = "";
|
|
4213
4289
|
try {
|
|
4214
|
-
const ctxDir =
|
|
4215
|
-
|
|
4216
|
-
const ctxFile =
|
|
4290
|
+
const ctxDir = path14.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4291
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4292
|
+
const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4217
4293
|
const ctxContent = [
|
|
4218
4294
|
`## Session Context`,
|
|
4219
4295
|
`You are running in tmux session: ${sessionName}.`,
|
|
4220
4296
|
`Your parent coordinator session is ${exeSession2}.`,
|
|
4221
4297
|
`Your employees (if any) use the -${exeSession2} suffix.`
|
|
4222
4298
|
].join("\n");
|
|
4223
|
-
|
|
4299
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4224
4300
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4225
4301
|
} catch {
|
|
4226
4302
|
}
|
|
@@ -4234,9 +4310,48 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4234
4310
|
}
|
|
4235
4311
|
}
|
|
4236
4312
|
}
|
|
4313
|
+
if (useCodex) {
|
|
4314
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4315
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4316
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4317
|
+
if (keyVal) {
|
|
4318
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4322
|
+
}
|
|
4323
|
+
if (useOpencode) {
|
|
4324
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4325
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4326
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4327
|
+
if (keyVal) {
|
|
4328
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4332
|
+
}
|
|
4333
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4334
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4335
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4336
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4237
4339
|
let spawnCommand;
|
|
4238
4340
|
if (useExeAgent) {
|
|
4239
4341
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName2} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4342
|
+
} else if (useCodex) {
|
|
4343
|
+
process.stderr.write(
|
|
4344
|
+
`[tmux-routing] agent-config: ${employeeName2} \u2192 codex (${agentRtConfig.model})
|
|
4345
|
+
`
|
|
4346
|
+
);
|
|
4347
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName2}${cleanupSuffix}`;
|
|
4348
|
+
} else if (useOpencode) {
|
|
4349
|
+
const binName = `${employeeName2}-opencode`;
|
|
4350
|
+
process.stderr.write(
|
|
4351
|
+
`[tmux-routing] agent-config: ${employeeName2} \u2192 opencode (${agentRtConfig.model})
|
|
4352
|
+
`
|
|
4353
|
+
);
|
|
4354
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4240
4355
|
} else if (useBinSymlink) {
|
|
4241
4356
|
const binName = `${employeeName2}-${ccProvider}`;
|
|
4242
4357
|
process.stderr.write(
|
|
@@ -4258,11 +4373,13 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4258
4373
|
transport.pipeLog(sessionName, logFile);
|
|
4259
4374
|
try {
|
|
4260
4375
|
const mySession = getMySession();
|
|
4261
|
-
const dispatchInfo =
|
|
4262
|
-
|
|
4376
|
+
const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4377
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4263
4378
|
dispatchedBy: mySession,
|
|
4264
4379
|
rootExe: exeSession2,
|
|
4265
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4380
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4381
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4382
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4266
4383
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4267
4384
|
}));
|
|
4268
4385
|
} catch {
|
|
@@ -4280,6 +4397,11 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4280
4397
|
booted = true;
|
|
4281
4398
|
break;
|
|
4282
4399
|
}
|
|
4400
|
+
} else if (useCodex) {
|
|
4401
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4402
|
+
booted = true;
|
|
4403
|
+
break;
|
|
4404
|
+
}
|
|
4283
4405
|
} else {
|
|
4284
4406
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4285
4407
|
booted = true;
|
|
@@ -4291,9 +4413,10 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
|
|
|
4291
4413
|
}
|
|
4292
4414
|
if (!booted) {
|
|
4293
4415
|
releaseSpawnLock(sessionName);
|
|
4294
|
-
|
|
4416
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4417
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4295
4418
|
}
|
|
4296
|
-
if (!useExeAgent) {
|
|
4419
|
+
if (!useExeAgent && !useCodex) {
|
|
4297
4420
|
try {
|
|
4298
4421
|
transport.sendKeys(sessionName, `/exe-call ${employeeName2}`);
|
|
4299
4422
|
} catch {
|
|
@@ -4320,17 +4443,19 @@ var init_tmux_routing = __esm({
|
|
|
4320
4443
|
init_cc_agent_support();
|
|
4321
4444
|
init_mcp_prefix();
|
|
4322
4445
|
init_provider_table();
|
|
4446
|
+
init_agent_config();
|
|
4447
|
+
init_runtime_table();
|
|
4323
4448
|
init_intercom_queue();
|
|
4324
4449
|
init_plan_limits();
|
|
4325
4450
|
init_employees();
|
|
4326
|
-
SPAWN_LOCK_DIR =
|
|
4327
|
-
SESSION_CACHE =
|
|
4451
|
+
SPAWN_LOCK_DIR = path14.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
4452
|
+
SESSION_CACHE = path14.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4328
4453
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4329
4454
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4330
4455
|
VERIFY_PANE_LINES = 200;
|
|
4331
4456
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4332
|
-
INTERCOM_LOG2 =
|
|
4333
|
-
DEBOUNCE_FILE =
|
|
4457
|
+
INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4458
|
+
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4334
4459
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4335
4460
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4336
4461
|
}
|
|
@@ -4349,13 +4474,13 @@ __export(shard_manager_exports, {
|
|
|
4349
4474
|
listShards: () => listShards,
|
|
4350
4475
|
shardExists: () => shardExists
|
|
4351
4476
|
});
|
|
4352
|
-
import
|
|
4353
|
-
import { existsSync as
|
|
4477
|
+
import path16 from "path";
|
|
4478
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
4354
4479
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4355
4480
|
function initShardManager(encryptionKey) {
|
|
4356
4481
|
_encryptionKey = encryptionKey;
|
|
4357
|
-
if (!
|
|
4358
|
-
|
|
4482
|
+
if (!existsSync13(SHARDS_DIR)) {
|
|
4483
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
4359
4484
|
}
|
|
4360
4485
|
_shardingEnabled = true;
|
|
4361
4486
|
}
|
|
@@ -4375,7 +4500,7 @@ function getShardClient(projectName) {
|
|
|
4375
4500
|
}
|
|
4376
4501
|
const cached = _shards.get(safeName);
|
|
4377
4502
|
if (cached) return cached;
|
|
4378
|
-
const dbPath =
|
|
4503
|
+
const dbPath = path16.join(SHARDS_DIR, `${safeName}.db`);
|
|
4379
4504
|
const client = createClient2({
|
|
4380
4505
|
url: `file:${dbPath}`,
|
|
4381
4506
|
encryptionKey: _encryptionKey
|
|
@@ -4385,10 +4510,10 @@ function getShardClient(projectName) {
|
|
|
4385
4510
|
}
|
|
4386
4511
|
function shardExists(projectName) {
|
|
4387
4512
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4388
|
-
return
|
|
4513
|
+
return existsSync13(path16.join(SHARDS_DIR, `${safeName}.db`));
|
|
4389
4514
|
}
|
|
4390
4515
|
function listShards() {
|
|
4391
|
-
if (!
|
|
4516
|
+
if (!existsSync13(SHARDS_DIR)) return [];
|
|
4392
4517
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4393
4518
|
}
|
|
4394
4519
|
async function ensureShardSchema(client) {
|
|
@@ -4574,7 +4699,7 @@ var init_shard_manager = __esm({
|
|
|
4574
4699
|
"src/lib/shard-manager.ts"() {
|
|
4575
4700
|
"use strict";
|
|
4576
4701
|
init_config();
|
|
4577
|
-
SHARDS_DIR =
|
|
4702
|
+
SHARDS_DIR = path16.join(EXE_AI_DIR, "shards");
|
|
4578
4703
|
_shards = /* @__PURE__ */ new Map();
|
|
4579
4704
|
_encryptionKey = null;
|
|
4580
4705
|
_shardingEnabled = false;
|
|
@@ -4772,16 +4897,16 @@ init_database();
|
|
|
4772
4897
|
|
|
4773
4898
|
// src/lib/keychain.ts
|
|
4774
4899
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4775
|
-
import { existsSync as
|
|
4776
|
-
import
|
|
4900
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4901
|
+
import path15 from "path";
|
|
4777
4902
|
import os8 from "os";
|
|
4778
4903
|
var SERVICE = "exe-mem";
|
|
4779
4904
|
var ACCOUNT = "master-key";
|
|
4780
4905
|
function getKeyDir() {
|
|
4781
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4906
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os8.homedir(), ".exe-os");
|
|
4782
4907
|
}
|
|
4783
4908
|
function getKeyPath() {
|
|
4784
|
-
return
|
|
4909
|
+
return path15.join(getKeyDir(), "master.key");
|
|
4785
4910
|
}
|
|
4786
4911
|
async function tryKeytar() {
|
|
4787
4912
|
try {
|
|
@@ -4802,7 +4927,7 @@ async function getMasterKey() {
|
|
|
4802
4927
|
}
|
|
4803
4928
|
}
|
|
4804
4929
|
const keyPath = getKeyPath();
|
|
4805
|
-
if (!
|
|
4930
|
+
if (!existsSync12(keyPath)) {
|
|
4806
4931
|
process.stderr.write(
|
|
4807
4932
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4808
4933
|
`
|