replicas-engine 0.1.298 → 0.1.300

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 +224 -98
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -14,6 +14,21 @@ function isRecord(value) {
14
14
  return typeof value === "object" && value !== null && !Array.isArray(value);
15
15
  }
16
16
 
17
+ // ../shared/src/result.ts
18
+ function createSuccessResult(data) {
19
+ return { ok: true, data };
20
+ }
21
+ function createErrorResult(error) {
22
+ return {
23
+ ok: false,
24
+ error: {
25
+ message: error.message,
26
+ code: error.code,
27
+ details: error.details
28
+ }
29
+ };
30
+ }
31
+
17
32
  // ../shared/src/agent.ts
18
33
  var CODEX_REASONING_EFFORT_BY_THINKING_LEVEL = {
19
34
  low: "low",
@@ -286,7 +301,7 @@ var WORKSPACE_SIZES = ["small", "large"];
286
301
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
287
302
 
288
303
  // ../shared/src/e2b.ts
289
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-12-v1";
304
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-12-v3";
290
305
 
291
306
  // ../shared/src/runtime-env.ts
292
307
  function parsePosixEnvFile(content) {
@@ -736,8 +751,66 @@ var GITHUB_ABILITY = {
736
751
  referenceFile: { name: "GITHUB.md", content: REFERENCE3 }
737
752
  };
738
753
 
754
+ // ../shared/src/default-skills/replicas-agent/abilities/gitlab.ts
755
+ var SECTION4 = `### GitLab
756
+ Workspace repos hosted on GitLab use pre-configured git credentials \u2014 push, pull, and open merge requests with plain \`git\`.
757
+
758
+ **Reference:** \`references/GITLAB.md\`
759
+
760
+ Use this when:
761
+ - A repo's remote is on gitlab.com or a self-managed GitLab instance
762
+ - You need to open or update a merge request`;
763
+ var REFERENCE4 = `# GitLab Integration
764
+
765
+ This guide covers how to work with GitLab-hosted repositories from within your Replicas workspace.
766
+
767
+ ## Prerequisites
768
+
769
+ Git credentials for the workspace's GitLab hosts are pre-configured in \`~/.git-credentials\` and refreshed automatically. Plain \`git fetch\` / \`git pull\` / \`git push\` over HTTPS work with no additional setup.
770
+
771
+ - Never ask the user for a GitLab token or PAT \u2014 credentials are already wired. If a push fails with an authentication error, report it to the user instead of working around it.
772
+ - There is no \`glab\` CLI in the workspace, and \`gh\` only works for GitHub remotes. Check a repo's host with \`git remote get-url origin\` before choosing the GitHub or GitLab workflow.
773
+
774
+ ## Merge Requests
775
+
776
+ Create a merge request directly from a push using push options:
777
+
778
+ \`\`\`bash
779
+ git push -o merge_request.create -o merge_request.target=<default-branch> origin HEAD
780
+ \`\`\`
781
+
782
+ Useful options:
783
+
784
+ \`\`\`bash
785
+ -o merge_request.title="Title"
786
+ -o merge_request.description="Description"
787
+ -o merge_request.draft # open as draft
788
+ -o merge_request.remove_source_branch # delete branch on merge
789
+ \`\`\`
790
+
791
+ GitLab prints the MR URL in the push output \u2014 include it in your reply to the user.
792
+
793
+ ## GitLab API (advanced)
794
+
795
+ For operations with no git equivalent (commenting on MRs, reading pipelines), call the REST API with the workspace credential:
796
+
797
+ \`\`\`bash
798
+ TOKEN=$(grep -m1 '://oauth2:' ~/.git-credentials | sed -E 's#https://oauth2:([^@]+)@.*#\\1#')
799
+ curl -s -H "Authorization: Bearer $TOKEN" "https://gitlab.com/api/v4/projects/<url-encoded-path>/merge_requests"
800
+ \`\`\`
801
+
802
+ Use the repo's own host in the API base URL for self-managed instances.
803
+ `;
804
+ var GITLAB_ABILITY = {
805
+ label: "GitLab",
806
+ description: "Pre-configured git credentials for GitLab repos; merge requests via push options.",
807
+ bullet: "- Interacting with GitLab (pushing to GitLab repos, opening merge requests)",
808
+ section: SECTION4,
809
+ referenceFile: { name: "GITLAB.md", content: REFERENCE4 }
810
+ };
811
+
739
812
  // ../shared/src/default-skills/replicas-agent/abilities/google.ts
740
- var SECTION4 = `### Google Workspace (Docs, Sheets, Forms, Drive)
813
+ var SECTION5 = `### Google Workspace (Docs, Sheets, Forms, Drive)
741
814
  Create and edit Google Docs, Sheets, and Forms via the Replicas gateway. Files are owned by Replicas \u2014 the integration cannot access pre-existing Google content created outside of it.
742
815
 
743
816
  **Reference:** \`references/GOOGLE.md\`
@@ -746,7 +819,7 @@ Use this when:
746
819
  - You need to create or edit a Google Doc, Sheet, or Form
747
820
  - You need to share, rename, move, or delete a Replicas-created Google file
748
821
  - You need to read responses from a Replicas-created Google Form`;
749
- var REFERENCE4 = `# Google Workspace (Docs, Sheets, Forms, Drive)
822
+ var REFERENCE5 = `# Google Workspace (Docs, Sheets, Forms, Drive)
750
823
 
751
824
  This guide covers how to create and edit Google Docs, Sheets, and Forms \u2014 plus do basic Drive file operations \u2014 from inside a Replicas workspace, using the monolith as a gateway to Google's APIs.
752
825
 
@@ -1007,12 +1080,12 @@ var GOOGLE_ABILITY = {
1007
1080
  label: "Google Workspace",
1008
1081
  description: "Create / edit Docs, Sheets, Forms, and Drive files via the Replicas gateway.",
1009
1082
  bullet: "- Interacting with Google Workspace (creating and editing Docs, Sheets, and Forms, sharing files, reading form responses, etc.)",
1010
- section: SECTION4,
1011
- referenceFile: { name: "GOOGLE.md", content: REFERENCE4 }
1083
+ section: SECTION5,
1084
+ referenceFile: { name: "GOOGLE.md", content: REFERENCE5 }
1012
1085
  };
1013
1086
 
1014
1087
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
1015
- var SECTION5 = `### Linear
1088
+ var SECTION6 = `### Linear
1016
1089
  Fetch issues, update state, add comments, and search via the Linear GraphQL API.
1017
1090
 
1018
1091
  **Reference:** \`references/LINEAR.md\`
@@ -1021,7 +1094,7 @@ Use this when:
1021
1094
  - You encounter a Linear issue link and need to understand the task
1022
1095
  - You need to update an issue's state (e.g. mark as done)
1023
1096
  - You need to comment on or search for Linear issues`;
1024
- var REFERENCE5 = `# Linear Integration
1097
+ var REFERENCE6 = `# Linear Integration
1025
1098
 
1026
1099
  This guide covers how to interact with Linear from within your Replicas workspace.
1027
1100
 
@@ -1096,12 +1169,12 @@ var LINEAR_ABILITY = {
1096
1169
  label: "Linear",
1097
1170
  description: "Fetch issues, post comments, update states via the Linear GraphQL API.",
1098
1171
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
1099
- section: SECTION5,
1100
- referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
1172
+ section: SECTION6,
1173
+ referenceFile: { name: "LINEAR.md", content: REFERENCE6 }
1101
1174
  };
1102
1175
 
1103
1176
  // ../shared/src/default-skills/replicas-agent/abilities/media.ts
1104
- var SECTION6 = `### Media
1177
+ var SECTION7 = `### Media
1105
1178
  Share screenshots, screen recordings, generated diagrams, and audio clips inline in the Replicas chat and natively embedded in external messages.
1106
1179
 
1107
1180
  **Reference:** \`references/MEDIA.md\`
@@ -1110,7 +1183,7 @@ Use this when:
1110
1183
  - You produce a screenshot, recording, generated image, or audio clip the user should see
1111
1184
  - You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
1112
1185
  - You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
1113
- var REFERENCE6 = `# Media (Screenshots, Recordings, Audio)
1186
+ var REFERENCE7 = `# Media (Screenshots, Recordings, Audio)
1114
1187
 
1115
1188
  This guide covers how to share screenshots, screen recordings, generated diagrams, and audio clips \u2014 both inline in the Replicas chat and natively embedded in external surfaces (Slack, Linear, GitHub).
1116
1189
 
@@ -1361,12 +1434,12 @@ var MEDIA_ABILITY = {
1361
1434
  label: "Media",
1362
1435
  description: "Share screenshots, recordings, generated images, and audio clips.",
1363
1436
  bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms",
1364
- section: SECTION6,
1365
- referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
1437
+ section: SECTION7,
1438
+ referenceFile: { name: "MEDIA.md", content: REFERENCE7 }
1366
1439
  };
1367
1440
 
1368
1441
  // ../shared/src/default-skills/replicas-agent/abilities/previews.ts
1369
- var SECTION7 = `### Previews
1442
+ var SECTION8 = `### Previews
1370
1443
  Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
1371
1444
 
1372
1445
  **Reference:** \`references/PREVIEWS.md\`
@@ -1375,7 +1448,7 @@ Use this when:
1375
1448
  - You need to start a service that a human should view or interact with
1376
1449
  - The task involves UI work that benefits from human review
1377
1450
  - You are verifying frontend/backend integrations visually`;
1378
- var REFERENCE7 = `# Preview URLs
1451
+ var REFERENCE8 = `# Preview URLs
1379
1452
 
1380
1453
  When you run services on ports \u2014 such as a web app, API server, or database \u2014 humans may want to interact with them directly. You can expose your locally running services as public preview URLs.
1381
1454
 
@@ -1457,12 +1530,12 @@ var PREVIEWS_ABILITY = {
1457
1530
  label: "Previews",
1458
1531
  description: "Expose locally running services on public preview URLs for humans.",
1459
1532
  bullet: "- Creating preview URLs for locally running services",
1460
- section: SECTION7,
1461
- referenceFile: { name: "PREVIEWS.md", content: REFERENCE7 }
1533
+ section: SECTION8,
1534
+ referenceFile: { name: "PREVIEWS.md", content: REFERENCE8 }
1462
1535
  };
1463
1536
 
1464
1537
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
1465
- var SECTION8 = `### Replicas (in-workspace CLI)
1538
+ var SECTION9 = `### Replicas (in-workspace CLI)
1466
1539
  Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
1467
1540
 
1468
1541
  **Reference:** \`references/REPLICAS.md\`
@@ -1472,7 +1545,7 @@ Use this when:
1472
1545
  - The user asks you to manage environments, environment variables, or environment files
1473
1546
  - The user asks "what envs / repos / automations do I have?"
1474
1547
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo`;
1475
- var REFERENCE8 = `# Replicas (in-workspace CLI)
1548
+ var REFERENCE9 = `# Replicas (in-workspace CLI)
1476
1549
 
1477
1550
  This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
1478
1551
 
@@ -1666,13 +1739,13 @@ var REPLICAS_ABILITY = {
1666
1739
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
1667
1740
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
1668
1741
  bullet: "",
1669
- section: SECTION8,
1670
- referenceFile: { name: "REPLICAS.md", content: REFERENCE8 },
1742
+ section: SECTION9,
1743
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE9 },
1671
1744
  locked: true
1672
1745
  };
1673
1746
 
1674
1747
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
1675
- var SECTION9 = `### Slack
1748
+ var SECTION10 = `### Slack
1676
1749
  Send messages, read threads, search conversations, and upload files via the Slack Web API.
1677
1750
 
1678
1751
  **Reference:** \`references/SLACK.md\`
@@ -1682,7 +1755,7 @@ Use this when:
1682
1755
  - You need to read or fetch a Slack conversation
1683
1756
  - You encounter a Slack message link and need to retrieve its content
1684
1757
  - The task asks you to notify, update, or communicate via Slack`;
1685
- var REFERENCE9 = `# Slack Integration
1758
+ var REFERENCE10 = `# Slack Integration
1686
1759
 
1687
1760
  This guide covers how to interact with Slack from within your Replicas workspace.
1688
1761
 
@@ -1783,8 +1856,8 @@ var SLACK_ABILITY = {
1783
1856
  label: "Slack",
1784
1857
  description: "Send messages, read threads, search conversations, upload files.",
1785
1858
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
1786
- section: SECTION9,
1787
- referenceFile: { name: "SLACK.md", content: REFERENCE9 }
1859
+ section: SECTION10,
1860
+ referenceFile: { name: "SLACK.md", content: REFERENCE10 }
1788
1861
  };
1789
1862
 
1790
1863
  // ../shared/src/default-skills/replicas-agent/registry.ts
@@ -1793,6 +1866,7 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
1793
1866
  computer: COMPUTER_ABILITY,
1794
1867
  docker: DOCKER_ABILITY,
1795
1868
  github: GITHUB_ABILITY,
1869
+ gitlab: GITLAB_ABILITY,
1796
1870
  google: GOOGLE_ABILITY,
1797
1871
  linear: LINEAR_ABILITY,
1798
1872
  media: MEDIA_ABILITY,
@@ -2790,7 +2864,7 @@ var BaseRefreshManager = class {
2790
2864
  };
2791
2865
 
2792
2866
  // src/services/monolith-service.ts
2793
- async function monolithRequest(path4, init = {}) {
2867
+ async function monolithRequest(path5, init = {}) {
2794
2868
  if (!ENGINE_ENV.WORKSPACE_ID) {
2795
2869
  throw new Error("WORKSPACE_ID is not set; cannot call monolith");
2796
2870
  }
@@ -2800,7 +2874,7 @@ async function monolithRequest(path4, init = {}) {
2800
2874
  "X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID
2801
2875
  };
2802
2876
  if (!isFormData) headers["Content-Type"] = "application/json";
2803
- return fetch(`${ENGINE_ENV.MONOLITH_URL}${path4}`, {
2877
+ return fetch(`${ENGINE_ENV.MONOLITH_URL}${path5}`, {
2804
2878
  method: init.method ?? "POST",
2805
2879
  headers,
2806
2880
  body: init.body === void 0 ? void 0 : isFormData ? init.body : JSON.stringify(init.body),
@@ -2826,13 +2900,13 @@ var MonolithService = class {
2826
2900
  var monolithService = new MonolithService();
2827
2901
 
2828
2902
  // src/utils/file.ts
2829
- import { mkdir, rename, unlink, writeFile } from "fs/promises";
2903
+ import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
2830
2904
  import { dirname } from "path";
2831
- async function atomicWriteFile(path4, data, options) {
2832
- const tmpFile = `${path4}.${process.pid}.${Date.now()}.tmp`;
2905
+ async function atomicWriteFile(path5, data, options) {
2906
+ const tmpFile = `${path5}.${process.pid}.${Date.now()}.tmp`;
2833
2907
  try {
2834
2908
  await writeFile(tmpFile, data, { encoding: "utf-8", mode: options?.mode });
2835
- await rename(tmpFile, path4);
2909
+ await rename(tmpFile, path5);
2836
2910
  } catch (error) {
2837
2911
  await unlink(tmpFile).catch(() => void 0);
2838
2912
  throw error;
@@ -2844,6 +2918,21 @@ async function writeSecureCredentialFile(filePath, content, options) {
2844
2918
  }
2845
2919
  await atomicWriteFile(filePath, content, { mode: 384 });
2846
2920
  }
2921
+ var credentialFileQueue = Promise.resolve();
2922
+ function upsertCredentialFileLines(filePath, hosts, lines) {
2923
+ const task = credentialFileQueue.then(async () => {
2924
+ let existing = [];
2925
+ try {
2926
+ existing = (await readFile(filePath, "utf-8")).split("\n").filter(Boolean);
2927
+ } catch {
2928
+ existing = [];
2929
+ }
2930
+ const kept = existing.filter((line) => !hosts.some((host) => line.endsWith(`@${host}`)));
2931
+ await writeSecureCredentialFile(filePath, [...lines, ...kept, ""].join("\n"));
2932
+ });
2933
+ credentialFileQueue = task.catch(() => void 0);
2934
+ return task;
2935
+ }
2847
2936
 
2848
2937
  // src/managers/github-token-manager.ts
2849
2938
  var GitHubTokenManager = class extends BaseRefreshManager {
@@ -2873,10 +2962,10 @@ var GitHubTokenManager = class extends BaseRefreshManager {
2873
2962
  }
2874
2963
  async updateGitCredentials(token) {
2875
2964
  const credentialsPath = path.join(ENGINE_ENV.HOME_DIR, ".git-credentials");
2876
- const credentialsContent = `https://x-access-token:${token}@github.com
2877
- `;
2878
2965
  try {
2879
- await writeSecureCredentialFile(credentialsPath, credentialsContent);
2966
+ await upsertCredentialFileLines(credentialsPath, ["github.com"], [
2967
+ `https://x-access-token:${token}@github.com`
2968
+ ]);
2880
2969
  console.log(`[GitHubTokenManager] Updated ${credentialsPath}`);
2881
2970
  } catch (error) {
2882
2971
  console.error("[GitHubTokenManager] Failed to update git credentials:", error);
@@ -2909,9 +2998,44 @@ var GitHubTokenManager = class extends BaseRefreshManager {
2909
2998
  };
2910
2999
  var githubTokenManager = new GitHubTokenManager();
2911
3000
 
3001
+ // src/managers/gitlab-token-manager.ts
3002
+ import path2 from "path";
3003
+ var GitLabTokenManager = class extends BaseRefreshManager {
3004
+ constructor() {
3005
+ super("GitLabTokenManager");
3006
+ }
3007
+ async doRefresh(_config) {
3008
+ const result = await this.fetchRefresh();
3009
+ if (!result.ok) {
3010
+ throw new Error(`Token refresh failed: ${result.error.message}`);
3011
+ }
3012
+ const data = result.data;
3013
+ if (!data.token) {
3014
+ console.log(`[GitLabTokenManager] No GitLab token to install: ${data.reason}`);
3015
+ return;
3016
+ }
3017
+ const hosts = data.hosts.length > 0 ? data.hosts : ["gitlab.com"];
3018
+ const credentialsPath = path2.join(ENGINE_ENV.HOME_DIR, ".git-credentials");
3019
+ await upsertCredentialFileLines(
3020
+ credentialsPath,
3021
+ hosts,
3022
+ hosts.map((host) => `https://oauth2:${data.token}@${host}`)
3023
+ );
3024
+ console.log(`[GitLabTokenManager] Updated ${credentialsPath} for ${hosts.join(", ")}`);
3025
+ }
3026
+ async fetchRefresh() {
3027
+ const response = await monolithRequest("/v1/engine/gitlab/refresh-token");
3028
+ if (!response.ok) {
3029
+ return createErrorResult({ message: `${response.status} ${await response.text()}` });
3030
+ }
3031
+ return createSuccessResult(await response.json());
3032
+ }
3033
+ };
3034
+ var gitlabTokenManager = new GitLabTokenManager();
3035
+
2912
3036
  // src/managers/claude-token-manager.ts
2913
3037
  import { promises as fs } from "fs";
2914
- import path2 from "path";
3038
+ import path3 from "path";
2915
3039
 
2916
3040
  // src/managers/auth-env-transition.ts
2917
3041
  function applyAuthEnvTransition(params) {
@@ -3003,7 +3127,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
3003
3127
  });
3004
3128
  }
3005
3129
  async writeOauthCredentialsFile(credentials) {
3006
- const credentialsPath = path2.join(ENGINE_ENV.HOME_DIR, ".claude", ".credentials.json");
3130
+ const credentialsPath = path3.join(ENGINE_ENV.HOME_DIR, ".claude", ".credentials.json");
3007
3131
  const claudeCliConfig = {
3008
3132
  claudeAiOauth: {
3009
3133
  accessToken: credentials.accessToken,
@@ -3025,7 +3149,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
3025
3149
  }
3026
3150
  }
3027
3151
  async removeOauthCredentialsFile() {
3028
- const credentialsPath = path2.join(ENGINE_ENV.HOME_DIR, ".claude", ".credentials.json");
3152
+ const credentialsPath = path3.join(ENGINE_ENV.HOME_DIR, ".claude", ".credentials.json");
3029
3153
  try {
3030
3154
  await fs.unlink(credentialsPath);
3031
3155
  } catch {
@@ -3036,7 +3160,7 @@ var claudeTokenManager = new ClaudeTokenManager();
3036
3160
 
3037
3161
  // src/managers/codex-token-manager.ts
3038
3162
  import { promises as fs2 } from "fs";
3039
- import path3 from "path";
3163
+ import path4 from "path";
3040
3164
  var CodexTokenManager = class extends BaseRefreshManager {
3041
3165
  constructor() {
3042
3166
  super("CodexTokenManager");
@@ -3104,7 +3228,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
3104
3228
  });
3105
3229
  }
3106
3230
  async writeOauthCredentialsFile(credentials) {
3107
- const authPath = path3.join(ENGINE_ENV.HOME_DIR, ".codex", "auth.json");
3231
+ const authPath = path4.join(ENGINE_ENV.HOME_DIR, ".codex", "auth.json");
3108
3232
  const codexAuthConfig = {
3109
3233
  OPENAI_API_KEY: null,
3110
3234
  tokens: {
@@ -3127,7 +3251,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
3127
3251
  }
3128
3252
  }
3129
3253
  async removeOauthCredentialsFile() {
3130
- const authPath = path3.join(ENGINE_ENV.HOME_DIR, ".codex", "auth.json");
3254
+ const authPath = path4.join(ENGINE_ENV.HOME_DIR, ".codex", "auth.json");
3131
3255
  try {
3132
3256
  await fs2.unlink(authPath);
3133
3257
  } catch {
@@ -3137,13 +3261,13 @@ var CodexTokenManager = class extends BaseRefreshManager {
3137
3261
  var codexTokenManager = new CodexTokenManager();
3138
3262
 
3139
3263
  // src/git/service.ts
3140
- import { readdir, readFile as readFile2, stat } from "fs/promises";
3264
+ import { readdir, readFile as readFile3, stat } from "fs/promises";
3141
3265
  import { existsSync as existsSync2, unlinkSync } from "fs";
3142
3266
  import { spawn } from "child_process";
3143
3267
  import { join as join5 } from "path";
3144
3268
 
3145
3269
  // src/utils/state.ts
3146
- import { readFile, mkdir as mkdir2 } from "fs/promises";
3270
+ import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
3147
3271
  import { existsSync } from "fs";
3148
3272
  import { join as join3 } from "path";
3149
3273
  import { homedir as homedir3 } from "os";
@@ -3224,7 +3348,7 @@ async function loadEngineState() {
3224
3348
  if (!existsSync(STATE_FILE)) {
3225
3349
  return { ...DEFAULT_STATE };
3226
3350
  }
3227
- const content = await readFile(STATE_FILE, "utf-8");
3351
+ const content = await readFile2(STATE_FILE, "utf-8");
3228
3352
  const state = coerceEngineState(JSON.parse(content));
3229
3353
  return {
3230
3354
  ...DEFAULT_STATE,
@@ -3600,9 +3724,9 @@ var GitService = class {
3600
3724
  try {
3601
3725
  const paths = await this.listUntrackedPaths(repoPath);
3602
3726
  let total = 0;
3603
- for (const path4 of paths) {
3727
+ for (const path5 of paths) {
3604
3728
  try {
3605
- const contents = await readFile2(join5(repoPath, path4));
3729
+ const contents = await readFile3(join5(repoPath, path5));
3606
3730
  if (contents.length === 0 || contents.includes(0)) {
3607
3731
  continue;
3608
3732
  }
@@ -3781,12 +3905,12 @@ var GitService = class {
3781
3905
  await saveRepoState(repo.name, state, state);
3782
3906
  return state;
3783
3907
  }
3784
- pathExists(path4) {
3785
- return existsSync2(path4);
3908
+ pathExists(path5) {
3909
+ return existsSync2(path5);
3786
3910
  }
3787
- async safeStat(path4) {
3911
+ async safeStat(path5) {
3788
3912
  try {
3789
- return await stat(path4);
3913
+ return await stat(path5);
3790
3914
  } catch {
3791
3915
  return null;
3792
3916
  }
@@ -3809,8 +3933,8 @@ var StreamWriter = class {
3809
3933
  backpressured = false;
3810
3934
  droppedCount = 0;
3811
3935
  flushTimer = null;
3812
- open(path4, highWaterMark = DEFAULT_HIGH_WATER) {
3813
- this.stream = createWriteStream(path4, { flags: "a", highWaterMark });
3936
+ open(path5, highWaterMark = DEFAULT_HIGH_WATER) {
3937
+ this.stream = createWriteStream(path5, { flags: "a", highWaterMark });
3814
3938
  this.stream.on("error", () => {
3815
3939
  this.stream = null;
3816
3940
  });
@@ -3916,14 +4040,14 @@ var EngineLogger = class {
3916
4040
  var engineLogger = new EngineLogger();
3917
4041
 
3918
4042
  // src/services/replicas-config-service.ts
3919
- import { readFile as readFile5, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
4043
+ import { readFile as readFile6, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
3920
4044
  import { existsSync as existsSync4 } from "fs";
3921
4045
  import { join as join9 } from "path";
3922
4046
  import { homedir as homedir7 } from "os";
3923
4047
  import { spawn as spawn2 } from "child_process";
3924
4048
 
3925
4049
  // src/services/environment-details-service.ts
3926
- import { mkdir as mkdir4, readFile as readFile3 } from "fs/promises";
4050
+ import { mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
3927
4051
  import { existsSync as existsSync3 } from "fs";
3928
4052
  import { homedir as homedir5 } from "os";
3929
4053
  import { join as join7 } from "path";
@@ -4000,7 +4124,7 @@ async function readDetails() {
4000
4124
  if (!existsSync3(DETAILS_FILE)) {
4001
4125
  return createDefaultDetails();
4002
4126
  }
4003
- const raw = await readFile3(DETAILS_FILE, "utf-8");
4127
+ const raw = await readFile4(DETAILS_FILE, "utf-8");
4004
4128
  const parsed = JSON.parse(raw);
4005
4129
  return { ...createDefaultDetails(), ...parsed };
4006
4130
  } catch {
@@ -4095,7 +4219,7 @@ var EnvironmentDetailsService = class {
4095
4219
  var environmentDetailsService = new EnvironmentDetailsService();
4096
4220
 
4097
4221
  // src/services/start-hook-logs-service.ts
4098
- import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
4222
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
4099
4223
  import { homedir as homedir6 } from "os";
4100
4224
  import { join as join8 } from "path";
4101
4225
 
@@ -4172,7 +4296,7 @@ var StartHookLogsService = class {
4172
4296
  continue;
4173
4297
  }
4174
4298
  try {
4175
- const raw = await readFile4(join8(LOGS_DIR, file), "utf-8");
4299
+ const raw = await readFile5(join8(LOGS_DIR, file), "utf-8");
4176
4300
  const stored = normalizeStored(JSON.parse(raw));
4177
4301
  if (stored) {
4178
4302
  logs.push(withPreview(stored));
@@ -4191,7 +4315,7 @@ var StartHookLogsService = class {
4191
4315
  async getFullOutput(hookType, hookName) {
4192
4316
  const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
4193
4317
  try {
4194
- const raw = await readFile4(join8(LOGS_DIR, filename), "utf-8");
4318
+ const raw = await readFile5(join8(LOGS_DIR, filename), "utf-8");
4195
4319
  const stored = normalizeStored(JSON.parse(raw));
4196
4320
  if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
4197
4321
  return null;
@@ -4223,7 +4347,7 @@ async function readReplicasConfigFromDir(dirPath) {
4223
4347
  if (!existsSync4(configPath)) {
4224
4348
  continue;
4225
4349
  }
4226
- const data = await readFile5(configPath, "utf-8");
4350
+ const data = await readFile6(configPath, "utf-8");
4227
4351
  const config = parseReplicasConfigString(data, filename);
4228
4352
  return { config, filename };
4229
4353
  }
@@ -4654,7 +4778,7 @@ var EventService = class {
4654
4778
  var eventService = new EventService();
4655
4779
 
4656
4780
  // src/services/preview-service.ts
4657
- import { mkdir as mkdir8, readFile as readFile6 } from "fs/promises";
4781
+ import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
4658
4782
  import { existsSync as existsSync5 } from "fs";
4659
4783
  import { randomUUID as randomUUID2 } from "crypto";
4660
4784
  import { homedir as homedir9 } from "os";
@@ -4665,7 +4789,7 @@ async function readPreviewsFile() {
4665
4789
  if (!existsSync5(PREVIEW_PORTS_FILE)) {
4666
4790
  return { previews: [] };
4667
4791
  }
4668
- const raw = await readFile6(PREVIEW_PORTS_FILE, "utf-8");
4792
+ const raw = await readFile7(PREVIEW_PORTS_FILE, "utf-8");
4669
4793
  return JSON.parse(raw);
4670
4794
  } catch {
4671
4795
  return { previews: [] };
@@ -4770,7 +4894,7 @@ async function registerDesktopPreview() {
4770
4894
 
4771
4895
  // src/services/chat/chat-service.ts
4772
4896
  import { existsSync as existsSync7 } from "fs";
4773
- import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile10, rename as rename2, rm } from "fs/promises";
4897
+ import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile11, rename as rename2, rm } from "fs/promises";
4774
4898
  import { homedir as homedir13 } from "os";
4775
4899
  import { join as join15 } from "path";
4776
4900
  import { randomUUID as randomUUID5 } from "crypto";
@@ -4785,10 +4909,10 @@ import { mkdir as mkdir10, appendFile as appendFile2 } from "fs/promises";
4785
4909
  import { homedir as homedir11 } from "os";
4786
4910
 
4787
4911
  // src/utils/jsonl-reader.ts
4788
- import { readFile as readFile7 } from "fs/promises";
4912
+ import { readFile as readFile8 } from "fs/promises";
4789
4913
  async function readJSONL(filePath) {
4790
4914
  try {
4791
- const content = await readFile7(filePath, "utf-8");
4915
+ const content = await readFile8(filePath, "utf-8");
4792
4916
  return parseAgentEventJsonl(content);
4793
4917
  } catch (error) {
4794
4918
  return [];
@@ -5159,7 +5283,7 @@ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(hom
5159
5283
  return tempPaths;
5160
5284
  }
5161
5285
  async function removeTempImageFiles(paths) {
5162
- await Promise.allSettled(paths.map((path4) => unlink2(path4)));
5286
+ await Promise.allSettled(paths.map((path5) => unlink2(path5)));
5163
5287
  }
5164
5288
 
5165
5289
  // src/services/message-queue-service.ts
@@ -6617,7 +6741,7 @@ var AspClient = class {
6617
6741
  // src/managers/codex-asp/app-server-process.ts
6618
6742
  var DEFAULT_CODEX_BINARY = "codex";
6619
6743
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
6620
- var ENGINE_PACKAGE_VERSION = "0.1.298";
6744
+ var ENGINE_PACKAGE_VERSION = "0.1.300";
6621
6745
  var INITIALIZE_METHOD = "initialize";
6622
6746
  var INITIALIZED_NOTIFICATION = "initialized";
6623
6747
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -6941,16 +7065,16 @@ function transcriptItemsForTurn(turn) {
6941
7065
  const otherItems = turn.items.filter((item) => item.type !== "userMessage");
6942
7066
  return [...userItems, ...otherItems];
6943
7067
  }
6944
- function userImageForLocalPath(path4) {
6945
- const cached = localImageCache.get(path4);
7068
+ function userImageForLocalPath(path5) {
7069
+ const cached = localImageCache.get(path5);
6946
7070
  if (cached) return cached;
6947
- if (!existsSync6(path4)) return null;
7071
+ if (!existsSync6(path5)) return null;
6948
7072
  const image = {
6949
7073
  type: "image",
6950
- mediaType: inferMediaType(path4),
6951
- data: readFileSync3(path4).toString("base64")
7074
+ mediaType: inferMediaType(path5),
7075
+ data: readFileSync3(path5).toString("base64")
6952
7076
  };
6953
- if (image.data.length > 0) localImageCache.set(path4, image);
7077
+ if (image.data.length > 0) localImageCache.set(path5, image);
6954
7078
  return image;
6955
7079
  }
6956
7080
  function userImagesForInput(input) {
@@ -7308,9 +7432,9 @@ async function buildTurnInput(request) {
7308
7432
  }
7309
7433
  const normalizedImages = await normalizeImages(request.images);
7310
7434
  const tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
7311
- input.push(...tempImagePaths.map((path4) => ({
7435
+ input.push(...tempImagePaths.map((path5) => ({
7312
7436
  type: "localImage",
7313
- path: path4
7437
+ path: path5
7314
7438
  })));
7315
7439
  return { input, tempImagePaths };
7316
7440
  }
@@ -7420,7 +7544,7 @@ var TranscriptUpdateCoalescer = class {
7420
7544
  };
7421
7545
 
7422
7546
  // src/managers/codex-asp/codex-history-file.ts
7423
- import { appendFile as appendFile3, readFile as readFile8 } from "fs/promises";
7547
+ import { appendFile as appendFile3, readFile as readFile9 } from "fs/promises";
7424
7548
  var CodexHistoryFile = class {
7425
7549
  constructor(filePath) {
7426
7550
  this.filePath = filePath;
@@ -7438,7 +7562,7 @@ var CodexHistoryFile = class {
7438
7562
  }
7439
7563
  async load() {
7440
7564
  try {
7441
- const content = await readFile8(this.filePath, "utf-8");
7565
+ const content = await readFile9(this.filePath, "utf-8");
7442
7566
  return parseAgentEventJsonlWithCodexAspTranscript(content);
7443
7567
  } catch (error) {
7444
7568
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
@@ -8347,9 +8471,9 @@ import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
8347
8471
  import { z } from "zod";
8348
8472
  var POLL_INTERVAL_MS = 2e3;
8349
8473
  var DEFAULT_SUBAGENT_TIMEOUT_MS = 6e5;
8350
- async function engineFetch(path4, options) {
8474
+ async function engineFetch(path5, options) {
8351
8475
  const baseUrl = `http://localhost:${ENGINE_ENV.REPLICAS_ENGINE_PORT}`;
8352
- return fetch(`${baseUrl}${path4}`, {
8476
+ return fetch(`${baseUrl}${path5}`, {
8353
8477
  ...options,
8354
8478
  headers: {
8355
8479
  "Content-Type": "application/json",
@@ -8918,7 +9042,7 @@ var KeepAliveService = class _KeepAliveService {
8918
9042
  var keepAliveService = new KeepAliveService();
8919
9043
 
8920
9044
  // src/services/upload-chat-transcripts.ts
8921
- import { readdir as readdir3, readFile as readFile9 } from "fs/promises";
9045
+ import { readdir as readdir3, readFile as readFile10 } from "fs/promises";
8922
9046
  import { basename, join as join14 } from "path";
8923
9047
  import { homedir as homedir12 } from "os";
8924
9048
  var ENGINE_DIR2 = join14(homedir12(), ".replicas", "engine");
@@ -8955,7 +9079,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
8955
9079
  return { flushed, failed };
8956
9080
  }
8957
9081
  async function uploadChatTranscript(chatId, filePath, chat) {
8958
- const bytes = await readFile9(filePath);
9082
+ const bytes = await readFile10(filePath);
8959
9083
  if (bytes.byteLength === 0) return;
8960
9084
  const form = new FormData();
8961
9085
  form.append("chat_id", chatId);
@@ -9232,7 +9356,7 @@ var ChatService = class {
9232
9356
  }
9233
9357
  async readSenders(chatId) {
9234
9358
  try {
9235
- const content = await readFile10(this.senderFilePath(chatId), "utf-8");
9359
+ const content = await readFile11(this.senderFilePath(chatId), "utf-8");
9236
9360
  const lines = content.split("\n").filter((line) => line.trim().length > 0);
9237
9361
  const senders = [];
9238
9362
  for (const line of lines) {
@@ -9598,7 +9722,7 @@ var ChatService = class {
9598
9722
  }
9599
9723
  async loadChats() {
9600
9724
  try {
9601
- const content = await readFile10(CHATS_FILE, "utf-8");
9725
+ const content = await readFile11(CHATS_FILE, "utf-8");
9602
9726
  return parsePersistedChatsContent(content);
9603
9727
  } catch (error) {
9604
9728
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -9613,7 +9737,7 @@ var ChatService = class {
9613
9737
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
9614
9738
  }
9615
9739
  try {
9616
- const backupContent = await readFile10(CHATS_BACKUP_FILE, "utf-8");
9740
+ const backupContent = await readFile11(CHATS_BACKUP_FILE, "utf-8");
9617
9741
  return parsePersistedChatsContent(backupContent);
9618
9742
  } catch (backupError) {
9619
9743
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -9698,7 +9822,7 @@ var ChatService = class {
9698
9822
 
9699
9823
  // src/services/repo-file-service.ts
9700
9824
  import { execFile as execFile2 } from "child_process";
9701
- import { readFile as readFile11, realpath, stat as stat2 } from "fs/promises";
9825
+ import { readFile as readFile12, realpath, stat as stat2 } from "fs/promises";
9702
9826
  import { join as join16, resolve, extname } from "path";
9703
9827
  var CACHE_TTL_MS = 3e4;
9704
9828
  var SEARCH_TIMEOUT_MS = 15e3;
@@ -9888,7 +10012,7 @@ var RepoFileService = class {
9888
10012
  tooLarge: true
9889
10013
  };
9890
10014
  }
9891
- const content = await readFile11(fullPath, "utf-8");
10015
+ const content = await readFile12(fullPath, "utf-8");
9892
10016
  return {
9893
10017
  repoName,
9894
10018
  path: filePath,
@@ -9966,11 +10090,11 @@ var RepoFileService = class {
9966
10090
  // src/v1-routes.ts
9967
10091
  import { Hono } from "hono";
9968
10092
  import { z as z2 } from "zod";
9969
- import { readdir as readdir6, stat as stat4, readFile as readFile15 } from "fs/promises";
10093
+ import { readdir as readdir6, stat as stat4, readFile as readFile16 } from "fs/promises";
9970
10094
  import { join as join20, resolve as resolve2 } from "path";
9971
10095
 
9972
10096
  // src/services/canvas-service.ts
9973
- import { readdir as readdir4, readFile as readFile12, stat as stat3 } from "fs/promises";
10097
+ import { readdir as readdir4, readFile as readFile13, stat as stat3 } from "fs/promises";
9974
10098
  import { homedir as homedir14 } from "os";
9975
10099
  import { basename as basename2, join as join17 } from "path";
9976
10100
  var CANVAS_DIRECTORIES = [
@@ -10035,10 +10159,10 @@ var CanvasService = class {
10035
10159
  }
10036
10160
  try {
10037
10161
  if (isTextKind(kind)) {
10038
- const content = await readFile12(filePath, "utf-8");
10162
+ const content = await readFile13(filePath, "utf-8");
10039
10163
  return { filename: safe, kind, sizeBytes, mimeType, content };
10040
10164
  }
10041
- const buf = await readFile12(filePath);
10165
+ const buf = await readFile13(filePath);
10042
10166
  return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
10043
10167
  } catch {
10044
10168
  continue;
@@ -10051,12 +10175,12 @@ var canvasService = new CanvasService();
10051
10175
 
10052
10176
  // src/services/warm-hooks-service.ts
10053
10177
  import { spawn as spawn4 } from "child_process";
10054
- import { readFile as readFile14 } from "fs/promises";
10178
+ import { readFile as readFile15 } from "fs/promises";
10055
10179
  import { existsSync as existsSync8 } from "fs";
10056
10180
  import { join as join19 } from "path";
10057
10181
 
10058
10182
  // src/services/warm-hook-logs-service.ts
10059
- import { mkdir as mkdir12, readFile as readFile13, writeFile as writeFile6, readdir as readdir5, appendFile as appendFile5, unlink as unlink3 } from "fs/promises";
10183
+ import { mkdir as mkdir12, readFile as readFile14, writeFile as writeFile6, readdir as readdir5, appendFile as appendFile5, unlink as unlink3 } from "fs/promises";
10060
10184
  import { homedir as homedir15 } from "os";
10061
10185
  import { join as join18 } from "path";
10062
10186
  var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
@@ -10116,7 +10240,7 @@ var WarmHookLogsService = class {
10116
10240
  continue;
10117
10241
  }
10118
10242
  try {
10119
- const raw = await readFile13(join18(LOGS_DIR2, file), "utf-8");
10243
+ const raw = await readFile14(join18(LOGS_DIR2, file), "utf-8");
10120
10244
  const stored = JSON.parse(raw);
10121
10245
  logs.push(withPreview2(stored));
10122
10246
  } catch {
@@ -10145,7 +10269,7 @@ var WarmHookLogsService = class {
10145
10269
  }
10146
10270
  async getCurrentRunLog() {
10147
10271
  try {
10148
- return await readFile13(CURRENT_RUN_LOG, "utf-8");
10272
+ return await readFile14(CURRENT_RUN_LOG, "utf-8");
10149
10273
  } catch (err) {
10150
10274
  if (err.code === "ENOENT") return null;
10151
10275
  throw err;
@@ -10154,7 +10278,7 @@ var WarmHookLogsService = class {
10154
10278
  async getFullOutput(hookType, hookName) {
10155
10279
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
10156
10280
  try {
10157
- const raw = await readFile13(join18(LOGS_DIR2, filename), "utf-8");
10281
+ const raw = await readFile14(join18(LOGS_DIR2, filename), "utf-8");
10158
10282
  const stored = JSON.parse(raw);
10159
10283
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
10160
10284
  return null;
@@ -10178,7 +10302,7 @@ async function readRepoWarmHook(repoPath) {
10178
10302
  continue;
10179
10303
  }
10180
10304
  try {
10181
- const raw = await readFile14(configPath, "utf-8");
10305
+ const raw = await readFile15(configPath, "utf-8");
10182
10306
  const config = parseReplicasConfigString(raw, filename);
10183
10307
  if (!config.warmHook) {
10184
10308
  return null;
@@ -10739,11 +10863,11 @@ function createV1Routes(deps) {
10739
10863
  });
10740
10864
  app2.get("/repo-files/content", async (c) => {
10741
10865
  const repoName = c.req.query("repoName");
10742
- const path4 = c.req.query("path");
10743
- if (!repoName || !path4) {
10866
+ const path5 = c.req.query("path");
10867
+ if (!repoName || !path5) {
10744
10868
  return c.json(jsonError("repoName and path are required"), 400);
10745
10869
  }
10746
- const result = await deps.repoFileService.readFile(repoName, path4);
10870
+ const result = await deps.repoFileService.readFile(repoName, path5);
10747
10871
  if (!result) {
10748
10872
  return c.json(jsonError("File not found"), 404);
10749
10873
  }
@@ -11141,7 +11265,7 @@ function createV1Routes(deps) {
11141
11265
  const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
11142
11266
  let content;
11143
11267
  try {
11144
- content = await readFile15(filePath, "utf-8");
11268
+ content = await readFile16(filePath, "utf-8");
11145
11269
  } catch {
11146
11270
  return c.json(jsonError("Log session not found"), 404);
11147
11271
  }
@@ -11291,6 +11415,7 @@ app.get("/status", async (c) => {
11291
11415
  app.get("/token-refresh/health", async (c) => {
11292
11416
  return c.json({
11293
11417
  github: githubTokenManager.getHealthStatus(),
11418
+ gitlab: gitlabTokenManager.getHealthStatus(),
11294
11419
  claude: claudeTokenManager.getHealthStatus(),
11295
11420
  codex: codexTokenManager.getHealthStatus()
11296
11421
  });
@@ -11436,6 +11561,7 @@ serve(
11436
11561
  void registerDesktopPreview();
11437
11562
  heartbeatService.start(bootTimeMs);
11438
11563
  if (!IS_WARMING_MODE) {
11564
+ await gitlabTokenManager.start();
11439
11565
  await claudeTokenManager.start();
11440
11566
  await codexTokenManager.start();
11441
11567
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.298",
3
+ "version": "0.1.300",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",