replicas-engine 0.1.206 → 0.1.208

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.
Files changed (2) hide show
  1. package/dist/src/index.js +208 -32
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -396,7 +396,7 @@ function parseReplicasConfigString(content, filename) {
396
396
  }
397
397
 
398
398
  // ../shared/src/engine/environment.ts
399
- var DAYTONA_SNAPSHOT_ID = "23-05-2026-royal-york-v5";
399
+ var DAYTONA_SNAPSHOT_ID = "23-05-2026-royal-york-v6";
400
400
 
401
401
  // ../shared/src/engine/types.ts
402
402
  var DEFAULT_CHAT_TITLES = {
@@ -436,6 +436,46 @@ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
436
436
  // ../shared/src/engine/v1.ts
437
437
  var MERGED_MESSAGE_SEPARATOR = "\n\n<!-- replicas:merged -->\n\n";
438
438
 
439
+ // ../shared/src/routes/codex.ts
440
+ var CODEX_AUTH_ENV_KEYS = [
441
+ "OPENAI_API_KEY",
442
+ "REPLICAS_CODEX_AUTH_METHOD"
443
+ ];
444
+ function codexAuthEnvFromResponse(response) {
445
+ switch (response.type) {
446
+ case "oauth":
447
+ return { REPLICAS_CODEX_AUTH_METHOD: "oauth" };
448
+ case "api_key":
449
+ return { OPENAI_API_KEY: response.apiKey, REPLICAS_CODEX_AUTH_METHOD: "api_key" };
450
+ }
451
+ }
452
+
453
+ // ../shared/src/routes/claude.ts
454
+ var CLAUDE_AUTH_ENV_KEYS = [
455
+ "ANTHROPIC_API_KEY",
456
+ "CLAUDE_CODE_USE_BEDROCK",
457
+ "AWS_ACCESS_KEY_ID",
458
+ "AWS_SECRET_ACCESS_KEY",
459
+ "AWS_REGION",
460
+ "REPLICAS_CLAUDE_AUTH_METHOD"
461
+ ];
462
+ function claudeAuthEnvFromResponse(response) {
463
+ switch (response.type) {
464
+ case "oauth":
465
+ return { REPLICAS_CLAUDE_AUTH_METHOD: "oauth" };
466
+ case "api_key":
467
+ return { ANTHROPIC_API_KEY: response.apiKey, REPLICAS_CLAUDE_AUTH_METHOD: "api_key" };
468
+ case "bedrock":
469
+ return {
470
+ CLAUDE_CODE_USE_BEDROCK: "1",
471
+ AWS_ACCESS_KEY_ID: response.awsAccessKeyId,
472
+ AWS_SECRET_ACCESS_KEY: response.awsSecretAccessKey,
473
+ AWS_REGION: response.awsRegion,
474
+ REPLICAS_CLAUDE_AUTH_METHOD: "bedrock"
475
+ };
476
+ }
477
+ }
478
+
439
479
  // ../shared/src/routes/workspaces.ts
440
480
  var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
441
481
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
@@ -707,12 +747,16 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
707
747
  super("ClaudeTokenManager");
708
748
  }
709
749
  getSkipReason() {
710
- if (ENGINE_ENV.ANTHROPIC_API_KEY) {
711
- return "ANTHROPIC_API_KEY is set";
750
+ const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
751
+ if (method === "api_key" || method === "bedrock") {
752
+ return `auth method is ${method}`;
712
753
  }
713
754
  return null;
714
755
  }
715
756
  async doRefresh(config) {
757
+ await this.refreshWithRequest(config);
758
+ }
759
+ async refreshWithRequest(config, request) {
716
760
  console.log("[ClaudeTokenManager] Refreshing Claude credentials...");
717
761
  const response = await fetch(`${config.monolithUrl}/v1/engine/claude/refresh-credentials`, {
718
762
  method: "POST",
@@ -720,39 +764,81 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
720
764
  Authorization: `Bearer ${config.engineSecret}`,
721
765
  "X-Workspace-Id": config.workspaceId,
722
766
  "Content-Type": "application/json"
723
- }
767
+ },
768
+ ...request ? { body: JSON.stringify(request) } : {}
724
769
  });
725
770
  if (!response.ok) {
726
771
  const errorText = await response.text();
727
772
  throw new Error(`Credentials refresh failed: ${response.status} ${errorText}`);
728
773
  }
729
774
  const data = await response.json();
