replicas-engine 0.1.595 → 0.1.597
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 +326 -640
- package/package.json +3 -2
- package/workspace-sdk/index.d.ts +14 -0
- package/workspace-sdk/index.js +118 -0
- package/workspace-sdk/index.test.ts +94 -0
- package/workspace-sdk/package.json +14 -0
- package/workspace-sdk/shared/routes/integrations.d.ts +11 -0
- package/workspace-sdk/shared/routes/plugins.d.ts +119 -0
- package/workspace-sdk/shared/workspace-sdk.d.ts +58 -0
package/dist/src/index.js
CHANGED
|
@@ -17,8 +17,8 @@ async function raceWithTimeout(promise, ms) {
|
|
|
17
17
|
try {
|
|
18
18
|
return await Promise.race([
|
|
19
19
|
promise,
|
|
20
|
-
new Promise((
|
|
21
|
-
timer = setTimeout(() =>
|
|
20
|
+
new Promise((resolve5) => {
|
|
21
|
+
timer = setTimeout(() => resolve5(TIMEOUT), ms);
|
|
22
22
|
})
|
|
23
23
|
]);
|
|
24
24
|
} finally {
|
|
@@ -1327,7 +1327,7 @@ var DOCKER_ABILITY = {
|
|
|
1327
1327
|
|
|
1328
1328
|
// ../shared/src/default-skills/replicas-agent/abilities/github.ts
|
|
1329
1329
|
var SECTION3 = `### GitHub
|
|
1330
|
-
Use the pre-authenticated \`gh\` CLI for
|
|
1330
|
+
Use the pre-authenticated \`gh\` CLI for repository workflows and the workspace-scoped TypeScript SDK for GitHub API calls.
|
|
1331
1331
|
|
|
1332
1332
|
**Reference:** \`references/GITHUB.md\`
|
|
1333
1333
|
|
|
@@ -1338,7 +1338,7 @@ Use this when:
|
|
|
1338
1338
|
- You need to include images in PR descriptions`;
|
|
1339
1339
|
var REFERENCE3 = `# GitHub Integration
|
|
1340
1340
|
|
|
1341
|
-
This guide covers how to interact with GitHub from within your Replicas workspace.
|
|
1341
|
+
This guide covers how to interact with GitHub from within your Replicas workspace. Prefer \`@replicas/sdk\` for API calls so batching and data processing stay in one TypeScript program. The \`gh\` CLI remains useful for ergonomic pull request and git-adjacent workflows.
|
|
1342
1342
|
|
|
1343
1343
|
## Prerequisites
|
|
1344
1344
|
|
|
@@ -1423,17 +1423,21 @@ gh run rerun RUN_ID --failed
|
|
|
1423
1423
|
|
|
1424
1424
|
### GitHub API (Advanced)
|
|
1425
1425
|
|
|
1426
|
-
For operations not covered by \`gh\` subcommands, use the API
|
|
1426
|
+
For operations not covered by \`gh\` subcommands, use the scoped SDK. Pass only a relative GitHub API path:
|
|
1427
1427
|
|
|
1428
|
-
\`\`\`
|
|
1429
|
-
|
|
1430
|
-
gh api repos/owner/repo/pulls/123/comments
|
|
1428
|
+
\`\`\`ts
|
|
1429
|
+
import { replicas } from '@replicas/sdk';
|
|
1431
1430
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1431
|
+
const comments = await replicas.github.request('/repos/owner/repo/pulls/123/comments');
|
|
1432
|
+
await replicas.github.request('/repos/owner/repo/issues/123/comments', {
|
|
1433
|
+
method: 'POST',
|
|
1434
|
+
body: { body: 'Comment text' },
|
|
1435
|
+
});
|
|
1434
1436
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
+
const result = await replicas.github.request('/graphql', {
|
|
1438
|
+
method: 'POST',
|
|
1439
|
+
body: { query: '{ viewer { login } }' },
|
|
1440
|
+
});
|
|
1437
1441
|
\`\`\`
|
|
1438
1442
|
|
|
1439
1443
|
### Working with PR Reviews
|
|
@@ -1463,7 +1467,7 @@ GitHub does NOT have a public API for uploading images to PRs/issues. When you n
|
|
|
1463
1467
|
`;
|
|
1464
1468
|
var GITHUB_ABILITY = {
|
|
1465
1469
|
label: "GitHub",
|
|
1466
|
-
description: "
|
|
1470
|
+
description: "GitHub workflows through gh and API calls through @replicas/sdk.",
|
|
1467
1471
|
bullet: "- Interacting with GitHub (creating PRs, managing issues, using the API, etc.)",
|
|
1468
1472
|
section: SECTION3,
|
|
1469
1473
|
referenceFile: { name: "GITHUB.md", content: REFERENCE3 }
|
|
@@ -1471,124 +1475,61 @@ var GITHUB_ABILITY = {
|
|
|
1471
1475
|
|
|
1472
1476
|
// ../shared/src/default-skills/replicas-agent/abilities/gitlab.ts
|
|
1473
1477
|
var SECTION4 = `### GitLab
|
|
1474
|
-
|
|
1478
|
+
Use plain git for repository traffic and the workspace-scoped TypeScript SDK for GitLab API operations.
|
|
1475
1479
|
|
|
1476
1480
|
**Reference:** \`references/GITLAB.md\`
|
|
1477
1481
|
|
|
1478
1482
|
Use this when:
|
|
1479
|
-
- A
|
|
1480
|
-
- You need to open or update a merge request
|
|
1483
|
+
- A repository remote is on gitlab.com or a self-managed GitLab instance
|
|
1484
|
+
- You need to open or update a merge request
|
|
1485
|
+
- You need GitLab issues, pipelines, comments, or other API data`;
|
|
1481
1486
|
var REFERENCE4 = `# GitLab Integration
|
|
1482
1487
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
## Prerequisites
|
|
1486
|
-
|
|
1487
|
-
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.
|
|
1488
|
-
|
|
1489
|
-
- 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.
|
|
1490
|
-
- 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.
|
|
1491
|
-
- 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.
|
|
1492
|
-
|
|
1493
|
-
### Reading the credential for a repo
|
|
1488
|
+
Git credentials are preconfigured for fetch, pull, and push. Use \`@replicas/sdk\` for API traffic; never inspect \`~/.git-credentials\`, request a token, or send a credential with curl.
|
|
1494
1489
|
|
|
1495
|
-
|
|
1496
|
-
credential belonging to the host you are about to call. Never grep
|
|
1497
|
-
\`~/.git-credentials\` yourself: taking the first line sends one instance's token to
|
|
1498
|
-
another, and interpolating a host into a grep pattern makes it a regular
|
|
1499
|
-
expression. \`git credential fill\` matches the host exactly and returns nothing
|
|
1500
|
-
when there is no credential for it:
|
|
1490
|
+
## Merge requests
|
|
1501
1491
|
|
|
1502
|
-
|
|
1503
|
-
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1504
|
-
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1505
|
-
[ -n "$TOKEN" ] || { echo "no credential for $HOST"; exit 1; }
|
|
1506
|
-
\`\`\`
|
|
1507
|
-
|
|
1508
|
-
Every recipe below repeats these two lines so each block runs on its own, and
|
|
1509
|
-
every one calls \`https://$HOST\`, so the token only ever reaches the host it
|
|
1510
|
-
belongs to. Run them inside the repo whose API you are calling. An empty
|
|
1511
|
-
\`$TOKEN\` means the workspace has no credential for that host. Report that rather
|
|
1512
|
-
than reaching for another one.
|
|
1513
|
-
|
|
1514
|
-
## Merge Requests
|
|
1515
|
-
|
|
1516
|
-
Create a merge request directly from a push using push options:
|
|
1492
|
+
Create a merge request directly from a push:
|
|
1517
1493
|
|
|
1518
1494
|
\`\`\`bash
|
|
1519
1495
|
git push -o merge_request.create -o merge_request.target=<default-branch> -o merge_request.title="Title" origin HEAD
|
|
1520
1496
|
\`\`\`
|
|
1521
1497
|
|
|
1522
|
-
Useful options
|
|
1498
|
+
Useful options are \`-o merge_request.draft\` and \`-o merge_request.remove_source_branch\`. GitLab prints the merge request URL.
|
|
1523
1499
|
|
|
1524
|
-
|
|
1525
|
-
-o merge_request.draft # open as draft
|
|
1526
|
-
-o merge_request.remove_source_branch # delete branch on merge
|
|
1527
|
-
\`\`\`
|
|
1500
|
+
## API requests
|
|
1528
1501
|
|
|
1529
|
-
|
|
1502
|
+
Pass a relative path below \`/api/v4\`. Replicas selects the credential only from GitLab hosts attached to the workspace repositories.
|
|
1530
1503
|
|
|
1531
|
-
|
|
1504
|
+
\`\`\`ts
|
|
1505
|
+
import { replicas } from '@replicas/sdk';
|
|
1532
1506
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1507
|
+
const project = encodeURIComponent('group/project');
|
|
1508
|
+
const mergeRequests = await replicas.gitlab.request(
|
|
1509
|
+
\`/projects/\${project}/merge_requests?state=opened\`,
|
|
1510
|
+
);
|
|
1537
1511
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
cat > /tmp/mr-body.md <<'EOF'
|
|
1543
|
-
## Summary
|
|
1544
|
-
...your full description, ending with the Replicas footer...
|
|
1545
|
-
EOF
|
|
1546
|
-
jq -Rs '{description: .}' /tmp/mr-body.md > /tmp/mr-body.json
|
|
1547
|
-
curl -sS --request PUT \\
|
|
1548
|
-
--header "Authorization: Bearer $TOKEN" \\
|
|
1549
|
-
--header "Content-Type: application/json" \\
|
|
1550
|
-
--data @/tmp/mr-body.json \\
|
|
1551
|
-
"https://$HOST/api/v4/projects/$PROJECT/merge_requests/<iid>"
|
|
1512
|
+
await replicas.gitlab.request(\`/projects/\${project}/issues/42/notes\`, {
|
|
1513
|
+
method: 'POST',
|
|
1514
|
+
body: { body: 'Update from Replicas' },
|
|
1515
|
+
});
|
|
1552
1516
|
\`\`\`
|
|
1553
1517
|
|
|
1554
|
-
|
|
1555
|
-
body, so re-include the Replicas footer every time \u2014 it is how merge requests
|
|
1556
|
-
opened through Replicas are counted.
|
|
1557
|
-
|
|
1558
|
-
## GitLab API (advanced)
|
|
1559
|
-
|
|
1560
|
-
For operations with no git equivalent (commenting on MRs, reading pipelines), call the REST API with the workspace credential:
|
|
1518
|
+
When a workspace contains repositories from more than one GitLab host, pass the exact repository host:
|
|
1561
1519
|
|
|
1562
|
-
\`\`\`
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1520
|
+
\`\`\`ts
|
|
1521
|
+
await replicas.gitlab.request('/projects/group%2Frepo/pipelines', {
|
|
1522
|
+
host: 'gitlab.example.com',
|
|
1523
|
+
});
|
|
1566
1524
|
\`\`\`
|
|
1567
1525
|
|
|
1568
|
-
|
|
1569
|
-
and the token both come from the repo's own host.
|
|
1570
|
-
|
|
1571
|
-
## Inline media in merge requests
|
|
1572
|
-
|
|
1573
|
-
GitLab provides a supported project Markdown uploads API. Upload media to Replicas first, then upload the same bytes natively so repository permissions protect the asset:
|
|
1526
|
+
## Descriptions and media
|
|
1574
1527
|
|
|
1575
|
-
|
|
1576
|
-
FILE=/abs/path/to/screenshot.png
|
|
1577
|
-
HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
|
|
1578
|
-
PROJECT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("group/project", safe=""))')
|
|
1579
|
-
TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
|
|
1580
|
-
UPLOAD=$(curl -sS --request POST \\
|
|
1581
|
-
--header "Authorization: Bearer $TOKEN" \\
|
|
1582
|
-
--form "file=@$FILE" \\
|
|
1583
|
-
"https://$HOST/api/v4/projects/$PROJECT/uploads")
|
|
1584
|
-
echo "$UPLOAD" | jq -r .markdown
|
|
1585
|
-
\`\`\`
|
|
1586
|
-
|
|
1587
|
-
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.
|
|
1528
|
+
API updates replace the entire merge request description, so always preserve the required Replicas footer and co-author trailers. For media, first run \`replicas media upload\` as documented in \`MEDIA.md\`; use GitLab's project uploads endpoint only when a native repository-protected embed is required.
|
|
1588
1529
|
`;
|
|
1589
1530
|
var GITLAB_ABILITY = {
|
|
1590
1531
|
label: "GitLab",
|
|
1591
|
-
description: "
|
|
1532
|
+
description: "Git for repository traffic and @replicas/sdk for the GitLab API.",
|
|
1592
1533
|
bullet: "- Interacting with GitLab (pushing to GitLab repos, opening merge requests)",
|
|
1593
1534
|
section: SECTION4,
|
|
1594
1535
|
referenceFile: { name: "GITLAB.md", content: REFERENCE4 }
|
|
@@ -1596,332 +1537,67 @@ var GITLAB_ABILITY = {
|
|
|
1596
1537
|
|
|
1597
1538
|
// ../shared/src/default-skills/replicas-agent/abilities/google.ts
|
|
1598
1539
|
var SECTION5 = `### Google Workspace and Search Console
|
|
1599
|
-
Create and edit
|
|
1540
|
+
Create and edit Docs, Sheets, and Forms, manage Replicas-created Drive files, and read Search Console through the workspace-scoped TypeScript SDK.
|
|
1600
1541
|
|
|
1601
1542
|
**Reference:** \`references/GOOGLE.md\`
|
|
1602
1543
|
|
|
1603
1544
|
Use this when:
|
|
1604
1545
|
- You need to create or edit a Google Doc, Sheet, or Form
|
|
1605
|
-
- You need to
|
|
1606
|
-
- You need
|
|
1607
|
-
- You need to list Search Console properties, query search performance, read sitemaps, or inspect an indexed URL`;
|
|
1546
|
+
- You need to manage a file Replicas created
|
|
1547
|
+
- You need Google Search Console data`;
|
|
1608
1548
|
var REFERENCE5 = `# Google Workspace and Search Console
|
|
1609
1549
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
## Prerequisites
|
|
1613
|
-
|
|
1614
|
-
The integration is configured at the org or user level by the Replicas admin. From inside a workspace you don't have a Google access token directly; instead you call the monolith's \`/v1/gdrive/*\` endpoints, authenticated with your workspace's engine secret. The monolith refreshes the org's (or user's) Google access token and proxies the call.
|
|
1615
|
-
|
|
1616
|
-
Quick check that the integration is connected:
|
|
1617
|
-
|
|
1618
|
-
\`\`\`bash
|
|
1619
|
-
curl -s -X GET "$REPLICAS_MONOLITH_URL/v1/gdrive/credentials" \\
|
|
1620
|
-
-H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
|
|
1621
|
-
-H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID"
|
|
1622
|
-
\`\`\`
|
|
1623
|
-
|
|
1624
|
-
- If \`hasCredentials\` is \`true\`: you're good to go.
|
|
1625
|
-
- If \`hasCredentials\` is \`false\`: Google has not been connected for this org. Ask the user to go to **Settings \u2192 Integrations \u2192 Google** in the [Replicas dashboard](https://replicas.dev) and connect a Google account. Do not attempt Google operations until it's connected.
|
|
1626
|
-
|
|
1627
|
-
Standard auth headers used by every call below:
|
|
1628
|
-
|
|
1629
|
-
\`\`\`
|
|
1630
|
-
Authorization: Bearer $REPLICAS_ENGINE_SECRET
|
|
1631
|
-
X-Workspace-Id: $REPLICAS_WORKSPACE_ID
|
|
1632
|
-
\`\`\`
|
|
1633
|
-
|
|
1634
|
-
For brevity the examples below use a shell variable:
|
|
1635
|
-
|
|
1636
|
-
\`\`\`bash
|
|
1637
|
-
GDRIVE_AUTH=(-H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID")
|
|
1638
|
-
\`\`\`
|
|
1639
|
-
|
|
1640
|
-
## Important constraint: drive.file scope
|
|
1641
|
-
|
|
1642
|
-
The integration uses the **sensitive-tier \`drive.file\` scope**. That means Replicas can only read and edit Google files **it created itself**. It **cannot**:
|
|
1643
|
-
|
|
1644
|
-
- Read or edit a user's pre-existing Google Docs, Sheets, or Forms \u2014 even ones that were shared with the connected Google account.
|
|
1645
|
-
- List or search the user's broader Drive.
|
|
1646
|
-
- Touch any file that was not created via these gateway endpoints.
|
|
1647
|
-
|
|
1648
|
-
If the user asks you to edit an existing doc that Replicas didn't create, tell them this constraint and offer to create a new doc that mirrors what they want.
|
|
1649
|
-
|
|
1650
|
-
## Google Docs
|
|
1651
|
-
|
|
1652
|
-
### Create a new doc
|
|
1653
|
-
|
|
1654
|
-
\`\`\`bash
|
|
1655
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/docs" "\${GDRIVE_AUTH[@]}" \\
|
|
1656
|
-
-H "Content-Type: application/json" \\
|
|
1657
|
-
-d '{"title":"Meeting notes 2026-05-14"}'
|
|
1658
|
-
\`\`\`
|
|
1659
|
-
|
|
1660
|
-
Returns the full Doc object; grab \`.documentId\` for follow-up calls.
|
|
1661
|
-
|
|
1662
|
-
### Read a doc
|
|
1663
|
-
|
|
1664
|
-
\`\`\`bash
|
|
1665
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/docs/$DOC_ID" "\${GDRIVE_AUTH[@]}"
|
|
1666
|
-
\`\`\`
|
|
1667
|
-
|
|
1668
|
-
Returns the full document structure \u2014 \`body.content\` is an ordered list of structural elements (paragraphs, tables, etc.) with character indexes you can target for edits.
|
|
1669
|
-
|
|
1670
|
-
### Edit a doc (batchUpdate)
|
|
1671
|
-
|
|
1672
|
-
The Docs API edits use a list of [structural requests](https://developers.google.com/workspace/docs/api/reference/rest/v1/documents/request). Insert text, then style it; or insert tables, images, page breaks, etc.
|
|
1673
|
-
|
|
1674
|
-
\`\`\`bash
|
|
1675
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/docs/$DOC_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
|
|
1676
|
-
-H "Content-Type: application/json" \\
|
|
1677
|
-
-d '{
|
|
1678
|
-
"requests": [
|
|
1679
|
-
{ "insertText": { "location": { "index": 1 }, "text": "Hello, world!\\n" } }
|
|
1680
|
-
]
|
|
1681
|
-
}'
|
|
1682
|
-
\`\`\`
|
|
1683
|
-
|
|
1684
|
-
Common request types:
|
|
1685
|
-
|
|
1686
|
-
- \`insertText\` \u2014 insert plain text at a given index
|
|
1687
|
-
- \`deleteContentRange\` \u2014 delete a range
|
|
1688
|
-
- \`replaceAllText\` \u2014 find-and-replace
|
|
1689
|
-
- \`updateTextStyle\` \u2014 bold/italic/colors/fonts/sizes for a range
|
|
1690
|
-
- \`updateParagraphStyle\` \u2014 headings (\`HEADING_1\`..\`HEADING_6\`), alignment, spacing
|
|
1691
|
-
- \`createParagraphBullets\` \u2014 turn paragraphs into bulleted/numbered lists
|
|
1692
|
-
- \`insertTable\` \u2014 insert a table
|
|
1693
|
-
- \`insertInlineImage\` \u2014 insert an image from a URL
|
|
1694
|
-
|
|
1695
|
-
Edits are verbose but powerful. Prefer batching many requests into a single \`batchUpdate\` call rather than making many round trips \u2014 it's faster and keeps the doc state consistent.
|
|
1696
|
-
|
|
1697
|
-
## Google Sheets
|
|
1698
|
-
|
|
1699
|
-
### Create a new spreadsheet
|
|
1700
|
-
|
|
1701
|
-
\`\`\`bash
|
|
1702
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets" "\${GDRIVE_AUTH[@]}" \\
|
|
1703
|
-
-H "Content-Type: application/json" \\
|
|
1704
|
-
-d '{"title":"Q2 metrics"}'
|
|
1705
|
-
\`\`\`
|
|
1706
|
-
|
|
1707
|
-
Returns the full Spreadsheet object; grab \`.spreadsheetId\`.
|
|
1550
|
+
Use \`@replicas/sdk\`; the workspace never receives Google OAuth credentials. Personal credentials are preferred for the workspace owner, then Replicas falls back to the organization connection.
|
|
1708
1551
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
\`\`\`bash
|
|
1712
|
-
# Range is in A1 notation, e.g. "Sheet1!A1:C10"
|
|
1713
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/values/$(printf %s 'Sheet1!A1:C10' | jq -sRr @uri)" \\
|
|
1714
|
-
"\${GDRIVE_AUTH[@]}"
|
|
1715
|
-
\`\`\`
|
|
1716
|
-
|
|
1717
|
-
### Write a range of cells
|
|
1718
|
-
|
|
1719
|
-
\`\`\`bash
|
|
1720
|
-
curl -s -X PUT \\
|
|
1721
|
-
"$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/values/$(printf %s 'Sheet1!A1:B2' | jq -sRr @uri)?valueInputOption=USER_ENTERED" \\
|
|
1722
|
-
"\${GDRIVE_AUTH[@]}" \\
|
|
1723
|
-
-H "Content-Type: application/json" \\
|
|
1724
|
-
-d '{
|
|
1725
|
-
"range": "Sheet1!A1:B2",
|
|
1726
|
-
"majorDimension": "ROWS",
|
|
1727
|
-
"values": [
|
|
1728
|
-
["Name", "Revenue"],
|
|
1729
|
-
["Q1", "=SUM(B3:B100)"]
|
|
1730
|
-
]
|
|
1731
|
-
}'
|
|
1732
|
-
\`\`\`
|
|
1733
|
-
|
|
1734
|
-
\`valueInputOption=USER_ENTERED\` makes formulas evaluate as if a human typed them. Use \`RAW\` to write literal strings.
|
|
1735
|
-
|
|
1736
|
-
### Bulk operations (formatting, charts, new tabs, etc.)
|
|
1737
|
-
|
|
1738
|
-
\`\`\`bash
|
|
1739
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
|
|
1740
|
-
-H "Content-Type: application/json" \\
|
|
1741
|
-
-d '{ "requests": [ { "addSheet": { "properties": { "title": "Raw data" } } } ] }'
|
|
1742
|
-
\`\`\`
|
|
1743
|
-
|
|
1744
|
-
\`batchUpdate\` accepts an array of [Sheets API requests](https://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets/request) \u2014 addSheet, updateSheetProperties, repeatCell, addChart, autoResizeDimensions, etc.
|
|
1745
|
-
|
|
1746
|
-
## Google Forms
|
|
1747
|
-
|
|
1748
|
-
### Create a new form
|
|
1749
|
-
|
|
1750
|
-
\`\`\`bash
|
|
1751
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/forms" "\${GDRIVE_AUTH[@]}" \\
|
|
1752
|
-
-H "Content-Type: application/json" \\
|
|
1753
|
-
-d '{"title":"Customer feedback"}'
|
|
1754
|
-
\`\`\`
|
|
1755
|
-
|
|
1756
|
-
Returns the form. Grab \`.formId\`.
|
|
1757
|
-
|
|
1758
|
-
### Add questions / edit form structure
|
|
1759
|
-
|
|
1760
|
-
The Forms API uses its own \`batchUpdate\`:
|
|
1761
|
-
|
|
1762
|
-
\`\`\`bash
|
|
1763
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/forms/$FORM_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
|
|
1764
|
-
-H "Content-Type: application/json" \\
|
|
1765
|
-
-d '{
|
|
1766
|
-
"requests": [
|
|
1767
|
-
{
|
|
1768
|
-
"createItem": {
|
|
1769
|
-
"item": {
|
|
1770
|
-
"title": "How likely are you to recommend us?",
|
|
1771
|
-
"questionItem": {
|
|
1772
|
-
"question": {
|
|
1773
|
-
"required": true,
|
|
1774
|
-
"scaleQuestion": { "low": 0, "high": 10, "lowLabel": "Not at all", "highLabel": "Very likely" }
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
},
|
|
1778
|
-
"location": { "index": 0 }
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
]
|
|
1782
|
-
}'
|
|
1783
|
-
\`\`\`
|
|
1784
|
-
|
|
1785
|
-
See the [Forms API Request reference](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms/request) for all supported request types \u2014 text questions, multiple choice, checkboxes, scale, grid, date, time, file upload, section breaks, branching logic, quiz settings, etc.
|
|
1786
|
-
|
|
1787
|
-
### Read responses
|
|
1788
|
-
|
|
1789
|
-
\`\`\`bash
|
|
1790
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/forms/$FORM_ID/responses" "\${GDRIVE_AUTH[@]}"
|
|
1791
|
-
\`\`\`
|
|
1792
|
-
|
|
1793
|
-
Pagination: pass \`?pageToken=...&pageSize=...\` to walk through responses.
|
|
1794
|
-
|
|
1795
|
-
## Drive operations (only on Replicas-created files)
|
|
1796
|
-
|
|
1797
|
-
### Share a file with a person
|
|
1798
|
-
|
|
1799
|
-
\`\`\`bash
|
|
1800
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}" \\
|
|
1801
|
-
-H "Content-Type: application/json" \\
|
|
1802
|
-
-d '{
|
|
1803
|
-
"type": "user",
|
|
1804
|
-
"role": "writer",
|
|
1805
|
-
"emailAddress": "alice@example.com",
|
|
1806
|
-
"sendNotificationEmail": true
|
|
1807
|
-
}'
|
|
1808
|
-
\`\`\`
|
|
1809
|
-
|
|
1810
|
-
Roles: \`reader\`, \`commenter\`, \`writer\`. Types: \`user\`, \`group\`, \`domain\`, \`anyone\`.
|
|
1811
|
-
|
|
1812
|
-
### Make a file viewable by anyone with the link
|
|
1813
|
-
|
|
1814
|
-
\`\`\`bash
|
|
1815
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}" \\
|
|
1816
|
-
-H "Content-Type: application/json" \\
|
|
1817
|
-
-d '{ "type": "anyone", "role": "reader" }'
|
|
1818
|
-
\`\`\`
|
|
1819
|
-
|
|
1820
|
-
### List existing permissions
|
|
1821
|
-
|
|
1822
|
-
\`\`\`bash
|
|
1823
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}"
|
|
1824
|
-
\`\`\`
|
|
1825
|
-
|
|
1826
|
-
### Rename / move a file
|
|
1827
|
-
|
|
1828
|
-
\`\`\`bash
|
|
1829
|
-
curl -s -X PATCH "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID" "\${GDRIVE_AUTH[@]}" \\
|
|
1830
|
-
-H "Content-Type: application/json" \\
|
|
1831
|
-
-d '{"name":"Final report.docx"}'
|
|
1832
|
-
\`\`\`
|
|
1833
|
-
|
|
1834
|
-
To move into a folder, also pass \`addParents\` and \`removeParents\` as query parameters per [Drive API docs](https://developers.google.com/workspace/drive/api/reference/rest/v3/files/update).
|
|
1835
|
-
|
|
1836
|
-
### Get file metadata
|
|
1837
|
-
|
|
1838
|
-
\`\`\`bash
|
|
1839
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID?fields=id,name,mimeType,webViewLink,parents,modifiedTime" \\
|
|
1840
|
-
"\${GDRIVE_AUTH[@]}"
|
|
1841
|
-
\`\`\`
|
|
1842
|
-
|
|
1843
|
-
\`webViewLink\` is the human-shareable URL that opens in Google Docs/Sheets/Forms.
|
|
1844
|
-
|
|
1845
|
-
### Delete a file
|
|
1846
|
-
|
|
1847
|
-
\`\`\`bash
|
|
1848
|
-
curl -s -X DELETE "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID" "\${GDRIVE_AUTH[@]}"
|
|
1849
|
-
\`\`\`
|
|
1850
|
-
|
|
1851
|
-
### List Replicas-created files
|
|
1552
|
+
\`\`\`ts
|
|
1553
|
+
import { replicas } from '@replicas/sdk';
|
|
1852
1554
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1555
|
+
const { native } = await replicas.integrations.list();
|
|
1556
|
+
if (!native.google) throw new Error('Connect Google in Replicas integrations first.');
|
|
1855
1557
|
\`\`\`
|
|
1856
1558
|
|
|
1857
|
-
|
|
1559
|
+
Google Drive uses \`drive.file\`: Replicas can access only files it created, not pre-existing files in the account.
|
|
1858
1560
|
|
|
1859
|
-
##
|
|
1561
|
+
## Docs
|
|
1860
1562
|
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
### Get a property
|
|
1872
|
-
|
|
1873
|
-
\`\`\`bash
|
|
1874
|
-
SITE_URL=$(printf %s 'sc-domain:example.com' | jq -sRr @uri)
|
|
1875
|
-
curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/site?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}"
|
|
1876
|
-
\`\`\`
|
|
1877
|
-
|
|
1878
|
-
### Query search performance
|
|
1879
|
-
|
|
1880
|
-
\`\`\`bash
|
|
1881
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/search-analytics/query?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}" \\
|
|
1882
|
-
-H "Content-Type: application/json" \\
|
|
1883
|
-
-d '{
|
|
1884
|
-
"startDate": "2026-06-01",
|
|
1885
|
-
"endDate": "2026-06-30",
|
|
1886
|
-
"dimensions": ["query", "page"],
|
|
1887
|
-
"rowLimit": 1000
|
|
1888
|
-
}'
|
|
1563
|
+
\`\`\`ts
|
|
1564
|
+
const doc = await replicas.google.request<{ documentId: string }>('docs', {
|
|
1565
|
+
method: 'POST',
|
|
1566
|
+
body: { title: 'Meeting notes' },
|
|
1567
|
+
});
|
|
1568
|
+
await replicas.google.request(\`docs/\${doc.documentId}/batchUpdate\`, {
|
|
1569
|
+
method: 'POST',
|
|
1570
|
+
body: { requests: [{ insertText: { location: { index: 1 }, text: 'Hello\\n' } }] },
|
|
1571
|
+
});
|
|
1889
1572
|
\`\`\`
|
|
1890
1573
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
### List sitemaps
|
|
1574
|
+
## Sheets
|
|
1894
1575
|
|
|
1895
|
-
\`\`\`
|
|
1896
|
-
|
|
1576
|
+
\`\`\`ts
|
|
1577
|
+
const sheet = await replicas.google.request<{ spreadsheetId: string }>('sheets', {
|
|
1578
|
+
method: 'POST',
|
|
1579
|
+
body: { title: 'Q2 metrics' },
|
|
1580
|
+
});
|
|
1581
|
+
await replicas.google.request(
|
|
1582
|
+
\`sheets/\${sheet.spreadsheetId}/values/\${encodeURIComponent('Sheet1!A1:B2')}?valueInputOption=USER_ENTERED\`,
|
|
1583
|
+
{ method: 'PUT', body: { values: [['Name', 'Revenue'], ['Q1', 100]] } },
|
|
1584
|
+
);
|
|
1897
1585
|
\`\`\`
|
|
1898
1586
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
### Inspect an indexed URL
|
|
1902
|
-
|
|
1903
|
-
\`\`\`bash
|
|
1904
|
-
curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/url-inspection" "\${GDRIVE_AUTH[@]}" \\
|
|
1905
|
-
-H "Content-Type: application/json" \\
|
|
1906
|
-
-d '{
|
|
1907
|
-
"inspectionUrl": "https://example.com/page",
|
|
1908
|
-
"siteUrl": "sc-domain:example.com",
|
|
1909
|
-
"languageCode": "en-US"
|
|
1910
|
-
}'
|
|
1911
|
-
\`\`\`
|
|
1587
|
+
## Forms, Drive, and Search Console
|
|
1912
1588
|
|
|
1913
|
-
|
|
1589
|
+
The same method maps directly to the guarded \`/v1/gdrive/*\` routes:
|
|
1914
1590
|
|
|
1915
|
-
|
|
1591
|
+
- \`forms\`, \`forms/:id\`, \`forms/:id/batchUpdate\`, \`forms/:id/responses\`
|
|
1592
|
+
- \`files\`, \`files/:id\`, \`files/:id/permissions\`
|
|
1593
|
+
- \`search-console/sites\`, \`search-console/search-analytics/query\`, \`search-console/sitemaps\`, \`search-console/url-inspection\`
|
|
1916
1594
|
|
|
1917
|
-
|
|
1918
|
-
- **Batch your edits.** A single \`batchUpdate\` with 20 requests beats 20 round-trips.
|
|
1919
|
-
- **Watch error responses.** A 412 from the gateway means the org has no Google credentials connected \u2014 tell the user to connect Google in the dashboard. Other 4xx responses come straight from Google and usually explain the issue clearly.
|
|
1595
|
+
Batch independent reads with \`Promise.all\`; use each API's batch-update endpoint for dependent writes.
|
|
1920
1596
|
`;
|
|
1921
1597
|
var GOOGLE_ABILITY = {
|
|
1922
|
-
label: "Google",
|
|
1923
|
-
description: "Use
|
|
1924
|
-
bullet: "- Interacting with Google Workspace or Search Console
|
|
1598
|
+
label: "Google Workspace",
|
|
1599
|
+
description: "Use Docs, Sheets, Forms, Drive, and Search Console through @replicas/sdk.",
|
|
1600
|
+
bullet: "- Interacting with Google Workspace or Search Console",
|
|
1925
1601
|
section: SECTION5,
|
|
1926
1602
|
referenceFile: { name: "GOOGLE.md", content: REFERENCE5 }
|
|
1927
1603
|
};
|
|
@@ -1955,102 +1631,63 @@ var LEARNINGS_ABILITY = {
|
|
|
1955
1631
|
|
|
1956
1632
|
// ../shared/src/default-skills/replicas-agent/abilities/linear.ts
|
|
1957
1633
|
var SECTION7 = `### Linear
|
|
1958
|
-
Fetch issues, update state, add comments, and search
|
|
1634
|
+
Fetch issues, update state, add comments, and search through the workspace-scoped TypeScript SDK.
|
|
1959
1635
|
|
|
1960
1636
|
**Reference:** \`references/LINEAR.md\`
|
|
1961
1637
|
|
|
1962
1638
|
Use this when:
|
|
1963
1639
|
- You encounter a Linear issue link and need to understand the task
|
|
1964
|
-
- You need to update an issue's state
|
|
1965
|
-
- You need to
|
|
1640
|
+
- You need to update an issue's state or add a comment
|
|
1641
|
+
- You need to search Linear issues`;
|
|
1966
1642
|
var REFERENCE7 = `# Linear Integration
|
|
1967
1643
|
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
## Prerequisites
|
|
1971
|
-
|
|
1972
|
-
Check that the workspace has the Replicas engine credentials needed to proxy Linear requests:
|
|
1973
|
-
|
|
1974
|
-
\`\`\`bash
|
|
1975
|
-
test -n "$REPLICAS_MONOLITH_URL" && test -n "$REPLICAS_ENGINE_SECRET" && test -n "$REPLICAS_WORKSPACE_ID" && echo set
|
|
1976
|
-
\`\`\`
|
|
1977
|
-
|
|
1978
|
-
- If **set**: Use the Replicas Linear proxy below. It refreshes Linear OAuth tokens for you.
|
|
1979
|
-
- If **not set**: The workspace cannot authenticate to Replicas. Let the user know and do not attempt Linear operations.
|
|
1980
|
-
|
|
1981
|
-
Then verify Linear is connected:
|
|
1982
|
-
|
|
1983
|
-
\`\`\`bash
|
|
1984
|
-
curl -fsS -X POST "$REPLICAS_MONOLITH_URL/v1/engine/linear/refresh-token" \\
|
|
1985
|
-
-H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
|
|
1986
|
-
-H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID" \\
|
|
1987
|
-
-H "Content-Type: application/json" \\
|
|
1988
|
-
-d '{}' >/dev/null
|
|
1989
|
-
\`\`\`
|
|
1990
|
-
|
|
1991
|
-
If this fails because no Linear credential is connected, the user needs to connect Linear in the [Replicas dashboard](https://replicas.dev) under their organization's integration settings.
|
|
1992
|
-
|
|
1993
|
-
## Using the Linear API
|
|
1994
|
-
|
|
1995
|
-
Linear uses a GraphQL API. Always call it through Replicas so the access token stays fresh:
|
|
1996
|
-
|
|
1997
|
-
\`\`\`bash
|
|
1998
|
-
linear_graphql() {
|
|
1999
|
-
curl -sS -X POST "$REPLICAS_MONOLITH_URL/v1/engine/linear/graphql" \\
|
|
2000
|
-
-H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
|
|
2001
|
-
-H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID" \\
|
|
2002
|
-
-H "Content-Type: application/json" \\
|
|
2003
|
-
--data-binary @-
|
|
2004
|
-
}
|
|
2005
|
-
\`\`\`
|
|
1644
|
+
Use \`@replicas/sdk\`; never request or handle a Linear OAuth token. Replicas refreshes the organization credential behind the gateway.
|
|
2006
1645
|
|
|
2007
|
-
|
|
1646
|
+
## Check access
|
|
2008
1647
|
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
\`\`\`bash
|
|
2012
|
-
jq -n --arg identifier "ENG-123" '{
|
|
2013
|
-
query: "query($identifier: String!) { issue(id: $identifier) { id identifier title description state { name } assignee { name } parent { identifier title description } } }",
|
|
2014
|
-
variables: { identifier: $identifier }
|
|
2015
|
-
}' | linear_graphql
|
|
2016
|
-
\`\`\`
|
|
2017
|
-
|
|
2018
|
-
### Updating Issue State
|
|
1648
|
+
\`\`\`ts
|
|
1649
|
+
import { replicas } from '@replicas/sdk';
|
|
2019
1650
|
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
query: "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success issue { identifier state { name } } } }",
|
|
2023
|
-
variables: { issueId: $issueId, stateId: $stateId }
|
|
2024
|
-
}' | linear_graphql
|
|
1651
|
+
const { native } = await replicas.integrations.list();
|
|
1652
|
+
if (!native.linear) throw new Error('Connect Linear in Replicas organization integrations first.');
|
|
2025
1653
|
\`\`\`
|
|
2026
1654
|
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
### Adding a Comment
|
|
1655
|
+
## Fetch an issue
|
|
2030
1656
|
|
|
2031
|
-
\`\`\`
|
|
2032
|
-
|
|
2033
|
-
query:
|
|
2034
|
-
|
|
2035
|
-
}
|
|
1657
|
+
\`\`\`ts
|
|
1658
|
+
const issue = await replicas.linear.graphql({
|
|
1659
|
+
query: \`query($identifier: String!) {
|
|
1660
|
+
issue(id: $identifier) {
|
|
1661
|
+
id identifier title description state { id name } assignee { name }
|
|
1662
|
+
parent { identifier title description }
|
|
1663
|
+
}
|
|
1664
|
+
}\`,
|
|
1665
|
+
variables: { identifier: 'ENG-123' },
|
|
1666
|
+
});
|
|
2036
1667
|
\`\`\`
|
|
2037
1668
|
|
|
2038
|
-
|
|
1669
|
+
## Update and comment in one script
|
|
2039
1670
|
|
|
2040
|
-
\`\`\`
|
|
2041
|
-
|
|
2042
|
-
query:
|
|
2043
|
-
|
|
2044
|
-
}
|
|
1671
|
+
\`\`\`ts
|
|
1672
|
+
await replicas.linear.graphql({
|
|
1673
|
+
query: \`mutation($issueId: String!, $stateId: String!) {
|
|
1674
|
+
issueUpdate(id: $issueId, input: { stateId: $stateId }) { success }
|
|
1675
|
+
}\`,
|
|
1676
|
+
variables: { issueId, stateId },
|
|
1677
|
+
});
|
|
1678
|
+
await replicas.linear.graphql({
|
|
1679
|
+
query: \`mutation($issueId: String!, $body: String!) {
|
|
1680
|
+
commentCreate(input: { issueId: $issueId, body: $body }) { success comment { id } }
|
|
1681
|
+
}\`,
|
|
1682
|
+
variables: { issueId, body: 'Implemented in PR #123.' },
|
|
1683
|
+
});
|
|
2045
1684
|
\`\`\`
|
|
2046
1685
|
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
The Linear GraphQL API supports creating issues, managing projects, labels, cycles, and more. For the full schema and documentation, see: https://developers.linear.app/docs/graphql/working-with-the-graphql-api
|
|
1686
|
+
Search with \`containsIgnoreCase: $query\` filters, for example on both \`title\` and \`description\`. Batch independent queries with \`Promise.all\` to save round trips.
|
|
2050
1687
|
`;
|
|
2051
1688
|
var LINEAR_ABILITY = {
|
|
2052
1689
|
label: "Linear",
|
|
2053
|
-
description: "Fetch issues,
|
|
1690
|
+
description: "Fetch issues, comment, and update state through @replicas/sdk.",
|
|
2054
1691
|
bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
|
|
2055
1692
|
section: SECTION7,
|
|
2056
1693
|
referenceFile: { name: "LINEAR.md", content: REFERENCE7 }
|
|
@@ -2467,8 +2104,80 @@ var PREVIEWS_ABILITY = {
|
|
|
2467
2104
|
referenceFile: { name: "PREVIEWS.md", content: REFERENCE9 }
|
|
2468
2105
|
};
|
|
2469
2106
|
|
|
2107
|
+
// ../shared/src/default-skills/replicas-agent/abilities/plugins.ts
|
|
2108
|
+
var SECTION10 = `### Plugins
|
|
2109
|
+
Discover and execute installed marketplace tools through the workspace-scoped TypeScript SDK.
|
|
2110
|
+
|
|
2111
|
+
**Reference:** \`references/PLUGINS.md\`
|
|
2112
|
+
|
|
2113
|
+
Use this when:
|
|
2114
|
+
- The user asks for CRM data from Attio
|
|
2115
|
+
- The user asks for Google Ads, Docs, Drive, Search Console, or Sheets data
|
|
2116
|
+
- The user asks for ClickHouse analytics or PostHog product data
|
|
2117
|
+
- The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
|
|
2118
|
+
var REFERENCE10 = `# Plugins
|
|
2119
|
+
|
|
2120
|
+
Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects only the owner\u2019s personal connection or the organization fallback. Composio credentials, sessions, and project keys are never exposed.
|
|
2121
|
+
|
|
2122
|
+
The initial plugin library is read-only. Tool discovery and execution both reject tools that are not tagged read-only.
|
|
2123
|
+
|
|
2124
|
+
## Discover installed plugins
|
|
2125
|
+
|
|
2126
|
+
\`\`\`ts
|
|
2127
|
+
import { replicas } from '@replicas/sdk';
|
|
2128
|
+
|
|
2129
|
+
const plugins = await replicas.plugins.list();
|
|
2130
|
+
const installed = plugins.filter((plugin) => plugin.connected);
|
|
2131
|
+
\`\`\`
|
|
2132
|
+
|
|
2133
|
+
If a plugin is not connected, ask the user to install it under personal or organization Integrations in the Replicas dashboard.
|
|
2134
|
+
|
|
2135
|
+
## Search, inspect, execute
|
|
2136
|
+
|
|
2137
|
+
\`\`\`ts
|
|
2138
|
+
const search = await replicas.plugins.search({
|
|
2139
|
+
plugin: 'stripe',
|
|
2140
|
+
query: 'list recent successful payments with customer details',
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
const [tool] = search.tools;
|
|
2144
|
+
if (!tool) throw new Error('No matching read-only tool found');
|
|
2145
|
+
|
|
2146
|
+
const { data } = await replicas.plugins.execute({
|
|
2147
|
+
plugin: 'stripe',
|
|
2148
|
+
tool: tool.slug,
|
|
2149
|
+
arguments: { limit: 25 },
|
|
2150
|
+
});
|
|
2151
|
+
\`\`\`
|
|
2152
|
+
|
|
2153
|
+
Use \`replicas.plugins.describe({ plugin, tools: [slug] })\` when search returned a partial schema or before constructing unfamiliar arguments.
|
|
2154
|
+
|
|
2155
|
+
## Batch work
|
|
2156
|
+
|
|
2157
|
+
Keep dependent operations in one TypeScript file and batch independent calls:
|
|
2158
|
+
|
|
2159
|
+
\`\`\`ts
|
|
2160
|
+
const results = await Promise.all(customerIds.map((customer) =>
|
|
2161
|
+
replicas.plugins.execute({
|
|
2162
|
+
plugin: 'stripe',
|
|
2163
|
+
tool: 'STRIPE_RETRIEVE_CUSTOMER',
|
|
2164
|
+
arguments: { customer },
|
|
2165
|
+
}),
|
|
2166
|
+
));
|
|
2167
|
+
\`\`\`
|
|
2168
|
+
|
|
2169
|
+
Never attempt to import Composio, create a session, manage a connection, or call a provider with raw credentials. Those operations are intentionally unavailable inside the workspace.
|
|
2170
|
+
`;
|
|
2171
|
+
var PLUGINS_ABILITY = {
|
|
2172
|
+
label: "Plugins",
|
|
2173
|
+
description: "Use installed marketplace tools through @replicas/sdk.",
|
|
2174
|
+
bullet: "- Interacting with installed marketplace plugins through TypeScript",
|
|
2175
|
+
section: SECTION10,
|
|
2176
|
+
referenceFile: { name: "PLUGINS.md", content: REFERENCE10 }
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2470
2179
|
// ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
|
|
2471
|
-
var
|
|
2180
|
+
var SECTION11 = `### Replicas (in-workspace CLI)
|
|
2472
2181
|
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.
|
|
2473
2182
|
|
|
2474
2183
|
**Reference:** \`references/REPLICAS.md\`
|
|
@@ -2479,7 +2188,7 @@ Use this when:
|
|
|
2479
2188
|
- The user asks "what envs / repos / automations do I have?"
|
|
2480
2189
|
- The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
|
|
2481
2190
|
- You need to run a long-lived service (dev server, daemon) \u2014 always use \`replicas service start\` so it survives workspace sleep/wake`;
|
|
2482
|
-
var
|
|
2191
|
+
var REFERENCE11 = `# Replicas (in-workspace CLI)
|
|
2483
2192
|
|
|
2484
2193
|
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.
|
|
2485
2194
|
|
|
@@ -2706,149 +2415,107 @@ var REPLICAS_ABILITY = {
|
|
|
2706
2415
|
description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
|
|
2707
2416
|
// No bullet — help_instructions covers the `replicas` CLI surface in detail.
|
|
2708
2417
|
bullet: "",
|
|
2709
|
-
section:
|
|
2710
|
-
referenceFile: { name: "REPLICAS.md", content:
|
|
2418
|
+
section: SECTION11,
|
|
2419
|
+
referenceFile: { name: "REPLICAS.md", content: REFERENCE11 },
|
|
2711
2420
|
locked: true
|
|
2712
2421
|
};
|
|
2713
2422
|
|
|
2714
|
-
// ../shared/src/default-skills/replicas-agent/abilities/
|
|
2715
|
-
var
|
|
2716
|
-
|
|
2423
|
+
// ../shared/src/default-skills/replicas-agent/abilities/sentry.ts
|
|
2424
|
+
var SECTION12 = `### Sentry
|
|
2425
|
+
Read issues and project data through the workspace-scoped TypeScript SDK.
|
|
2717
2426
|
|
|
2718
|
-
**Reference:** \`references/
|
|
2427
|
+
**Reference:** \`references/SENTRY.md\`
|
|
2719
2428
|
|
|
2720
2429
|
Use this when:
|
|
2721
|
-
- You need to
|
|
2722
|
-
-
|
|
2723
|
-
|
|
2724
|
-
- The task asks you to notify, update, or communicate via Slack`;
|
|
2725
|
-
var REFERENCE11 = `# Slack Integration
|
|
2430
|
+
- You need to inspect Sentry projects or issues
|
|
2431
|
+
- A user asks for error-monitoring context outside a webhook-triggered task`;
|
|
2432
|
+
var REFERENCE12 = `# Sentry Integration
|
|
2726
2433
|
|
|
2727
|
-
|
|
2434
|
+
Use \`@replicas/sdk\`; never request or read a Sentry token. Sentry access is organization-scoped and read-only.
|
|
2728
2435
|
|
|
2729
|
-
|
|
2436
|
+
\`\`\`ts
|
|
2437
|
+
import { replicas } from '@replicas/sdk';
|
|
2730
2438
|
|
|
2731
|
-
|
|
2439
|
+
const { native } = await replicas.integrations.list();
|
|
2440
|
+
if (!native.sentry) throw new Error('Connect Sentry in Replicas organization integrations first.');
|
|
2732
2441
|
|
|
2733
|
-
|
|
2734
|
-
if [ -n "\${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" ]; then echo set; fi
|
|
2442
|
+
const issues = await replicas.sentry.request('/organizations/acme/issues/?query=is:unresolved');
|
|
2735
2443
|
\`\`\`
|
|
2736
2444
|
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
If you encounter a Slack message link (e.g. \`https://team.slack.com/archives/C0123ABC/p1234567890123456\`), extract the channel ID and thread timestamp:
|
|
2747
|
-
|
|
2748
|
-
- **Channel ID**: The segment after \`/archives/\` (e.g. \`C0123ABC\`)
|
|
2749
|
-
- **Thread TS**: The \`p\` value with a dot inserted before the last 6 digits (e.g. \`p1234567890123456\` -> \`1234567890.123456\`)
|
|
2445
|
+
Pass only a relative Sentry API path. The Replicas gateway selects the connected organization credential and rejects writes, absolute URLs, oversized responses, and long-running requests.
|
|
2446
|
+
`;
|
|
2447
|
+
var SENTRY_ABILITY = {
|
|
2448
|
+
label: "Sentry",
|
|
2449
|
+
description: "Read Sentry projects and issues through @replicas/sdk.",
|
|
2450
|
+
bullet: "- Interacting with Sentry (reading projects, issues, and error context)",
|
|
2451
|
+
section: SECTION12,
|
|
2452
|
+
referenceFile: { name: "SENTRY.md", content: REFERENCE12 }
|
|
2453
|
+
};
|
|
2750
2454
|
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
\`\`\`
|
|
2455
|
+
// ../shared/src/default-skills/replicas-agent/abilities/slack.ts
|
|
2456
|
+
var SECTION13 = `### Slack
|
|
2457
|
+
Send messages, read threads, search conversations, and upload files through the workspace-scoped TypeScript SDK.
|
|
2755
2458
|
|
|
2756
|
-
|
|
2459
|
+
**Reference:** \`references/SLACK.md\`
|
|
2757
2460
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
"channel": "CHANNEL_ID",
|
|
2764
|
-
"text": "Your message here",
|
|
2765
|
-
"thread_ts": "OPTIONAL_THREAD_TS"
|
|
2766
|
-
}'
|
|
2767
|
-
\`\`\`
|
|
2461
|
+
Use this when:
|
|
2462
|
+
- You need to send a message to a Slack channel or thread
|
|
2463
|
+
- You need to read or search Slack conversations
|
|
2464
|
+
- You encounter a Slack message link and need its full thread`;
|
|
2465
|
+
var REFERENCE13 = `# Slack Integration
|
|
2768
2466
|
|
|
2769
|
-
|
|
2467
|
+
Use \`@replicas/sdk\`; never read or send a Slack token directly. The SDK calls Replicas, which selects only this workspace's organization connection.
|
|
2770
2468
|
|
|
2771
|
-
|
|
2469
|
+
## Check access
|
|
2772
2470
|
|
|
2773
|
-
|
|
2471
|
+
\`\`\`ts
|
|
2472
|
+
import { replicas } from '@replicas/sdk';
|
|
2774
2473
|
|
|
2775
|
-
|
|
2776
|
-
|
|
2474
|
+
const { native } = await replicas.integrations.list();
|
|
2475
|
+
if (!native.slack) throw new Error('Connect Slack in Replicas organization integrations first.');
|
|
2777
2476
|
\`\`\`
|
|
2778
2477
|
|
|
2779
|
-
|
|
2780
|
-
- For a reply in another existing thread, use its root \`thread_ts\`, not the reply's \`ts\`.
|
|
2781
|
-
- Do not attach the originating thread again when it already routes to the current workspace.
|
|
2782
|
-
- Treat sending and attaching as one operation: check that both API calls succeed, and report an attachment failure instead of implying future replies will reach you.
|
|
2783
|
-
|
|
2784
|
-
#### Switch a thread to an existing workspace
|
|
2478
|
+
## Read a thread
|
|
2785
2479
|
|
|
2786
|
-
|
|
2480
|
+
For a link such as \`https://team.slack.com/archives/C0123ABC/p1234567890123456\`, the channel is \`C0123ABC\` and the timestamp is \`1234567890.123456\`.
|
|
2787
2481
|
|
|
2788
|
-
\`\`\`
|
|
2789
|
-
replicas
|
|
2790
|
-
|
|
2482
|
+
\`\`\`ts
|
|
2483
|
+
const thread = await replicas.slack.call('conversations.replies', {
|
|
2484
|
+
channel: 'C0123ABC',
|
|
2485
|
+
ts: '1234567890.123456',
|
|
2486
|
+
limit: 200,
|
|
2487
|
+
});
|
|
2791
2488
|
\`\`\`
|
|
2792
2489
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
### Searching Messages
|
|
2490
|
+
## Send and attach a message
|
|
2796
2491
|
|
|
2797
|
-
\`\`\`
|
|
2798
|
-
|
|
2799
|
-
|
|
2492
|
+
\`\`\`ts
|
|
2493
|
+
const message = await replicas.slack.call<{ channel: string; ts: string }>('chat.postMessage', {
|
|
2494
|
+
channel: 'C0123ABC',
|
|
2495
|
+
text: 'Update from Replicas',
|
|
2496
|
+
});
|
|
2497
|
+
await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
|
|
2800
2498
|
\`\`\`
|
|
2801
2499
|
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
Slack deprecated \`files.upload\`; the raw HTTP replacement is a 3-step flow. (\`files.uploadV2\` is **only** an SDK convenience wrapper and is NOT a real Web API HTTP endpoint \u2014 \`POST https://slack.com/api/files.uploadV2\` returns \`unknown_method\`.)
|
|
2805
|
-
|
|
2806
|
-
\`\`\`bash
|
|
2807
|
-
FILE=/path/to/file
|
|
2808
|
-
FILENAME=$(basename "$FILE")
|
|
2809
|
-
LENGTH=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
|
|
2500
|
+
Attach every new top-level conversation so future replies route to this workspace. For a reply in another thread, attach its root \`thread_ts\`, not the reply timestamp. Do not attach the originating thread again.
|
|
2810
2501
|
|
|
2811
|
-
|
|
2812
|
-
RESERVE=$(curl -s -G "https://slack.com/api/files.getUploadURLExternal" \\
|
|
2813
|
-
-H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
|
|
2814
|
-
--data-urlencode "filename=$FILENAME" \\
|
|
2815
|
-
--data-urlencode "length=$LENGTH")
|
|
2816
|
-
UPLOAD_URL=$(echo "$RESERVE" | jq -r '.upload_url')
|
|
2817
|
-
FILE_ID=$(echo "$RESERVE" | jq -r '.file_id')
|
|
2818
|
-
|
|
2819
|
-
# 2. POST the raw bytes to the reserved URL.
|
|
2820
|
-
# Do not use -F / multipart here \u2014 the presigned URL expects the bytes
|
|
2821
|
-
# directly in the request body.
|
|
2822
|
-
curl -s -X POST "$UPLOAD_URL" \\
|
|
2823
|
-
-H "Content-Type: application/octet-stream" \\
|
|
2824
|
-
--data-binary @"$FILE"
|
|
2502
|
+
## Search and batch
|
|
2825
2503
|
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
\\"files\\": [{\\"id\\": \\"$FILE_ID\\", \\"title\\": \\"File title\\"}],
|
|
2832
|
-
\\"channel_id\\": \\"CHANNEL_ID\\",
|
|
2833
|
-
\\"thread_ts\\": \\"OPTIONAL_THREAD_TS\\",
|
|
2834
|
-
\\"initial_comment\\": \\"Optional message body\\"
|
|
2835
|
-
}"
|
|
2504
|
+
\`\`\`ts
|
|
2505
|
+
const [messages, users] = await Promise.all([
|
|
2506
|
+
replicas.slack.call('search.messages', { query: 'launch status' }),
|
|
2507
|
+
replicas.slack.call('users.list', { limit: 200 }),
|
|
2508
|
+
]);
|
|
2836
2509
|
\`\`\`
|
|
2837
2510
|
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
### Other Operations
|
|
2841
|
-
|
|
2842
|
-
You can list channels, read channel history, add reactions, and perform any other operation supported by the Slack Web API using the same authentication pattern.
|
|
2843
|
-
|
|
2844
|
-
For full API documentation, see: https://docs.slack.dev/apis/web-api/
|
|
2511
|
+
Supported methods cover conversations, messages, search, users, reactions, and Slack's external file-upload flow. An unsupported method is rejected by Replicas before Slack is called.
|
|
2845
2512
|
`;
|
|
2846
2513
|
var SLACK_ABILITY = {
|
|
2847
2514
|
label: "Slack",
|
|
2848
|
-
description: "Send messages, read threads, search
|
|
2515
|
+
description: "Send messages, read threads, and search through @replicas/sdk.",
|
|
2849
2516
|
bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
|
|
2850
|
-
section:
|
|
2851
|
-
referenceFile: { name: "SLACK.md", content:
|
|
2517
|
+
section: SECTION13,
|
|
2518
|
+
referenceFile: { name: "SLACK.md", content: REFERENCE13 }
|
|
2852
2519
|
};
|
|
2853
2520
|
|
|
2854
2521
|
// ../shared/src/default-skills/replicas-agent/registry.ts
|
|
@@ -2862,7 +2529,9 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
|
|
|
2862
2529
|
learnings: LEARNINGS_ABILITY,
|
|
2863
2530
|
linear: LINEAR_ABILITY,
|
|
2864
2531
|
media: MEDIA_ABILITY,
|
|
2532
|
+
plugins: PLUGINS_ABILITY,
|
|
2865
2533
|
previews: PREVIEWS_ABILITY,
|
|
2534
|
+
sentry: SENTRY_ABILITY,
|
|
2866
2535
|
slack: SLACK_ABILITY
|
|
2867
2536
|
};
|
|
2868
2537
|
var REPLICAS_AGENT_ABILITIES = Object.keys(
|
|
@@ -5832,7 +5501,7 @@ var BaseRefreshManager = class {
|
|
|
5832
5501
|
this.health.lastErrorMessage = message;
|
|
5833
5502
|
if (attempt < 3) {
|
|
5834
5503
|
console.warn(`[${this.managerName}] Initial refresh attempt ${attempt} failed, retrying in 2s...`);
|
|
5835
|
-
await new Promise((
|
|
5504
|
+
await new Promise((resolve5) => setTimeout(resolve5, 2e3));
|
|
5836
5505
|
} else {
|
|
5837
5506
|
console.error(`[${this.managerName}] Initial refresh failed after 3 attempts:`, error);
|
|
5838
5507
|
}
|
|
@@ -6371,7 +6040,7 @@ var GitService = class {
|
|
|
6371
6040
|
}
|
|
6372
6041
|
}
|
|
6373
6042
|
readUntrackedDiff(repoPath, paths) {
|
|
6374
|
-
return new Promise((
|
|
6043
|
+
return new Promise((resolve5) => {
|
|
6375
6044
|
const child = spawn("git", ["diff", "--", ...paths], {
|
|
6376
6045
|
cwd: repoPath,
|
|
6377
6046
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6392,12 +6061,12 @@ var GitService = class {
|
|
|
6392
6061
|
});
|
|
6393
6062
|
child.stderr.on("data", () => {
|
|
6394
6063
|
});
|
|
6395
|
-
child.on("error", () =>
|
|
6064
|
+
child.on("error", () => resolve5(""));
|
|
6396
6065
|
child.on("close", (code) => {
|
|
6397
6066
|
if (code === 0 || code === 1 || truncated) {
|
|
6398
|
-
|
|
6067
|
+
resolve5(Buffer.concat(chunks).toString("utf-8"));
|
|
6399
6068
|
} else {
|
|
6400
|
-
|
|
6069
|
+
resolve5("");
|
|
6401
6070
|
}
|
|
6402
6071
|
});
|
|
6403
6072
|
});
|
|
@@ -7134,9 +6803,9 @@ var StreamWriter = class {
|
|
|
7134
6803
|
return true;
|
|
7135
6804
|
}
|
|
7136
6805
|
flush(signal) {
|
|
7137
|
-
return new Promise((
|
|
6806
|
+
return new Promise((resolve5, reject) => {
|
|
7138
6807
|
if (!this.stream) {
|
|
7139
|
-
|
|
6808
|
+
resolve5();
|
|
7140
6809
|
return;
|
|
7141
6810
|
}
|
|
7142
6811
|
if (this.flushTimer) {
|
|
@@ -7153,7 +6822,7 @@ var StreamWriter = class {
|
|
|
7153
6822
|
s.write("", (error) => {
|
|
7154
6823
|
signal?.removeEventListener("abort", onAbort);
|
|
7155
6824
|
if (error) reject(error);
|
|
7156
|
-
else
|
|
6825
|
+
else resolve5();
|
|
7157
6826
|
});
|
|
7158
6827
|
});
|
|
7159
6828
|
}
|
|
@@ -7691,7 +7360,7 @@ var ReplicasConfigService = class {
|
|
|
7691
7360
|
await this.logToFile(`[${params.repoName}] --- Running: ${params.label} ---`);
|
|
7692
7361
|
emit(`$ ${params.label}
|
|
7693
7362
|
`);
|
|
7694
|
-
return new Promise((
|
|
7363
|
+
return new Promise((resolve5) => {
|
|
7695
7364
|
const proc = spawn2("bash", ["-lc", params.command], {
|
|
7696
7365
|
cwd: params.cwd,
|
|
7697
7366
|
env: process.env,
|
|
@@ -7702,7 +7371,7 @@ var ReplicasConfigService = class {
|
|
|
7702
7371
|
const finish = (result) => {
|
|
7703
7372
|
if (settled) return;
|
|
7704
7373
|
settled = true;
|
|
7705
|
-
|
|
7374
|
+
resolve5(result);
|
|
7706
7375
|
};
|
|
7707
7376
|
const timer = setTimeout(() => {
|
|
7708
7377
|
timedOut = true;
|
|
@@ -8128,7 +7797,7 @@ async function registerDesktopPreview() {
|
|
|
8128
7797
|
if (await attemptRegistration()) {
|
|
8129
7798
|
return;
|
|
8130
7799
|
}
|
|
8131
|
-
await new Promise((
|
|
7800
|
+
await new Promise((resolve5) => setTimeout(resolve5, RETRY_DELAY_MS));
|
|
8132
7801
|
}
|
|
8133
7802
|
console.error(
|
|
8134
7803
|
`[DesktopPreview] Gave up registering port ${DESKTOP_STREAM_PORT} after ${REGISTRATION_TIMEOUT_MS}ms`
|
|
@@ -9669,8 +9338,8 @@ var PromptStream = class {
|
|
|
9669
9338
|
if (this.closed) {
|
|
9670
9339
|
return Promise.resolve({ value: void 0, done: true });
|
|
9671
9340
|
}
|
|
9672
|
-
return new Promise((
|
|
9673
|
-
this.waiters.push(
|
|
9341
|
+
return new Promise((resolve5) => {
|
|
9342
|
+
this.waiters.push(resolve5);
|
|
9674
9343
|
});
|
|
9675
9344
|
}
|
|
9676
9345
|
};
|
|
@@ -10050,14 +9719,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10050
9719
|
const requestId = randomUUID4();
|
|
10051
9720
|
const toolUseId = options.toolUseID;
|
|
10052
9721
|
const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
|
|
10053
|
-
const result = await new Promise((
|
|
9722
|
+
const result = await new Promise((resolve5) => {
|
|
10054
9723
|
this.pendingToolInputs.set(requestId, {
|
|
10055
9724
|
requestId,
|
|
10056
9725
|
toolUseId,
|
|
10057
9726
|
toolName,
|
|
10058
9727
|
input,
|
|
10059
9728
|
handler,
|
|
10060
|
-
resolve:
|
|
9729
|
+
resolve: resolve5
|
|
10061
9730
|
});
|
|
10062
9731
|
const onAbort = () => {
|
|
10063
9732
|
const pending = this.pendingToolInputs.get(requestId);
|
|
@@ -10162,7 +9831,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10162
9831
|
error
|
|
10163
9832
|
);
|
|
10164
9833
|
await this.tearDownSession();
|
|
10165
|
-
await new Promise((
|
|
9834
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
10166
9835
|
currentRequest = { ...request, message: CLAUDE_MIDTURN_CONTINUE_PROMPT, images: [] };
|
|
10167
9836
|
attempt++;
|
|
10168
9837
|
continue;
|
|
@@ -10175,7 +9844,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10175
9844
|
error
|
|
10176
9845
|
);
|
|
10177
9846
|
await this.tearDownSession();
|
|
10178
|
-
await new Promise((
|
|
9847
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
10179
9848
|
attempt++;
|
|
10180
9849
|
continue;
|
|
10181
9850
|
}
|
|
@@ -10321,8 +9990,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10321
9990
|
this.pendingInterrupt = false;
|
|
10322
9991
|
return;
|
|
10323
9992
|
}
|
|
10324
|
-
await new Promise((
|
|
10325
|
-
this.pendingTurn = { resolve:
|
|
9993
|
+
await new Promise((resolve5, reject) => {
|
|
9994
|
+
this.pendingTurn = { resolve: resolve5, reject, sawActivity: false };
|
|
10326
9995
|
promptStream.push(userMessage);
|
|
10327
9996
|
});
|
|
10328
9997
|
}
|
|
@@ -10909,13 +10578,13 @@ var AspClient = class {
|
|
|
10909
10578
|
}
|
|
10910
10579
|
const id = this.nextId;
|
|
10911
10580
|
this.nextId += 1;
|
|
10912
|
-
const promise = new Promise((
|
|
10581
|
+
const promise = new Promise((resolve5, reject) => {
|
|
10913
10582
|
const timeoutMs = opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
10914
10583
|
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
10915
10584
|
this.pending.delete(id);
|
|
10916
10585
|
reject(new Error(`ASP request timed out for ${method}`));
|
|
10917
10586
|
}, timeoutMs) : null;
|
|
10918
|
-
this.pending.set(id, { resolve:
|
|
10587
|
+
this.pending.set(id, { resolve: resolve5, reject, method, timer });
|
|
10919
10588
|
});
|
|
10920
10589
|
this.write({ method, id, params });
|
|
10921
10590
|
return promise;
|
|
@@ -11068,7 +10737,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
11068
10737
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
11069
10738
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
11070
10739
|
var codexCliVersionEnsured = null;
|
|
11071
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10740
|
+
var ENGINE_PACKAGE_VERSION = "0.1.597";
|
|
11072
10741
|
var INITIALIZE_METHOD = "initialize";
|
|
11073
10742
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
11074
10743
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -11188,13 +10857,13 @@ var AppServerProcess = class {
|
|
|
11188
10857
|
if (!child || child.killed) {
|
|
11189
10858
|
return;
|
|
11190
10859
|
}
|
|
11191
|
-
await new Promise((
|
|
10860
|
+
await new Promise((resolve5) => {
|
|
11192
10861
|
const timer = setTimeout(() => {
|
|
11193
10862
|
child.kill("SIGKILL");
|
|
11194
10863
|
}, 2e3);
|
|
11195
10864
|
child.once("exit", () => {
|
|
11196
10865
|
clearTimeout(timer);
|
|
11197
|
-
|
|
10866
|
+
resolve5();
|
|
11198
10867
|
});
|
|
11199
10868
|
child.kill("SIGTERM");
|
|
11200
10869
|
});
|
|
@@ -11239,8 +10908,8 @@ var AppServerProcess = class {
|
|
|
11239
10908
|
}
|
|
11240
10909
|
this.shuttingDown = true;
|
|
11241
10910
|
child.kill("SIGKILL");
|
|
11242
|
-
await new Promise((
|
|
11243
|
-
child.once("exit", () =>
|
|
10911
|
+
await new Promise((resolve5) => {
|
|
10912
|
+
child.once("exit", () => resolve5());
|
|
11244
10913
|
});
|
|
11245
10914
|
}
|
|
11246
10915
|
};
|
|
@@ -12520,8 +12189,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
12520
12189
|
}
|
|
12521
12190
|
async observeTurn(host, threadId, request, startTurn, options = {}) {
|
|
12522
12191
|
let resolveCompleted;
|
|
12523
|
-
const completed = new Promise((
|
|
12524
|
-
resolveCompleted =
|
|
12192
|
+
const completed = new Promise((resolve5) => {
|
|
12193
|
+
resolveCompleted = resolve5;
|
|
12525
12194
|
});
|
|
12526
12195
|
let rejectDisposed = () => {
|
|
12527
12196
|
};
|
|
@@ -13487,8 +13156,8 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
13487
13156
|
}
|
|
13488
13157
|
async processMessageInternal(request) {
|
|
13489
13158
|
let run = null;
|
|
13490
|
-
const aborted = new Promise((
|
|
13491
|
-
this.abortActiveTurn = () =>
|
|
13159
|
+
const aborted = new Promise((resolve5) => {
|
|
13160
|
+
this.abortActiveTurn = () => resolve5(TURN_ABORTED);
|
|
13492
13161
|
});
|
|
13493
13162
|
const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
|
|
13494
13163
|
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
@@ -14129,8 +13798,8 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
14129
13798
|
this.configuredModels = this.providerId === OPENCODE_GO_PROVIDER ? /* @__PURE__ */ new Set() : new Set(getConfiguredOpencodeModels(model, providerId));
|
|
14130
13799
|
const eventController = new AbortController();
|
|
14131
13800
|
this.eventAbortController = eventController;
|
|
14132
|
-
this.eventSubscriptionReady = new Promise((
|
|
14133
|
-
this.resolveEventSubscriptionReady =
|
|
13801
|
+
this.eventSubscriptionReady = new Promise((resolve5) => {
|
|
13802
|
+
this.resolveEventSubscriptionReady = resolve5;
|
|
14134
13803
|
});
|
|
14135
13804
|
this.subscribeToEvents(client, eventController).catch((error) => {
|
|
14136
13805
|
this.resolveEventSubscriptionReady?.();
|
|
@@ -14140,7 +13809,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
14140
13809
|
});
|
|
14141
13810
|
await Promise.race([
|
|
14142
13811
|
this.eventSubscriptionReady,
|
|
14143
|
-
new Promise((
|
|
13812
|
+
new Promise((resolve5) => setTimeout(resolve5, 2e3))
|
|
14144
13813
|
]);
|
|
14145
13814
|
return client;
|
|
14146
13815
|
}
|
|
@@ -15762,7 +15431,7 @@ function isReconcileCanvasItemsResponse(value) {
|
|
|
15762
15431
|
}
|
|
15763
15432
|
async function withCanvasUploadSlot(operation) {
|
|
15764
15433
|
if (activeCanvasUploads >= MAX_CONCURRENT_CANVAS_UPLOADS) {
|
|
15765
|
-
await new Promise((
|
|
15434
|
+
await new Promise((resolve5) => pendingCanvasUploads.push(resolve5));
|
|
15766
15435
|
} else {
|
|
15767
15436
|
activeCanvasUploads++;
|
|
15768
15437
|
}
|
|
@@ -15897,7 +15566,7 @@ var HISTORY_DIRS = [
|
|
|
15897
15566
|
join25(ENGINE_DIR2, "codex-histories")
|
|
15898
15567
|
];
|
|
15899
15568
|
async function putTranscript(uploadUrl, filePath, size) {
|
|
15900
|
-
await new Promise((
|
|
15569
|
+
await new Promise((resolve5, reject) => {
|
|
15901
15570
|
const url = new URL(uploadUrl);
|
|
15902
15571
|
const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(url, {
|
|
15903
15572
|
method: "PUT",
|
|
@@ -15913,7 +15582,7 @@ async function putTranscript(uploadUrl, filePath, size) {
|
|
|
15913
15582
|
});
|
|
15914
15583
|
response.on("end", () => {
|
|
15915
15584
|
const status = response.statusCode ?? 0;
|
|
15916
|
-
if (status >= 200 && status < 300)
|
|
15585
|
+
if (status >= 200 && status < 300) resolve5();
|
|
15917
15586
|
else reject(new Error(`upload failed: ${status} ${body}`));
|
|
15918
15587
|
});
|
|
15919
15588
|
response.on("error", reject);
|
|
@@ -16120,13 +15789,13 @@ function runBeforeDeadline(operation, deadline) {
|
|
|
16120
15789
|
const timeoutMs = deadline - Date.now();
|
|
16121
15790
|
if (timeoutMs <= 0) return Promise.reject(new DOMException("engine log flush deadline exceeded", "TimeoutError"));
|
|
16122
15791
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
16123
|
-
return new Promise((
|
|
15792
|
+
return new Promise((resolve5, reject) => {
|
|
16124
15793
|
const onAbort = () => reject(signal.reason);
|
|
16125
15794
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
16126
15795
|
operation(signal).then(
|
|
16127
15796
|
(value) => {
|
|
16128
15797
|
signal.removeEventListener("abort", onAbort);
|
|
16129
|
-
|
|
15798
|
+
resolve5(value);
|
|
16130
15799
|
},
|
|
16131
15800
|
(error) => {
|
|
16132
15801
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -17155,7 +16824,7 @@ function scoreMatch(query2, filePath) {
|
|
|
17155
16824
|
return 0;
|
|
17156
16825
|
}
|
|
17157
16826
|
function execGitAsync(args, cwd, timeoutMs) {
|
|
17158
|
-
return new Promise((
|
|
16827
|
+
return new Promise((resolve5, reject) => {
|
|
17159
16828
|
const child = execFile2("git", args, {
|
|
17160
16829
|
cwd,
|
|
17161
16830
|
encoding: "utf-8",
|
|
@@ -17165,7 +16834,7 @@ function execGitAsync(args, cwd, timeoutMs) {
|
|
|
17165
16834
|
if (error) {
|
|
17166
16835
|
reject(error);
|
|
17167
16836
|
} else {
|
|
17168
|
-
|
|
16837
|
+
resolve5(stdout);
|
|
17169
16838
|
}
|
|
17170
16839
|
});
|
|
17171
16840
|
child.stdin?.end();
|
|
@@ -17557,7 +17226,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17557
17226
|
let bufferExceeded = false;
|
|
17558
17227
|
params.onChunk(`$ ${params.label}
|
|
17559
17228
|
`);
|
|
17560
|
-
return new Promise((
|
|
17229
|
+
return new Promise((resolve5) => {
|
|
17561
17230
|
const proc = spawn4("bash", ["-lc", params.content], {
|
|
17562
17231
|
cwd: params.cwd,
|
|
17563
17232
|
env: process.env,
|
|
@@ -17590,7 +17259,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17590
17259
|
proc.on("close", (code) => {
|
|
17591
17260
|
clearTimeout(timer);
|
|
17592
17261
|
const output = [`$ ${params.label}`, ...outputParts].join("");
|
|
17593
|
-
|
|
17262
|
+
resolve5({
|
|
17594
17263
|
exitCode: bufferExceeded ? 1 : code ?? 1,
|
|
17595
17264
|
output,
|
|
17596
17265
|
timedOut: killed && !bufferExceeded
|
|
@@ -17600,7 +17269,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17600
17269
|
clearTimeout(timer);
|
|
17601
17270
|
const output = [`$ ${params.label}
|
|
17602
17271
|
`, ...outputParts, err.message].join("");
|
|
17603
|
-
|
|
17272
|
+
resolve5({
|
|
17604
17273
|
exitCode: 1,
|
|
17605
17274
|
output,
|
|
17606
17275
|
timedOut: false
|
|
@@ -18873,14 +18542,30 @@ var HeartbeatService = class _HeartbeatService {
|
|
|
18873
18542
|
};
|
|
18874
18543
|
var heartbeatService = new HeartbeatService();
|
|
18875
18544
|
|
|
18545
|
+
// src/services/workspace-sdk-service.ts
|
|
18546
|
+
import { cp, mkdir as mkdir17 } from "fs/promises";
|
|
18547
|
+
import { dirname as dirname8, resolve as resolve4 } from "path";
|
|
18548
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18549
|
+
async function installWorkspaceSdk() {
|
|
18550
|
+
const source = resolve4(dirname8(fileURLToPath2(import.meta.url)), "../../workspace-sdk");
|
|
18551
|
+
const targets = [
|
|
18552
|
+
resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
|
|
18553
|
+
"/tmp/node_modules/@replicas/sdk"
|
|
18554
|
+
];
|
|
18555
|
+
await Promise.all(targets.map(async (target) => {
|
|
18556
|
+
await mkdir17(target, { recursive: true });
|
|
18557
|
+
await cp(source, target, { recursive: true, force: true });
|
|
18558
|
+
}));
|
|
18559
|
+
}
|
|
18560
|
+
|
|
18876
18561
|
// src/index.ts
|
|
18877
18562
|
var startupStartedAt = performance.now();
|
|
18878
18563
|
var startupTimings = {};
|
|
18879
18564
|
var ENGINE_INIT_GATE_TIMEOUT_MS = 35 * 60 * 1e3;
|
|
18880
18565
|
var resolveEngineReady = () => {
|
|
18881
18566
|
};
|
|
18882
|
-
var engineReadyPromise = new Promise((
|
|
18883
|
-
resolveEngineReady =
|
|
18567
|
+
var engineReadyPromise = new Promise((resolve5) => {
|
|
18568
|
+
resolveEngineReady = resolve5;
|
|
18884
18569
|
});
|
|
18885
18570
|
async function timeStartupStep(name, fn) {
|
|
18886
18571
|
const startedAt = performance.now();
|
|
@@ -18900,7 +18585,7 @@ async function waitForInitializationGate() {
|
|
|
18900
18585
|
if (Date.now() >= deadline) {
|
|
18901
18586
|
throw new Error("Timed out waiting for deferred engine initialization gate");
|
|
18902
18587
|
}
|
|
18903
|
-
await new Promise((
|
|
18588
|
+
await new Promise((resolve5) => setTimeout(resolve5, 25));
|
|
18904
18589
|
}
|
|
18905
18590
|
}
|
|
18906
18591
|
await timeStartupStep("engine_logger_initialize", () => engineLogger.initialize());
|
|
@@ -18932,11 +18617,11 @@ async function checkActiveSSHSessions() {
|
|
|
18932
18617
|
}
|
|
18933
18618
|
}
|
|
18934
18619
|
function checkDesktopReady() {
|
|
18935
|
-
return new Promise((
|
|
18620
|
+
return new Promise((resolve5) => {
|
|
18936
18621
|
const socket = connect({ host: "127.0.0.1", port: DESKTOP_STREAM_PORT });
|
|
18937
18622
|
const done = (ready) => {
|
|
18938
18623
|
socket.destroy();
|
|
18939
|
-
|
|
18624
|
+
resolve5(ready);
|
|
18940
18625
|
};
|
|
18941
18626
|
socket.once("connect", () => done(true));
|
|
18942
18627
|
socket.once("error", () => done(false));
|
|
@@ -18968,7 +18653,7 @@ app.get("/health", async (c) => {
|
|
|
18968
18653
|
if (!engineReady && waitMs > 0) {
|
|
18969
18654
|
await Promise.race([
|
|
18970
18655
|
engineReadyPromise,
|
|
18971
|
-
new Promise((
|
|
18656
|
+
new Promise((resolve5) => setTimeout(resolve5, waitMs))
|
|
18972
18657
|
]);
|
|
18973
18658
|
}
|
|
18974
18659
|
c.header(ENGINE_HEALTH_WAIT_HEADER, "1");
|
|
@@ -19168,6 +18853,7 @@ serve(
|
|
|
19168
18853
|
console.log(`Replicas Engine running on port ${info.port}`);
|
|
19169
18854
|
}
|
|
19170
18855
|
await timeStartupStep("wait_for_initialization_gate", waitForInitializationGate);
|
|
18856
|
+
await timeStartupStep("workspace_sdk_install", installWorkspaceSdk);
|
|
19171
18857
|
if (!IS_WARMING_MODE) {
|
|
19172
18858
|
await timeStartupStep("replicas_config_initialize", () => replicasConfigService.initialize());
|
|
19173
18859
|
}
|