replicas-engine 0.1.595 → 0.1.596
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 +325 -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 +89 -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,79 @@ 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 Attio, Google Ads, and Stripe 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 campaigns or reporting
|
|
2116
|
+
- The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
|
|
2117
|
+
var REFERENCE10 = `# Plugins
|
|
2118
|
+
|
|
2119
|
+
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.
|
|
2120
|
+
|
|
2121
|
+
The initial plugin library is read-only. Tool discovery and execution both reject tools that are not tagged read-only.
|
|
2122
|
+
|
|
2123
|
+
## Discover installed plugins
|
|
2124
|
+
|
|
2125
|
+
\`\`\`ts
|
|
2126
|
+
import { replicas } from '@replicas/sdk';
|
|
2127
|
+
|
|
2128
|
+
const plugins = await replicas.plugins.list();
|
|
2129
|
+
const installed = plugins.filter((plugin) => plugin.connected);
|
|
2130
|
+
\`\`\`
|
|
2131
|
+
|
|
2132
|
+
If a plugin is not connected, ask the user to install it under personal or organization Integrations in the Replicas dashboard.
|
|
2133
|
+
|
|
2134
|
+
## Search, inspect, execute
|
|
2135
|
+
|
|
2136
|
+
\`\`\`ts
|
|
2137
|
+
const search = await replicas.plugins.search({
|
|
2138
|
+
plugin: 'stripe',
|
|
2139
|
+
query: 'list recent successful payments with customer details',
|
|
2140
|
+
});
|
|
2141
|
+
|
|
2142
|
+
const [tool] = search.tools;
|
|
2143
|
+
if (!tool) throw new Error('No matching read-only tool found');
|
|
2144
|
+
|
|
2145
|
+
const { data } = await replicas.plugins.execute({
|
|
2146
|
+
plugin: 'stripe',
|
|
2147
|
+
tool: tool.slug,
|
|
2148
|
+
arguments: { limit: 25 },
|
|
2149
|
+
});
|
|
2150
|
+
\`\`\`
|
|
2151
|
+
|
|
2152
|
+
Use \`replicas.plugins.describe({ plugin, tools: [slug] })\` when search returned a partial schema or before constructing unfamiliar arguments.
|
|
2153
|
+
|
|
2154
|
+
## Batch work
|
|
2155
|
+
|
|
2156
|
+
Keep dependent operations in one TypeScript file and batch independent calls:
|
|
2157
|
+
|
|
2158
|
+
\`\`\`ts
|
|
2159
|
+
const results = await Promise.all(customerIds.map((customer) =>
|
|
2160
|
+
replicas.plugins.execute({
|
|
2161
|
+
plugin: 'stripe',
|
|
2162
|
+
tool: 'STRIPE_RETRIEVE_CUSTOMER',
|
|
2163
|
+
arguments: { customer },
|
|
2164
|
+
}),
|
|
2165
|
+
));
|
|
2166
|
+
\`\`\`
|
|
2167
|
+
|
|
2168
|
+
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.
|
|
2169
|
+
`;
|
|
2170
|
+
var PLUGINS_ABILITY = {
|
|
2171
|
+
label: "Plugins",
|
|
2172
|
+
description: "Use installed Attio, Google Ads, and Stripe tools through @replicas/sdk.",
|
|
2173
|
+
bullet: "- Interacting with installed plugins such as Attio, Google Ads, or Stripe",
|
|
2174
|
+
section: SECTION10,
|
|
2175
|
+
referenceFile: { name: "PLUGINS.md", content: REFERENCE10 }
|
|
2176
|
+
};
|
|
2177
|
+
|
|
2470
2178
|
// ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
|
|
2471
|
-
var
|
|
2179
|
+
var SECTION11 = `### Replicas (in-workspace CLI)
|
|
2472
2180
|
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
2181
|
|
|
2474
2182
|
**Reference:** \`references/REPLICAS.md\`
|
|
@@ -2479,7 +2187,7 @@ Use this when:
|
|
|
2479
2187
|
- The user asks "what envs / repos / automations do I have?"
|
|
2480
2188
|
- The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
|
|
2481
2189
|
- 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
|
|
2190
|
+
var REFERENCE11 = `# Replicas (in-workspace CLI)
|
|
2483
2191
|
|
|
2484
2192
|
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
2193
|
|
|
@@ -2706,149 +2414,107 @@ var REPLICAS_ABILITY = {
|
|
|
2706
2414
|
description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
|
|
2707
2415
|
// No bullet — help_instructions covers the `replicas` CLI surface in detail.
|
|
2708
2416
|
bullet: "",
|
|
2709
|
-
section:
|
|
2710
|
-
referenceFile: { name: "REPLICAS.md", content:
|
|
2417
|
+
section: SECTION11,
|
|
2418
|
+
referenceFile: { name: "REPLICAS.md", content: REFERENCE11 },
|
|
2711
2419
|
locked: true
|
|
2712
2420
|
};
|
|
2713
2421
|
|
|
2714
|
-
// ../shared/src/default-skills/replicas-agent/abilities/
|
|
2715
|
-
var
|
|
2716
|
-
|
|
2422
|
+
// ../shared/src/default-skills/replicas-agent/abilities/sentry.ts
|
|
2423
|
+
var SECTION12 = `### Sentry
|
|
2424
|
+
Read issues and project data through the workspace-scoped TypeScript SDK.
|
|
2717
2425
|
|
|
2718
|
-
**Reference:** \`references/
|
|
2426
|
+
**Reference:** \`references/SENTRY.md\`
|
|
2719
2427
|
|
|
2720
2428
|
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
|
|
2429
|
+
- You need to inspect Sentry projects or issues
|
|
2430
|
+
- A user asks for error-monitoring context outside a webhook-triggered task`;
|
|
2431
|
+
var REFERENCE12 = `# Sentry Integration
|
|
2726
2432
|
|
|
2727
|
-
|
|
2433
|
+
Use \`@replicas/sdk\`; never request or read a Sentry token. Sentry access is organization-scoped and read-only.
|
|
2728
2434
|
|
|
2729
|
-
|
|
2435
|
+
\`\`\`ts
|
|
2436
|
+
import { replicas } from '@replicas/sdk';
|
|
2730
2437
|
|
|
2731
|
-
|
|
2438
|
+
const { native } = await replicas.integrations.list();
|
|
2439
|
+
if (!native.sentry) throw new Error('Connect Sentry in Replicas organization integrations first.');
|
|
2732
2440
|
|
|
2733
|
-
|
|
2734
|
-
if [ -n "\${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" ]; then echo set; fi
|
|
2441
|
+
const issues = await replicas.sentry.request('/organizations/acme/issues/?query=is:unresolved');
|
|
2735
2442
|
\`\`\`
|
|
2736
2443
|
|
|
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\`)
|
|
2444
|
+
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.
|
|
2445
|
+
`;
|
|
2446
|
+
var SENTRY_ABILITY = {
|
|
2447
|
+
label: "Sentry",
|
|
2448
|
+
description: "Read Sentry projects and issues through @replicas/sdk.",
|
|
2449
|
+
bullet: "- Interacting with Sentry (reading projects, issues, and error context)",
|
|
2450
|
+
section: SECTION12,
|
|
2451
|
+
referenceFile: { name: "SENTRY.md", content: REFERENCE12 }
|
|
2452
|
+
};
|
|
2750
2453
|
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
\`\`\`
|
|
2454
|
+
// ../shared/src/default-skills/replicas-agent/abilities/slack.ts
|
|
2455
|
+
var SECTION13 = `### Slack
|
|
2456
|
+
Send messages, read threads, search conversations, and upload files through the workspace-scoped TypeScript SDK.
|
|
2755
2457
|
|
|
2756
|
-
|
|
2458
|
+
**Reference:** \`references/SLACK.md\`
|
|
2757
2459
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
"channel": "CHANNEL_ID",
|
|
2764
|
-
"text": "Your message here",
|
|
2765
|
-
"thread_ts": "OPTIONAL_THREAD_TS"
|
|
2766
|
-
}'
|
|
2767
|
-
\`\`\`
|
|
2460
|
+
Use this when:
|
|
2461
|
+
- You need to send a message to a Slack channel or thread
|
|
2462
|
+
- You need to read or search Slack conversations
|
|
2463
|
+
- You encounter a Slack message link and need its full thread`;
|
|
2464
|
+
var REFERENCE13 = `# Slack Integration
|
|
2768
2465
|
|
|
2769
|
-
|
|
2466
|
+
Use \`@replicas/sdk\`; never read or send a Slack token directly. The SDK calls Replicas, which selects only this workspace's organization connection.
|
|
2770
2467
|
|
|
2771
|
-
|
|
2468
|
+
## Check access
|
|
2772
2469
|
|
|
2773
|
-
|
|
2470
|
+
\`\`\`ts
|
|
2471
|
+
import { replicas } from '@replicas/sdk';
|
|
2774
2472
|
|
|
2775
|
-
|
|
2776
|
-
|
|
2473
|
+
const { native } = await replicas.integrations.list();
|
|
2474
|
+
if (!native.slack) throw new Error('Connect Slack in Replicas organization integrations first.');
|
|
2777
2475
|
\`\`\`
|
|
2778
2476
|
|
|
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
|
|
2477
|
+
## Read a thread
|
|
2785
2478
|
|
|
2786
|
-
|
|
2479
|
+
For a link such as \`https://team.slack.com/archives/C0123ABC/p1234567890123456\`, the channel is \`C0123ABC\` and the timestamp is \`1234567890.123456\`.
|
|
2787
2480
|
|
|
2788
|
-
\`\`\`
|
|
2789
|
-
replicas
|
|
2790
|
-
|
|
2481
|
+
\`\`\`ts
|
|
2482
|
+
const thread = await replicas.slack.call('conversations.replies', {
|
|
2483
|
+
channel: 'C0123ABC',
|
|
2484
|
+
ts: '1234567890.123456',
|
|
2485
|
+
limit: 200,
|
|
2486
|
+
});
|
|
2791
2487
|
\`\`\`
|
|
2792
2488
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
### Searching Messages
|
|
2489
|
+
## Send and attach a message
|
|
2796
2490
|
|
|
2797
|
-
\`\`\`
|
|
2798
|
-
|
|
2799
|
-
|
|
2491
|
+
\`\`\`ts
|
|
2492
|
+
const message = await replicas.slack.call<{ channel: string; ts: string }>('chat.postMessage', {
|
|
2493
|
+
channel: 'C0123ABC',
|
|
2494
|
+
text: 'Update from Replicas',
|
|
2495
|
+
});
|
|
2496
|
+
await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
|
|
2800
2497
|
\`\`\`
|
|
2801
2498
|
|
|
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")
|
|
2499
|
+
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
2500
|
|
|
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"
|
|
2501
|
+
## Search and batch
|
|
2825
2502
|
|
|
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
|
-
}"
|
|
2503
|
+
\`\`\`ts
|
|
2504
|
+
const [messages, users] = await Promise.all([
|
|
2505
|
+
replicas.slack.call('search.messages', { query: 'launch status' }),
|
|
2506
|
+
replicas.slack.call('users.list', { limit: 200 }),
|
|
2507
|
+
]);
|
|
2836
2508
|
\`\`\`
|
|
2837
2509
|
|
|
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/
|
|
2510
|
+
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
2511
|
`;
|
|
2846
2512
|
var SLACK_ABILITY = {
|
|
2847
2513
|
label: "Slack",
|
|
2848
|
-
description: "Send messages, read threads, search
|
|
2514
|
+
description: "Send messages, read threads, and search through @replicas/sdk.",
|
|
2849
2515
|
bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
|
|
2850
|
-
section:
|
|
2851
|
-
referenceFile: { name: "SLACK.md", content:
|
|
2516
|
+
section: SECTION13,
|
|
2517
|
+
referenceFile: { name: "SLACK.md", content: REFERENCE13 }
|
|
2852
2518
|
};
|
|
2853
2519
|
|
|
2854
2520
|
// ../shared/src/default-skills/replicas-agent/registry.ts
|
|
@@ -2862,7 +2528,9 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
|
|
|
2862
2528
|
learnings: LEARNINGS_ABILITY,
|
|
2863
2529
|
linear: LINEAR_ABILITY,
|
|
2864
2530
|
media: MEDIA_ABILITY,
|
|
2531
|
+
plugins: PLUGINS_ABILITY,
|
|
2865
2532
|
previews: PREVIEWS_ABILITY,
|
|
2533
|
+
sentry: SENTRY_ABILITY,
|
|
2866
2534
|
slack: SLACK_ABILITY
|
|
2867
2535
|
};
|
|
2868
2536
|
var REPLICAS_AGENT_ABILITIES = Object.keys(
|
|
@@ -5832,7 +5500,7 @@ var BaseRefreshManager = class {
|
|
|
5832
5500
|
this.health.lastErrorMessage = message;
|
|
5833
5501
|
if (attempt < 3) {
|
|
5834
5502
|
console.warn(`[${this.managerName}] Initial refresh attempt ${attempt} failed, retrying in 2s...`);
|
|
5835
|
-
await new Promise((
|
|
5503
|
+
await new Promise((resolve5) => setTimeout(resolve5, 2e3));
|
|
5836
5504
|
} else {
|
|
5837
5505
|
console.error(`[${this.managerName}] Initial refresh failed after 3 attempts:`, error);
|
|
5838
5506
|
}
|
|
@@ -6371,7 +6039,7 @@ var GitService = class {
|
|
|
6371
6039
|
}
|
|
6372
6040
|
}
|
|
6373
6041
|
readUntrackedDiff(repoPath, paths) {
|
|
6374
|
-
return new Promise((
|
|
6042
|
+
return new Promise((resolve5) => {
|
|
6375
6043
|
const child = spawn("git", ["diff", "--", ...paths], {
|
|
6376
6044
|
cwd: repoPath,
|
|
6377
6045
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6392,12 +6060,12 @@ var GitService = class {
|
|
|
6392
6060
|
});
|
|
6393
6061
|
child.stderr.on("data", () => {
|
|
6394
6062
|
});
|
|
6395
|
-
child.on("error", () =>
|
|
6063
|
+
child.on("error", () => resolve5(""));
|
|
6396
6064
|
child.on("close", (code) => {
|
|
6397
6065
|
if (code === 0 || code === 1 || truncated) {
|
|
6398
|
-
|
|
6066
|
+
resolve5(Buffer.concat(chunks).toString("utf-8"));
|
|
6399
6067
|
} else {
|
|
6400
|
-
|
|
6068
|
+
resolve5("");
|
|
6401
6069
|
}
|
|
6402
6070
|
});
|
|
6403
6071
|
});
|
|
@@ -7134,9 +6802,9 @@ var StreamWriter = class {
|
|
|
7134
6802
|
return true;
|
|
7135
6803
|
}
|
|
7136
6804
|
flush(signal) {
|
|
7137
|
-
return new Promise((
|
|
6805
|
+
return new Promise((resolve5, reject) => {
|
|
7138
6806
|
if (!this.stream) {
|
|
7139
|
-
|
|
6807
|
+
resolve5();
|
|
7140
6808
|
return;
|
|
7141
6809
|
}
|
|
7142
6810
|
if (this.flushTimer) {
|
|
@@ -7153,7 +6821,7 @@ var StreamWriter = class {
|
|
|
7153
6821
|
s.write("", (error) => {
|
|
7154
6822
|
signal?.removeEventListener("abort", onAbort);
|
|
7155
6823
|
if (error) reject(error);
|
|
7156
|
-
else
|
|
6824
|
+
else resolve5();
|
|
7157
6825
|
});
|
|
7158
6826
|
});
|
|
7159
6827
|
}
|
|
@@ -7691,7 +7359,7 @@ var ReplicasConfigService = class {
|
|
|
7691
7359
|
await this.logToFile(`[${params.repoName}] --- Running: ${params.label} ---`);
|
|
7692
7360
|
emit(`$ ${params.label}
|
|
7693
7361
|
`);
|
|
7694
|
-
return new Promise((
|
|
7362
|
+
return new Promise((resolve5) => {
|
|
7695
7363
|
const proc = spawn2("bash", ["-lc", params.command], {
|
|
7696
7364
|
cwd: params.cwd,
|
|
7697
7365
|
env: process.env,
|
|
@@ -7702,7 +7370,7 @@ var ReplicasConfigService = class {
|
|
|
7702
7370
|
const finish = (result) => {
|
|
7703
7371
|
if (settled) return;
|
|
7704
7372
|
settled = true;
|
|
7705
|
-
|
|
7373
|
+
resolve5(result);
|
|
7706
7374
|
};
|
|
7707
7375
|
const timer = setTimeout(() => {
|
|
7708
7376
|
timedOut = true;
|
|
@@ -8128,7 +7796,7 @@ async function registerDesktopPreview() {
|
|
|
8128
7796
|
if (await attemptRegistration()) {
|
|
8129
7797
|
return;
|
|
8130
7798
|
}
|
|
8131
|
-
await new Promise((
|
|
7799
|
+
await new Promise((resolve5) => setTimeout(resolve5, RETRY_DELAY_MS));
|
|
8132
7800
|
}
|
|
8133
7801
|
console.error(
|
|
8134
7802
|
`[DesktopPreview] Gave up registering port ${DESKTOP_STREAM_PORT} after ${REGISTRATION_TIMEOUT_MS}ms`
|
|
@@ -9669,8 +9337,8 @@ var PromptStream = class {
|
|
|
9669
9337
|
if (this.closed) {
|
|
9670
9338
|
return Promise.resolve({ value: void 0, done: true });
|
|
9671
9339
|
}
|
|
9672
|
-
return new Promise((
|
|
9673
|
-
this.waiters.push(
|
|
9340
|
+
return new Promise((resolve5) => {
|
|
9341
|
+
this.waiters.push(resolve5);
|
|
9674
9342
|
});
|
|
9675
9343
|
}
|
|
9676
9344
|
};
|
|
@@ -10050,14 +9718,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10050
9718
|
const requestId = randomUUID4();
|
|
10051
9719
|
const toolUseId = options.toolUseID;
|
|
10052
9720
|
const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
|
|
10053
|
-
const result = await new Promise((
|
|
9721
|
+
const result = await new Promise((resolve5) => {
|
|
10054
9722
|
this.pendingToolInputs.set(requestId, {
|
|
10055
9723
|
requestId,
|
|
10056
9724
|
toolUseId,
|
|
10057
9725
|
toolName,
|
|
10058
9726
|
input,
|
|
10059
9727
|
handler,
|
|
10060
|
-
resolve:
|
|
9728
|
+
resolve: resolve5
|
|
10061
9729
|
});
|
|
10062
9730
|
const onAbort = () => {
|
|
10063
9731
|
const pending = this.pendingToolInputs.get(requestId);
|
|
@@ -10162,7 +9830,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10162
9830
|
error
|
|
10163
9831
|
);
|
|
10164
9832
|
await this.tearDownSession();
|
|
10165
|
-
await new Promise((
|
|
9833
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
10166
9834
|
currentRequest = { ...request, message: CLAUDE_MIDTURN_CONTINUE_PROMPT, images: [] };
|
|
10167
9835
|
attempt++;
|
|
10168
9836
|
continue;
|
|
@@ -10175,7 +9843,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10175
9843
|
error
|
|
10176
9844
|
);
|
|
10177
9845
|
await this.tearDownSession();
|
|
10178
|
-
await new Promise((
|
|
9846
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
10179
9847
|
attempt++;
|
|
10180
9848
|
continue;
|
|
10181
9849
|
}
|
|
@@ -10321,8 +9989,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10321
9989
|
this.pendingInterrupt = false;
|
|
10322
9990
|
return;
|
|
10323
9991
|
}
|
|
10324
|
-
await new Promise((
|
|
10325
|
-
this.pendingTurn = { resolve:
|
|
9992
|
+
await new Promise((resolve5, reject) => {
|
|
9993
|
+
this.pendingTurn = { resolve: resolve5, reject, sawActivity: false };
|
|
10326
9994
|
promptStream.push(userMessage);
|
|
10327
9995
|
});
|
|
10328
9996
|
}
|
|
@@ -10909,13 +10577,13 @@ var AspClient = class {
|
|
|
10909
10577
|
}
|
|
10910
10578
|
const id = this.nextId;
|
|
10911
10579
|
this.nextId += 1;
|
|
10912
|
-
const promise = new Promise((
|
|
10580
|
+
const promise = new Promise((resolve5, reject) => {
|
|
10913
10581
|
const timeoutMs = opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
10914
10582
|
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
10915
10583
|
this.pending.delete(id);
|
|
10916
10584
|
reject(new Error(`ASP request timed out for ${method}`));
|
|
10917
10585
|
}, timeoutMs) : null;
|
|
10918
|
-
this.pending.set(id, { resolve:
|
|
10586
|
+
this.pending.set(id, { resolve: resolve5, reject, method, timer });
|
|
10919
10587
|
});
|
|
10920
10588
|
this.write({ method, id, params });
|
|
10921
10589
|
return promise;
|
|
@@ -11068,7 +10736,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
11068
10736
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
11069
10737
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
11070
10738
|
var codexCliVersionEnsured = null;
|
|
11071
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10739
|
+
var ENGINE_PACKAGE_VERSION = "0.1.596";
|
|
11072
10740
|
var INITIALIZE_METHOD = "initialize";
|
|
11073
10741
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
11074
10742
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -11188,13 +10856,13 @@ var AppServerProcess = class {
|
|
|
11188
10856
|
if (!child || child.killed) {
|
|
11189
10857
|
return;
|
|
11190
10858
|
}
|
|
11191
|
-
await new Promise((
|
|
10859
|
+
await new Promise((resolve5) => {
|
|
11192
10860
|
const timer = setTimeout(() => {
|
|
11193
10861
|
child.kill("SIGKILL");
|
|
11194
10862
|
}, 2e3);
|
|
11195
10863
|
child.once("exit", () => {
|
|
11196
10864
|
clearTimeout(timer);
|
|
11197
|
-
|
|
10865
|
+
resolve5();
|
|
11198
10866
|
});
|
|
11199
10867
|
child.kill("SIGTERM");
|
|
11200
10868
|
});
|
|
@@ -11239,8 +10907,8 @@ var AppServerProcess = class {
|
|
|
11239
10907
|
}
|
|
11240
10908
|
this.shuttingDown = true;
|
|
11241
10909
|
child.kill("SIGKILL");
|
|
11242
|
-
await new Promise((
|
|
11243
|
-
child.once("exit", () =>
|
|
10910
|
+
await new Promise((resolve5) => {
|
|
10911
|
+
child.once("exit", () => resolve5());
|
|
11244
10912
|
});
|
|
11245
10913
|
}
|
|
11246
10914
|
};
|
|
@@ -12520,8 +12188,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
12520
12188
|
}
|
|
12521
12189
|
async observeTurn(host, threadId, request, startTurn, options = {}) {
|
|
12522
12190
|
let resolveCompleted;
|
|
12523
|
-
const completed = new Promise((
|
|
12524
|
-
resolveCompleted =
|
|
12191
|
+
const completed = new Promise((resolve5) => {
|
|
12192
|
+
resolveCompleted = resolve5;
|
|
12525
12193
|
});
|
|
12526
12194
|
let rejectDisposed = () => {
|
|
12527
12195
|
};
|
|
@@ -13487,8 +13155,8 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
13487
13155
|
}
|
|
13488
13156
|
async processMessageInternal(request) {
|
|
13489
13157
|
let run = null;
|
|
13490
|
-
const aborted = new Promise((
|
|
13491
|
-
this.abortActiveTurn = () =>
|
|
13158
|
+
const aborted = new Promise((resolve5) => {
|
|
13159
|
+
this.abortActiveTurn = () => resolve5(TURN_ABORTED);
|
|
13492
13160
|
});
|
|
13493
13161
|
const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
|
|
13494
13162
|
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
@@ -14129,8 +13797,8 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
14129
13797
|
this.configuredModels = this.providerId === OPENCODE_GO_PROVIDER ? /* @__PURE__ */ new Set() : new Set(getConfiguredOpencodeModels(model, providerId));
|
|
14130
13798
|
const eventController = new AbortController();
|
|
14131
13799
|
this.eventAbortController = eventController;
|
|
14132
|
-
this.eventSubscriptionReady = new Promise((
|
|
14133
|
-
this.resolveEventSubscriptionReady =
|
|
13800
|
+
this.eventSubscriptionReady = new Promise((resolve5) => {
|
|
13801
|
+
this.resolveEventSubscriptionReady = resolve5;
|
|
14134
13802
|
});
|
|
14135
13803
|
this.subscribeToEvents(client, eventController).catch((error) => {
|
|
14136
13804
|
this.resolveEventSubscriptionReady?.();
|
|
@@ -14140,7 +13808,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
14140
13808
|
});
|
|
14141
13809
|
await Promise.race([
|
|
14142
13810
|
this.eventSubscriptionReady,
|
|
14143
|
-
new Promise((
|
|
13811
|
+
new Promise((resolve5) => setTimeout(resolve5, 2e3))
|
|
14144
13812
|
]);
|
|
14145
13813
|
return client;
|
|
14146
13814
|
}
|
|
@@ -15762,7 +15430,7 @@ function isReconcileCanvasItemsResponse(value) {
|
|
|
15762
15430
|
}
|
|
15763
15431
|
async function withCanvasUploadSlot(operation) {
|
|
15764
15432
|
if (activeCanvasUploads >= MAX_CONCURRENT_CANVAS_UPLOADS) {
|
|
15765
|
-
await new Promise((
|
|
15433
|
+
await new Promise((resolve5) => pendingCanvasUploads.push(resolve5));
|
|
15766
15434
|
} else {
|
|
15767
15435
|
activeCanvasUploads++;
|
|
15768
15436
|
}
|
|
@@ -15897,7 +15565,7 @@ var HISTORY_DIRS = [
|
|
|
15897
15565
|
join25(ENGINE_DIR2, "codex-histories")
|
|
15898
15566
|
];
|
|
15899
15567
|
async function putTranscript(uploadUrl, filePath, size) {
|
|
15900
|
-
await new Promise((
|
|
15568
|
+
await new Promise((resolve5, reject) => {
|
|
15901
15569
|
const url = new URL(uploadUrl);
|
|
15902
15570
|
const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(url, {
|
|
15903
15571
|
method: "PUT",
|
|
@@ -15913,7 +15581,7 @@ async function putTranscript(uploadUrl, filePath, size) {
|
|
|
15913
15581
|
});
|
|
15914
15582
|
response.on("end", () => {
|
|
15915
15583
|
const status = response.statusCode ?? 0;
|
|
15916
|
-
if (status >= 200 && status < 300)
|
|
15584
|
+
if (status >= 200 && status < 300) resolve5();
|
|
15917
15585
|
else reject(new Error(`upload failed: ${status} ${body}`));
|
|
15918
15586
|
});
|
|
15919
15587
|
response.on("error", reject);
|
|
@@ -16120,13 +15788,13 @@ function runBeforeDeadline(operation, deadline) {
|
|
|
16120
15788
|
const timeoutMs = deadline - Date.now();
|
|
16121
15789
|
if (timeoutMs <= 0) return Promise.reject(new DOMException("engine log flush deadline exceeded", "TimeoutError"));
|
|
16122
15790
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
16123
|
-
return new Promise((
|
|
15791
|
+
return new Promise((resolve5, reject) => {
|
|
16124
15792
|
const onAbort = () => reject(signal.reason);
|
|
16125
15793
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
16126
15794
|
operation(signal).then(
|
|
16127
15795
|
(value) => {
|
|
16128
15796
|
signal.removeEventListener("abort", onAbort);
|
|
16129
|
-
|
|
15797
|
+
resolve5(value);
|
|
16130
15798
|
},
|
|
16131
15799
|
(error) => {
|
|
16132
15800
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -17155,7 +16823,7 @@ function scoreMatch(query2, filePath) {
|
|
|
17155
16823
|
return 0;
|
|
17156
16824
|
}
|
|
17157
16825
|
function execGitAsync(args, cwd, timeoutMs) {
|
|
17158
|
-
return new Promise((
|
|
16826
|
+
return new Promise((resolve5, reject) => {
|
|
17159
16827
|
const child = execFile2("git", args, {
|
|
17160
16828
|
cwd,
|
|
17161
16829
|
encoding: "utf-8",
|
|
@@ -17165,7 +16833,7 @@ function execGitAsync(args, cwd, timeoutMs) {
|
|
|
17165
16833
|
if (error) {
|
|
17166
16834
|
reject(error);
|
|
17167
16835
|
} else {
|
|
17168
|
-
|
|
16836
|
+
resolve5(stdout);
|
|
17169
16837
|
}
|
|
17170
16838
|
});
|
|
17171
16839
|
child.stdin?.end();
|
|
@@ -17557,7 +17225,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17557
17225
|
let bufferExceeded = false;
|
|
17558
17226
|
params.onChunk(`$ ${params.label}
|
|
17559
17227
|
`);
|
|
17560
|
-
return new Promise((
|
|
17228
|
+
return new Promise((resolve5) => {
|
|
17561
17229
|
const proc = spawn4("bash", ["-lc", params.content], {
|
|
17562
17230
|
cwd: params.cwd,
|
|
17563
17231
|
env: process.env,
|
|
@@ -17590,7 +17258,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17590
17258
|
proc.on("close", (code) => {
|
|
17591
17259
|
clearTimeout(timer);
|
|
17592
17260
|
const output = [`$ ${params.label}`, ...outputParts].join("");
|
|
17593
|
-
|
|
17261
|
+
resolve5({
|
|
17594
17262
|
exitCode: bufferExceeded ? 1 : code ?? 1,
|
|
17595
17263
|
output,
|
|
17596
17264
|
timedOut: killed && !bufferExceeded
|
|
@@ -17600,7 +17268,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
17600
17268
|
clearTimeout(timer);
|
|
17601
17269
|
const output = [`$ ${params.label}
|
|
17602
17270
|
`, ...outputParts, err.message].join("");
|
|
17603
|
-
|
|
17271
|
+
resolve5({
|
|
17604
17272
|
exitCode: 1,
|
|
17605
17273
|
output,
|
|
17606
17274
|
timedOut: false
|
|
@@ -18873,14 +18541,30 @@ var HeartbeatService = class _HeartbeatService {
|
|
|
18873
18541
|
};
|
|
18874
18542
|
var heartbeatService = new HeartbeatService();
|
|
18875
18543
|
|
|
18544
|
+
// src/services/workspace-sdk-service.ts
|
|
18545
|
+
import { cp, mkdir as mkdir17 } from "fs/promises";
|
|
18546
|
+
import { dirname as dirname8, resolve as resolve4 } from "path";
|
|
18547
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18548
|
+
async function installWorkspaceSdk() {
|
|
18549
|
+
const source = resolve4(dirname8(fileURLToPath2(import.meta.url)), "../../workspace-sdk");
|
|
18550
|
+
const targets = [
|
|
18551
|
+
resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
|
|
18552
|
+
"/tmp/node_modules/@replicas/sdk"
|
|
18553
|
+
];
|
|
18554
|
+
await Promise.all(targets.map(async (target) => {
|
|
18555
|
+
await mkdir17(target, { recursive: true });
|
|
18556
|
+
await cp(source, target, { recursive: true, force: true });
|
|
18557
|
+
}));
|
|
18558
|
+
}
|
|
18559
|
+
|
|
18876
18560
|
// src/index.ts
|
|
18877
18561
|
var startupStartedAt = performance.now();
|
|
18878
18562
|
var startupTimings = {};
|
|
18879
18563
|
var ENGINE_INIT_GATE_TIMEOUT_MS = 35 * 60 * 1e3;
|
|
18880
18564
|
var resolveEngineReady = () => {
|
|
18881
18565
|
};
|
|
18882
|
-
var engineReadyPromise = new Promise((
|
|
18883
|
-
resolveEngineReady =
|
|
18566
|
+
var engineReadyPromise = new Promise((resolve5) => {
|
|
18567
|
+
resolveEngineReady = resolve5;
|
|
18884
18568
|
});
|
|
18885
18569
|
async function timeStartupStep(name, fn) {
|
|
18886
18570
|
const startedAt = performance.now();
|
|
@@ -18900,7 +18584,7 @@ async function waitForInitializationGate() {
|
|
|
18900
18584
|
if (Date.now() >= deadline) {
|
|
18901
18585
|
throw new Error("Timed out waiting for deferred engine initialization gate");
|
|
18902
18586
|
}
|
|
18903
|
-
await new Promise((
|
|
18587
|
+
await new Promise((resolve5) => setTimeout(resolve5, 25));
|
|
18904
18588
|
}
|
|
18905
18589
|
}
|
|
18906
18590
|
await timeStartupStep("engine_logger_initialize", () => engineLogger.initialize());
|
|
@@ -18932,11 +18616,11 @@ async function checkActiveSSHSessions() {
|
|
|
18932
18616
|
}
|
|
18933
18617
|
}
|
|
18934
18618
|
function checkDesktopReady() {
|
|
18935
|
-
return new Promise((
|
|
18619
|
+
return new Promise((resolve5) => {
|
|
18936
18620
|
const socket = connect({ host: "127.0.0.1", port: DESKTOP_STREAM_PORT });
|
|
18937
18621
|
const done = (ready) => {
|
|
18938
18622
|
socket.destroy();
|
|
18939
|
-
|
|
18623
|
+
resolve5(ready);
|
|
18940
18624
|
};
|
|
18941
18625
|
socket.once("connect", () => done(true));
|
|
18942
18626
|
socket.once("error", () => done(false));
|
|
@@ -18968,7 +18652,7 @@ app.get("/health", async (c) => {
|
|
|
18968
18652
|
if (!engineReady && waitMs > 0) {
|
|
18969
18653
|
await Promise.race([
|
|
18970
18654
|
engineReadyPromise,
|
|
18971
|
-
new Promise((
|
|
18655
|
+
new Promise((resolve5) => setTimeout(resolve5, waitMs))
|
|
18972
18656
|
]);
|
|
18973
18657
|
}
|
|
18974
18658
|
c.header(ENGINE_HEALTH_WAIT_HEADER, "1");
|
|
@@ -19168,6 +18852,7 @@ serve(
|
|
|
19168
18852
|
console.log(`Replicas Engine running on port ${info.port}`);
|
|
19169
18853
|
}
|
|
19170
18854
|
await timeStartupStep("wait_for_initialization_gate", waitForInitializationGate);
|
|
18855
|
+
await timeStartupStep("workspace_sdk_install", installWorkspaceSdk);
|
|
19171
18856
|
if (!IS_WARMING_MODE) {
|
|
19172
18857
|
await timeStartupStep("replicas_config_initialize", () => replicasConfigService.initialize());
|
|
19173
18858
|
}
|