730
- await this.updateClaudeCredentials(data);
731
- console.log(`[ClaudeTokenManager] Credentials refreshed successfully, expires at ${data.expiresAt}`);
775
+ await this.applyCredentialsResponse(data);
776
+ console.log(`[ClaudeTokenManager] Credentials refreshed (method=${data.type})`);
732
777
  }
733
- /**
734
- * Re-fetches credentials from the monolith after an auth failure is detected.
735
- * With the 1-hour refresh threshold, the monolith will have already
736
- * proactively refreshed the token, so this just pulls the latest.
737
- *
738
- * Returns true if credentials were successfully updated, false otherwise.
739
- */
740
- async fetchFreshCredentials() {
741
- if (this.getSkipReason()) {
742
- return false;
743
- }
778
+ async fetchFreshCredentials(failureReason) {
744
779
  const config = this.getRuntimeConfig();
745
780
  if (!config) return false;
746
781
  try {
747
782
  console.log("[ClaudeTokenManager] Fetching fresh credentials from monolith after auth failure...");
748
- await this.doRefresh(config);
783
+ const failedMethod = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
784
+ await this.refreshWithRequest(config, failedMethod && failedMethod !== "none" ? {
785
+ failedMethod,
786
+ failureReason
787
+ } : void 0);
788
+ if (ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "oauth") {
789
+ this.start().catch((error) => {
790
+ console.error("[ClaudeTokenManager] Failed to restart OAuth refresh service after fallback:", error);
791
+ });
792
+ }
749
793
  return true;
750
794
  } catch (error) {
751
795
  console.error("[ClaudeTokenManager] Failed to fetch fresh credentials:", error);
752
796
  return false;
753
797
  }
754
798
  }
755
- async updateClaudeCredentials(credentials) {
799
+ async applyCredentialsResponse(response) {
800
+ if (response.type === "oauth") {
801
+ await this.writeOauthCredentialsFile({
802
+ accessToken: response.accessToken,
803
+ refreshToken: response.refreshToken,
804
+ expiresAt: response.expiresAt,
805
+ scopes: response.scopes,
806
+ subscriptionType: response.subscriptionType
807
+ });
808
+ } else {
809
+ await this.removeOauthCredentialsFile();
810
+ }
811
+ const envVars = claudeAuthEnvFromResponse(response);
812
+ for (const key of CLAUDE_AUTH_ENV_KEYS) {
813
+ switch (key) {
814
+ case "REPLICAS_CLAUDE_AUTH_METHOD":
815
+ ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD = envVars.REPLICAS_CLAUDE_AUTH_METHOD;
816
+ break;
817
+ case "ANTHROPIC_API_KEY":
818
+ ENGINE_ENV.ANTHROPIC_API_KEY = envVars.ANTHROPIC_API_KEY;
819
+ break;
820
+ case "CLAUDE_CODE_USE_BEDROCK":
821
+ ENGINE_ENV.CLAUDE_CODE_USE_BEDROCK = envVars.CLAUDE_CODE_USE_BEDROCK;
822
+ break;
823
+ case "AWS_ACCESS_KEY_ID":
824
+ ENGINE_ENV.AWS_ACCESS_KEY_ID = envVars.AWS_ACCESS_KEY_ID;
825
+ break;
826
+ case "AWS_SECRET_ACCESS_KEY":
827
+ ENGINE_ENV.AWS_SECRET_ACCESS_KEY = envVars.AWS_SECRET_ACCESS_KEY;
828
+ break;
829
+ case "AWS_REGION":
830
+ ENGINE_ENV.AWS_REGION = envVars.AWS_REGION;
831
+ break;
832
+ }
833
+ const value = envVars[key];
834
+ if (value !== void 0) {
835
+ process.env[key] = value;
836
+ } else {
837
+ delete process.env[key];
838
+ }
839
+ }
840
+ }
841
+ async writeOauthCredentialsFile(credentials) {
756
842
  const workspaceHome = ENGINE_ENV.HOME_DIR;
757
843
  const claudeDir = path2.join(workspaceHome, ".claude");
758
844
  const credentialsPath = path2.join(claudeDir, ".credentials.json");
@@ -773,6 +859,13 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
773
859
  console.error("[ClaudeTokenManager] Failed to update credentials file:", error);
774
860
  }
775
861
  }
862
+ async removeOauthCredentialsFile() {
863
+ const credentialsPath = path2.join(ENGINE_ENV.HOME_DIR, ".claude", ".credentials.json");
864
+ try {
865
+ await fs2.unlink(credentialsPath);
866
+ } catch {
867
+ }
868
+ }
776
869
  };
