replicas-engine 0.1.50 → 0.1.52

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 CHANGED
@@ -44,35 +44,36 @@ function requireValidURL(value, name) {
44
44
  throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
45
45
  }
46
46
  }
47
+ var IS_WARMING_MODE = process.argv.includes("--warming");
47
48
  function loadEngineEnv() {
48
- const HOME_DIR = readEnv("HOME") ?? homedir();
49
- return {
50
- // Required boot env.
49
+ const HOME_DIR = homedir();
50
+ const env = {
51
+ // Defined: always available
51
52
  REPLICAS_ENGINE_SECRET: requireDefined(readEnv("REPLICAS_ENGINE_SECRET"), "REPLICAS_ENGINE_SECRET"),
52
- WORKSPACE_ID: requireDefined(readEnv("WORKSPACE_ID"), "WORKSPACE_ID"),
53
- MONOLITH_URL: requireValidURL(requireDefined(readEnv("MONOLITH_URL"), "MONOLITH_URL"), "MONOLITH_URL"),
54
- // Engine config defaults.
55
53
  REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
54
+ MONOLITH_URL: requireValidURL(requireDefined(readEnv("MONOLITH_URL"), "MONOLITH_URL"), "MONOLITH_URL"),
55
+ GH_TOKEN: readEnv("GH_TOKEN"),
56
56
  HOME_DIR,
57
57
  WORKSPACE_ROOT: join(HOME_DIR, "workspaces"),
58
- // Engine-consumed optional values.
58
+ // Runtime: may not be set during warming
59
+ WORKSPACE_ID: readEnv("WORKSPACE_ID"),
59
60
  WORKSPACE_NAME: readEnv("WORKSPACE_NAME"),
60
61
  LINEAR_SESSION_ID: readEnv("LINEAR_SESSION_ID"),
61
62
  LINEAR_ACCESS_TOKEN: readEnv("LINEAR_ACCESS_TOKEN"),
62
63
  SLACK_BOT_TOKEN: readEnv("SLACK_BOT_TOKEN"),
63
- GH_TOKEN: readEnv("GH_TOKEN"),
64
- // Ambient runtime values
65
- // not directly used by the engine code, but are required in the VM
66
- // for use by the agent or SDKs (e.g claude/codex)
64
+ SLACK_CHANNEL_ID: readEnv("SLACK_CHANNEL_ID"),
65
+ SLACK_THREAD_TS: readEnv("SLACK_THREAD_TS"),
67
66
  ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
68
67
  OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
69
68
  CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
70
69
  AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
71
70
  AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
72
- AWS_REGION: readEnv("AWS_REGION"),
73
- SLACK_CHANNEL_ID: readEnv("SLACK_CHANNEL_ID"),
74
- SLACK_THREAD_TS: readEnv("SLACK_THREAD_TS")
71
+ AWS_REGION: readEnv("AWS_REGION")
75
72
  };
73
+ if (!IS_WARMING_MODE && !env.WORKSPACE_ID) {
74
+ console.error("WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
75
+ }
76
+ return env;
76
77
  }
77
78
  var ENGINE_ENV = loadEngineEnv();
78
79
 
@@ -96,7 +97,7 @@ var BaseRefreshManager = class {
96
97
  if (this.intervalHandle) {
97
98
  return;
98
99
  }
99
- const skipReason = this.getSkipReasonForRun();
100
+ const skipReason = this.getSkipReason();
100
101
  if (skipReason) {
101
102
  console.log(`[${this.managerName}] Skipping: ${skipReason}`);
102
103
  return;
@@ -127,21 +128,17 @@ var BaseRefreshManager = class {
127
128
  return null;
128
129
  }
129
130
  getRuntimeConfig() {
131
+ if (!ENGINE_ENV.WORKSPACE_ID) {
132
+ return null;
133
+ }
130
134
  return {
131
135
  monolithUrl: ENGINE_ENV.MONOLITH_URL,
132
136
  workspaceId: ENGINE_ENV.WORKSPACE_ID,
133
137
  engineSecret: ENGINE_ENV.REPLICAS_ENGINE_SECRET
134
138
  };
135
139
  }
136
- getSkipReasonForRun() {
137
- const skipReason = this.getSkipReason();
138
- if (skipReason) {
139
- return skipReason;
140
- }
141
- return null;
142
- }
143
140
  async refreshOnce() {
144
- if (this.getSkipReasonForRun()) {
141
+ if (this.getSkipReason()) {
145
142
  return;
146
143
  }
147
144
  const config = this.getRuntimeConfig();
@@ -830,10 +827,12 @@ var EngineLogger = class {
830
827
  var engineLogger = new EngineLogger();
831
828
 
832
829
  // src/services/environment-details-service.ts
833
- import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
830
+ import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
834
831
  import { existsSync as existsSync3 } from "fs";
835
832
  import { homedir as homedir4 } from "os";
836
833
  import { join as join5 } from "path";
834
+ import { execFile } from "child_process";
835
+ import { promisify } from "util";
837
836
 
838
837
  // ../shared/src/sandbox.ts
839
838
  var SANDBOX_LIFECYCLE = {
@@ -842,6 +841,14 @@ var SANDBOX_LIFECYCLE = {
842
841
  AUTO_DELETE_MINUTES: -1,
843
842
  SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
844
843
  };
844
+ var SANDBOX_PATHS = {
845
+ HOME_DIR: "/home/ubuntu",
846
+ WORKSPACES_DIR: "/home/ubuntu/workspaces",
847
+ REPLICAS_DIR: "/home/ubuntu/.replicas",
848
+ REPLICAS_FILES_DIR: "/home/ubuntu/.replicas/files",
849
+ REPLICAS_FILES_DISPLAY_DIR: "~/.replicas/files",
850
+ REPLICAS_RUNTIME_ENV_FILE: "/home/ubuntu/.replicas/runtime-env.sh"
851
+ };
845
852
 
846
853
  // ../shared/src/warm-hooks.ts
847
854
  var DEFAULT_WARM_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
@@ -911,137 +918,190 @@ var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
911
918
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
912
919
 
913
920
  // src/services/environment-details-service.ts
914
- var REPLICAS_DIR = join5(homedir4(), ".replicas");
915
- var ENVIRONMENT_DETAILS_FILE = join5(REPLICAS_DIR, "environment-details.json");
916
- function createExecutionItem(status, details) {
917
- return { status, details: details ?? null };
921
+ var execFileAsync = promisify(execFile);
922
+ var REPLICAS_DIR = SANDBOX_PATHS.REPLICAS_DIR;
923
+ var RUNTIME_ENV_FILE = SANDBOX_PATHS.REPLICAS_RUNTIME_ENV_FILE;
924
+ var FILES_DIR = SANDBOX_PATHS.REPLICAS_FILES_DIR;
925
+ var PERSISTED_STATE_FILE = join5(REPLICAS_DIR, "environment-details.json");
926
+ var CLAUDE_CREDENTIALS_PATH = join5(homedir4(), ".claude", ".credentials.json");
927
+ var CODEX_AUTH_PATH = join5(homedir4(), ".codex", "auth.json");
928
+ function detectClaudeAuthMethod() {
929
+ if (existsSync3(CLAUDE_CREDENTIALS_PATH)) {
930
+ return "oauth";
931
+ }
932
+ if (ENGINE_ENV.CLAUDE_CODE_USE_BEDROCK && ENGINE_ENV.AWS_ACCESS_KEY_ID && ENGINE_ENV.AWS_SECRET_ACCESS_KEY) {
933
+ return "bedrock";
934
+ }
935
+ if (ENGINE_ENV.ANTHROPIC_API_KEY) {
936
+ return "api_key";
937
+ }
938
+ return "none";
939
+ }
940
+ function detectCodexAuthMethod() {
941
+ if (existsSync3(CODEX_AUTH_PATH)) {
942
+ return "oauth";
943
+ }
944
+ if (ENGINE_ENV.OPENAI_API_KEY) {
945
+ return "api_key";
946
+ }
947
+ return "none";
948
+ }
949
+ async function detectGitIdentityConfigured() {
950
+ try {
951
+ const { stdout } = await execFileAsync("git", ["config", "--global", "user.name"]);
952
+ return stdout.trim().length > 0;
953
+ } catch {
954
+ return false;
955
+ }
956
+ }
957
+ async function parseRuntimeEnvKeys() {
958
+ try {
959
+ if (!existsSync3(RUNTIME_ENV_FILE)) {
960
+ return [];
961
+ }
962
+ const content = await readFile2(RUNTIME_ENV_FILE, "utf-8");
963
+ const keys = [];
964
+ for (const line of content.split("\n")) {
965
+ const trimmed = line.trim();
966
+ if (!trimmed.startsWith("export ")) {
967
+ continue;
968
+ }
969
+ const assignment = trimmed.slice("export ".length);
970
+ const eqIndex = assignment.indexOf("=");
971
+ if (eqIndex > 0) {
972
+ const key = assignment.slice(0, eqIndex).trim();
973
+ if (key) {
974
+ keys.push(key);
975
+ }
976
+ }
977
+ }
978
+ return keys;
979
+ } catch {
980
+ return [];
981
+ }
918
982
  }
919
- function createDefaultDetails() {
983
+ async function detectStaticState() {
984
+ const [repositories, gitIdentityConfigured, runtimeEnvKeys] = await Promise.all([
985
+ gitService.listRepositories(),
986
+ detectGitIdentityConfigured(),
987
+ parseRuntimeEnvKeys()
988
+ ]);
920
989
  return {
921
- engineVersion: REPLICAS_ENGINE_VERSION,
922
- globalWarmHookCompleted: createExecutionItem("n/a"),
923
- repositories: [],
924
- filesUploaded: [],
925
- envVarsSet: [],
926
- skillsInstalled: [],
927
- runtimeEnvVarsSet: [],
928
- repositoriesCloned: [],
929
- gitIdentityConfigured: false,
930
- githubCredentialsConfigured: false,
931
- codexAuthConfigured: false,
932
- claudeAuthConfigured: false,
933
- bedrockAuthConfigured: false,
934
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
990
+ repositoriesCloned: repositories.map((repo) => repo.name),
991
+ gitIdentityConfigured,
992
+ envVarsSet: runtimeEnvKeys,
993
+ runtimeEnvVarsSet: runtimeEnvKeys,
994
+ claudeAuthMethod: detectClaudeAuthMethod(),
995
+ codexAuthMethod: detectCodexAuthMethod(),
996
+ githubAccessConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
997
+ githubCredentialsConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
998
+ linearAccessConfigured: Boolean(ENGINE_ENV.LINEAR_SESSION_ID || ENGINE_ENV.LINEAR_ACCESS_TOKEN),
999
+ slackAccessConfigured: Boolean(ENGINE_ENV.SLACK_BOT_TOKEN)
935
1000
  };
936
1001
  }
937
- function mergeUnique(current, incoming) {
938
- if (!incoming || incoming.length === 0) {
939
- return current;
1002
+ async function detectUploadedFiles() {
1003
+ try {
1004
+ if (!existsSync3(FILES_DIR)) {
1005
+ return [];
1006
+ }
1007
+ const entries = await readdir2(FILES_DIR);
1008
+ return entries.filter((entry) => !entry.startsWith(".")).map((entry) => `${SANDBOX_PATHS.REPLICAS_FILES_DISPLAY_DIR}/${entry}`);
1009
+ } catch {
1010
+ return [];
940
1011
  }
941
- return Array.from(/* @__PURE__ */ new Set([...current, ...incoming]));
1012
+ }
1013
+ function createDefaultPersistedState() {
1014
+ return {
1015
+ globalWarmHookCompleted: { status: "n/a", details: null },
1016
+ repositories: [],
1017
+ skillsInstalled: []
1018
+ };
942
1019
  }
943
1020
  function upsertRepositoryStatus(current, incoming) {
944
- const byName = new Map(current.map((repository) => [repository.repositoryName, repository]));
945
- for (const repository of incoming) {
946
- const existing = byName.get(repository.repositoryName);
947
- byName.set(repository.repositoryName, {
948
- repositoryName: repository.repositoryName,
949
- warmHookCompleted: repository.warmHookCompleted !== "n/a" ? repository.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
950
- startHookCompleted: repository.startHookCompleted !== "n/a" ? repository.startHookCompleted : existing?.startHookCompleted ?? "n/a"
1021
+ const byName = new Map(current.map((r) => [r.repositoryName, r]));
1022
+ for (const repo of incoming) {
1023
+ const existing = byName.get(repo.repositoryName);
1024
+ byName.set(repo.repositoryName, {
1025
+ repositoryName: repo.repositoryName,
1026
+ warmHookCompleted: repo.warmHookCompleted !== "n/a" ? repo.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
1027
+ startHookCompleted: repo.startHookCompleted !== "n/a" ? repo.startHookCompleted : existing?.startHookCompleted ?? "n/a"
951
1028
  });
952
1029
  }
953
1030
  return [...byName.values()].sort((a, b) => a.repositoryName.localeCompare(b.repositoryName));
954
1031
  }
1032
+ async function readPersistedState() {
1033
+ try {
1034
+ if (!existsSync3(PERSISTED_STATE_FILE)) {
1035
+ return createDefaultPersistedState();
1036
+ }
1037
+ const raw = await readFile2(PERSISTED_STATE_FILE, "utf-8");
1038
+ const parsed = JSON.parse(raw);
1039
+ return { ...createDefaultPersistedState(), ...parsed };
1040
+ } catch {
1041
+ return createDefaultPersistedState();
1042
+ }
1043
+ }
1044
+ async function writePersistedState(state) {
1045
+ await mkdir3(REPLICAS_DIR, { recursive: true });
1046
+ await writeFile3(PERSISTED_STATE_FILE, `${JSON.stringify(state, null, 2)}
1047
+ `, "utf-8");
1048
+ }
955
1049
  var EnvironmentDetailsService = class {
1050
+ staticState = null;
956
1051
  async initialize() {
957
- const current = await this.readDetails();
958
- const repositories = await gitService.listRepositories();
959
- const merged = upsertRepositoryStatus(
960
- current.repositories,
961
- repositories.map((repository) => ({
962
- repositoryName: repository.name,
1052
+ this.staticState = await detectStaticState();
1053
+ const persisted = await readPersistedState();
1054
+ persisted.repositories = upsertRepositoryStatus(
1055
+ persisted.repositories,
1056
+ this.staticState.repositoriesCloned.map((name) => ({
1057
+ repositoryName: name,
963
1058
  warmHookCompleted: "n/a",
964
1059
  startHookCompleted: "n/a"
965
1060
  }))
966
1061
  );
967
- await this.writeDetails({
968
- ...current,
969
- engineVersion: REPLICAS_ENGINE_VERSION,
970
- repositories: merged,
971
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
972
- });
1062
+ await writePersistedState(persisted);
973
1063
  }
974
1064
  async getDetails() {
975
- const current = await this.readDetails();
1065
+ const staticState = this.staticState ?? await detectStaticState();
1066
+ const [persisted, filesUploaded] = await Promise.all([
1067
+ readPersistedState(),
1068
+ detectUploadedFiles()
1069
+ ]);
976
1070
  return {
977
- ...current,
978
- engineVersion: REPLICAS_ENGINE_VERSION
979
- };
980
- }
981
- async track(update) {
982
- const current = await this.readDetails();
983
- const next = {
984
- ...current,
985
1071
  engineVersion: REPLICAS_ENGINE_VERSION,
986
- globalWarmHookCompleted: update.globalWarmHookCompleted ?? current.globalWarmHookCompleted,
987
- repositories: update.repositories ? upsertRepositoryStatus(current.repositories, update.repositories) : current.repositories,
988
- filesUploaded: mergeUnique(current.filesUploaded, update.filesUploaded),
989
- envVarsSet: mergeUnique(current.envVarsSet, update.envVarsSet),
990
- skillsInstalled: mergeUnique(current.skillsInstalled, update.skillsInstalled),
991
- runtimeEnvVarsSet: mergeUnique(current.runtimeEnvVarsSet, update.runtimeEnvVarsSet),
992
- repositoriesCloned: mergeUnique(current.repositoriesCloned, update.repositoriesCloned),
993
- gitIdentityConfigured: update.gitIdentityConfigured ?? current.gitIdentityConfigured,
994
- githubCredentialsConfigured: update.githubCredentialsConfigured ?? current.githubCredentialsConfigured,
995
- codexAuthConfigured: update.codexAuthConfigured ?? current.codexAuthConfigured,
996
- claudeAuthConfigured: update.claudeAuthConfigured ?? current.claudeAuthConfigured,
997
- bedrockAuthConfigured: update.bedrockAuthConfigured ?? current.bedrockAuthConfigured,
1072
+ ...staticState,
1073
+ ...persisted,
1074
+ filesUploaded,
998
1075
  lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
999
1076
  };
1000
- await this.writeDetails(next);
1001
- return next;
1077
+ }
1078
+ async trackSkillsInstalled(skills) {
1079
+ const persisted = await readPersistedState();
1080
+ persisted.skillsInstalled = Array.from(/* @__PURE__ */ new Set([...persisted.skillsInstalled, ...skills]));
1081
+ await writePersistedState(persisted);
1002
1082
  }
1003
1083
  async setGlobalWarmHook(status, details) {
1004
- await this.track({
1005
- globalWarmHookCompleted: createExecutionItem(status, details)
1006
- });
1084
+ const persisted = await readPersistedState();
1085
+ persisted.globalWarmHookCompleted = { status, details: details ?? null };
1086
+ await writePersistedState(persisted);
1007
1087
  }
1008
1088
  async setRepositoryWarmHook(repositoryName, status) {
1009
- await this.track({
1010
- repositories: [{
1011
- repositoryName,
1012
- warmHookCompleted: status,
1013
- startHookCompleted: "n/a"
1014
- }]
1015
- });
1089
+ const persisted = await readPersistedState();
1090
+ persisted.repositories = upsertRepositoryStatus(persisted.repositories, [{
1091
+ repositoryName,
1092
+ warmHookCompleted: status,
1093
+ startHookCompleted: "n/a"
1094
+ }]);
1095
+ await writePersistedState(persisted);
1016
1096
  }
1017
1097
  async setRepositoryStartHook(repositoryName, status) {
1018
- await this.track({
1019
- repositories: [{
1020
- repositoryName,
1021
- warmHookCompleted: "n/a",
1022
- startHookCompleted: status
1023
- }]
1024
- });
1025
- }
1026
- async readDetails() {
1027
- try {
1028
- if (!existsSync3(ENVIRONMENT_DETAILS_FILE)) {
1029
- return createDefaultDetails();
1030
- }
1031
- const raw = await readFile2(ENVIRONMENT_DETAILS_FILE, "utf-8");
1032
- return {
1033
- ...createDefaultDetails(),
1034
- ...JSON.parse(raw),
1035
- engineVersion: REPLICAS_ENGINE_VERSION
1036
- };
1037
- } catch {
1038
- return createDefaultDetails();
1039
- }
1040
- }
1041
- async writeDetails(details) {
1042
- await mkdir3(REPLICAS_DIR, { recursive: true });
1043
- await writeFile3(ENVIRONMENT_DETAILS_FILE, `${JSON.stringify(details, null, 2)}
1044
- `, "utf-8");
1098
+ const persisted = await readPersistedState();
1099
+ persisted.repositories = upsertRepositoryStatus(persisted.repositories, [{
1100
+ repositoryName,
1101
+ warmHookCompleted: "n/a",
1102
+ startHookCompleted: status
1103
+ }]);
1104
+ await writePersistedState(persisted);
1045
1105
  }
1046
1106
  };
1047
1107
  var environmentDetailsService = new EnvironmentDetailsService();
@@ -1052,8 +1112,8 @@ import { existsSync as existsSync4 } from "fs";
1052
1112
  import { join as join6 } from "path";
1053
1113
  import { homedir as homedir5 } from "os";
1054
1114
  import { exec } from "child_process";
1055
- import { promisify } from "util";
1056
- var execAsync = promisify(exec);
1115
+ import { promisify as promisify2 } from "util";
1116
+ var execAsync = promisify2(exec);
1057
1117
  var START_HOOKS_LOG = join6(homedir5(), ".replicas", "startHooks.log");
1058
1118
  var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
1059
1119
  Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
@@ -1398,6 +1458,9 @@ async function readJSONL(filePath) {
1398
1458
  // src/services/monolith-service.ts
1399
1459
  var MonolithService = class {
1400
1460
  async sendEvent(event) {
1461
+ if (!ENGINE_ENV.WORKSPACE_ID) {
1462
+ return;
1463
+ }
1401
1464
  try {
1402
1465
  const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
1403
1466
  method: "POST",
@@ -2289,7 +2352,7 @@ var ClaudeManager = class extends CodingAgentManager {
2289
2352
  // src/managers/codex-manager.ts
2290
2353
  import { Codex } from "@openai/codex-sdk";
2291
2354
  import { randomUUID as randomUUID2 } from "crypto";
2292
- import { readdir as readdir2, stat as stat2, writeFile as writeFile5, mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
2355
+ import { readdir as readdir3, stat as stat2, writeFile as writeFile5, mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
2293
2356
  import { existsSync as existsSync5 } from "fs";
2294
2357
  import { join as join9 } from "path";
2295
2358
  import { homedir as homedir8 } from "os";
@@ -2531,7 +2594,7 @@ var CodexManager = class extends CodingAgentManager {
2531
2594
  }
2532
2595
  async findFileInDirectory(directory, threadId) {
2533
2596
  try {
2534
- const files = await readdir2(directory);
2597
+ const files = await readdir3(directory);
2535
2598
  for (const file of files) {
2536
2599
  if (file.endsWith(".jsonl") && file.includes(threadId)) {
2537
2600
  const fullPath = join9(directory, file);
@@ -2933,7 +2996,7 @@ import { Hono } from "hono";
2933
2996
  import { z } from "zod";
2934
2997
 
2935
2998
  // src/services/plan-service.ts
2936
- import { readdir as readdir3, readFile as readFile7 } from "fs/promises";
2999
+ import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
2937
3000
  import { homedir as homedir10 } from "os";
2938
3001
  import { basename, join as join11 } from "path";
2939
3002
  var PLAN_DIRECTORIES = [
@@ -2951,7 +3014,7 @@ var PlanService = class {
2951
3014
  const planNames = /* @__PURE__ */ new Set();
2952
3015
  for (const directory of PLAN_DIRECTORIES) {
2953
3016
  try {
2954
- const entries = await readdir3(directory, { withFileTypes: true });
3017
+ const entries = await readdir4(directory, { withFileTypes: true });
2955
3018
  for (const entry of entries) {
2956
3019
  if (!entry.isFile()) {
2957
3020
  continue;
@@ -2985,15 +3048,15 @@ var PlanService = class {
2985
3048
  var planService = new PlanService();
2986
3049
 
2987
3050
  // src/services/warm-hooks-service.ts
2988
- import { execFile } from "child_process";
2989
- import { promisify as promisify2 } from "util";
3051
+ import { execFile as execFile2 } from "child_process";
3052
+ import { promisify as promisify3 } from "util";
2990
3053
  import { readFile as readFile8 } from "fs/promises";
2991
3054
  import { join as join12 } from "path";
2992
- var execFileAsync = promisify2(execFile);
3055
+ var execFileAsync2 = promisify3(execFile2);
2993
3056
  async function installSkill(params) {
2994
3057
  const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
2995
3058
  try {
2996
- const { stdout, stderr } = await execFileAsync(
3059
+ const { stdout, stderr } = await execFileAsync2(
2997
3060
  "npx",
2998
3061
  ["skills", "add", params.source, "--all", "--global"],
2999
3062
  {
@@ -3029,7 +3092,7 @@ async function installSkill(params) {
3029
3092
  async function executeHookScript(params) {
3030
3093
  const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
3031
3094
  try {
3032
- const { stdout, stderr } = await execFileAsync("bash", ["-lc", params.content], {
3095
+ const { stdout, stderr } = await execFileAsync2("bash", ["-lc", params.content], {
3033
3096
  cwd: params.cwd,
3034
3097
  timeout,
3035
3098
  maxBuffer: 1024 * 1024,
@@ -3100,7 +3163,7 @@ async function runWarmHooks(params) {
3100
3163
  });
3101
3164
  outputBlocks.push(installResult.output);
3102
3165
  if (installResult.exitCode === 0) {
3103
- await environmentDetailsService.track({ skillsInstalled: [source] });
3166
+ await environmentDetailsService.trackSkillsInstalled([source]);
3104
3167
  }
3105
3168
  if (installResult.exitCode !== 0) {
3106
3169
  await environmentDetailsService.setGlobalWarmHook("no", installResult.output);
@@ -3418,18 +3481,6 @@ function createV1Routes(deps) {
3418
3481
  );
3419
3482
  }
3420
3483
  });
3421
- app2.post("/environment/track", async (c) => {
3422
- try {
3423
- const body = await c.req.json();
3424
- const details = await environmentDetailsService.track(body);
3425
- return c.json(details);
3426
- } catch (error) {
3427
- return c.json(
3428
- jsonError("Failed to track environment details", error instanceof Error ? error.message : "Unknown error"),
3429
- 500
3430
- );
3431
- }
3432
- });
3433
3484
  app2.post("/warm-hooks/run", async (c) => {
3434
3485
  try {
3435
3486
  const body = await c.req.json();
@@ -3642,7 +3693,11 @@ serve(
3642
3693
  port
3643
3694
  },
3644
3695
  async (info) => {
3645
- console.log(`Replicas Engine running on port ${info.port}`);
3696
+ if (IS_WARMING_MODE) {
3697
+ console.log(`Replicas Engine running on port ${info.port} (warming mode)`);
3698
+ } else {
3699
+ console.log(`Replicas Engine running on port ${info.port}`);
3700
+ }
3646
3701
  const gitResult = await gitService.initializeGitRepository();
3647
3702
  if (!gitResult.success) {
3648
3703
  console.warn(`Git initialization warning: ${gitResult.error}`);
@@ -3650,9 +3705,11 @@ serve(
3650
3705
  await replicasConfigService.initialize();
3651
3706
  await chatService.initialize();
3652
3707
  engineReady = true;
3653
- await githubTokenManager.start();
3654
- await claudeTokenManager.start();
3655
- await codexTokenManager.start();
3708
+ if (!IS_WARMING_MODE) {
3709
+ await githubTokenManager.start();
3710
+ await claudeTokenManager.start();
3711
+ await codexTokenManager.start();
3712
+ }
3656
3713
  const repos = await gitService.listRepos();
3657
3714
  await eventService.publish({
3658
3715
  id: randomUUID4(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.50",
3
+ "version": "0.1.52",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -1,28 +0,0 @@
1
- import { defineConfig } from 'tsup';
2
- export default defineConfig({
3
- entry: ['src/index.ts'],
4
- format: ['esm'],
5
- bundle: true,
6
- // Bundle @replicas/shared inline, all other dependencies are automatically external
7
- noExternal: ['@replicas/shared'],
8
- dts: false, // We don't need type definitions for the published package
9
- clean: true,
10
- // Output to dist/src to match existing structure
11
- outDir: 'dist/src',
12
- // Add shebang for the bin script
13
- banner: {
14
- js: '#!/usr/bin/env node',
15
- },
16
- // Preserve the directory structure
17
- outExtension() {
18
- return {
19
- js: '.js',
20
- };
21
- },
22
- // Resolve path mappings from tsconfig
23
- esbuildOptions(options) {
24
- options.alias = {
25
- '@replicas/shared': '../shared/src',
26
- };
27
- },
28
- });