agents-relay 1.0.0
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/.github/workflows/publish.yml +91 -0
- package/AGENTS.md +16 -0
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/adapters.js +311 -0
- package/dist/cli.js +455 -0
- package/dist/continuation.js +21 -0
- package/dist/dashboard.js +446 -0
- package/dist/events.js +36 -0
- package/dist/github-auth.js +34 -0
- package/dist/github-webhook.js +47 -0
- package/dist/markers.js +42 -0
- package/dist/planner.js +172 -0
- package/dist/pool.js +98 -0
- package/dist/reconciler.js +434 -0
- package/dist/registry.js +27 -0
- package/dist/relayd.js +177 -0
- package/dist/scheduler.js +49 -0
- package/dist/store.js +586 -0
- package/dist/types.js +6 -0
- package/dist/usage.js +370 -0
- package/dist/workspace.js +76 -0
- package/docs/agent-network.md +34 -0
- package/docs/architecture.md +120 -0
- package/docs/autonomous-objective-jobs.md +121 -0
- package/docs/example.md +30 -0
- package/docs/github-app-rate-limit.md +124 -0
- package/docs/service.md +43 -0
- package/pack.json +326 -0
- package/package.json +14 -0
- package/scripts/npm-version.mjs +11 -0
- package/skills/agents-relay/SKILL.md +77 -0
- package/skills/agents-relay/agents/planner.agent.md +28 -0
- package/src/adapters.ts +231 -0
- package/src/cli.ts +324 -0
- package/src/continuation.ts +6 -0
- package/src/dashboard.ts +421 -0
- package/src/events.ts +25 -0
- package/src/github-auth.ts +35 -0
- package/src/github-webhook.ts +37 -0
- package/src/markers.ts +33 -0
- package/src/planner.ts +150 -0
- package/src/pool.ts +87 -0
- package/src/reconciler.ts +235 -0
- package/src/registry.ts +35 -0
- package/src/relayd.ts +137 -0
- package/src/scheduler.ts +27 -0
- package/src/store.ts +526 -0
- package/src/types.ts +45 -0
- package/src/usage.ts +385 -0
- package/src/workspace.ts +62 -0
- package/test/adapters.test.js +303 -0
- package/test/autonomous.test.js +119 -0
- package/test/core.test.js +363 -0
- package/test/dashboard.test.js +178 -0
- package/test/github-auth.test.js +51 -0
- package/test/github-webhook.test.js +21 -0
- package/test/service.test.js +116 -0
- package/test/store.test.js +390 -0
- package/test/usage.test.js +88 -0
- package/test/workspace.test.js +95 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
import { marker, JOB_MARKER } from '../dist/markers.js';
|
|
9
|
+
import { aggregateManagedGitHubJobs } from '../dist/pool.js';
|
|
10
|
+
import { discoverGitRepositories, discoverWorkspaceRepositories, githubRepositoryFromRemote } from '../dist/workspace.js';
|
|
11
|
+
|
|
12
|
+
const exec = promisify(execFile);
|
|
13
|
+
const now = () => new Date().toISOString();
|
|
14
|
+
const job = (id, repository, prNumber) => ({ id, title: id, prNumber, repository, state: 'OPEN', continuation: null, createdAt: now(), updatedAt: now(), tasks: undefined });
|
|
15
|
+
|
|
16
|
+
test('workspace discovery finds nested git checkouts and GitHub origins', async () => {
|
|
17
|
+
const root = await mkdtemp(join(tmpdir(), 'agents-relay-workspace-'));
|
|
18
|
+
try {
|
|
19
|
+
const repo = join(root, 'neo');
|
|
20
|
+
const nested = join(root, 'group', 'agents-relay');
|
|
21
|
+
await mkdir(nested, { recursive: true });
|
|
22
|
+
for (const [path, remote] of [[repo, 'git@github.com:lalalic/neo.git'], [nested, 'https://github.com/lalalic/agents-relay.git']]) {
|
|
23
|
+
await exec('git', ['init', '-q', path]);
|
|
24
|
+
await exec('git', ['-C', path, 'remote', 'add', 'origin', remote]);
|
|
25
|
+
}
|
|
26
|
+
assert.deepEqual(await discoverGitRepositories(root), [nested, repo]);
|
|
27
|
+
assert.deepEqual(await discoverWorkspaceRepositories(root), [{ path: nested, repository: 'lalalic/agents-relay' }, { path: repo, repository: 'lalalic/neo' }]);
|
|
28
|
+
} finally { await rm(root, { recursive: true, force: true }); }
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('workspace repository parsing ignores non-GitHub remotes', () => {
|
|
32
|
+
assert.equal(githubRepositoryFromRemote('git@gitlab.com:someone/project.git'), null);
|
|
33
|
+
assert.equal(githubRepositoryFromRemote('ssh://git@github.com/lalalic/neo.git'), 'lalalic/neo');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('managed job aggregation spans repositories', async () => {
|
|
37
|
+
const jobs = { 'lalalic/neo': job('neo-job', 'lalalic/neo', 20), 'lalalic/agents-relay': job('relay-job', 'lalalic/agents-relay', 28) };
|
|
38
|
+
const client = { request: async path => { throw new Error(`unexpected REST ${path}`); }, graphql: async (_query, variables={}) => {
|
|
39
|
+
const repository = `${variables.owner}/${variables.name}`;
|
|
40
|
+
const root = jobs[repository];
|
|
41
|
+
if (!root) throw new Error(`unexpected ${repository}`);
|
|
42
|
+
return {repository:{open:{nodes:[{number:root.prNumber,body:'',state:'OPEN',isDraft:false,merged:false,mergedAt:null,closedAt:null,comments:{nodes:[{author:{login:'bot'},body:marker(JOB_MARKER,root)}]}}],pageInfo:{hasNextPage:false,endCursor:null}},recentClosed:{nodes:[]}}};
|
|
43
|
+
} };
|
|
44
|
+
const result = await aggregateManagedGitHubJobs(client, ['lalalic/neo', 'lalalic/agents-relay'], new Set(['bot']));
|
|
45
|
+
assert.deepEqual(result.map(item => item.job.id).sort(), ['neo-job', 'relay-job']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
test('workspace discovery deduplicates multiple checkouts of the same GitHub repository', async () => {
|
|
50
|
+
const root = await mkdtemp(join(tmpdir(), 'agents-relay-workspace-dedupe-'));
|
|
51
|
+
try {
|
|
52
|
+
const primary = join(root, 'neo');
|
|
53
|
+
const worktree = join(root, '.worktrees', 'neo-feature');
|
|
54
|
+
const backup = join(root, '.backup', 'neo-old');
|
|
55
|
+
for (const path of [primary, worktree, backup]) {
|
|
56
|
+
await mkdir(path, { recursive: true });
|
|
57
|
+
await exec('git', ['init', '-q', path]);
|
|
58
|
+
await exec('git', ['-C', path, 'remote', 'add', 'origin', 'git@github.com:lalalic/neo.git']);
|
|
59
|
+
}
|
|
60
|
+
assert.deepEqual(await discoverWorkspaceRepositories(root), [{ path: primary, repository: 'lalalic/neo' }]);
|
|
61
|
+
} finally { await rm(root, { recursive: true, force: true }); }
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('managed job aggregation deduplicates duplicate repository inputs', async () => {
|
|
65
|
+
let pullLists = 0;
|
|
66
|
+
const rootJob = job('relay-job', 'lalalic/agents-relay', 28);
|
|
67
|
+
const client = { request: async path => { throw new Error(`unexpected REST ${path}`); }, graphql: async () => {
|
|
68
|
+
pullLists += 1;
|
|
69
|
+
return {repository:{open:{nodes:[{number:28,body:'',state:'OPEN',isDraft:false,merged:false,mergedAt:null,closedAt:null,comments:{nodes:[{author:{login:'bot'},body:marker(JOB_MARKER,rootJob)}]}}],pageInfo:{hasNextPage:false,endCursor:null}},recentClosed:{nodes:[]}}};
|
|
70
|
+
} };
|
|
71
|
+
const result = await aggregateManagedGitHubJobs(client, ['lalalic/agents-relay', 'lalalic/agents-relay'], new Set(['bot']));
|
|
72
|
+
assert.equal(pullLists, 1);
|
|
73
|
+
assert.deepEqual(result.map(item => item.job.id), ['relay-job']);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
test('managed job discovery paginates all open PRs but bounds closed history', async () => {
|
|
78
|
+
const openA = job('open-a', 'lalalic/agents-relay', 30);
|
|
79
|
+
const openB = job('open-b', 'lalalic/agents-relay', 29);
|
|
80
|
+
const closed = { ...job('closed', 'lalalic/agents-relay', 28), state: 'COMPLETED' };
|
|
81
|
+
let calls = 0;
|
|
82
|
+
const node = (root, state='OPEN') => ({number:root.prNumber,body:'',state,isDraft:false,merged:state==='CLOSED',mergedAt:state==='CLOSED'?now():null,closedAt:state==='CLOSED'?now():null,comments:{nodes:[{author:{login:'bot'},body:marker(JOB_MARKER,root)}]}});
|
|
83
|
+
const client = { request: async path => { throw new Error(`unexpected REST ${path}`); }, graphql: async (query, variables={}) => {
|
|
84
|
+
calls += 1;
|
|
85
|
+
if (!variables.cursor) {
|
|
86
|
+
assert.match(query, /recentClosed:pullRequests\(first:20,states:CLOSED/);
|
|
87
|
+
return {repository:{open:{nodes:[node(openA)],pageInfo:{hasNextPage:true,endCursor:'next'}},recentClosed:{nodes:[node(closed,'CLOSED')]}}};
|
|
88
|
+
}
|
|
89
|
+
assert.doesNotMatch(query, /recentClosed:/);
|
|
90
|
+
return {repository:{open:{nodes:[node(openB)],pageInfo:{hasNextPage:false,endCursor:null}}}};
|
|
91
|
+
} };
|
|
92
|
+
const result = await aggregateManagedGitHubJobs(client, ['lalalic/agents-relay'], new Set(['bot']));
|
|
93
|
+
assert.equal(calls, 2);
|
|
94
|
+
assert.deepEqual(result.map(item => item.job.id), ['open-a', 'open-b', 'closed']);
|
|
95
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "dist", "rootDir": "src", "strict": true, "noImplicitAny": true, "esModuleInterop": true, "skipLibCheck": true, "lib": ["ES2022", "DOM"] },
|
|
3
|
+
"include": ["src/**/*.ts"]
|
|
4
|
+
}
|