777
870
  var claudeTokenManager = new ClaudeTokenManager();
778
871
 
@@ -784,12 +877,18 @@ var CodexTokenManager = class extends BaseRefreshManager {
784
877
  super("CodexTokenManager");
785
878
  }
786
879
  getSkipReason() {
787
- if (ENGINE_ENV.OPENAI_API_KEY) {
880
+ if (ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "api_key") {
881
+ return "auth method is api_key";
882
+ }
883
+ if (!ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD && ENGINE_ENV.OPENAI_API_KEY) {
788
884
  return "OPENAI_API_KEY is set";
789
885
  }
790
886
  return null;
791
887
  }
792
888
  async doRefresh(config) {
889
+ await this.refreshWithRequest(config);
890
+ }
891
+ async refreshWithRequest(config, request) {
793
892
  console.log("[CodexTokenManager] Refreshing Codex credentials...");
794
893
  const response = await fetch(`${config.monolithUrl}/v1/engine/codex/refresh-credentials`, {
795
894
  method: "POST",
@@ -797,17 +896,63 @@ var CodexTokenManager = class extends BaseRefreshManager {
797
896
  Authorization: `Bearer ${config.engineSecret}`,
798
897
  "X-Workspace-Id": config.workspaceId,
799
898
  "Content-Type": "application/json"
800
- }
899
+ },
900
+ ...request ? { body: JSON.stringify(request) } : {}
801
901
  });
802
902
  if (!response.ok) {
803
903
  const errorText = await response.text();
804
904
  throw new Error(`Credentials refresh failed: ${response.status} ${errorText}`);
805
905
  }
806
906
  const data = await response.json();
807
- await this.updateCodexCredentials(data);
808
- console.log(`[CodexTokenManager] Credentials refreshed successfully, expires at ${data.expiresAt}`);
907
+ await this.applyCredentialsResponse(data);
908
+ console.log(`[CodexTokenManager] Credentials refreshed (method=${data.type})`);
809
909
  }
810
- async updateCodexCredentials(credentials) {
910
+ async fetchFreshCredentials(failureReason) {
911
+ const config = this.getRuntimeConfig();
912
+ if (!config) return false;
913
+ try {
914
+ console.log("[CodexTokenManager] Fetching fresh credentials from monolith after auth failure...");
915
+ const failedMethod = ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD;
916
+ await this.refreshWithRequest(config, failedMethod === "oauth" || failedMethod === "api_key" ? {
917
+ failedMethod,
918
+ failureReason
919
+ } : void 0);
920
+ if (ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth") {
921
+ this.start().catch((error) => {
922
+ console.error("[CodexTokenManager] Failed to restart OAuth refresh service after fallback:", error);
923
+ });
924
+ }
925
+ return true;
926
+ } catch (error) {
927
+ console.error("[CodexTokenManager] Failed to fetch fresh credentials:", error);
928
+ return false;
929
+ }
930
+ }
931
+ async applyCredentialsResponse(response) {
932
+ if (response.type === "oauth") {
933
+ await this.writeOauthCredentialsFile(response);
934
+ } else {
935
+ await this.removeOauthCredentialsFile();
936
+ }
937
+ const envVars = codexAuthEnvFromResponse(response);
938
+ for (const key of CODEX_AUTH_ENV_KEYS) {
939
+ switch (key) {
940
+ case "REPLICAS_CODEX_AUTH_METHOD":
941
+ ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD = envVars.REPLICAS_CODEX_AUTH_METHOD;
942
+ break;
943
+ case "OPENAI_API_KEY":
944
+ ENGINE_ENV.OPENAI_API_KEY = envVars.OPENAI_API_KEY;
945
+ break;
946
+ }
947
+ const value = envVars[key];
948
+ if (value !== void 0) {
949
+ process.env[key] = value;
950
+ } else {
951
+ delete process.env[key];
952
+ }
953
+ }
954
+ }
955
+ async writeOauthCredentialsFile(credentials) {
811
956
  const workspaceHome = ENGINE_ENV.HOME_DIR;
812
957
  const codexDir = path3.join(workspaceHome, ".codex");
813
958
  const authPath = path3.join(codexDir, "auth.json");
@@ -829,6 +974,13 @@ var CodexTokenManager = class extends BaseRefreshManager {
829
974
  console.error("[CodexTokenManager] Failed to update credentials file:", error);
830
975
  }
831
976
  }
977
+ async removeOauthCredentialsFile() {
978
+ const authPath = path3.join(ENGINE_ENV.HOME_DIR, ".codex", "auth.json");
979
+ try {
980
+ await fs3.unlink(authPath);
981
+ } catch {
982
+ }
983
+ }
832
984
  };
