agents-relay 1.0.49 → 1.0.50
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/README.md +8 -6
- package/dist/cli.js +124 -81
- package/dist/store.js +20 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npx agents-relay submit --repo OWNER/REPO --pr 12 --id job-1 \
|
|
|
20
20
|
--input "Implement the described child task and publish a terminal event."
|
|
21
21
|
|
|
22
22
|
npx agents-relay reconcile --repo OWNER/REPO --pr 12 --id job-1
|
|
23
|
-
npx agents-relay
|
|
23
|
+
npx agents-relay job show --repo OWNER/REPO --pr 12 --id job-1
|
|
24
24
|
~~~
|
|
25
25
|
|
|
26
26
|
Mutable queued/retryable task fields can be changed in place without replacing the durable task identity. `task update` rejects `RUNNING`, `SUCCEEDED`, and `CANCELLED` tasks so active execution and terminal history cannot be rewritten. For example:
|
|
@@ -57,19 +57,21 @@ operational contract.
|
|
|
57
57
|
|
|
58
58
|
job create first resolves an existing open PR with the same head/base, then creates it only when needed. Re-running it is idempotent: it reuses the PR and maintains exactly one trusted agents-relay:job:v1 marker. Job creation is always create-only. Task execution options belong to `submit`, and the orchestrator may submit zero, one, or multiple initial tasks according to user intent.
|
|
59
59
|
|
|
60
|
-
To
|
|
60
|
+
To create a Job on an existing unmanaged PR, use the same `job create` action with `--pr` instead of `--head`:
|
|
61
61
|
|
|
62
62
|
~~~sh
|
|
63
|
-
npx agents-relay job
|
|
63
|
+
npx agents-relay job create --repo OWNER/REPO --pr 6 --id job-6 --title "Existing objective"
|
|
64
64
|
~~~
|
|
65
65
|
|
|
66
|
-
Use job
|
|
66
|
+
Use `job update` for partial mutable metadata changes on an already healthy Job marker/binding:
|
|
67
67
|
|
|
68
68
|
~~~sh
|
|
69
|
-
npx agents-relay job
|
|
69
|
+
npx agents-relay job update --repo OWNER/REPO --pr 6 --id job-6 --priority P0 --no-auto-merge
|
|
70
70
|
~~~
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
`job update` validates the trusted Job marker first. Missing, malformed, duplicated, mismatched, or conflicting bindings fail with a precise diagnostic and are not repaired implicitly. The orchestrator handles exceptional repair explicitly according to the Job contract, then retries the update. `job show` reads one Job and `job list` lists managed Jobs in a repository.
|
|
73
|
+
|
|
74
|
+
Managed GitHub submit/reconcile/retry/cancel/serve commands also require a durable Job marker. Orchestrators do not need raw GitHub commands for normal Job creation or metadata updates: Agents Relay uses its authenticated GitHub client internally.
|
|
73
75
|
|
|
74
76
|
The original init command remains supported for backward compatibility:
|
|
75
77
|
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ import { extendRetryBudget, parseJobSchedule } from './scheduler.js';
|
|
|
19
19
|
import { dueBuckets, processDueScheduledJobs, syncScheduleLabels } from './scheduled-jobs.js';
|
|
20
20
|
import { resolveExecutionContext } from './execution-context.js';
|
|
21
21
|
import { defaultWorkspaceRoot, resolveManagedProjectPath } from './workspace.js';
|
|
22
|
-
const usage = 'agents-relay <job|task|context|init|submit|record|agent-register|agent-discover|
|
|
22
|
+
const usage = 'agents-relay <job|task|context|init|submit|record|agent-register|agent-discover|reconcile|tick|retry|cancel|serve> [options]';
|
|
23
23
|
const help = {
|
|
24
24
|
root: `Agents Relay coordinates durable asynchronous agent jobs through GitHub pull requests.
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ Usage:
|
|
|
27
27
|
npx agents-relay <command> [options]
|
|
28
28
|
|
|
29
29
|
Commands:
|
|
30
|
-
job create|
|
|
30
|
+
job create|update|show|list Manage durable GitHub-backed jobs
|
|
31
31
|
task update Update mutable durable task fields in place
|
|
32
32
|
context resolve Resolve shared project instructions, skills, agents, and runtimes
|
|
33
33
|
init Create a local demo job or initialize a GitHub job
|
|
@@ -35,7 +35,6 @@ Commands:
|
|
|
35
35
|
record Backfill a completed task into durable history
|
|
36
36
|
agent-register Register an agent role and its capabilities
|
|
37
37
|
agent-discover Find registered agents matching hard filters
|
|
38
|
-
status Show the durable job state
|
|
39
38
|
reconcile Run reconciliation once
|
|
40
39
|
tick Run the indexed autonomous scheduler heartbeat
|
|
41
40
|
retry|cancel Change a task state
|
|
@@ -43,52 +42,53 @@ Commands:
|
|
|
43
42
|
|
|
44
43
|
Run 'npx agents-relay <command> --help' for command options and examples.
|
|
45
44
|
Use 'node dist/cli.js' only for explicit local development/demo mode.`,
|
|
46
|
-
job: `Manage
|
|
45
|
+
job: `Manage GitHub-backed durable jobs.
|
|
47
46
|
|
|
48
47
|
Usage:
|
|
49
|
-
npx agents-relay job <create|
|
|
48
|
+
npx agents-relay job <create|update|show|list> [options]
|
|
50
49
|
|
|
51
50
|
Actions:
|
|
52
|
-
create
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
create Create a durable Job, optionally binding an existing unmanaged PR
|
|
52
|
+
update Partially update mutable metadata on a healthy existing Job
|
|
53
|
+
show Show one durable Job
|
|
54
|
+
list List managed Jobs in a repository
|
|
55
55
|
|
|
56
56
|
Common options:
|
|
57
|
-
--repo OWNER/REPO GitHub repository
|
|
58
|
-
--id JOB_ID
|
|
59
|
-
--pr NUMBER
|
|
60
|
-
--title TEXT Job title
|
|
61
|
-
--objective TEXT Objective for autonomous mode
|
|
62
|
-
--mode fixed|autonomous Execution mode (default: fixed)
|
|
63
|
-
--schedule CRON Autonomous schedule; minute resolution is 10 minutes
|
|
64
|
-
--schedule-timezone TZ IANA timezone for --schedule (default: UTC)
|
|
65
|
-
--schedule-next-run ISO Optional first run (must match the cron expression)
|
|
66
|
-
--missed-run-policy run|skip Run each missed slot or skip to the next future slot
|
|
67
|
-
--schedule-concurrency skip|queue Skip or retain the slot while work is active
|
|
57
|
+
--repo OWNER/REPO GitHub repository
|
|
58
|
+
--id JOB_ID Durable job identifier
|
|
59
|
+
--pr NUMBER Existing PR number
|
|
68
60
|
|
|
69
61
|
Example:
|
|
70
|
-
npx agents-relay job
|
|
71
|
-
'job create': `Create
|
|
62
|
+
npx agents-relay job show --repo OWNER/REPO --pr 12 --id job-1`,
|
|
63
|
+
'job create': `Create a durable Job and its trusted marker.
|
|
72
64
|
|
|
73
|
-
Required: --repo OWNER/REPO, --id JOB_ID, and --head BRANCH.
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
Required: --repo OWNER/REPO, --id JOB_ID, and either --head BRANCH or --pr NUMBER.
|
|
66
|
+
With --head, reuse/create the PR for that head. With --pr, bind an existing unmanaged PR.
|
|
67
|
+
Common: --base BRANCH (default: main), --title TEXT, --body TEXT, --objective TEXT, --priority P0|P1|P2|P3, --mode fixed|autonomous, --no-auto-merge, --no-review.
|
|
68
|
+
Job creation never creates executable tasks. The orchestrator decides the initial task batch separately. Re-running create against an already healthy Job is idempotent and does not mutate its metadata.
|
|
76
69
|
|
|
77
70
|
Example:
|
|
78
71
|
npx agents-relay job create --repo OWNER/REPO --head feat/example --base main --id job-1 --title "Objective"`,
|
|
79
|
-
'job
|
|
72
|
+
'job update': `Partially update mutable metadata on a healthy existing Job.
|
|
80
73
|
|
|
81
74
|
Required: --repo OWNER/REPO, --pr NUMBER, and --id JOB_ID.
|
|
82
|
-
|
|
75
|
+
Mutable: --title, --objective, --priority, --mode fixed|autonomous, --schedule, --auto-merge|--no-auto-merge, --review|--no-review, and continuation options.
|
|
76
|
+
Unspecified fields are preserved. Marker/binding corruption is never repaired implicitly; update fails with a precise diagnostic instead.
|
|
83
77
|
|
|
84
78
|
Example:
|
|
85
|
-
npx agents-relay job
|
|
86
|
-
'job
|
|
79
|
+
npx agents-relay job update --repo OWNER/REPO --pr 12 --id job-1 --priority P0 --no-auto-merge`,
|
|
80
|
+
'job show': `Show one durable Job.
|
|
87
81
|
|
|
88
82
|
Required: --repo OWNER/REPO, --pr NUMBER, and --id JOB_ID.
|
|
89
83
|
|
|
90
84
|
Example:
|
|
91
|
-
npx agents-relay job
|
|
85
|
+
npx agents-relay job show --repo OWNER/REPO --pr 12 --id job-1`,
|
|
86
|
+
'job list': `List managed Jobs in a repository.
|
|
87
|
+
|
|
88
|
+
Required: --repo OWNER/REPO.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
npx agents-relay job list --repo OWNER/REPO`,
|
|
92
92
|
task: `Manage a durable task.
|
|
93
93
|
|
|
94
94
|
Usage:
|
|
@@ -133,12 +133,6 @@ Common: --task-id ID, --commit SHA, --project NAME.
|
|
|
133
133
|
|
|
134
134
|
Example:
|
|
135
135
|
npx agents-relay record --file .agents-relay.json --summary "Built release" --commit abc123`,
|
|
136
|
-
status: `Show the durable state for a job.
|
|
137
|
-
|
|
138
|
-
Required: --file PATH for local demo mode, or --repo OWNER/REPO --pr NUMBER --id JOB_ID.
|
|
139
|
-
|
|
140
|
-
Example:
|
|
141
|
-
npx agents-relay status --repo OWNER/REPO --pr 12 --id job-1`,
|
|
142
136
|
reconcile: `Reconcile a job once, launching ready tasks and persisting results.
|
|
143
137
|
|
|
144
138
|
Required: --file PATH for local demo mode, or --repo OWNER/REPO --pr NUMBER --id JOB_ID.
|
|
@@ -287,34 +281,36 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
|
|
|
287
281
|
throw new Error('--id is required for managed GitHub jobs');
|
|
288
282
|
const pr = await resolvePullRequest(client, repositoryName, { prNumber: options.prNumber, head: options.head, base: options.base, title: options.title, body: options.body });
|
|
289
283
|
const store = new GitHubStore(client, repositoryName, pr.number, trustedAuthors);
|
|
290
|
-
let job;
|
|
291
284
|
try {
|
|
292
|
-
|
|
285
|
+
const existing = await store.load(options.id);
|
|
286
|
+
if (existing.schedule)
|
|
287
|
+
await syncScheduleLabels(client, repositoryName, existing);
|
|
288
|
+
return { job: existing, pr };
|
|
293
289
|
}
|
|
294
290
|
catch (error) {
|
|
295
|
-
if (!(error instanceof Error) || !error.message.
|
|
291
|
+
if (!(error instanceof Error) || !error.message.startsWith('JOB_MARKER_MISSING:'))
|
|
296
292
|
throw error;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
293
|
+
}
|
|
294
|
+
const now = new Date().toISOString();
|
|
295
|
+
const job = {
|
|
296
|
+
id: options.id,
|
|
297
|
+
title: options.title || pr.title || 'Agents Relay job',
|
|
298
|
+
objective: options.objective || pr.body || undefined,
|
|
299
|
+
description: pr.body || undefined,
|
|
300
|
+
priority: options.priority ?? 'P2',
|
|
301
|
+
executionMode: options.executionMode ?? 'fixed',
|
|
302
|
+
autoMerge: options.autoMerge ?? true,
|
|
303
|
+
reviewEnabled: options.reviewEnabled ?? true,
|
|
304
|
+
prNumber: pr.number,
|
|
305
|
+
repository: repositoryName,
|
|
306
|
+
state: 'OPEN',
|
|
307
|
+
continuation: options.continuation ?? null,
|
|
308
|
+
createdAt: now,
|
|
309
|
+
updatedAt: now,
|
|
310
|
+
tasks: []
|
|
311
|
+
};
|
|
306
312
|
if (options.schedule)
|
|
307
313
|
job.schedule = options.schedule;
|
|
308
|
-
if (options.autoMerge !== undefined)
|
|
309
|
-
job.autoMerge = options.autoMerge;
|
|
310
|
-
if (options.reviewEnabled !== undefined)
|
|
311
|
-
job.reviewEnabled = options.reviewEnabled;
|
|
312
|
-
if (options.priority)
|
|
313
|
-
job.priority = options.priority;
|
|
314
|
-
else if (!job.priority)
|
|
315
|
-
job.priority = 'P2';
|
|
316
|
-
if (!job.title && pr.title)
|
|
317
|
-
job.title = pr.title;
|
|
318
314
|
await store.saveJob(job);
|
|
319
315
|
if (job.schedule)
|
|
320
316
|
await syncScheduleLabels(client, repositoryName, job);
|
|
@@ -474,43 +470,90 @@ async function runContextCommand(action, args) {
|
|
|
474
470
|
console.log(JSON.stringify(context, null, 2));
|
|
475
471
|
}
|
|
476
472
|
async function runJobCommand(action, args) {
|
|
477
|
-
if (!['create', '
|
|
473
|
+
if (!['create', 'update', 'show', 'list'].includes(action))
|
|
478
474
|
throw new Error(usage);
|
|
479
475
|
const repo = repository(args);
|
|
480
|
-
const id = arg(args, '--id');
|
|
481
|
-
if (!id)
|
|
482
|
-
throw new Error('--id is required for managed GitHub jobs');
|
|
483
476
|
if (action === 'create') {
|
|
484
477
|
const taskOnlyFlags = ['--start', '--task-id', '--input', '--adapter', '--provider', '--model', '--profile', '--reasoning', '--story-size', '--project', '--agent', '--parent', '--deps', '--capabilities', '--attachments', '--max-attempts', '--timeout', '--cwd', '--chatgpt-project', '--chatgpt-tab-close-policy', '--output', '--output-path'];
|
|
485
478
|
const invalid = taskOnlyFlags.find(flag => hasArg(args, flag));
|
|
486
479
|
if (invalid)
|
|
487
|
-
throw new Error(`job create does not accept task execution option ${invalid}; create the job first, then let the orchestrator
|
|
480
|
+
throw new Error(`job create does not accept task execution option ${invalid}; create the job first, then let the orchestrator create the initial plan/task batch`);
|
|
488
481
|
}
|
|
489
482
|
const auth = await githubAuthContext(args);
|
|
490
|
-
|
|
491
|
-
|
|
483
|
+
if (action === 'list') {
|
|
484
|
+
console.log(JSON.stringify(await listManagedGitHubJobs(auth.client, repo, auth.trustedAuthors), null, 2));
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const id = arg(args, '--id');
|
|
488
|
+
if (!id)
|
|
489
|
+
throw new Error('--id is required for managed GitHub jobs');
|
|
492
490
|
const prValue = arg(args, '--pr');
|
|
493
491
|
const prNumber = prValue ? Number(prValue) : undefined;
|
|
494
492
|
if (prValue && (!Number.isInteger(prNumber) || (prNumber ?? 0) <= 0))
|
|
495
493
|
throw new Error('--pr must be a positive integer');
|
|
496
|
-
if (
|
|
494
|
+
if (action === 'create') {
|
|
495
|
+
if (!prNumber && !arg(args, '--head'))
|
|
496
|
+
throw new Error('job create requires either --head or --pr');
|
|
497
|
+
const result = await ensureManagedGitHubJob(auth.client, repo, auth.trustedAuthors, {
|
|
498
|
+
id,
|
|
499
|
+
title: arg(args, '--title') || undefined,
|
|
500
|
+
objective: arg(args, '--objective') || undefined,
|
|
501
|
+
priority: priority(args),
|
|
502
|
+
executionMode: requestedExecutionMode(args),
|
|
503
|
+
schedule: scheduleFromArgs(args),
|
|
504
|
+
autoMerge: !hasArg(args, '--no-auto-merge'),
|
|
505
|
+
reviewEnabled: !hasArg(args, '--no-review'),
|
|
506
|
+
continuation: continuation(args),
|
|
507
|
+
prNumber,
|
|
508
|
+
head: arg(args, '--head') || undefined,
|
|
509
|
+
base: arg(args, '--base', 'main'),
|
|
510
|
+
body: arg(args, '--body') || undefined
|
|
511
|
+
});
|
|
512
|
+
console.log(JSON.stringify({ repository: repo, pr: result.pr.number, job: result.job.id, executionMode: result.job.executionMode, state: result.job.state }, null, 2));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (!prNumber)
|
|
497
516
|
throw new Error(`job ${action} requires --pr`);
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
517
|
+
const store = new GitHubStore(auth.client, repo, prNumber, auth.trustedAuthors);
|
|
518
|
+
const job = await store.load(id);
|
|
519
|
+
if (action === 'show') {
|
|
520
|
+
console.log(JSON.stringify(job, null, 2));
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
await store.validateJobBinding(id);
|
|
524
|
+
if (hasArg(args, '--title'))
|
|
525
|
+
job.title = arg(args, '--title');
|
|
526
|
+
if (hasArg(args, '--objective'))
|
|
527
|
+
job.objective = arg(args, '--objective');
|
|
528
|
+
if (hasArg(args, '--priority'))
|
|
529
|
+
job.priority = priority(args, job.priority ?? 'P2');
|
|
530
|
+
if (hasArg(args, '--mode') || hasArg(args, '--execution-mode'))
|
|
531
|
+
job.executionMode = executionMode(args, job.executionMode ?? 'fixed');
|
|
532
|
+
if (hasArg(args, '--schedule'))
|
|
533
|
+
job.schedule = scheduleFromArgs(args);
|
|
534
|
+
if (hasArg(args, '--auto-merge') && hasArg(args, '--no-auto-merge'))
|
|
535
|
+
throw new Error('choose only one of --auto-merge or --no-auto-merge');
|
|
536
|
+
if (hasArg(args, '--auto-merge'))
|
|
537
|
+
job.autoMerge = true;
|
|
538
|
+
if (hasArg(args, '--no-auto-merge'))
|
|
539
|
+
job.autoMerge = false;
|
|
540
|
+
if (hasArg(args, '--review') && hasArg(args, '--no-review'))
|
|
541
|
+
throw new Error('choose only one of --review or --no-review');
|
|
542
|
+
if (hasArg(args, '--review'))
|
|
543
|
+
job.reviewEnabled = true;
|
|
544
|
+
if (hasArg(args, '--no-review'))
|
|
545
|
+
job.reviewEnabled = false;
|
|
546
|
+
if (hasArg(args, '--continue-kind') || hasArg(args, '--continue-target')) {
|
|
547
|
+
const next = continuation(args);
|
|
548
|
+
if (!next)
|
|
549
|
+
throw new Error('--continue-kind and --continue-target must be supplied together');
|
|
550
|
+
job.continuation = next;
|
|
551
|
+
}
|
|
552
|
+
job.updatedAt = new Date().toISOString();
|
|
553
|
+
await store.saveJob(job);
|
|
554
|
+
if (hasArg(args, '--schedule') && job.schedule)
|
|
555
|
+
await syncScheduleLabels(auth.client, repo, job);
|
|
556
|
+
console.log(JSON.stringify(await store.load(id), null, 2));
|
|
514
557
|
}
|
|
515
558
|
export function runtimePlanner(args) { const plannerCommand = arg([...args], '--planner-command'); const modelRuntime = arg([...args], '--codex', 'codex'); return plannerCommand ? new CommandObjectivePlanner(plannerCommand) : new AgentObjectivePlanner(modelRuntime); }
|
|
516
559
|
export function runtimeReviewer() { return { requirements: { capabilities: ['model'] } }; }
|
package/dist/store.js
CHANGED
|
@@ -485,7 +485,7 @@ export class GitHubStore {
|
|
|
485
485
|
const jobs = comments.map(x => parseJob(x.body)).filter((x) => x !== null && typeof x.id === 'string' && x.id.length > 0);
|
|
486
486
|
const job = jobs.find(x => x.id === jobId);
|
|
487
487
|
if (!job)
|
|
488
|
-
throw new Error(`Managed GitHub job ${jobId} has no durable ${JOB_MARKER} marker in PR #${this.prNumber}
|
|
488
|
+
throw new Error(`JOB_MARKER_MISSING: Managed GitHub job ${jobId} has no durable ${JOB_MARKER} marker in PR #${this.prNumber}`);
|
|
489
489
|
const soleTrustedJob = jobs.length === 1;
|
|
490
490
|
job.tasks = comments.map(comment => {
|
|
491
491
|
const task = parseTask(comment.body);
|
|
@@ -496,6 +496,25 @@ export class GitHubStore {
|
|
|
496
496
|
}).filter((x) => x !== null).filter(task => isTaskForJob(task, jobId, soleTrustedJob));
|
|
497
497
|
return job;
|
|
498
498
|
}
|
|
499
|
+
async validateJobBinding(jobId) {
|
|
500
|
+
const comments = (await this.list()).filter(comment => this.isTrusted(comment));
|
|
501
|
+
const markerComments = comments.filter(comment => comment.body.includes(JOB_MARKER));
|
|
502
|
+
const parsed = markerComments.map(comment => parseJob(comment.body));
|
|
503
|
+
const malformed = parsed.filter(job => job === null).length;
|
|
504
|
+
if (malformed > 0)
|
|
505
|
+
throw new Error(`JOB_MARKER_MALFORMED: PR #${this.prNumber} contains ${malformed} trusted ${JOB_MARKER} marker(s) that cannot be parsed`);
|
|
506
|
+
const jobs = parsed.filter((job) => job !== null);
|
|
507
|
+
const matches = jobs.filter(job => job.id === jobId);
|
|
508
|
+
if (matches.length === 0) {
|
|
509
|
+
if (jobs.length > 0)
|
|
510
|
+
throw new Error(`JOB_ID_MISMATCH: PR #${this.prNumber} is bound to ${jobs.map(job => job.id).join(', ')}, not ${jobId}`);
|
|
511
|
+
throw new Error(`JOB_MARKER_MISSING: Managed GitHub job ${jobId} has no durable ${JOB_MARKER} marker in PR #${this.prNumber}`);
|
|
512
|
+
}
|
|
513
|
+
if (matches.length > 1)
|
|
514
|
+
throw new Error(`JOB_MARKER_DUPLICATED: PR #${this.prNumber} contains ${matches.length} trusted ${JOB_MARKER} markers for job ${jobId}`);
|
|
515
|
+
if (jobs.length > 1)
|
|
516
|
+
throw new Error(`JOB_BINDING_CONFLICT: PR #${this.prNumber} contains trusted ${JOB_MARKER} markers for multiple job ids`);
|
|
517
|
+
}
|
|
499
518
|
async listAgents() { return (await this.list()).filter(comment => this.isTrusted(comment)).map(x => parseAgent(x.body)).filter((x) => x !== null); }
|
|
500
519
|
async comment(body) {
|
|
501
520
|
const pr = await this.prNode();
|