@blogic-cz/agent-tools 0.5.0 → 0.5.2
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/package.json +1 -1
- package/src/gh-tool/workflow.ts +96 -35
package/package.json
CHANGED
package/src/gh-tool/workflow.ts
CHANGED
|
@@ -48,6 +48,10 @@ type LogEntry = {
|
|
|
48
48
|
message: string;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
const repoOption = Flag.string("repo").pipe(
|
|
52
|
+
Flag.withDescription("Target repository (owner/name). Defaults to current repo"),
|
|
53
|
+
Flag.optional,
|
|
54
|
+
);
|
|
51
55
|
// ---------------------------------------------------------------------------
|
|
52
56
|
// Internal handlers
|
|
53
57
|
// ---------------------------------------------------------------------------
|
|
@@ -89,26 +93,34 @@ const listRuns = Effect.fn("workflow.listRuns")(function* (opts: {
|
|
|
89
93
|
return yield* gh.runGhJson<WorkflowRun[]>(args);
|
|
90
94
|
});
|
|
91
95
|
|
|
92
|
-
const viewRun = Effect.fn("workflow.viewRun")(function* (runId: number) {
|
|
96
|
+
const viewRun = Effect.fn("workflow.viewRun")(function* (runId: number, repo: string | null) {
|
|
93
97
|
const gh = yield* GitHubService;
|
|
94
98
|
|
|
95
|
-
const
|
|
99
|
+
const args = [
|
|
96
100
|
"run",
|
|
97
101
|
"view",
|
|
98
102
|
String(runId),
|
|
99
103
|
"--json",
|
|
100
104
|
"databaseId,displayTitle,status,conclusion,headBranch,createdAt,event,url,workflowName,jobs",
|
|
101
|
-
]
|
|
105
|
+
];
|
|
106
|
+
if (repo !== null) {
|
|
107
|
+
args.push("--repo", repo);
|
|
108
|
+
}
|
|
102
109
|
|
|
103
|
-
return
|
|
110
|
+
return yield* gh.runGhJson<WorkflowRunDetail>(args);
|
|
104
111
|
});
|
|
105
112
|
|
|
106
|
-
const listJobs = Effect.fn("workflow.listJobs")(function* (runId: number) {
|
|
113
|
+
const listJobs = Effect.fn("workflow.listJobs")(function* (runId: number, repo: string | null) {
|
|
107
114
|
const gh = yield* GitHubService;
|
|
108
115
|
|
|
116
|
+
const args = ["run", "view", String(runId), "--json", "jobs"];
|
|
117
|
+
if (repo !== null) {
|
|
118
|
+
args.push("--repo", repo);
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
const run = yield* gh.runGhJson<{
|
|
110
122
|
jobs: WorkflowJob[];
|
|
111
|
-
}>(
|
|
123
|
+
}>(args);
|
|
112
124
|
|
|
113
125
|
return run.jobs;
|
|
114
126
|
});
|
|
@@ -117,10 +129,15 @@ const fetchLogs = Effect.fn("workflow.fetchLogs")(function* (
|
|
|
117
129
|
runId: number,
|
|
118
130
|
failedOnly: boolean,
|
|
119
131
|
jobId: number | null = null,
|
|
132
|
+
repo: string | null = null,
|
|
120
133
|
) {
|
|
121
134
|
const gh = yield* GitHubService;
|
|
122
135
|
const args = ["run", "view", String(runId)];
|
|
123
136
|
|
|
137
|
+
if (repo !== null) {
|
|
138
|
+
args.push("--repo", repo);
|
|
139
|
+
}
|
|
140
|
+
|
|
124
141
|
if (jobId !== null) {
|
|
125
142
|
args.push("--log", "--job", String(jobId));
|
|
126
143
|
} else if (failedOnly) {
|
|
@@ -164,10 +181,15 @@ const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
|
|
|
164
181
|
};
|
|
165
182
|
});
|
|
166
183
|
|
|
167
|
-
const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number) {
|
|
184
|
+
const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number, repo: string | null) {
|
|
168
185
|
const gh = yield* GitHubService;
|
|
169
186
|
|
|
170
|
-
|
|
187
|
+
const args = ["run", "cancel", String(runId)];
|
|
188
|
+
if (repo !== null) {
|
|
189
|
+
args.push("--repo", repo);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
yield* gh.runGh(args);
|
|
171
193
|
|
|
172
194
|
return {
|
|
173
195
|
cancelled: true as const,
|
|
@@ -176,10 +198,15 @@ const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number) {
|
|
|
176
198
|
};
|
|
177
199
|
});
|
|
178
200
|
|
|
179
|
-
const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number) {
|
|
201
|
+
const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number, repo: string | null) {
|
|
180
202
|
const gh = yield* GitHubService;
|
|
181
203
|
|
|
182
|
-
const
|
|
204
|
+
const watchArgs = ["run", "watch", String(runId), "--exit-status"];
|
|
205
|
+
if (repo !== null) {
|
|
206
|
+
watchArgs.push("--repo", repo);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const result = yield* gh.runGh(watchArgs).pipe(
|
|
183
210
|
Effect.catchTag("GitHubCommandError", (error) => {
|
|
184
211
|
// exit-status returns non-zero if run failed, but we still want the output
|
|
185
212
|
if (error.exitCode > 0 && error.stderr === "") {
|
|
@@ -193,7 +220,7 @@ const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number) {
|
|
|
193
220
|
}),
|
|
194
221
|
);
|
|
195
222
|
|
|
196
|
-
const finalState = yield* viewRun(runId);
|
|
223
|
+
const finalState = yield* viewRun(runId, repo);
|
|
197
224
|
|
|
198
225
|
return {
|
|
199
226
|
runId,
|
|
@@ -268,8 +295,12 @@ export function formatLogEntries(entries: LogEntry[]): string {
|
|
|
268
295
|
// Job-level log handlers
|
|
269
296
|
// ---------------------------------------------------------------------------
|
|
270
297
|
|
|
271
|
-
const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (
|
|
272
|
-
|
|
298
|
+
const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (
|
|
299
|
+
runId: number,
|
|
300
|
+
jobName: string,
|
|
301
|
+
repo: string | null,
|
|
302
|
+
) {
|
|
303
|
+
const jobs = yield* listJobs(runId, repo);
|
|
273
304
|
|
|
274
305
|
// Exact match first
|
|
275
306
|
const exact = jobs.find((j) => j.name === jobName);
|
|
@@ -305,8 +336,9 @@ const filterFailedStepEntries = Effect.fn("workflow.filterFailedStepEntries")(fu
|
|
|
305
336
|
runId: number,
|
|
306
337
|
jobId: number,
|
|
307
338
|
entries: LogEntry[],
|
|
339
|
+
repo: string | null,
|
|
308
340
|
) {
|
|
309
|
-
const jobs = yield* listJobs(runId);
|
|
341
|
+
const jobs = yield* listJobs(runId, repo);
|
|
310
342
|
const job = jobs.find((j) => j.databaseId === jobId);
|
|
311
343
|
if (!job) return entries;
|
|
312
344
|
|
|
@@ -324,25 +356,47 @@ const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (opts: {
|
|
|
324
356
|
job: string;
|
|
325
357
|
failedStepsOnly: boolean;
|
|
326
358
|
format: string;
|
|
359
|
+
repo: string | null;
|
|
327
360
|
}) {
|
|
328
361
|
const gh = yield* GitHubService;
|
|
329
|
-
const { owner, name: repo } = yield* gh.getRepoInfo();
|
|
330
362
|
|
|
331
|
-
|
|
363
|
+
let owner: string;
|
|
364
|
+
let repoName: string;
|
|
365
|
+
if (opts.repo !== null) {
|
|
366
|
+
const parts = opts.repo.split("/");
|
|
367
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
368
|
+
return yield* new GitHubCommandError({
|
|
369
|
+
message: `Invalid --repo format: "${opts.repo}". Expected "owner/name" (e.g. "blogic-cz/agent-tools").`,
|
|
370
|
+
command: "workflow job-logs",
|
|
371
|
+
exitCode: 1,
|
|
372
|
+
stderr: "",
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
owner = parts[0];
|
|
376
|
+
repoName = parts[1];
|
|
377
|
+
} else {
|
|
378
|
+
const info = yield* gh.getRepoInfo();
|
|
379
|
+
owner = info.owner;
|
|
380
|
+
repoName = info.name;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const jobId = yield* resolveJobId(opts.runId, opts.job, opts.repo);
|
|
332
384
|
|
|
333
385
|
// Fetch raw logs via API (follows 302 redirect automatically)
|
|
334
|
-
const raw = yield* gh
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
386
|
+
const raw = yield* gh
|
|
387
|
+
.runGh(["api", `repos/${owner}/${repoName}/actions/jobs/${jobId}/logs`])
|
|
388
|
+
.pipe(
|
|
389
|
+
Effect.map((r) => r.stdout),
|
|
390
|
+
Effect.catchTag("GitHubCommandError", () => {
|
|
391
|
+
// Fallback: use gh run view --log --job
|
|
392
|
+
return fetchLogs(opts.runId, false, jobId, opts.repo).pipe(Effect.map((r) => r.log));
|
|
393
|
+
}),
|
|
394
|
+
);
|
|
341
395
|
|
|
342
396
|
let entries = parseRawJobLogs(raw);
|
|
343
397
|
|
|
344
398
|
if (opts.failedStepsOnly) {
|
|
345
|
-
entries = yield* filterFailedStepEntries(opts.runId, jobId, entries);
|
|
399
|
+
entries = yield* filterFailedStepEntries(opts.runId, jobId, entries, opts.repo);
|
|
346
400
|
}
|
|
347
401
|
|
|
348
402
|
if (opts.format === "json") {
|
|
@@ -420,11 +474,12 @@ export const workflowViewCommand = Command.make(
|
|
|
420
474
|
"view",
|
|
421
475
|
{
|
|
422
476
|
format: formatOption,
|
|
477
|
+
repo: repoOption,
|
|
423
478
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
|
|
424
479
|
},
|
|
425
|
-
({ format, run }) =>
|
|
480
|
+
({ format, repo, run }) =>
|
|
426
481
|
Effect.gen(function* () {
|
|
427
|
-
const detail = yield* viewRun(run);
|
|
482
|
+
const detail = yield* viewRun(run, Option.getOrNull(repo));
|
|
428
483
|
yield* logFormatted(detail, format);
|
|
429
484
|
}),
|
|
430
485
|
).pipe(Command.withDescription("View workflow run details including jobs and steps"));
|
|
@@ -433,11 +488,12 @@ export const workflowJobsCommand = Command.make(
|
|
|
433
488
|
"jobs",
|
|
434
489
|
{
|
|
435
490
|
format: formatOption,
|
|
491
|
+
repo: repoOption,
|
|
436
492
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
|
|
437
493
|
},
|
|
438
|
-
({ format, run }) =>
|
|
494
|
+
({ format, repo, run }) =>
|
|
439
495
|
Effect.gen(function* () {
|
|
440
|
-
const jobs = yield* listJobs(run);
|
|
496
|
+
const jobs = yield* listJobs(run, Option.getOrNull(repo));
|
|
441
497
|
yield* logFormatted(jobs, format);
|
|
442
498
|
}),
|
|
443
499
|
).pipe(Command.withDescription("List jobs and their steps for a workflow run"));
|
|
@@ -450,11 +506,12 @@ export const workflowLogsCommand = Command.make(
|
|
|
450
506
|
Flag.withDefault(true),
|
|
451
507
|
),
|
|
452
508
|
format: formatOption,
|
|
509
|
+
repo: repoOption,
|
|
453
510
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
|
|
454
511
|
},
|
|
455
|
-
({ failedOnly, format, run }) =>
|
|
512
|
+
({ failedOnly, format, repo, run }) =>
|
|
456
513
|
Effect.gen(function* () {
|
|
457
|
-
const logs = yield* fetchLogs(run, failedOnly);
|
|
514
|
+
const logs = yield* fetchLogs(run, failedOnly, null, Option.getOrNull(repo));
|
|
458
515
|
|
|
459
516
|
if (format === "toon" || format === "json") {
|
|
460
517
|
yield* logFormatted(logs, format);
|
|
@@ -489,11 +546,12 @@ export const workflowCancelCommand = Command.make(
|
|
|
489
546
|
"cancel",
|
|
490
547
|
{
|
|
491
548
|
format: formatOption,
|
|
549
|
+
repo: repoOption,
|
|
492
550
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID to cancel")),
|
|
493
551
|
},
|
|
494
|
-
({ format, run }) =>
|
|
552
|
+
({ format, repo, run }) =>
|
|
495
553
|
Effect.gen(function* () {
|
|
496
|
-
const result = yield* cancelRun(run);
|
|
554
|
+
const result = yield* cancelRun(run, Option.getOrNull(repo));
|
|
497
555
|
yield* logFormatted(result, format);
|
|
498
556
|
}),
|
|
499
557
|
).pipe(Command.withDescription("Cancel an in-progress workflow run"));
|
|
@@ -502,11 +560,12 @@ export const workflowWatchCommand = Command.make(
|
|
|
502
560
|
"watch",
|
|
503
561
|
{
|
|
504
562
|
format: formatOption,
|
|
563
|
+
repo: repoOption,
|
|
505
564
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID to watch")),
|
|
506
565
|
},
|
|
507
|
-
({ format, run }) =>
|
|
566
|
+
({ format, repo, run }) =>
|
|
508
567
|
Effect.gen(function* () {
|
|
509
|
-
const result = yield* watchRun(run);
|
|
568
|
+
const result = yield* watchRun(run, Option.getOrNull(repo));
|
|
510
569
|
yield* logFormatted(result, format);
|
|
511
570
|
}),
|
|
512
571
|
).pipe(Command.withDescription("Watch a workflow run until it completes, then show final status"));
|
|
@@ -522,15 +581,17 @@ export const workflowJobLogsCommand = Command.make(
|
|
|
522
581
|
job: Flag.string("job").pipe(
|
|
523
582
|
Flag.withDescription("Job name to fetch logs for (exact or partial match)"),
|
|
524
583
|
),
|
|
584
|
+
repo: repoOption,
|
|
525
585
|
run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
|
|
526
586
|
},
|
|
527
|
-
({ failedStepsOnly, format, job, run }) =>
|
|
587
|
+
({ failedStepsOnly, format, job, repo, run }) =>
|
|
528
588
|
Effect.gen(function* () {
|
|
529
589
|
const result = yield* fetchJobLogs({
|
|
530
590
|
runId: run,
|
|
531
591
|
job,
|
|
532
592
|
failedStepsOnly,
|
|
533
593
|
format,
|
|
594
|
+
repo: Option.getOrNull(repo),
|
|
534
595
|
});
|
|
535
596
|
|
|
536
597
|
if ("formatted" in result) {
|