833
985
  var codexTokenManager = new CodexTokenManager();
834
986
 
@@ -3244,7 +3396,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
3244
3396
  } catch (error) {
3245
3397
  if (_ClaudeManager.isAuthError(error)) {
3246
3398
  console.warn("[ClaudeManager] Auth failure detected, refreshing credentials and retrying...", error);
3247
- const refreshed = await claudeTokenManager.fetchFreshCredentials();
3399
+ const refreshed = await claudeTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
3248
3400
  if (refreshed) {
3249
3401
  await this.executeQuery(request);
3250
3402
  return;
@@ -3555,7 +3707,7 @@ function extractRateLimitsSnapshot(parsed) {
3555
3707
  }
3556
3708
  return { state, balance, rateLimitResetType, planType };
3557
3709
  }
3558
- var CodexManager = class extends CodingAgentManager {
3710
+ var CodexManager = class _CodexManager extends CodingAgentManager {
3559
3711
  codex;
3560
3712
  currentThreadId = null;
3561
3713
  currentThread = null;
@@ -3571,13 +3723,20 @@ var CodexManager = class extends CodingAgentManager {
3571
3723
  quotaBlocked = false;
3572
3724
  constructor(options) {
3573
3725
  super(options);
3726
+ this.codex = this.createCodexClient();
3727
+ this.tempImageDir = join13(homedir11(), ".replicas", "codex", "temp-images");
3728
+ this.initializeManager(this.processMessageInternal.bind(this));
3729
+ }
3730
+ createCodexClient() {
3574
3731
  const codexApiKey = resolveCodexApiKey();
3575
- this.codex = new Codex({
3732
+ return new Codex({
3576
3733
  env: buildCodexAgentEnv(),
3577
3734
  ...codexApiKey ? { apiKey: codexApiKey } : {}
3578
3735
  });
3579
- this.tempImageDir = join13(homedir11(), ".replicas", "codex", "temp-images");
3580
- this.initializeManager(this.processMessageInternal.bind(this));
3736
+ }
3737
+ resetCodexClient() {
3738
+ this.codex = this.createCodexClient();
3739
+ this.currentThread = null;
3581
3740
  }
3582
3741
  async initialize() {
3583
3742
  if (this.initialSessionId) {
@@ -3706,10 +3865,22 @@ var CodexManager = class extends CodingAgentManager {
3706
3865
  }
3707
3866
  return tempPaths;
3708
3867
  }
3709
- /**
3710
- * Internal method that actually processes the message
3711
- */
3712
3868
  async processMessageInternal(request) {
3869
+ try {
3870
+ await this.executeCodexTurn(request);
3871
+ } catch (error) {
3872
+ if (_CodexManager.isAuthError(error)) {
3873
+ const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
3874
+ if (refreshed) {
3875
+ this.resetCodexClient();
3876
+ await this.executeCodexTurn(request);
3877
+ return;
3878
+ }
3879
+ }
3880
+ throw error;
3881
+ }
3882
+ }
3883
+ async executeCodexTurn(request) {
3713
3884
  if (this.quotaBlocked && this.latestQuotaSnapshot) {
3714
3885
  await this.flushQuotaSnapshotFromCurrentSession();
3715
3886
  if (this.quotaBlocked && this.latestQuotaSnapshot) {
@@ -3839,6 +4010,11 @@ var CodexManager = class extends CodingAgentManager {
3839
4010
  this.activeAbortController = null;
3840
4011
  }
3841
4012
  }
4013
+ static isAuthError(error) {
4014
+ const msg = error instanceof Error ? error.message : String(error);
4015
+ const lower = msg.toLowerCase();
4016
+ return lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid api key") || lower.includes("incorrect api key") || lower.includes("api key") && lower.includes("invalid") || lower.includes("login") || lower.includes("401");
4017
+ }
3842
4018
  async getHistory() {
3843
4019
  if (!this.currentThreadId) {
3844
4020
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.206",
3
+ "version": "0.1.208",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",