@agent-relay/github-primitive 4.0.9
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/DESIGN.md +1055 -0
- package/README.md +151 -0
- package/docs/actions.md +49 -0
- package/examples/end-to-end-pr-workflow.ts +286 -0
- package/examples/github-client.ts +38 -0
- package/examples/github-step.ts +105 -0
- package/examples/multi-tenant-pr-workflow.ts +201 -0
- package/package.json +50 -0
- package/templates/repository-inspection.yaml +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# @agent-relay/github-primitive
|
|
2
|
+
|
|
3
|
+
GitHub workflow primitive for Agent Relay. It exposes a typed client and a
|
|
4
|
+
workflow integration step that can run through the local `gh` CLI or a cloud
|
|
5
|
+
GitHub proxy.
|
|
6
|
+
|
|
7
|
+
## Runtime Selection
|
|
8
|
+
|
|
9
|
+
The primitive supports three runtime modes:
|
|
10
|
+
|
|
11
|
+
- `auto`: prefer cloud when Nango or relay-cloud credentials are present,
|
|
12
|
+
otherwise use the local `gh` CLI when available.
|
|
13
|
+
- `local`: use `gh api` from the current machine.
|
|
14
|
+
- `cloud`: use Nango first, then relay-cloud proxy when configured.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { GitHubClient } from '@agent-relay/github-primitive';
|
|
18
|
+
|
|
19
|
+
const github = await GitHubClient.create({
|
|
20
|
+
runtime: 'auto',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const repo = await github.getRepo('AgentWorkforce', 'relay');
|
|
24
|
+
console.log(repo.defaultBranch);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Actions
|
|
28
|
+
|
|
29
|
+
The client and workflow step support:
|
|
30
|
+
|
|
31
|
+
- Repositories: `listRepos`, `getRepo`
|
|
32
|
+
- Issues: `listIssues`, `createIssue`, `updateIssue`, `closeIssue`
|
|
33
|
+
- Pull requests: `listPRs`, `getPR`, `createPR`, `updatePR`, `mergePR`
|
|
34
|
+
- Files: `listFiles`, `readFile`, `createFile`, `updateFile`, `deleteFile`
|
|
35
|
+
- Branches and commits: `listBranches`, `createBranch`, `listCommits`, `createCommit`
|
|
36
|
+
- Identity: `getUser`, `listOrganizations`
|
|
37
|
+
|
|
38
|
+
## Workflow Step
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createGitHubStep } from '@agent-relay/github-primitive/workflow-step';
|
|
42
|
+
|
|
43
|
+
createGitHubStep({
|
|
44
|
+
name: 'read-readme',
|
|
45
|
+
action: 'readFile',
|
|
46
|
+
repo: 'AgentWorkforce/relay',
|
|
47
|
+
params: {
|
|
48
|
+
path: 'README.md',
|
|
49
|
+
},
|
|
50
|
+
output: {
|
|
51
|
+
mode: 'data',
|
|
52
|
+
format: 'text',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
See `examples/github-step.ts` for a workflow runner example and
|
|
58
|
+
`examples/github-client.ts` for a standalone client example.
|
|
59
|
+
|
|
60
|
+
## End-to-end PR workflow
|
|
61
|
+
|
|
62
|
+
`examples/end-to-end-pr-workflow.ts` walks the full PR lifecycle against
|
|
63
|
+
a single runtime:
|
|
64
|
+
|
|
65
|
+
1. `getRepo` — inspect + log the selected runtime.
|
|
66
|
+
2. `createBranch` off the base branch.
|
|
67
|
+
3. `createFile` — write a marker file on the new branch.
|
|
68
|
+
4. `createPR` — the core integration step cloud workflows migrate to.
|
|
69
|
+
5. `getPR` — round-trip verify.
|
|
70
|
+
6. `updatePR` — edit the PR body without code changes.
|
|
71
|
+
7. `listPRs` — confirm indexability.
|
|
72
|
+
8. (commented) `mergePR` — off by default so the example never merges
|
|
73
|
+
anything real.
|
|
74
|
+
|
|
75
|
+
Run against a scratch repo:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
GITHUB_REPO=AgentWorkforce/scratch npx tsx \
|
|
79
|
+
packages/github-primitive/examples/end-to-end-pr-workflow.ts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Runtime selection is automatic:
|
|
83
|
+
|
|
84
|
+
| Path | Triggered when |
|
|
85
|
+
| ----------- | ----------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| Local `gh` | `gh auth status` succeeds and no cloud creds are set |
|
|
87
|
+
| Nango | `NANGO_SECRET_KEY` + `NANGO_GITHUB_CONNECTION_ID` + `NANGO_GITHUB_PROVIDER_CONFIG_KEY` present |
|
|
88
|
+
| relay-cloud | `RELAY_CLOUD_API_URL` + `RELAY_CLOUD_API_TOKEN` + `WORKSPACE_ID` present (fallback when Nango absent) |
|
|
89
|
+
|
|
90
|
+
## Multi-tenant routing
|
|
91
|
+
|
|
92
|
+
In cloud, every workspace has its own GitHub App install — one Nango
|
|
93
|
+
connection per tenant. `createGitHubStep` accepts a per-step `config`
|
|
94
|
+
field so a single workflow can route different actions through
|
|
95
|
+
different connections. One workflow, many tenants.
|
|
96
|
+
|
|
97
|
+
`examples/multi-tenant-pr-workflow.ts` demonstrates this: it opens PRs
|
|
98
|
+
in `AgentWorkforce/cloud` (via the AgentWorkforce app) AND in an
|
|
99
|
+
MSD-owned repo (via MSD's app), from the same workflow definition, by
|
|
100
|
+
varying the `config:` field on each step.
|
|
101
|
+
|
|
102
|
+
### Cloud adoption: the resolver helper
|
|
103
|
+
|
|
104
|
+
The primitive stays tenant-unaware — it takes a `GitHubRuntimeConfig`
|
|
105
|
+
and does what it's told. The tenant lookup lives in cloud, in a small
|
|
106
|
+
helper:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// cloud/packages/web/lib/github/connection-resolver.ts
|
|
110
|
+
import type { GitHubRuntimeConfig } from '@agent-relay/github-primitive';
|
|
111
|
+
|
|
112
|
+
export async function githubConfigForRepo(opts: {
|
|
113
|
+
repo: string; // "owner/repo"
|
|
114
|
+
workspaceId?: string; // optional — cloud-owned repos default
|
|
115
|
+
}): Promise<GitHubRuntimeConfig> {
|
|
116
|
+
const connection = await resolveWorkspaceIntegration(opts.workspaceId, 'github', opts.repo);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
runtime: 'auto',
|
|
120
|
+
nango: {
|
|
121
|
+
connectionId: connection.connectionId,
|
|
122
|
+
providerConfigKey: connection.providerConfigKey, // 'github-agentworkforce' | 'github-msd' | 'github-nightcto' | ...
|
|
123
|
+
secretKey: process.env.NANGO_SECRET_KEY,
|
|
124
|
+
},
|
|
125
|
+
relayCloud: {
|
|
126
|
+
apiUrl: process.env.CLOUD_API_URL,
|
|
127
|
+
accessToken: process.env.CLOUD_API_TOKEN,
|
|
128
|
+
workspaceId: opts.workspaceId,
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Workflow authors in cloud then write:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
createGitHubStep({
|
|
138
|
+
name: 'open-pr',
|
|
139
|
+
action: 'createPR',
|
|
140
|
+
repo: 'AgentWorkforce/cloud',
|
|
141
|
+
params: { title, head, base, body },
|
|
142
|
+
config: await githubConfigForRepo({
|
|
143
|
+
repo: 'AgentWorkforce/cloud',
|
|
144
|
+
workspaceId: process.env.RELAY_WORKSPACE_ID,
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
One resolver, one call-site shape. Adding a new GitHub App install is a
|
|
150
|
+
`workspace_integrations` row + (optionally) a `NANGO_*` secret — no
|
|
151
|
+
code change in the workflows that create PRs.
|
package/docs/actions.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# GitHub Primitive Actions
|
|
2
|
+
|
|
3
|
+
All actions use the same adapter request layer, so local and cloud runtimes have
|
|
4
|
+
the same behavior after runtime selection.
|
|
5
|
+
|
|
6
|
+
## Repository
|
|
7
|
+
|
|
8
|
+
- `listRepos`: lists repositories visible to the authenticated user.
|
|
9
|
+
- `getRepo`: fetches a repository by owner and name.
|
|
10
|
+
|
|
11
|
+
## Issues
|
|
12
|
+
|
|
13
|
+
- `listIssues`: lists true issues and filters out pull request entries returned
|
|
14
|
+
by the GitHub issues endpoint.
|
|
15
|
+
- `createIssue`: creates an issue.
|
|
16
|
+
- `updateIssue`: updates title, body, state, assignee, or labels.
|
|
17
|
+
- `closeIssue`: closes an issue with the same PATCH path used by
|
|
18
|
+
`updateIssue`.
|
|
19
|
+
|
|
20
|
+
## Pull Requests
|
|
21
|
+
|
|
22
|
+
- `listPRs`: lists pull requests.
|
|
23
|
+
- `getPR`: fetches one pull request.
|
|
24
|
+
- `createPR`: creates a pull request.
|
|
25
|
+
- `updatePR`: updates title, body, state, base branch, or maintainer edit access.
|
|
26
|
+
- `mergePR`: merges a pull request and then fetches the refreshed pull request.
|
|
27
|
+
|
|
28
|
+
## Files
|
|
29
|
+
|
|
30
|
+
- `listFiles`: lists directory contents, or returns a single file as a one-item
|
|
31
|
+
list.
|
|
32
|
+
- `readFile`: decodes base64 file content from the GitHub contents API.
|
|
33
|
+
- `createFile`: creates a file with a commit message.
|
|
34
|
+
- `updateFile`: updates a file by SHA with a commit message.
|
|
35
|
+
- `deleteFile`: deletes a file by SHA with a commit message.
|
|
36
|
+
|
|
37
|
+
## Branches And Commits
|
|
38
|
+
|
|
39
|
+
- `listBranches`: lists repository branches.
|
|
40
|
+
- `createBranch`: creates a branch from `fromBranch` or the repository default
|
|
41
|
+
branch.
|
|
42
|
+
- `listCommits`: lists commits with optional filters.
|
|
43
|
+
- `createCommit`: creates a Git commit object without moving refs.
|
|
44
|
+
|
|
45
|
+
## Identity
|
|
46
|
+
|
|
47
|
+
- `getUser`: fetches the authenticated user, or a public user by username.
|
|
48
|
+
- `listOrganizations`: lists organizations for the authenticated user, or for a
|
|
49
|
+
public user.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end pull-request workflow using the GitHub primitive.
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates the full PR lifecycle from a single workflow definition:
|
|
5
|
+
* 1. Detect + log which runtime will be used (local gh vs Nango vs
|
|
6
|
+
* relay-cloud proxy).
|
|
7
|
+
* 2. createBranch — branch off the default branch for the change.
|
|
8
|
+
* 3. createFile — write a timestamped marker file on the new branch.
|
|
9
|
+
* 4. createPR — open the pull request. This is the core interface the
|
|
10
|
+
* cloud workflows need to swap to.
|
|
11
|
+
* 5. getPR — round-trip verify.
|
|
12
|
+
* 6. updatePR — add a description edit (shows mutation).
|
|
13
|
+
* 7. listPRs — confirm it appears in the default filter.
|
|
14
|
+
* 8. (commented out) mergePR — the last mile. Left off by default so
|
|
15
|
+
* running this example does NOT merge anything against real repos.
|
|
16
|
+
*
|
|
17
|
+
* The same file runs identically in three environments because the
|
|
18
|
+
* primitive's runtime selection handles the transport:
|
|
19
|
+
*
|
|
20
|
+
* - LOCAL gh CLI (needs `gh auth status` to succeed)
|
|
21
|
+
* - CLOUD (tenant) Nango — NANGO_GITHUB_CONNECTION_ID +
|
|
22
|
+
* NANGO_GITHUB_PROVIDER_CONFIG_KEY +
|
|
23
|
+
* NANGO_SECRET_KEY
|
|
24
|
+
* - CLOUD (fallback) relay-cloud proxy — RELAY_CLOUD_API_URL +
|
|
25
|
+
* RELAY_CLOUD_API_TOKEN + WORKSPACE_ID
|
|
26
|
+
*
|
|
27
|
+
* Run:
|
|
28
|
+
* GITHUB_REPO=AgentWorkforce/scratch npx tsx examples/end-to-end-pr-workflow.ts
|
|
29
|
+
*
|
|
30
|
+
* Defaults to AgentWorkforce/scratch (a sandbox repo) so no one
|
|
31
|
+
* accidentally opens a PR against a real repo. Override via env vars.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { WorkflowRunner, type RelayYamlConfig } from '@agent-relay/sdk/workflows';
|
|
35
|
+
|
|
36
|
+
import { GitHubClient } from '../src/client.js';
|
|
37
|
+
import { GitHubStepExecutor, createGitHubStep } from '../src/workflow-step.js';
|
|
38
|
+
import type { GitHubRuntimeConfig } from '../src/types.js';
|
|
39
|
+
|
|
40
|
+
const repo = process.env.GITHUB_REPO ?? 'AgentWorkforce/scratch';
|
|
41
|
+
const baseBranch = process.env.GITHUB_BASE_BRANCH ?? 'main';
|
|
42
|
+
const branchName = process.env.GITHUB_BRANCH_OVERRIDE ?? `examples/github-primitive-${Date.now()}`;
|
|
43
|
+
const markerPath = `examples/github-primitive-runs/${Date.now()}.md`;
|
|
44
|
+
|
|
45
|
+
const githubConfig: GitHubRuntimeConfig = {
|
|
46
|
+
// `auto` prefers cloud credentials when present, otherwise falls back
|
|
47
|
+
// to the local `gh` CLI. Override with GITHUB_RUNTIME=local|cloud when
|
|
48
|
+
// you want to pin a specific path.
|
|
49
|
+
runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
|
|
50
|
+
|
|
51
|
+
// Cloud path A — Nango. Per-tenant GitHub App installation. Cloud
|
|
52
|
+
// callers typically resolve this via a connection-resolver helper
|
|
53
|
+
// that maps { workspaceId, repo } -> { connectionId, providerConfigKey }.
|
|
54
|
+
nango: {
|
|
55
|
+
connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
|
|
56
|
+
providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
|
|
57
|
+
secretKey: process.env.NANGO_SECRET_KEY,
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Cloud path B — relay-cloud GitHub proxy. Used when Nango isn't
|
|
61
|
+
// wired but a relay-cloud bearer token is available.
|
|
62
|
+
relayCloud: {
|
|
63
|
+
apiUrl: process.env.RELAY_CLOUD_API_URL,
|
|
64
|
+
accessToken: process.env.RELAY_CLOUD_API_TOKEN,
|
|
65
|
+
workspaceId: process.env.WORKSPACE_ID,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const githubExecutor = new GitHubStepExecutor(githubConfig);
|
|
70
|
+
|
|
71
|
+
const config: RelayYamlConfig = {
|
|
72
|
+
version: '1.0',
|
|
73
|
+
name: 'end-to-end-pr-workflow',
|
|
74
|
+
description:
|
|
75
|
+
'Walk through the full PR lifecycle — branch, commit, open, update, list — using the GitHub primitive.',
|
|
76
|
+
swarm: { pattern: 'pipeline' },
|
|
77
|
+
agents: [],
|
|
78
|
+
workflows: [
|
|
79
|
+
{
|
|
80
|
+
name: 'end-to-end-pr-workflow',
|
|
81
|
+
steps: [
|
|
82
|
+
// 1. Resolve the default branch — sanity check the connection
|
|
83
|
+
// works before we start making mutations.
|
|
84
|
+
createGitHubStep({
|
|
85
|
+
name: 'inspect-repo',
|
|
86
|
+
action: 'getRepo',
|
|
87
|
+
repo,
|
|
88
|
+
output: {
|
|
89
|
+
mode: 'summary',
|
|
90
|
+
includeRuntime: true,
|
|
91
|
+
pretty: true,
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
|
|
95
|
+
// 2. Create the feature branch off the base branch's HEAD.
|
|
96
|
+
// Chains {{steps.inspect-repo.output.data.defaultBranch}}
|
|
97
|
+
// when no base override is provided — here we keep it
|
|
98
|
+
// explicit for readability.
|
|
99
|
+
createGitHubStep({
|
|
100
|
+
name: 'create-branch',
|
|
101
|
+
dependsOn: ['inspect-repo'],
|
|
102
|
+
action: 'createBranch',
|
|
103
|
+
repo,
|
|
104
|
+
params: {
|
|
105
|
+
branch: branchName,
|
|
106
|
+
source: baseBranch,
|
|
107
|
+
},
|
|
108
|
+
output: { mode: 'data', format: 'json', path: 'ref' },
|
|
109
|
+
}),
|
|
110
|
+
|
|
111
|
+
// 3. Write a marker file on the new branch. createFile handles
|
|
112
|
+
// the blob + tree + commit dance for you.
|
|
113
|
+
createGitHubStep({
|
|
114
|
+
name: 'write-marker-file',
|
|
115
|
+
dependsOn: ['create-branch'],
|
|
116
|
+
action: 'createFile',
|
|
117
|
+
repo,
|
|
118
|
+
params: {
|
|
119
|
+
path: markerPath,
|
|
120
|
+
branch: branchName,
|
|
121
|
+
content: [
|
|
122
|
+
'# GitHub primitive example run',
|
|
123
|
+
'',
|
|
124
|
+
`- Runtime chosen: see workflow log for inspect-repo detection`,
|
|
125
|
+
`- Generated: ${new Date().toISOString()}`,
|
|
126
|
+
'',
|
|
127
|
+
'This file is created by',
|
|
128
|
+
'`packages/github-primitive/examples/end-to-end-pr-workflow.ts`',
|
|
129
|
+
'to prove the full PR lifecycle works against the configured runtime.',
|
|
130
|
+
].join('\n'),
|
|
131
|
+
message: `examples: github-primitive demo run ${new Date().toISOString()}`,
|
|
132
|
+
},
|
|
133
|
+
output: { mode: 'data', format: 'json', path: 'commit.sha' },
|
|
134
|
+
}),
|
|
135
|
+
|
|
136
|
+
// 4. Open the pull request. This is the core step cloud
|
|
137
|
+
// workflows need. title/body/head/base mirror the REST API
|
|
138
|
+
// shape — no translation work at the call site.
|
|
139
|
+
createGitHubStep({
|
|
140
|
+
name: 'open-pr',
|
|
141
|
+
dependsOn: ['write-marker-file'],
|
|
142
|
+
action: 'createPR',
|
|
143
|
+
repo,
|
|
144
|
+
params: {
|
|
145
|
+
title: `examples: github-primitive end-to-end demo (${branchName})`,
|
|
146
|
+
head: branchName,
|
|
147
|
+
base: baseBranch,
|
|
148
|
+
body: [
|
|
149
|
+
'## Summary',
|
|
150
|
+
'',
|
|
151
|
+
'Automated PR opened by',
|
|
152
|
+
'`packages/github-primitive/examples/end-to-end-pr-workflow.ts`',
|
|
153
|
+
'to exercise the GitHub primitive interface end-to-end.',
|
|
154
|
+
'',
|
|
155
|
+
'## Runtime selection',
|
|
156
|
+
'',
|
|
157
|
+
'See the workflow log for the `inspect-repo` step — it logs the',
|
|
158
|
+
'selected runtime (`local`, `nango`, or `relay-cloud`).',
|
|
159
|
+
'',
|
|
160
|
+
'## Safe to close',
|
|
161
|
+
'',
|
|
162
|
+
'This PR is a demonstration. No one should merge it — close it',
|
|
163
|
+
'once you have inspected the end-to-end round-trip.',
|
|
164
|
+
].join('\n'),
|
|
165
|
+
draft: true,
|
|
166
|
+
},
|
|
167
|
+
output: {
|
|
168
|
+
mode: 'data',
|
|
169
|
+
format: 'json',
|
|
170
|
+
includeRuntime: true,
|
|
171
|
+
includeMetadata: true,
|
|
172
|
+
pretty: true,
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
|
|
176
|
+
// 5. Read the PR back to prove the resolver + runtime actually
|
|
177
|
+
// persisted the change, and to surface the PR number for
|
|
178
|
+
// downstream steps.
|
|
179
|
+
createGitHubStep({
|
|
180
|
+
name: 'verify-pr',
|
|
181
|
+
dependsOn: ['open-pr'],
|
|
182
|
+
action: 'getPR',
|
|
183
|
+
repo,
|
|
184
|
+
params: {
|
|
185
|
+
// The output of `open-pr` is the Pulls REST response; we pull
|
|
186
|
+
// the number off it for subsequent mutation.
|
|
187
|
+
number: '{{steps.open-pr.output.data.number}}',
|
|
188
|
+
},
|
|
189
|
+
output: {
|
|
190
|
+
mode: 'summary',
|
|
191
|
+
includeRuntime: true,
|
|
192
|
+
pretty: true,
|
|
193
|
+
},
|
|
194
|
+
}),
|
|
195
|
+
|
|
196
|
+
// 6. Update the PR — shows how to use updatePR for edits that
|
|
197
|
+
// don't need code changes (body, title, draft state, etc.).
|
|
198
|
+
createGitHubStep({
|
|
199
|
+
name: 'edit-pr-body',
|
|
200
|
+
dependsOn: ['verify-pr'],
|
|
201
|
+
action: 'updatePR',
|
|
202
|
+
repo,
|
|
203
|
+
params: {
|
|
204
|
+
number: '{{steps.open-pr.output.data.number}}',
|
|
205
|
+
body: [
|
|
206
|
+
'## Summary',
|
|
207
|
+
'',
|
|
208
|
+
'Automated PR opened by the GitHub primitive end-to-end',
|
|
209
|
+
'example. This body was updated by the `edit-pr-body` step',
|
|
210
|
+
'to demonstrate `updatePR` works through the same adapter.',
|
|
211
|
+
'',
|
|
212
|
+
'## Safe to close',
|
|
213
|
+
'',
|
|
214
|
+
'Demo — close, do not merge.',
|
|
215
|
+
].join('\n'),
|
|
216
|
+
},
|
|
217
|
+
output: { mode: 'result', format: 'json' },
|
|
218
|
+
}),
|
|
219
|
+
|
|
220
|
+
// 7. List open PRs to prove the new one is indexable.
|
|
221
|
+
createGitHubStep({
|
|
222
|
+
name: 'list-open-prs',
|
|
223
|
+
dependsOn: ['edit-pr-body'],
|
|
224
|
+
action: 'listPRs',
|
|
225
|
+
repo,
|
|
226
|
+
params: {
|
|
227
|
+
state: 'open',
|
|
228
|
+
perPage: 10,
|
|
229
|
+
},
|
|
230
|
+
output: { mode: 'summary', pretty: true },
|
|
231
|
+
}),
|
|
232
|
+
|
|
233
|
+
// 8. (Commented) The last mile — merge. Intentionally left off:
|
|
234
|
+
// running this example against a real repo should not merge
|
|
235
|
+
// anything. Uncomment when exercising a disposable scratch
|
|
236
|
+
// repo or CI harness.
|
|
237
|
+
//
|
|
238
|
+
// createGitHubStep({
|
|
239
|
+
// name: 'merge-pr',
|
|
240
|
+
// dependsOn: ['list-open-prs'],
|
|
241
|
+
// action: 'mergePR',
|
|
242
|
+
// repo,
|
|
243
|
+
// params: {
|
|
244
|
+
// number: '{{steps.open-pr.output.data.number}}',
|
|
245
|
+
// mergeMethod: 'squash',
|
|
246
|
+
// commitTitle: 'examples: github-primitive demo (squash)',
|
|
247
|
+
// },
|
|
248
|
+
// output: { mode: 'data', format: 'json' },
|
|
249
|
+
// }),
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
errorHandling: { strategy: 'fail-fast' },
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
async function main(): Promise<void> {
|
|
257
|
+
const detection = await GitHubClient.detect(githubConfig);
|
|
258
|
+
|
|
259
|
+
console.log('────────────────────────────────────────');
|
|
260
|
+
console.log(`repo: ${repo}`);
|
|
261
|
+
console.log(`base branch: ${baseBranch}`);
|
|
262
|
+
console.log(`feature branch: ${branchName}`);
|
|
263
|
+
console.log(`runtime selected: ${detection.runtime}`);
|
|
264
|
+
console.log(`detection source: ${detection.source}`);
|
|
265
|
+
console.log(`local gh available: ${detection.local.available}`);
|
|
266
|
+
console.log(`cloud available: ${detection.cloud.available}`);
|
|
267
|
+
if (detection.reason) {
|
|
268
|
+
console.log(`reason: ${detection.reason}`);
|
|
269
|
+
}
|
|
270
|
+
console.log('────────────────────────────────────────');
|
|
271
|
+
|
|
272
|
+
const runner = new WorkflowRunner({
|
|
273
|
+
cwd: process.cwd(),
|
|
274
|
+
executor: githubExecutor,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const result = await runner.execute(config);
|
|
278
|
+
console.log(`\nWorkflow completed: ${result.status}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
282
|
+
main().catch((error) => {
|
|
283
|
+
console.error(error instanceof Error ? error.stack : error);
|
|
284
|
+
process.exitCode = 1;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { GitHubClient } from '../src/client.js';
|
|
2
|
+
import type { GitHubRuntimeConfig } from '../src/types.js';
|
|
3
|
+
|
|
4
|
+
const [owner = 'AgentWorkforce', repo = 'relay'] = (process.env.GITHUB_REPO || 'AgentWorkforce/relay').split(
|
|
5
|
+
'/'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const githubConfig: GitHubRuntimeConfig = {
|
|
9
|
+
runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
|
|
10
|
+
nango: {
|
|
11
|
+
connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
|
|
12
|
+
providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
|
|
13
|
+
},
|
|
14
|
+
relayCloud: {
|
|
15
|
+
apiUrl: process.env.RELAY_CLOUD_API_URL,
|
|
16
|
+
accessToken: process.env.RELAY_CLOUD_API_TOKEN,
|
|
17
|
+
workspaceId: process.env.WORKSPACE_ID,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function main(): Promise<void> {
|
|
22
|
+
const github = await GitHubClient.create(githubConfig);
|
|
23
|
+
const runtime = await github.getRuntime();
|
|
24
|
+
const repository = await github.getRepo(owner, repo);
|
|
25
|
+
const branches = await github.listBranches(owner, repo);
|
|
26
|
+
|
|
27
|
+
console.log(`Runtime: ${runtime}`);
|
|
28
|
+
console.log(`Repository: ${repository.fullName}`);
|
|
29
|
+
console.log(`Default branch: ${repository.defaultBranch}`);
|
|
30
|
+
console.log(`Branches: ${branches.map((branch) => branch.name).join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
34
|
+
main().catch((error) => {
|
|
35
|
+
console.error(error instanceof Error ? error.stack : error);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { WorkflowRunner, type RelayYamlConfig } from '@agent-relay/sdk/workflows';
|
|
2
|
+
|
|
3
|
+
import { GitHubClient } from '../src/client.js';
|
|
4
|
+
import { GitHubStepExecutor, createGitHubStep } from '../src/workflow-step.js';
|
|
5
|
+
import type { GitHubRuntimeConfig } from '../src/types.js';
|
|
6
|
+
|
|
7
|
+
const repo = process.env.GITHUB_REPO ?? 'AgentWorkforce/relay';
|
|
8
|
+
|
|
9
|
+
const githubConfig: GitHubRuntimeConfig = {
|
|
10
|
+
runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
|
|
11
|
+
nango: {
|
|
12
|
+
connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
|
|
13
|
+
providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
|
|
14
|
+
},
|
|
15
|
+
relayCloud: {
|
|
16
|
+
apiUrl: process.env.RELAY_CLOUD_API_URL,
|
|
17
|
+
accessToken: process.env.RELAY_CLOUD_API_TOKEN,
|
|
18
|
+
workspaceId: process.env.WORKSPACE_ID,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const githubExecutor = new GitHubStepExecutor(githubConfig);
|
|
23
|
+
|
|
24
|
+
const config: RelayYamlConfig = {
|
|
25
|
+
version: '1.0',
|
|
26
|
+
name: 'github-primitive-workflow',
|
|
27
|
+
description: 'GitHub primitive workflow with runtime auto-detection and chained output.',
|
|
28
|
+
swarm: {
|
|
29
|
+
pattern: 'pipeline',
|
|
30
|
+
},
|
|
31
|
+
agents: [],
|
|
32
|
+
workflows: [
|
|
33
|
+
{
|
|
34
|
+
name: 'github-primitive-workflow',
|
|
35
|
+
steps: [
|
|
36
|
+
createGitHubStep({
|
|
37
|
+
name: 'inspect-repository',
|
|
38
|
+
action: 'getRepo',
|
|
39
|
+
repo,
|
|
40
|
+
output: {
|
|
41
|
+
mode: 'summary',
|
|
42
|
+
includeRuntime: true,
|
|
43
|
+
pretty: true,
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
createGitHubStep({
|
|
47
|
+
name: 'list-open-issues',
|
|
48
|
+
dependsOn: ['inspect-repository'],
|
|
49
|
+
action: 'listIssues',
|
|
50
|
+
repo,
|
|
51
|
+
params: {
|
|
52
|
+
state: 'open',
|
|
53
|
+
perPage: 5,
|
|
54
|
+
},
|
|
55
|
+
output: {
|
|
56
|
+
mode: 'summary',
|
|
57
|
+
includeRuntime: true,
|
|
58
|
+
pretty: true,
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
createGitHubStep({
|
|
62
|
+
name: 'read-readme',
|
|
63
|
+
dependsOn: ['list-open-issues'],
|
|
64
|
+
action: 'readFile',
|
|
65
|
+
repo,
|
|
66
|
+
params: {
|
|
67
|
+
path: 'README.md',
|
|
68
|
+
},
|
|
69
|
+
output: {
|
|
70
|
+
mode: 'data',
|
|
71
|
+
format: 'text',
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
errorHandling: {
|
|
78
|
+
strategy: 'fail-fast',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
async function main(): Promise<void> {
|
|
83
|
+
const detection = await GitHubClient.detect(githubConfig);
|
|
84
|
+
|
|
85
|
+
console.log(`GitHub runtime selected: ${detection.runtime}`);
|
|
86
|
+
console.log(`Detection source: ${detection.source}`);
|
|
87
|
+
console.log(`Local gh CLI: ${detection.local.available ? 'available' : 'unavailable'}`);
|
|
88
|
+
console.log(`Cloud GitHub: ${detection.cloud.available ? 'available' : 'unavailable'}`);
|
|
89
|
+
console.log(detection.reason);
|
|
90
|
+
|
|
91
|
+
const runner = new WorkflowRunner({
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
executor: githubExecutor,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const result = await runner.execute(config);
|
|
97
|
+
console.log(`GitHub workflow completed: ${result.status}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
101
|
+
main().catch((error) => {
|
|
102
|
+
console.error(error instanceof Error ? error.stack : error);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
});
|
|
105
|
+
}
|