replicas-engine 0.1.508 → 0.1.510
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 +142 -11
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -611,7 +611,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
611
611
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
612
612
|
|
|
613
613
|
// ../shared/src/e2b.ts
|
|
614
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-
|
|
614
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-27-v1";
|
|
615
615
|
|
|
616
616
|
// ../shared/src/runtime-env.ts
|
|
617
617
|
function shellQuotePosix(value) {
|
|
@@ -727,6 +727,53 @@ function isGitHubUrl(url) {
|
|
|
727
727
|
}
|
|
728
728
|
|
|
729
729
|
// ../shared/src/urls.ts
|
|
730
|
+
var PR_URL_REGEX = /github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;
|
|
731
|
+
function parsePrUrl(url) {
|
|
732
|
+
const match = url.match(PR_URL_REGEX);
|
|
733
|
+
if (!match) return null;
|
|
734
|
+
const [, owner, repo, numberStr] = match;
|
|
735
|
+
const number = Number.parseInt(numberStr, 10);
|
|
736
|
+
if (!Number.isFinite(number)) return null;
|
|
737
|
+
return { owner, repo, number };
|
|
738
|
+
}
|
|
739
|
+
function parseCodeHostPrUrl(url) {
|
|
740
|
+
const github = parsePrUrl(url);
|
|
741
|
+
if (github) {
|
|
742
|
+
return {
|
|
743
|
+
...github,
|
|
744
|
+
provider: "github",
|
|
745
|
+
host: "github.com",
|
|
746
|
+
repositoryPath: `${github.owner}/${github.repo}`,
|
|
747
|
+
repoUrl: `https://github.com/${github.owner}/${github.repo}`
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
let parsed;
|
|
751
|
+
try {
|
|
752
|
+
parsed = new URL(url);
|
|
753
|
+
} catch {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
757
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
758
|
+
const separatorIndex = segments.indexOf("-");
|
|
759
|
+
if (separatorIndex <= 0 || segments[separatorIndex + 1] !== "merge_requests") return null;
|
|
760
|
+
const number = Number.parseInt(segments[separatorIndex + 2] ?? "", 10);
|
|
761
|
+
if (!Number.isFinite(number)) return null;
|
|
762
|
+
const repositorySegments = decodePathSegments(segments.slice(0, separatorIndex));
|
|
763
|
+
const repo = repositorySegments[repositorySegments.length - 1];
|
|
764
|
+
const owner = repositorySegments[0];
|
|
765
|
+
if (!owner || !repo) return null;
|
|
766
|
+
const repositoryPath = repositorySegments.join("/");
|
|
767
|
+
return {
|
|
768
|
+
provider: "gitlab",
|
|
769
|
+
host: parsed.host.toLowerCase(),
|
|
770
|
+
owner,
|
|
771
|
+
repo,
|
|
772
|
+
number,
|
|
773
|
+
repositoryPath,
|
|
774
|
+
repoUrl: `${parsed.origin}/${repositoryPath}`
|
|
775
|
+
};
|
|
776
|
+
}
|
|
730
777
|
function decodePathSegments(segments) {
|
|
731
778
|
return segments.map((segment) => {
|
|
732
779
|
try {
|
|
@@ -1246,37 +1293,86 @@ This guide covers how to work with GitLab-hosted repositories from within your R
|
|
|
1246
1293
|
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.
|
|
1247
1294
|
|
|
1248
1295
|
- 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.
|
|
1296
|
+
- A workspace can hold credentials for several GitLab hosts, and \`origin\` is whatever the repo says it is. Always select the token by the host you are about to call, as the snippets below do. Grabbing the first entry in \`~/.git-credentials\` can send one host's token to another.
|
|
1249
1297
|
- 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.
|
|
1250
1298
|
|
|
1299
|
+
### Reading the credential for a repo
|
|
1300
|
+
|
|
1301
|
+
A workspace can hold tokens for several GitLab hosts, so always ask git for the
|
|
1302
|
+
credential belonging to the host you are about to call. Never grep
|
|
1303
|
+
\`~/.git-credentials\` yourself: taking the first line sends one instance's token to
|
|
1304
|
+
another, and interpolating a host into a grep pattern makes it a regular
|
|
1305
|
+
expression. \`git credential fill\` matches the host exactly and returns nothing
|
|
1306
|
+
when there is no credential for it:
|
|
1307
|
+
|
|
1308
|
+
\`\`\`bash
|
|
1309
|
+
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1310
|
+
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1311
|
+
[ -n "$TOKEN" ] || { echo "no credential for $HOST"; exit 1; }
|
|
1312
|
+
\`\`\`
|
|
1313
|
+
|
|
1314
|
+
Every recipe below repeats these two lines so each block runs on its own, and
|
|
1315
|
+
every one calls \`https://$HOST\`, so the token only ever reaches the host it
|
|
1316
|
+
belongs to. Run them inside the repo whose API you are calling. An empty
|
|
1317
|
+
\`$TOKEN\` means the workspace has no credential for that host. Report that rather
|
|
1318
|
+
than reaching for another one.
|
|
1319
|
+
|
|
1251
1320
|
## Merge Requests
|
|
1252
1321
|
|
|
1253
1322
|
Create a merge request directly from a push using push options:
|
|
1254
1323
|
|
|
1255
1324
|
\`\`\`bash
|
|
1256
|
-
git push -o merge_request.create -o merge_request.target=<default-branch> origin HEAD
|
|
1325
|
+
git push -o merge_request.create -o merge_request.target=<default-branch> -o merge_request.title="Title" origin HEAD
|
|
1257
1326
|
\`\`\`
|
|
1258
1327
|
|
|
1259
1328
|
Useful options:
|
|
1260
1329
|
|
|
1261
1330
|
\`\`\`bash
|
|
1262
|
-
-o merge_request.title="Title"
|
|
1263
|
-
-o merge_request.description="Description"
|
|
1264
1331
|
-o merge_request.draft # open as draft
|
|
1265
1332
|
-o merge_request.remove_source_branch # delete branch on merge
|
|
1266
1333
|
\`\`\`
|
|
1267
1334
|
|
|
1268
1335
|
GitLab prints the MR URL in the push output \u2014 include it in your reply to the user.
|
|
1269
1336
|
|
|
1337
|
+
### Setting the description
|
|
1338
|
+
|
|
1339
|
+
Do **not** pass the description as a push option. Push option values are shell
|
|
1340
|
+
arguments on one line, so a real description \u2014 headings, lists, the Replicas
|
|
1341
|
+
footer HTML \u2014 gets mangled or truncated. Write the body to a file and PUT it as
|
|
1342
|
+
JSON instead, which preserves it exactly:
|
|
1343
|
+
|
|
1344
|
+
\`\`\`bash
|
|
1345
|
+
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1346
|
+
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1347
|
+
PROJECT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("group/project", safe=""))')
|
|
1348
|
+
cat > /tmp/mr-body.md <<'EOF'
|
|
1349
|
+
## Summary
|
|
1350
|
+
...your full description, ending with the Replicas footer...
|
|
1351
|
+
EOF
|
|
1352
|
+
jq -Rs '{description: .}' /tmp/mr-body.md > /tmp/mr-body.json
|
|
1353
|
+
curl -sS --request PUT \\
|
|
1354
|
+
--header "Authorization: Bearer $TOKEN" \\
|
|
1355
|
+
--header "Content-Type: application/json" \\
|
|
1356
|
+
--data @/tmp/mr-body.json \\
|
|
1357
|
+
"https://$HOST/api/v4/projects/$PROJECT/merge_requests/<iid>"
|
|
1358
|
+
\`\`\`
|
|
1359
|
+
|
|
1360
|
+
Use the same command to edit a description later. Editing replaces the whole
|
|
1361
|
+
body, so re-include the Replicas footer every time \u2014 it is how merge requests
|
|
1362
|
+
opened through Replicas are counted.
|
|
1363
|
+
|
|
1270
1364
|
## GitLab API (advanced)
|
|
1271
1365
|
|
|
1272
1366
|
For operations with no git equivalent (commenting on MRs, reading pipelines), call the REST API with the workspace credential:
|
|
1273
1367
|
|
|
1274
1368
|
\`\`\`bash
|
|
1275
|
-
|
|
1276
|
-
|
|
1369
|
+
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1370
|
+
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1371
|
+
curl -s -H "Authorization: Bearer $TOKEN" "https://$HOST/api/v4/projects/<url-encoded-path>/merge_requests"
|
|
1277
1372
|
\`\`\`
|
|
1278
1373
|
|
|
1279
|
-
|
|
1374
|
+
This works for gitlab.com and self-managed instances alike, because the base URL
|
|
1375
|
+
and the token both come from the repo's own host.
|
|
1280
1376
|
|
|
1281
1377
|
## Inline media in merge requests
|
|
1282
1378
|
|
|
@@ -1284,16 +1380,17 @@ GitLab provides a supported project Markdown uploads API. Upload media to Replic
|
|
|
1284
1380
|
|
|
1285
1381
|
\`\`\`bash
|
|
1286
1382
|
FILE=/abs/path/to/screenshot.png
|
|
1383
|
+
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1287
1384
|
PROJECT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("group/project", safe=""))')
|
|
1288
|
-
TOKEN=$(
|
|
1385
|
+
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1289
1386
|
UPLOAD=$(curl -sS --request POST \\
|
|
1290
1387
|
--header "Authorization: Bearer $TOKEN" \\
|
|
1291
1388
|
--form "file=@$FILE" \\
|
|
1292
|
-
"https
|
|
1389
|
+
"https://$HOST/api/v4/projects/$PROJECT/uploads")
|
|
1293
1390
|
echo "$UPLOAD" | jq -r .markdown
|
|
1294
1391
|
\`\`\`
|
|
1295
1392
|
|
|
1296
|
-
|
|
1393
|
+
Insert the returned \`markdown\` into the merge request description or comment and add the media's **View in Replicas** dashboard link. Images render inline; MP4, MOV, and WebM render as inline video players. Do not create a public Replicas forge share when this native upload succeeds.
|
|
1297
1394
|
`;
|
|
1298
1395
|
var GITLAB_ABILITY = {
|
|
1299
1396
|
label: "GitLab",
|
|
@@ -5794,6 +5891,11 @@ var GitHubTokenManager = class extends BaseRefreshManager {
|
|
|
5794
5891
|
constructor() {
|
|
5795
5892
|
super("GitHubTokenManager");
|
|
5796
5893
|
}
|
|
5894
|
+
async refreshCredentials() {
|
|
5895
|
+
const config = this.getRuntimeConfig();
|
|
5896
|
+
if (!config) throw new Error("GitHub credential refresh is not configured");
|
|
5897
|
+
await this.doRefresh(config);
|
|
5898
|
+
}
|
|
5797
5899
|
async doRefresh(_config) {
|
|
5798
5900
|
console.log("[GitHubTokenManager] Refreshing GitHub token...");
|
|
5799
5901
|
const response = await monolithRequest("/v1/engine/github/refresh-token");
|
|
@@ -9979,7 +10081,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
9979
10081
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
9980
10082
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
9981
10083
|
var codexCliVersionEnsured = null;
|
|
9982
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10084
|
+
var ENGINE_PACKAGE_VERSION = "0.1.510";
|
|
9983
10085
|
var INITIALIZE_METHOD = "initialize";
|
|
9984
10086
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
9985
10087
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -16229,6 +16331,9 @@ var writeTerminalSessionSchema = z7.object({
|
|
|
16229
16331
|
generation: z7.number().int().nonnegative(),
|
|
16230
16332
|
sequence: z7.number().int().nonnegative()
|
|
16231
16333
|
});
|
|
16334
|
+
var mergePullRequestSchema = z7.object({
|
|
16335
|
+
prUrl: z7.string().url()
|
|
16336
|
+
});
|
|
16232
16337
|
var sendMessageSchema = z7.object({
|
|
16233
16338
|
messageId: z7.string().min(1).optional(),
|
|
16234
16339
|
submittedAt: z7.string().datetime().optional(),
|
|
@@ -16635,6 +16740,32 @@ function createV1Routes(deps) {
|
|
|
16635
16740
|
}
|
|
16636
16741
|
return c.json(result);
|
|
16637
16742
|
});
|
|
16743
|
+
app2.post("/pull-requests/merge", async (c) => {
|
|
16744
|
+
const body = mergePullRequestSchema.parse(await c.req.json());
|
|
16745
|
+
const parsed = parseCodeHostPrUrl(body.prUrl);
|
|
16746
|
+
if (parsed?.provider !== "github") {
|
|
16747
|
+
return c.json(jsonError("Only GitHub pull requests are supported"), 400);
|
|
16748
|
+
}
|
|
16749
|
+
const repository = (await gitService.listRepositories()).find(
|
|
16750
|
+
(repo) => repo.name.toLowerCase() === parsed.repo.toLowerCase()
|
|
16751
|
+
);
|
|
16752
|
+
if (!repository) {
|
|
16753
|
+
return c.json(jsonError("Pull request repository is not available in this workspace"), 404);
|
|
16754
|
+
}
|
|
16755
|
+
try {
|
|
16756
|
+
await githubTokenManager.refreshCredentials();
|
|
16757
|
+
await execFileAsync("gh", ["pr", "merge", body.prUrl, "--squash"], {
|
|
16758
|
+
cwd: repository.path,
|
|
16759
|
+
encoding: "utf-8",
|
|
16760
|
+
timeout: 12e4,
|
|
16761
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
16762
|
+
});
|
|
16763
|
+
return c.json({ success: true });
|
|
16764
|
+
} catch (error) {
|
|
16765
|
+
const detail = error && typeof error === "object" && "stderr" in error && typeof error.stderr === "string" ? error.stderr.trim() : error instanceof Error ? error.message : "GitHub CLI merge failed";
|
|
16766
|
+
return c.json(jsonError("Failed to merge pull request", detail), 409);
|
|
16767
|
+
}
|
|
16768
|
+
});
|
|
16638
16769
|
app2.get("/terminal/sessions", (c) => {
|
|
16639
16770
|
return c.json({ sessions: terminalService.list() });
|
|
16640
16771
|
});
|