replicas-engine 0.1.118 → 0.1.120
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/src/index.js +235 -128
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -42,6 +42,18 @@ function requireValidURL(value, name) {
|
|
|
42
42
|
throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
function parseClaudeAuthMethod(value) {
|
|
46
|
+
if (value === "oauth" || value === "api_key" || value === "bedrock") {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
function parseCodexAuthMethod(value) {
|
|
52
|
+
if (value === "oauth" || value === "api_key") {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
45
57
|
var IS_WARMING_MODE = process.argv.includes("--warming");
|
|
46
58
|
function loadEngineEnv() {
|
|
47
59
|
const HOME_DIR = homedir();
|
|
@@ -66,7 +78,9 @@ function loadEngineEnv() {
|
|
|
66
78
|
CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
|
|
67
79
|
AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
|
|
68
80
|
AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
|
|
69
|
-
AWS_REGION: readEnv("AWS_REGION")
|
|
81
|
+
AWS_REGION: readEnv("AWS_REGION"),
|
|
82
|
+
REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
|
|
83
|
+
REPLICAS_CODEX_AUTH_METHOD: parseCodexAuthMethod(readEnv("REPLICAS_CODEX_AUTH_METHOD"))
|
|
70
84
|
};
|
|
71
85
|
if (!IS_WARMING_MODE && !env.WORKSPACE_ID) {
|
|
72
86
|
console.error("WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
|
|
@@ -362,7 +376,7 @@ var codexTokenManager = new CodexTokenManager();
|
|
|
362
376
|
import { readdir, stat } from "fs/promises";
|
|
363
377
|
import { existsSync as existsSync2 } from "fs";
|
|
364
378
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
365
|
-
import { join as
|
|
379
|
+
import { join as join4 } from "path";
|
|
366
380
|
|
|
367
381
|
// src/utils/state.ts
|
|
368
382
|
import { readFile, writeFile, mkdir, rename, unlink } from "fs/promises";
|
|
@@ -413,7 +427,12 @@ function coerceRepoState(value) {
|
|
|
413
427
|
if (typeof value.path !== "string") return null;
|
|
414
428
|
if (typeof value.defaultBranch !== "string") return null;
|
|
415
429
|
if (typeof value.currentBranch !== "string") return null;
|
|
416
|
-
if (!(value.
|
|
430
|
+
if (!Array.isArray(value.prUrls)) return null;
|
|
431
|
+
const prUrls = [];
|
|
432
|
+
for (const entry of value.prUrls) {
|
|
433
|
+
if (typeof entry !== "string") return null;
|
|
434
|
+
if (!prUrls.includes(entry)) prUrls.push(entry);
|
|
435
|
+
}
|
|
417
436
|
if (!(value.gitDiff === null || isEngineRepoDiff(value.gitDiff))) return null;
|
|
418
437
|
if (typeof value.startHooksCompleted !== "boolean") return null;
|
|
419
438
|
return {
|
|
@@ -421,7 +440,7 @@ function coerceRepoState(value) {
|
|
|
421
440
|
path: value.path,
|
|
422
441
|
defaultBranch: value.defaultBranch,
|
|
423
442
|
currentBranch: value.currentBranch,
|
|
424
|
-
|
|
443
|
+
prUrls,
|
|
425
444
|
gitDiff: value.gitDiff,
|
|
426
445
|
startHooksCompleted: value.startHooksCompleted
|
|
427
446
|
};
|
|
@@ -482,6 +501,8 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
482
501
|
|
|
483
502
|
// src/git/commands.ts
|
|
484
503
|
import { execFileSync } from "child_process";
|
|
504
|
+
import { readFileSync } from "fs";
|
|
505
|
+
import { join as join3 } from "path";
|
|
485
506
|
function runGitCommand(args, cwd) {
|
|
486
507
|
return execFileSync("git", args, {
|
|
487
508
|
cwd,
|
|
@@ -489,6 +510,15 @@ function runGitCommand(args, cwd) {
|
|
|
489
510
|
stdio: ["pipe", "pipe", "pipe"]
|
|
490
511
|
}).trim();
|
|
491
512
|
}
|
|
513
|
+
function readRepoHeadBranch(repoPath) {
|
|
514
|
+
try {
|
|
515
|
+
const contents = readFileSync(join3(repoPath, ".git", "HEAD"), "utf-8").trim();
|
|
516
|
+
const match = contents.match(/^ref:\s+refs\/heads\/(.+)$/);
|
|
517
|
+
return match ? match[1] : null;
|
|
518
|
+
} catch {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
492
522
|
function branchExists(branchName, cwd) {
|
|
493
523
|
try {
|
|
494
524
|
runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
@@ -507,6 +537,9 @@ function getCurrentBranch(cwd) {
|
|
|
507
537
|
}
|
|
508
538
|
|
|
509
539
|
// src/git/service.ts
|
|
540
|
+
function appendUniqueUrl(urls, url) {
|
|
541
|
+
return urls.includes(url) ? urls : [...urls, url];
|
|
542
|
+
}
|
|
510
543
|
var GitService = class {
|
|
511
544
|
defaultBranchCache = /* @__PURE__ */ new Map();
|
|
512
545
|
cachedPrByRepo = /* @__PURE__ */ new Map();
|
|
@@ -522,13 +555,13 @@ var GitService = class {
|
|
|
522
555
|
const entries = await readdir(root);
|
|
523
556
|
const repos = [];
|
|
524
557
|
for (const entry of entries) {
|
|
525
|
-
const fullPath =
|
|
558
|
+
const fullPath = join4(root, entry);
|
|
526
559
|
try {
|
|
527
560
|
const entryStat = await stat(fullPath);
|
|
528
561
|
if (!entryStat.isDirectory()) {
|
|
529
562
|
continue;
|
|
530
563
|
}
|
|
531
|
-
const hasGit = Boolean(await this.safeStat(
|
|
564
|
+
const hasGit = Boolean(await this.safeStat(join4(fullPath, ".git")));
|
|
532
565
|
if (!hasGit) {
|
|
533
566
|
continue;
|
|
534
567
|
}
|
|
@@ -563,7 +596,7 @@ var GitService = class {
|
|
|
563
596
|
path: repo.path,
|
|
564
597
|
defaultBranch: repo.defaultBranch,
|
|
565
598
|
currentBranch,
|
|
566
|
-
|
|
599
|
+
prUrls: persistedState?.prUrls ?? [],
|
|
567
600
|
gitDiff: persistedMatchesCurrentBranch ? persistedState.gitDiff : null,
|
|
568
601
|
startHooksCompleted: persistedState?.startHooksCompleted ?? false
|
|
569
602
|
});
|
|
@@ -572,7 +605,7 @@ var GitService = class {
|
|
|
572
605
|
}
|
|
573
606
|
return states;
|
|
574
607
|
}
|
|
575
|
-
async refreshRepos() {
|
|
608
|
+
async refreshRepos(observedBranchesByRepo) {
|
|
576
609
|
const repos = await this.listRepositories();
|
|
577
610
|
const states = [];
|
|
578
611
|
for (const repo of repos) {
|
|
@@ -580,12 +613,29 @@ var GitService = class {
|
|
|
580
613
|
const persistedState = await loadRepoState(repo.name);
|
|
581
614
|
const currentBranch = getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
582
615
|
const startHooksCompleted = persistedState?.startHooksCompleted ?? false;
|
|
583
|
-
|
|
616
|
+
const observed = observedBranchesByRepo?.get(repo.name);
|
|
617
|
+
states.push(
|
|
618
|
+
await this.refreshRepoMetadata(repo, currentBranch, startHooksCompleted, persistedState, observed)
|
|
619
|
+
);
|
|
584
620
|
} catch {
|
|
585
621
|
}
|
|
586
622
|
}
|
|
587
623
|
return states;
|
|
588
624
|
}
|
|
625
|
+
// Fast branch snapshot for per-event observation during a turn. Reads
|
|
626
|
+
// .git/HEAD directly for each repo (no subprocess) so callers can safely
|
|
627
|
+
// invoke this on every agent event without measurable overhead.
|
|
628
|
+
async snapshotCurrentBranches() {
|
|
629
|
+
const repos = await this.listRepositories();
|
|
630
|
+
const branches = /* @__PURE__ */ new Map();
|
|
631
|
+
for (const repo of repos) {
|
|
632
|
+
const branch = readRepoHeadBranch(repo.path);
|
|
633
|
+
if (branch) {
|
|
634
|
+
branches.set(repo.name, branch);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return branches;
|
|
638
|
+
}
|
|
589
639
|
async setBranchNameAndCheckout(name) {
|
|
590
640
|
ENGINE_ENV.WORKSPACE_BRANCH_NAME = name;
|
|
591
641
|
return this.initializeGitRepository();
|
|
@@ -618,7 +668,7 @@ var GitService = class {
|
|
|
618
668
|
path: repo.path,
|
|
619
669
|
defaultBranch: repo.defaultBranch,
|
|
620
670
|
currentBranch: repo.defaultBranch,
|
|
621
|
-
|
|
671
|
+
prUrls: [],
|
|
622
672
|
gitDiff: null,
|
|
623
673
|
startHooksCompleted: false
|
|
624
674
|
};
|
|
@@ -645,7 +695,7 @@ var GitService = class {
|
|
|
645
695
|
}
|
|
646
696
|
const branchName = this.findAvailableBranchName(workspaceName, repo.path);
|
|
647
697
|
runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
648
|
-
await saveRepoState(repo.name, { currentBranch: branchName
|
|
698
|
+
await saveRepoState(repo.name, { currentBranch: branchName }, baselineState);
|
|
649
699
|
results.push({
|
|
650
700
|
name: repo.name,
|
|
651
701
|
success: true,
|
|
@@ -723,56 +773,50 @@ var GitService = class {
|
|
|
723
773
|
return { status: "found", url: cachedPr.prUrl };
|
|
724
774
|
}
|
|
725
775
|
const persistedRepoState = persistedRepoStateArg ?? await loadRepoState(repoName);
|
|
726
|
-
if (persistedRepoState?.prUrl && persistedRepoState.currentBranch === currentBranch) {
|
|
727
|
-
this.cachedPrByRepo.set(repoName, {
|
|
728
|
-
prUrl: persistedRepoState.prUrl,
|
|
729
|
-
currentBranch
|
|
730
|
-
});
|
|
731
|
-
return { status: "found", url: persistedRepoState.prUrl };
|
|
732
|
-
}
|
|
733
776
|
this.cachedPrByRepo.delete(repoName);
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (!remoteRef) {
|
|
744
|
-
return { status: "not_found" };
|
|
745
|
-
}
|
|
746
|
-
} catch {
|
|
747
|
-
return { status: "error" };
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
const prInfo = execFileSync2("gh", ["pr", "view", "--json", "url", "--jq", ".url"], {
|
|
751
|
-
cwd: repoPath,
|
|
752
|
-
encoding: "utf-8",
|
|
753
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
754
|
-
}).trim();
|
|
755
|
-
if (prInfo) {
|
|
756
|
-
this.cachedPrByRepo.set(repoName, {
|
|
757
|
-
prUrl: prInfo,
|
|
758
|
-
currentBranch
|
|
759
|
-
});
|
|
760
|
-
if (persistedRepoState) {
|
|
761
|
-
await saveRepoState(repoName, { prUrl: prInfo }, persistedRepoState);
|
|
762
|
-
}
|
|
763
|
-
return { status: "found", url: prInfo };
|
|
777
|
+
const result = this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
778
|
+
if (result.status === "found") {
|
|
779
|
+
this.cachedPrByRepo.set(repoName, { prUrl: result.url, currentBranch });
|
|
780
|
+
if (persistedRepoState && !persistedRepoState.prUrls.includes(result.url)) {
|
|
781
|
+
await saveRepoState(
|
|
782
|
+
repoName,
|
|
783
|
+
{ prUrls: appendUniqueUrl(persistedRepoState.prUrls, result.url) },
|
|
784
|
+
persistedRepoState
|
|
785
|
+
);
|
|
764
786
|
}
|
|
765
|
-
} catch (error) {
|
|
766
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
767
|
-
console.warn(`[GitService] gh pr view failed for ${repoName}: ${message}`);
|
|
768
|
-
return { status: "error" };
|
|
769
787
|
}
|
|
770
|
-
return
|
|
788
|
+
return result;
|
|
771
789
|
} catch (error) {
|
|
772
790
|
console.error("Error checking for pull request:", error);
|
|
773
791
|
return { status: "error" };
|
|
774
792
|
}
|
|
775
793
|
}
|
|
794
|
+
lookupPrOnRemote(repoName, repoPath, branch) {
|
|
795
|
+
try {
|
|
796
|
+
const remoteRef = execFileSync2("git", ["ls-remote", "--heads", "origin", branch], {
|
|
797
|
+
cwd: repoPath,
|
|
798
|
+
encoding: "utf-8",
|
|
799
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
800
|
+
}).trim();
|
|
801
|
+
if (!remoteRef) {
|
|
802
|
+
return { status: "not_found" };
|
|
803
|
+
}
|
|
804
|
+
} catch {
|
|
805
|
+
return { status: "error" };
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const prInfo = execFileSync2("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
|
|
809
|
+
cwd: repoPath,
|
|
810
|
+
encoding: "utf-8",
|
|
811
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
812
|
+
}).trim();
|
|
813
|
+
return prInfo ? { status: "found", url: prInfo } : { status: "not_found" };
|
|
814
|
+
} catch (error) {
|
|
815
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
816
|
+
console.warn(`[GitService] gh pr view ${branch} failed for ${repoName}: ${message}`);
|
|
817
|
+
return { status: "error" };
|
|
818
|
+
}
|
|
819
|
+
}
|
|
776
820
|
resolveDefaultBranch(repoPath) {
|
|
777
821
|
const cached = this.defaultBranchCache.get(repoPath);
|
|
778
822
|
if (cached) {
|
|
@@ -807,22 +851,27 @@ var GitService = class {
|
|
|
807
851
|
const normalized = name.toLowerCase().replace(/[^a-z0-9._/-]+/g, "-").replace(/\/{2,}/g, "/").replace(/^-+|-+$/g, "");
|
|
808
852
|
return normalized || "replicas";
|
|
809
853
|
}
|
|
810
|
-
async refreshRepoMetadata(repo, currentBranch, startHooksCompleted, persistedState) {
|
|
854
|
+
async refreshRepoMetadata(repo, currentBranch, startHooksCompleted, persistedState, observedBranches) {
|
|
811
855
|
const prResult = await this.getPullRequestUrl(repo.name, repo.path, currentBranch, persistedState);
|
|
812
|
-
let
|
|
856
|
+
let prUrls = persistedState?.prUrls ?? [];
|
|
813
857
|
if (prResult.status === "found") {
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
858
|
+
prUrls = appendUniqueUrl(prUrls, prResult.url);
|
|
859
|
+
}
|
|
860
|
+
if (observedBranches) {
|
|
861
|
+
for (const branch of observedBranches) {
|
|
862
|
+
if (branch === currentBranch) continue;
|
|
863
|
+
const branchResult = this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
864
|
+
if (branchResult.status === "found") {
|
|
865
|
+
prUrls = appendUniqueUrl(prUrls, branchResult.url);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
819
868
|
}
|
|
820
869
|
const state = {
|
|
821
870
|
name: repo.name,
|
|
822
871
|
path: repo.path,
|
|
823
872
|
defaultBranch: repo.defaultBranch,
|
|
824
873
|
currentBranch,
|
|
825
|
-
|
|
874
|
+
prUrls,
|
|
826
875
|
gitDiff: this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
827
876
|
startHooksCompleted
|
|
828
877
|
};
|
|
@@ -845,10 +894,10 @@ var gitService = new GitService();
|
|
|
845
894
|
// src/utils/logger.ts
|
|
846
895
|
import { appendFile, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
847
896
|
import { homedir as homedir3 } from "os";
|
|
848
|
-
import { join as
|
|
897
|
+
import { join as join5 } from "path";
|
|
849
898
|
import { format } from "util";
|
|
850
899
|
import { randomBytes } from "crypto";
|
|
851
|
-
var LOG_DIR =
|
|
900
|
+
var LOG_DIR = join5(homedir3(), ".replicas", "logs");
|
|
852
901
|
var EngineLogger = class {
|
|
853
902
|
_sessionId = null;
|
|
854
903
|
filePath = null;
|
|
@@ -860,7 +909,7 @@ var EngineLogger = class {
|
|
|
860
909
|
async initialize() {
|
|
861
910
|
await mkdir2(LOG_DIR, { recursive: true });
|
|
862
911
|
this._sessionId = this.createSessionId();
|
|
863
|
-
this.filePath =
|
|
912
|
+
this.filePath = join5(LOG_DIR, `${this._sessionId}.log`);
|
|
864
913
|
await writeFile2(this.filePath, `=== Replicas Engine Session ${this._sessionId} ===
|
|
865
914
|
`, "utf-8");
|
|
866
915
|
this.patchConsole();
|
|
@@ -913,7 +962,7 @@ var engineLogger = new EngineLogger();
|
|
|
913
962
|
// src/services/replicas-config-service.ts
|
|
914
963
|
import { readFile as readFile4, appendFile as appendFile2, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
915
964
|
import { existsSync as existsSync4 } from "fs";
|
|
916
|
-
import { join as
|
|
965
|
+
import { join as join8 } from "path";
|
|
917
966
|
import { homedir as homedir5 } from "os";
|
|
918
967
|
import { exec } from "child_process";
|
|
919
968
|
import { promisify as promisify2 } from "util";
|
|
@@ -1102,7 +1151,7 @@ function parseReplicasConfigString(content, filename) {
|
|
|
1102
1151
|
}
|
|
1103
1152
|
|
|
1104
1153
|
// ../shared/src/engine/environment.ts
|
|
1105
|
-
var DAYTONA_SNAPSHOT_ID = "
|
|
1154
|
+
var DAYTONA_SNAPSHOT_ID = "23-04-2026-islington-v1";
|
|
1106
1155
|
|
|
1107
1156
|
// ../shared/src/engine/types.ts
|
|
1108
1157
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -1120,14 +1169,14 @@ var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
|
1120
1169
|
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
1121
1170
|
import { existsSync as existsSync3 } from "fs";
|
|
1122
1171
|
import { homedir as homedir4 } from "os";
|
|
1123
|
-
import { join as
|
|
1172
|
+
import { join as join6 } from "path";
|
|
1124
1173
|
import { execFile } from "child_process";
|
|
1125
1174
|
import { promisify } from "util";
|
|
1126
1175
|
var execFileAsync = promisify(execFile);
|
|
1127
1176
|
var REPLICAS_DIR = SANDBOX_PATHS.REPLICAS_DIR;
|
|
1128
|
-
var DETAILS_FILE =
|
|
1129
|
-
var CLAUDE_CREDENTIALS_PATH =
|
|
1130
|
-
var CODEX_AUTH_PATH =
|
|
1177
|
+
var DETAILS_FILE = join6(REPLICAS_DIR, "environment-details.json");
|
|
1178
|
+
var CLAUDE_CREDENTIALS_PATH = join6(homedir4(), ".claude", ".credentials.json");
|
|
1179
|
+
var CODEX_AUTH_PATH = join6(homedir4(), ".codex", "auth.json");
|
|
1131
1180
|
function detectClaudeAuthMethod() {
|
|
1132
1181
|
if (existsSync3(CLAUDE_CREDENTIALS_PATH)) {
|
|
1133
1182
|
return "oauth";
|
|
@@ -1273,8 +1322,8 @@ var environmentDetailsService = new EnvironmentDetailsService();
|
|
|
1273
1322
|
// src/services/start-hook-logs-service.ts
|
|
1274
1323
|
import { createHash } from "crypto";
|
|
1275
1324
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4, readdir as readdir2 } from "fs/promises";
|
|
1276
|
-
import { join as
|
|
1277
|
-
var LOGS_DIR =
|
|
1325
|
+
import { join as join7 } from "path";
|
|
1326
|
+
var LOGS_DIR = join7(SANDBOX_PATHS.REPLICAS_DIR, "start-hook-logs");
|
|
1278
1327
|
function sanitizeFilename(name) {
|
|
1279
1328
|
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1280
1329
|
const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
|
|
@@ -1294,7 +1343,7 @@ var StartHookLogsService = class {
|
|
|
1294
1343
|
async saveRepoLog(repoName, entry) {
|
|
1295
1344
|
await this.ensureDir();
|
|
1296
1345
|
const log = { repoName, ...entry };
|
|
1297
|
-
await writeFile4(
|
|
1346
|
+
await writeFile4(join7(LOGS_DIR, repoFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
1298
1347
|
`, "utf-8");
|
|
1299
1348
|
}
|
|
1300
1349
|
async getAllLogs() {
|
|
@@ -1313,7 +1362,7 @@ var StartHookLogsService = class {
|
|
|
1313
1362
|
continue;
|
|
1314
1363
|
}
|
|
1315
1364
|
try {
|
|
1316
|
-
const raw = await readFile3(
|
|
1365
|
+
const raw = await readFile3(join7(LOGS_DIR, file), "utf-8");
|
|
1317
1366
|
const stored = JSON.parse(raw);
|
|
1318
1367
|
logs.push(withPreview(stored));
|
|
1319
1368
|
} catch {
|
|
@@ -1324,7 +1373,7 @@ var StartHookLogsService = class {
|
|
|
1324
1373
|
}
|
|
1325
1374
|
async getFullOutput(repoName) {
|
|
1326
1375
|
try {
|
|
1327
|
-
const raw = await readFile3(
|
|
1376
|
+
const raw = await readFile3(join7(LOGS_DIR, repoFilename(repoName)), "utf-8");
|
|
1328
1377
|
const stored = JSON.parse(raw);
|
|
1329
1378
|
if (stored.repoName !== repoName) {
|
|
1330
1379
|
return null;
|
|
@@ -1342,7 +1391,7 @@ var startHookLogsService = new StartHookLogsService();
|
|
|
1342
1391
|
|
|
1343
1392
|
// src/services/replicas-config-service.ts
|
|
1344
1393
|
var execAsync = promisify2(exec);
|
|
1345
|
-
var START_HOOKS_LOG =
|
|
1394
|
+
var START_HOOKS_LOG = join8(homedir5(), ".replicas", "startHooks.log");
|
|
1346
1395
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
1347
1396
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
1348
1397
|
These hooks are currently executing in the background. You can:
|
|
@@ -1353,7 +1402,7 @@ The start hooks may install dependencies, build projects, or perform other setup
|
|
|
1353
1402
|
If your task depends on setup being complete, check the log file before proceeding.`;
|
|
1354
1403
|
async function readReplicasConfigFromDir(dirPath) {
|
|
1355
1404
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
1356
|
-
const configPath =
|
|
1405
|
+
const configPath = join8(dirPath, filename);
|
|
1357
1406
|
if (!existsSync4(configPath)) {
|
|
1358
1407
|
continue;
|
|
1359
1408
|
}
|
|
@@ -1417,7 +1466,7 @@ var ReplicasConfigService = class {
|
|
|
1417
1466
|
const logLine = `[${timestamp}] ${message}
|
|
1418
1467
|
`;
|
|
1419
1468
|
try {
|
|
1420
|
-
await mkdir5(
|
|
1469
|
+
await mkdir5(join8(homedir5(), ".replicas"), { recursive: true });
|
|
1421
1470
|
await appendFile2(START_HOOKS_LOG, logLine, "utf-8");
|
|
1422
1471
|
} catch (error) {
|
|
1423
1472
|
console.error("Failed to write to start hooks log:", error);
|
|
@@ -1441,7 +1490,7 @@ var ReplicasConfigService = class {
|
|
|
1441
1490
|
this.hooksRunning = true;
|
|
1442
1491
|
this.hooksCompleted = false;
|
|
1443
1492
|
try {
|
|
1444
|
-
await mkdir5(
|
|
1493
|
+
await mkdir5(join8(homedir5(), ".replicas"), { recursive: true });
|
|
1445
1494
|
await writeFile5(
|
|
1446
1495
|
START_HOOKS_LOG,
|
|
1447
1496
|
`=== Start Hooks Execution Log ===
|
|
@@ -1528,7 +1577,7 @@ Repositories: ${hookEntries.length}
|
|
|
1528
1577
|
path: entry.workingDirectory,
|
|
1529
1578
|
defaultBranch: entry.defaultBranch,
|
|
1530
1579
|
currentBranch: entry.defaultBranch,
|
|
1531
|
-
|
|
1580
|
+
prUrls: [],
|
|
1532
1581
|
gitDiff: null,
|
|
1533
1582
|
startHooksCompleted: false
|
|
1534
1583
|
};
|
|
@@ -1593,10 +1642,10 @@ var replicasConfigService = new ReplicasConfigService();
|
|
|
1593
1642
|
// src/services/event-service.ts
|
|
1594
1643
|
import { appendFile as appendFile3, mkdir as mkdir6 } from "fs/promises";
|
|
1595
1644
|
import { homedir as homedir6 } from "os";
|
|
1596
|
-
import { join as
|
|
1645
|
+
import { join as join9 } from "path";
|
|
1597
1646
|
import { randomUUID } from "crypto";
|
|
1598
|
-
var ENGINE_DIR =
|
|
1599
|
-
var EVENTS_FILE =
|
|
1647
|
+
var ENGINE_DIR = join9(homedir6(), ".replicas", "engine");
|
|
1648
|
+
var EVENTS_FILE = join9(ENGINE_DIR, "events.jsonl");
|
|
1600
1649
|
var EventService = class {
|
|
1601
1650
|
subscribers = /* @__PURE__ */ new Map();
|
|
1602
1651
|
writeChain = Promise.resolve();
|
|
@@ -1689,14 +1738,14 @@ var previewService = new PreviewService();
|
|
|
1689
1738
|
import { existsSync as existsSync7 } from "fs";
|
|
1690
1739
|
import { mkdir as mkdir10, readFile as readFile8, rm, writeFile as writeFile8 } from "fs/promises";
|
|
1691
1740
|
import { homedir as homedir9 } from "os";
|
|
1692
|
-
import { join as
|
|
1741
|
+
import { join as join12 } from "path";
|
|
1693
1742
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1694
1743
|
|
|
1695
1744
|
// src/managers/claude-manager.ts
|
|
1696
1745
|
import {
|
|
1697
1746
|
query
|
|
1698
1747
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
1699
|
-
import { join as
|
|
1748
|
+
import { join as join10 } from "path";
|
|
1700
1749
|
import { mkdir as mkdir8, appendFile as appendFile4 } from "fs/promises";
|
|
1701
1750
|
import { homedir as homedir7 } from "os";
|
|
1702
1751
|
|
|
@@ -2412,6 +2461,43 @@ var CodingAgentManager = class {
|
|
|
2412
2461
|
}
|
|
2413
2462
|
};
|
|
2414
2463
|
|
|
2464
|
+
// src/utils/agent-env.ts
|
|
2465
|
+
function buildClaudeAgentEnv(overrides) {
|
|
2466
|
+
const env = { ...process.env };
|
|
2467
|
+
if (overrides) {
|
|
2468
|
+
Object.assign(env, overrides);
|
|
2469
|
+
}
|
|
2470
|
+
if (shouldStripAnthropicApiKey()) {
|
|
2471
|
+
env.ANTHROPIC_API_KEY = void 0;
|
|
2472
|
+
}
|
|
2473
|
+
return env;
|
|
2474
|
+
}
|
|
2475
|
+
function buildCodexAgentEnv() {
|
|
2476
|
+
const env = {};
|
|
2477
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
2478
|
+
if (typeof value === "string") {
|
|
2479
|
+
env[key] = value;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
if (shouldStripOpenAIApiKey()) {
|
|
2483
|
+
delete env.OPENAI_API_KEY;
|
|
2484
|
+
}
|
|
2485
|
+
return env;
|
|
2486
|
+
}
|
|
2487
|
+
function resolveCodexApiKey() {
|
|
2488
|
+
if (shouldStripOpenAIApiKey()) {
|
|
2489
|
+
return void 0;
|
|
2490
|
+
}
|
|
2491
|
+
return ENGINE_ENV.OPENAI_API_KEY;
|
|
2492
|
+
}
|
|
2493
|
+
function shouldStripAnthropicApiKey() {
|
|
2494
|
+
const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
|
|
2495
|
+
return method === "oauth" || method === "bedrock";
|
|
2496
|
+
}
|
|
2497
|
+
function shouldStripOpenAIApiKey() {
|
|
2498
|
+
return ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth";
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2415
2501
|
// src/managers/claude-manager.ts
|
|
2416
2502
|
var PromptStream = class {
|
|
2417
2503
|
queue = [];
|
|
@@ -2467,7 +2553,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2467
2553
|
disallowedToolsOverride;
|
|
2468
2554
|
constructor(options) {
|
|
2469
2555
|
super(options);
|
|
2470
|
-
this.historyFile = options.historyFilePath ??
|
|
2556
|
+
this.historyFile = options.historyFilePath ?? join10(homedir7(), ".replicas", "claude", "history.jsonl");
|
|
2471
2557
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
2472
2558
|
this.toolsOverride = options.tools;
|
|
2473
2559
|
this.mcpServersConfig = options.mcpServers;
|
|
@@ -2557,7 +2643,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2557
2643
|
preset: "claude_code",
|
|
2558
2644
|
append: combinedInstructions
|
|
2559
2645
|
};
|
|
2560
|
-
const queryEnv = this.envOverrides
|
|
2646
|
+
const queryEnv = buildClaudeAgentEnv(this.envOverrides);
|
|
2561
2647
|
const response = query({
|
|
2562
2648
|
prompt: promptStream,
|
|
2563
2649
|
options: {
|
|
@@ -2650,7 +2736,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2650
2736
|
};
|
|
2651
2737
|
}
|
|
2652
2738
|
async initialize() {
|
|
2653
|
-
const historyDir =
|
|
2739
|
+
const historyDir = join10(homedir7(), ".replicas", "claude");
|
|
2654
2740
|
await mkdir8(historyDir, { recursive: true });
|
|
2655
2741
|
if (this.initialSessionId) {
|
|
2656
2742
|
this.sessionId = this.initialSessionId;
|
|
@@ -2704,11 +2790,11 @@ import { Codex } from "@openai/codex-sdk";
|
|
|
2704
2790
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2705
2791
|
import { readdir as readdir3, stat as stat2, writeFile as writeFile7, mkdir as mkdir9, readFile as readFile7 } from "fs/promises";
|
|
2706
2792
|
import { existsSync as existsSync6 } from "fs";
|
|
2707
|
-
import { join as
|
|
2793
|
+
import { join as join11 } from "path";
|
|
2708
2794
|
import { homedir as homedir8 } from "os";
|
|
2709
2795
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2710
2796
|
var DEFAULT_MODEL = "gpt-5.4";
|
|
2711
|
-
var CODEX_CONFIG_PATH =
|
|
2797
|
+
var CODEX_CONFIG_PATH = join11(homedir8(), ".codex", "config.toml");
|
|
2712
2798
|
function isLinearThoughtEvent2(event) {
|
|
2713
2799
|
return event.content.type === "thought";
|
|
2714
2800
|
}
|
|
@@ -2729,10 +2815,12 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2729
2815
|
activeAbortController = null;
|
|
2730
2816
|
constructor(options) {
|
|
2731
2817
|
super(options);
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2818
|
+
const codexApiKey = resolveCodexApiKey();
|
|
2819
|
+
this.codex = new Codex({
|
|
2820
|
+
env: buildCodexAgentEnv(),
|
|
2821
|
+
...codexApiKey ? { apiKey: codexApiKey } : {}
|
|
2822
|
+
});
|
|
2823
|
+
this.tempImageDir = join11(homedir8(), ".replicas", "codex", "temp-images");
|
|
2736
2824
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
2737
2825
|
}
|
|
2738
2826
|
async initialize() {
|
|
@@ -2752,7 +2840,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2752
2840
|
*/
|
|
2753
2841
|
async updateCodexConfig(developerInstructions) {
|
|
2754
2842
|
try {
|
|
2755
|
-
const codexDir =
|
|
2843
|
+
const codexDir = join11(homedir8(), ".codex");
|
|
2756
2844
|
await mkdir9(codexDir, { recursive: true });
|
|
2757
2845
|
let config = {};
|
|
2758
2846
|
if (existsSync6(CODEX_CONFIG_PATH)) {
|
|
@@ -2788,7 +2876,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2788
2876
|
for (const image of images) {
|
|
2789
2877
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
2790
2878
|
const filename = `img_${randomUUID3()}.${ext}`;
|
|
2791
|
-
const filepath =
|
|
2879
|
+
const filepath = join11(this.tempImageDir, filename);
|
|
2792
2880
|
const buffer = Buffer.from(image.source.data, "base64");
|
|
2793
2881
|
await writeFile7(filepath, buffer);
|
|
2794
2882
|
tempPaths.push(filepath);
|
|
@@ -2927,13 +3015,13 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2927
3015
|
}
|
|
2928
3016
|
// Helper methods for finding session files
|
|
2929
3017
|
async findSessionFile(threadId) {
|
|
2930
|
-
const sessionsDir =
|
|
3018
|
+
const sessionsDir = join11(homedir8(), ".codex", "sessions");
|
|
2931
3019
|
try {
|
|
2932
3020
|
const now = /* @__PURE__ */ new Date();
|
|
2933
3021
|
const year = now.getFullYear();
|
|
2934
3022
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2935
3023
|
const day = String(now.getDate()).padStart(2, "0");
|
|
2936
|
-
const todayDir =
|
|
3024
|
+
const todayDir = join11(sessionsDir, String(year), month, day);
|
|
2937
3025
|
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
2938
3026
|
if (file) return file;
|
|
2939
3027
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
@@ -2942,7 +3030,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2942
3030
|
const searchYear = date.getFullYear();
|
|
2943
3031
|
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
2944
3032
|
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
2945
|
-
const searchDir =
|
|
3033
|
+
const searchDir = join11(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
2946
3034
|
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
2947
3035
|
if (file2) return file2;
|
|
2948
3036
|
}
|
|
@@ -2956,7 +3044,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2956
3044
|
const files = await readdir3(directory);
|
|
2957
3045
|
for (const file of files) {
|
|
2958
3046
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
2959
|
-
const fullPath =
|
|
3047
|
+
const fullPath = join11(directory, file);
|
|
2960
3048
|
const stats = await stat2(fullPath);
|
|
2961
3049
|
if (stats.isFile()) {
|
|
2962
3050
|
return fullPath;
|
|
@@ -3495,7 +3583,7 @@ function getEnvironmentSection() {
|
|
|
3495
3583
|
`Platform: ${process.platform}`,
|
|
3496
3584
|
getShellInfoLine(),
|
|
3497
3585
|
`OS Version: ${unameSR}`,
|
|
3498
|
-
`The most recent Claude model family is Claude 4.5/4.6. Model IDs \u2014 Opus 4.
|
|
3586
|
+
`The most recent Claude model family is Claude 4.5/4.6/4.7. Model IDs \u2014 Opus 4.7: 'claude-opus-4-7', Sonnet 4.6: 'claude-sonnet-4-6', Haiku 4.5: 'claude-haiku-4-5-20251001'. When building AI applications, default to the latest and most capable Claude models.`
|
|
3499
3587
|
];
|
|
3500
3588
|
return [
|
|
3501
3589
|
`# Environment`,
|
|
@@ -3639,11 +3727,11 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
3639
3727
|
};
|
|
3640
3728
|
|
|
3641
3729
|
// src/services/chat/chat-service.ts
|
|
3642
|
-
var ENGINE_DIR2 =
|
|
3643
|
-
var CHATS_FILE =
|
|
3644
|
-
var CLAUDE_HISTORY_DIR =
|
|
3645
|
-
var RELAY_HISTORY_DIR =
|
|
3646
|
-
var CODEX_AUTH_PATH2 =
|
|
3730
|
+
var ENGINE_DIR2 = join12(homedir9(), ".replicas", "engine");
|
|
3731
|
+
var CHATS_FILE = join12(ENGINE_DIR2, "chats.json");
|
|
3732
|
+
var CLAUDE_HISTORY_DIR = join12(ENGINE_DIR2, "claude-histories");
|
|
3733
|
+
var RELAY_HISTORY_DIR = join12(ENGINE_DIR2, "relay-histories");
|
|
3734
|
+
var CODEX_AUTH_PATH2 = join12(homedir9(), ".codex", "auth.json");
|
|
3647
3735
|
function isCodexAvailable() {
|
|
3648
3736
|
return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
3649
3737
|
}
|
|
@@ -3801,7 +3889,7 @@ var ChatService = class {
|
|
|
3801
3889
|
async deleteHistoryFile(persisted) {
|
|
3802
3890
|
if (persisted.provider === "claude" || persisted.provider === "relay") {
|
|
3803
3891
|
const dir = persisted.provider === "claude" ? CLAUDE_HISTORY_DIR : RELAY_HISTORY_DIR;
|
|
3804
|
-
await rm(
|
|
3892
|
+
await rm(join12(dir, `${persisted.id}.jsonl`), { force: true });
|
|
3805
3893
|
}
|
|
3806
3894
|
}
|
|
3807
3895
|
async getChatHistory(chatId) {
|
|
@@ -3844,7 +3932,7 @@ var ChatService = class {
|
|
|
3844
3932
|
if (persisted.provider === "claude") {
|
|
3845
3933
|
provider = new ClaudeManager({
|
|
3846
3934
|
workingDirectory: this.workingDirectory,
|
|
3847
|
-
historyFilePath:
|
|
3935
|
+
historyFilePath: join12(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
3848
3936
|
initialSessionId: persisted.providerSessionId,
|
|
3849
3937
|
onSaveSessionId: saveSession,
|
|
3850
3938
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -3853,7 +3941,7 @@ var ChatService = class {
|
|
|
3853
3941
|
} else if (persisted.provider === "relay") {
|
|
3854
3942
|
provider = new RelayManager({
|
|
3855
3943
|
workingDirectory: this.workingDirectory,
|
|
3856
|
-
historyFilePath:
|
|
3944
|
+
historyFilePath: join12(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
3857
3945
|
initialSessionId: persisted.providerSessionId,
|
|
3858
3946
|
onSaveSessionId: saveSession,
|
|
3859
3947
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -3874,7 +3962,8 @@ var ChatService = class {
|
|
|
3874
3962
|
persisted,
|
|
3875
3963
|
provider,
|
|
3876
3964
|
pendingMessageIds: [],
|
|
3877
|
-
hasActiveTurn: false
|
|
3965
|
+
hasActiveTurn: false,
|
|
3966
|
+
observedBranchesByRepo: /* @__PURE__ */ new Map()
|
|
3878
3967
|
};
|
|
3879
3968
|
}
|
|
3880
3969
|
touch(chat) {
|
|
@@ -3922,6 +4011,8 @@ var ChatService = class {
|
|
|
3922
4011
|
});
|
|
3923
4012
|
}
|
|
3924
4013
|
this.touch(chat);
|
|
4014
|
+
this.observeCurrentBranches(chat).catch(() => {
|
|
4015
|
+
});
|
|
3925
4016
|
this.publish({
|
|
3926
4017
|
type: "chat.turn.delta",
|
|
3927
4018
|
payload: {
|
|
@@ -3937,7 +4028,7 @@ var ChatService = class {
|
|
|
3937
4028
|
return;
|
|
3938
4029
|
}
|
|
3939
4030
|
if (!chat.hasActiveTurn) {
|
|
3940
|
-
await this.publishAgentTurnCompleteWebhook();
|
|
4031
|
+
await this.publishAgentTurnCompleteWebhook(chat);
|
|
3941
4032
|
return;
|
|
3942
4033
|
}
|
|
3943
4034
|
chat.hasActiveTurn = false;
|
|
@@ -3950,7 +4041,7 @@ var ChatService = class {
|
|
|
3950
4041
|
}
|
|
3951
4042
|
}).catch(() => {
|
|
3952
4043
|
});
|
|
3953
|
-
await this.publishAgentTurnCompleteWebhook();
|
|
4044
|
+
await this.publishAgentTurnCompleteWebhook(chat);
|
|
3954
4045
|
}
|
|
3955
4046
|
getRuntimeChat(chatId) {
|
|
3956
4047
|
return this.chats.get(chatId) ?? null;
|
|
@@ -3986,15 +4077,31 @@ var ChatService = class {
|
|
|
3986
4077
|
};
|
|
3987
4078
|
await eventService.publish(event);
|
|
3988
4079
|
}
|
|
3989
|
-
async
|
|
4080
|
+
async observeCurrentBranches(chat) {
|
|
4081
|
+
try {
|
|
4082
|
+
const snapshot = await gitService.snapshotCurrentBranches();
|
|
4083
|
+
for (const [repoName, branch] of snapshot) {
|
|
4084
|
+
let observed = chat.observedBranchesByRepo.get(repoName);
|
|
4085
|
+
if (!observed) {
|
|
4086
|
+
observed = /* @__PURE__ */ new Set();
|
|
4087
|
+
chat.observedBranchesByRepo.set(repoName, observed);
|
|
4088
|
+
}
|
|
4089
|
+
observed.add(branch);
|
|
4090
|
+
}
|
|
4091
|
+
} catch {
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
async publishAgentTurnCompleteWebhook(chat) {
|
|
3990
4095
|
try {
|
|
3991
4096
|
await githubTokenManager.refreshOnce();
|
|
3992
4097
|
} catch {
|
|
3993
4098
|
}
|
|
3994
4099
|
const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
|
|
4100
|
+
const observedBranches = chat.observedBranchesByRepo;
|
|
4101
|
+
chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
|
|
3995
4102
|
let repoStatuses;
|
|
3996
4103
|
try {
|
|
3997
|
-
repoStatuses = await gitService.refreshRepos();
|
|
4104
|
+
repoStatuses = await gitService.refreshRepos(observedBranches);
|
|
3998
4105
|
console.log(`Repository Statuses Refreshed: `, repoStatuses);
|
|
3999
4106
|
} catch (error) {
|
|
4000
4107
|
console.error("[ChatService] Failed to refresh repo statuses:", error);
|
|
@@ -4012,15 +4119,15 @@ var ChatService = class {
|
|
|
4012
4119
|
import { Hono } from "hono";
|
|
4013
4120
|
import { z as z2 } from "zod";
|
|
4014
4121
|
import { readdir as readdir6, stat as stat3, readFile as readFile12 } from "fs/promises";
|
|
4015
|
-
import { join as
|
|
4122
|
+
import { join as join16, resolve } from "path";
|
|
4016
4123
|
|
|
4017
4124
|
// src/services/plan-service.ts
|
|
4018
4125
|
import { readdir as readdir4, readFile as readFile9 } from "fs/promises";
|
|
4019
4126
|
import { homedir as homedir10 } from "os";
|
|
4020
|
-
import { basename, join as
|
|
4127
|
+
import { basename, join as join13 } from "path";
|
|
4021
4128
|
var PLAN_DIRECTORIES = [
|
|
4022
|
-
|
|
4023
|
-
|
|
4129
|
+
join13(homedir10(), ".claude", "plans"),
|
|
4130
|
+
join13(homedir10(), ".replicas", "plans")
|
|
4024
4131
|
];
|
|
4025
4132
|
function isMarkdownFile(filename) {
|
|
4026
4133
|
return filename.toLowerCase().endsWith(".md");
|
|
@@ -4054,7 +4161,7 @@ var PlanService = class {
|
|
|
4054
4161
|
return null;
|
|
4055
4162
|
}
|
|
4056
4163
|
for (const directory of PLAN_DIRECTORIES) {
|
|
4057
|
-
const filePath =
|
|
4164
|
+
const filePath = join13(directory, safeFilename);
|
|
4058
4165
|
try {
|
|
4059
4166
|
const content = await readFile9(filePath, "utf-8");
|
|
4060
4167
|
return { filename: safeFilename, content };
|
|
@@ -4071,13 +4178,13 @@ import { execFile as execFile2 } from "child_process";
|
|
|
4071
4178
|
import { promisify as promisify3 } from "util";
|
|
4072
4179
|
import { readFile as readFile11 } from "fs/promises";
|
|
4073
4180
|
import { existsSync as existsSync8 } from "fs";
|
|
4074
|
-
import { join as
|
|
4181
|
+
import { join as join15 } from "path";
|
|
4075
4182
|
|
|
4076
4183
|
// src/services/warm-hook-logs-service.ts
|
|
4077
4184
|
import { createHash as createHash2 } from "crypto";
|
|
4078
4185
|
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile9, readdir as readdir5 } from "fs/promises";
|
|
4079
|
-
import { join as
|
|
4080
|
-
var LOGS_DIR2 =
|
|
4186
|
+
import { join as join14 } from "path";
|
|
4187
|
+
var LOGS_DIR2 = join14(SANDBOX_PATHS.REPLICAS_DIR, "warm-hook-logs");
|
|
4081
4188
|
function sanitizeFilename2(name) {
|
|
4082
4189
|
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4083
4190
|
const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
|
|
@@ -4104,7 +4211,7 @@ var WarmHookLogsService = class {
|
|
|
4104
4211
|
hookName: "organization",
|
|
4105
4212
|
...entry
|
|
4106
4213
|
};
|
|
4107
|
-
await writeFile9(
|
|
4214
|
+
await writeFile9(join14(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
|
|
4108
4215
|
`, "utf-8");
|
|
4109
4216
|
}
|
|
4110
4217
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -4114,7 +4221,7 @@ var WarmHookLogsService = class {
|
|
|
4114
4221
|
hookName: repoName,
|
|
4115
4222
|
...entry
|
|
4116
4223
|
};
|
|
4117
|
-
await writeFile9(
|
|
4224
|
+
await writeFile9(join14(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
|
|
4118
4225
|
`, "utf-8");
|
|
4119
4226
|
}
|
|
4120
4227
|
async getAllLogs() {
|
|
@@ -4133,7 +4240,7 @@ var WarmHookLogsService = class {
|
|
|
4133
4240
|
continue;
|
|
4134
4241
|
}
|
|
4135
4242
|
try {
|
|
4136
|
-
const raw = await readFile10(
|
|
4243
|
+
const raw = await readFile10(join14(LOGS_DIR2, file), "utf-8");
|
|
4137
4244
|
const stored = JSON.parse(raw);
|
|
4138
4245
|
logs.push(withPreview2(stored));
|
|
4139
4246
|
} catch {
|
|
@@ -4150,7 +4257,7 @@ var WarmHookLogsService = class {
|
|
|
4150
4257
|
async getFullOutput(hookType, hookName) {
|
|
4151
4258
|
const filename = hookType === "global" ? globalFilename() : repoFilename2(hookName);
|
|
4152
4259
|
try {
|
|
4153
|
-
const raw = await readFile10(
|
|
4260
|
+
const raw = await readFile10(join14(LOGS_DIR2, filename), "utf-8");
|
|
4154
4261
|
const stored = JSON.parse(raw);
|
|
4155
4262
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
4156
4263
|
return null;
|
|
@@ -4197,7 +4304,7 @@ async function executeHookScript(params) {
|
|
|
4197
4304
|
}
|
|
4198
4305
|
async function readRepoWarmHook(repoPath) {
|
|
4199
4306
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
4200
|
-
const configPath =
|
|
4307
|
+
const configPath = join15(repoPath, filename);
|
|
4201
4308
|
if (!existsSync8(configPath)) {
|
|
4202
4309
|
continue;
|
|
4203
4310
|
}
|
|
@@ -4724,7 +4831,7 @@ function createV1Routes(deps) {
|
|
|
4724
4831
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
4725
4832
|
const sessions = await Promise.all(
|
|
4726
4833
|
logFiles.map(async (filename) => {
|
|
4727
|
-
const filePath =
|
|
4834
|
+
const filePath = join16(LOG_DIR, filename);
|
|
4728
4835
|
const fileStat = await stat3(filePath);
|
|
4729
4836
|
const sessionId = filename.replace(/\.log$/, "");
|
|
4730
4837
|
return {
|