@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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-tenant pull-request workflow.
|
|
3
|
+
*
|
|
4
|
+
* Cloud's reality: every workspace (AgentWorkforce, MSD, NightCTO, ...)
|
|
5
|
+
* has its own GitHub App installation. The primitive's per-step `config`
|
|
6
|
+
* field lets one workflow route different actions through different
|
|
7
|
+
* Nango connections — no need for one workflow per tenant.
|
|
8
|
+
*
|
|
9
|
+
* The usual cloud pattern:
|
|
10
|
+
*
|
|
11
|
+
* 1. A resolver helper — lives in cloud, NOT in this primitive — maps
|
|
12
|
+
* { workspaceId, repo } -> { connectionId, providerConfigKey }.
|
|
13
|
+
* Recommended signature:
|
|
14
|
+
*
|
|
15
|
+
* githubConfigForRepo({ repo, workspaceId }): Promise<GitHubRuntimeConfig>
|
|
16
|
+
*
|
|
17
|
+
* It reads the workspace_integrations table, picks the row whose
|
|
18
|
+
* provider matches the target repo's app, and returns a ready-to-
|
|
19
|
+
* use config object.
|
|
20
|
+
*
|
|
21
|
+
* 2. Workflow authors call that resolver at step-build time and pass
|
|
22
|
+
* the result as `config` to each `createGitHubStep` call.
|
|
23
|
+
*
|
|
24
|
+
* This example simulates the resolver with a static table so the
|
|
25
|
+
* illustration is self-contained — in production, swap it for the DB
|
|
26
|
+
* lookup.
|
|
27
|
+
*
|
|
28
|
+
* Run:
|
|
29
|
+
* NANGO_SECRET_KEY=... \
|
|
30
|
+
* AGENTWORKFORCE_CONNECTION_ID=... \
|
|
31
|
+
* MSD_CONNECTION_ID=... \
|
|
32
|
+
* npx tsx examples/multi-tenant-pr-workflow.ts
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { WorkflowRunner, type RelayYamlConfig } from '@agent-relay/sdk/workflows';
|
|
36
|
+
|
|
37
|
+
import { GitHubStepExecutor, createGitHubStep } from '../src/workflow-step.js';
|
|
38
|
+
import type { GitHubRuntimeConfig, RepositoryRef } from '../src/types.js';
|
|
39
|
+
|
|
40
|
+
// ─── Resolver (stand-in for cloud's real implementation) ───────────────
|
|
41
|
+
|
|
42
|
+
interface TenantConnection {
|
|
43
|
+
workspaceId: string;
|
|
44
|
+
providerConfigKey: string; // 'github-agentworkforce' | 'github-msd' | 'github-nightcto'
|
|
45
|
+
connectionIdEnvVar: string; // env var that carries the Nango connection id
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// In cloud, this table is the workspace_integrations DB rows joined to
|
|
49
|
+
// the Nango provider registry. Here we keep it inline for illustration.
|
|
50
|
+
const TENANTS: Record<string, TenantConnection> = {
|
|
51
|
+
'AgentWorkforce/cloud': {
|
|
52
|
+
workspaceId: 'rw_agentworkforce',
|
|
53
|
+
providerConfigKey: 'github-agentworkforce',
|
|
54
|
+
connectionIdEnvVar: 'AGENTWORKFORCE_CONNECTION_ID',
|
|
55
|
+
},
|
|
56
|
+
'AgentWorkforce/sage': {
|
|
57
|
+
workspaceId: 'rw_agentworkforce',
|
|
58
|
+
providerConfigKey: 'github-agentworkforce',
|
|
59
|
+
connectionIdEnvVar: 'AGENTWORKFORCE_CONNECTION_ID',
|
|
60
|
+
},
|
|
61
|
+
'msd-ventures/platform': {
|
|
62
|
+
workspaceId: 'rw_msd',
|
|
63
|
+
providerConfigKey: 'github-msd',
|
|
64
|
+
connectionIdEnvVar: 'MSD_CONNECTION_ID',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function githubConfigForRepo(opts: {
|
|
69
|
+
repo: string | RepositoryRef;
|
|
70
|
+
/** Workspace scope. Optional — cloud-owned repos default to the shared app. */
|
|
71
|
+
workspaceId?: string;
|
|
72
|
+
}): GitHubRuntimeConfig {
|
|
73
|
+
const repoKey = typeof opts.repo === 'string' ? opts.repo : `${opts.repo.owner}/${opts.repo.repo}`;
|
|
74
|
+
const tenant = TENANTS[repoKey];
|
|
75
|
+
|
|
76
|
+
if (!tenant) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`No GitHub connection mapped for ${repoKey} — register it in the tenants table or workspace_integrations.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const connectionId = process.env[tenant.connectionIdEnvVar];
|
|
83
|
+
if (!connectionId) {
|
|
84
|
+
throw new Error(`Missing ${tenant.connectionIdEnvVar} — set the Nango connection id for ${repoKey}.`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
runtime: 'auto',
|
|
89
|
+
nango: {
|
|
90
|
+
connectionId,
|
|
91
|
+
providerConfigKey: tenant.providerConfigKey,
|
|
92
|
+
secretKey: process.env.NANGO_SECRET_KEY,
|
|
93
|
+
},
|
|
94
|
+
relayCloud: {
|
|
95
|
+
apiUrl: process.env.RELAY_CLOUD_API_URL,
|
|
96
|
+
accessToken: process.env.RELAY_CLOUD_API_TOKEN,
|
|
97
|
+
workspaceId: opts.workspaceId ?? tenant.workspaceId,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Workflow ────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
const agentworkforceRepo = 'AgentWorkforce/cloud';
|
|
105
|
+
const msdRepo = 'msd-ventures/platform';
|
|
106
|
+
|
|
107
|
+
const executor = new GitHubStepExecutor({ runtime: 'auto' });
|
|
108
|
+
|
|
109
|
+
const config: RelayYamlConfig = {
|
|
110
|
+
version: '1.0',
|
|
111
|
+
name: 'multi-tenant-pr-workflow',
|
|
112
|
+
description:
|
|
113
|
+
'Open PRs in two tenants — AgentWorkforce/cloud (shared app) and msd-ventures/platform (MSD app) — from one workflow by varying per-step config.',
|
|
114
|
+
swarm: { pattern: 'pipeline' },
|
|
115
|
+
agents: [],
|
|
116
|
+
workflows: [
|
|
117
|
+
{
|
|
118
|
+
name: 'multi-tenant-pr-workflow',
|
|
119
|
+
steps: [
|
|
120
|
+
// ─── Tenant A: AgentWorkforce ───────────────────────────────
|
|
121
|
+
createGitHubStep({
|
|
122
|
+
name: 'inspect-agentworkforce-cloud',
|
|
123
|
+
action: 'getRepo',
|
|
124
|
+
repo: agentworkforceRepo,
|
|
125
|
+
config: githubConfigForRepo({ repo: agentworkforceRepo }),
|
|
126
|
+
output: { mode: 'summary', includeRuntime: true, pretty: true },
|
|
127
|
+
}),
|
|
128
|
+
|
|
129
|
+
createGitHubStep({
|
|
130
|
+
name: 'open-pr-agentworkforce',
|
|
131
|
+
dependsOn: ['inspect-agentworkforce-cloud'],
|
|
132
|
+
action: 'createPR',
|
|
133
|
+
repo: agentworkforceRepo,
|
|
134
|
+
params: {
|
|
135
|
+
// Pretend we prepared this branch in an earlier workflow step
|
|
136
|
+
// (push-branch in the caller workflow).
|
|
137
|
+
head: 'feat/typed-webhook-consumers',
|
|
138
|
+
base: 'main',
|
|
139
|
+
title: 'feat(web): typed webhook-consumer config',
|
|
140
|
+
body: "Routes through AgentWorkforce's github-agentworkforce Nango connection.",
|
|
141
|
+
draft: true,
|
|
142
|
+
},
|
|
143
|
+
config: githubConfigForRepo({ repo: agentworkforceRepo }),
|
|
144
|
+
output: { mode: 'data', format: 'json', path: 'data.html_url' },
|
|
145
|
+
}),
|
|
146
|
+
|
|
147
|
+
// ─── Tenant B: MSD ──────────────────────────────────────────
|
|
148
|
+
// Same workflow, same action verbs, different connection
|
|
149
|
+
// resolved by the per-step `config` field. Runs sequentially
|
|
150
|
+
// here but could run in parallel — tenants are independent.
|
|
151
|
+
createGitHubStep({
|
|
152
|
+
name: 'inspect-msd-platform',
|
|
153
|
+
dependsOn: ['open-pr-agentworkforce'],
|
|
154
|
+
action: 'getRepo',
|
|
155
|
+
repo: msdRepo,
|
|
156
|
+
config: githubConfigForRepo({ repo: msdRepo }),
|
|
157
|
+
output: { mode: 'summary', includeRuntime: true, pretty: true },
|
|
158
|
+
}),
|
|
159
|
+
|
|
160
|
+
createGitHubStep({
|
|
161
|
+
name: 'open-pr-msd',
|
|
162
|
+
dependsOn: ['inspect-msd-platform'],
|
|
163
|
+
action: 'createPR',
|
|
164
|
+
repo: msdRepo,
|
|
165
|
+
params: {
|
|
166
|
+
head: 'integrations/agent-relay-webhook',
|
|
167
|
+
base: 'main',
|
|
168
|
+
title: 'feat: wire up Agent Relay webhook receiver',
|
|
169
|
+
body: "Routes through MSD's github-msd Nango connection — separate GitHub App install.",
|
|
170
|
+
draft: true,
|
|
171
|
+
},
|
|
172
|
+
config: githubConfigForRepo({ repo: msdRepo }),
|
|
173
|
+
output: { mode: 'data', format: 'json', path: 'data.html_url' },
|
|
174
|
+
}),
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
errorHandling: { strategy: 'fail-fast' },
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
async function main(): Promise<void> {
|
|
182
|
+
console.log('Opening PRs in two tenants via per-step GitHub config overrides:');
|
|
183
|
+
console.log(` ${agentworkforceRepo} → connection ${TENANTS[agentworkforceRepo].providerConfigKey}`);
|
|
184
|
+
console.log(` ${msdRepo} → connection ${TENANTS[msdRepo].providerConfigKey}`);
|
|
185
|
+
console.log();
|
|
186
|
+
|
|
187
|
+
const runner = new WorkflowRunner({
|
|
188
|
+
cwd: process.cwd(),
|
|
189
|
+
executor,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const result = await runner.execute(config);
|
|
193
|
+
console.log(`\nWorkflow completed: ${result.status}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
197
|
+
main().catch((error) => {
|
|
198
|
+
console.error(error instanceof Error ? error.stack : error);
|
|
199
|
+
process.exitCode = 1;
|
|
200
|
+
});
|
|
201
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-relay/github-primitive",
|
|
3
|
+
"version": "4.0.9",
|
|
4
|
+
"description": "GitHub workflow primitive for Agent Relay",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./workflow-step": {
|
|
15
|
+
"types": "./dist/workflow-step.d.ts",
|
|
16
|
+
"import": "./dist/workflow-step.js",
|
|
17
|
+
"default": "./dist/workflow-step.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"examples",
|
|
23
|
+
"docs",
|
|
24
|
+
"templates",
|
|
25
|
+
"DESIGN.md",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@agent-relay/sdk": "4.0.9"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.19.3",
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
|
+
"vitest": "^3.2.4"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/AgentWorkforce/relay.git",
|
|
48
|
+
"directory": "packages/github-primitive"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: '1.0'
|
|
2
|
+
name: github-repository-inspection
|
|
3
|
+
description: Inspect a GitHub repository and read its README.
|
|
4
|
+
swarm:
|
|
5
|
+
pattern: pipeline
|
|
6
|
+
agents: []
|
|
7
|
+
workflows:
|
|
8
|
+
- name: inspect
|
|
9
|
+
steps:
|
|
10
|
+
- name: inspect-repository
|
|
11
|
+
type: integration
|
|
12
|
+
integration: github
|
|
13
|
+
action: getRepo
|
|
14
|
+
params:
|
|
15
|
+
repo: AgentWorkforce/relay
|
|
16
|
+
output: '{"mode":"summary","includeRuntime":true,"pretty":true}'
|
|
17
|
+
- name: read-readme
|
|
18
|
+
type: integration
|
|
19
|
+
integration: github
|
|
20
|
+
action: readFile
|
|
21
|
+
dependsOn:
|
|
22
|
+
- inspect-repository
|
|
23
|
+
params:
|
|
24
|
+
repo: AgentWorkforce/relay
|
|
25
|
+
path: README.md
|
|
26
|
+
output: '{"mode":"data","format":"text"}'
|
|
27
|
+
errorHandling:
|
|
28
|
+
strategy: fail-fast
|