@allocator-one/harness-cli 1.9.0 → 1.10.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/README.md +14 -3
- package/dist/{chunk-NQVQNKMC.js → chunk-KKAAKRKF.js} +161 -4
- package/dist/cli.js +24 -2
- package/dist/{server-FWNT3HMX.js → server-QVYSED3Z.js} +14 -1
- package/package.json +1 -1
- package/spec/openapi.json +361 -0
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ the browser login attributes writes to the real user.
|
|
|
52
52
|
## For agents
|
|
53
53
|
|
|
54
54
|
`harness mcp` runs a stdio MCP server exposing every operation as a typed
|
|
55
|
-
tool — issue_list, issue_show, issue_create, issue_update, issue_claim,
|
|
55
|
+
tool — issue_list, issue_show, activity_list, issue_create, issue_update, issue_claim,
|
|
56
56
|
issue_close, issue_comment, issue_comment_edit, issue_comment_delete,
|
|
57
57
|
relation_list/create/update/delete, relation_add/rm (blocking shorthand),
|
|
58
58
|
link_list/add/update/rm,
|
|
@@ -71,6 +71,12 @@ Example tool call: `issue_list` with `{"ready": true, "team": "ALL"}`
|
|
|
71
71
|
returns the ready queue as JSON; `issue_claim` with `{"id": "IO-123"}`
|
|
72
72
|
claims atomically (a conflict means someone else holds it).
|
|
73
73
|
|
|
74
|
+
Read the same mixed timeline shown in an issue's Activity section with
|
|
75
|
+
`harness activity IO-123`. It returns the newest 20 entries and points out
|
|
76
|
+
when older entries exist; pass `--all` to follow the opaque cursors through
|
|
77
|
+
the full history. `activity_list` exposes the same `id`, `limit`, and `all`
|
|
78
|
+
inputs to MCP clients.
|
|
79
|
+
|
|
74
80
|
Typed issue relations are available without losing the concise blocking
|
|
75
81
|
workflow. `harness dep add|rm` and the `relation_add`/`relation_rm` MCP tools
|
|
76
82
|
remain the blocker-first shorthand; the full surface is:
|
|
@@ -181,10 +187,15 @@ CI regenerates the types and fails on drift.
|
|
|
181
187
|
|
|
182
188
|
## Release
|
|
183
189
|
|
|
184
|
-
|
|
190
|
+
First bump `package.json` and `package-lock.json` through a pull request. After
|
|
191
|
+
that PR is squash-merged, fetch `main` and tag the merge commit — never the
|
|
192
|
+
pre-merge branch tip. Pushing the tag starts the Release workflow, which builds,
|
|
193
|
+
tests, and publishes to npm:
|
|
185
194
|
|
|
186
195
|
```sh
|
|
187
|
-
git
|
|
196
|
+
git fetch origin main
|
|
197
|
+
git tag -a vX.Y.Z MERGE_COMMIT_SHA -m "Release vX.Y.Z"
|
|
198
|
+
git push origin vX.Y.Z
|
|
188
199
|
```
|
|
189
200
|
|
|
190
201
|
Publishing requires the `NPM_TOKEN` actions secret (an automation token for
|
|
@@ -166,11 +166,16 @@ var Client = class {
|
|
|
166
166
|
throw new TransportError(transportMessage(error));
|
|
167
167
|
}
|
|
168
168
|
if (text === "") return { data: void 0 };
|
|
169
|
+
let parsed;
|
|
169
170
|
try {
|
|
170
|
-
|
|
171
|
+
parsed = JSON.parse(text);
|
|
171
172
|
} catch {
|
|
172
173
|
throw new TransportError(`the API answered with a non-JSON body (HTTP ${response.status})`);
|
|
173
174
|
}
|
|
175
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
176
|
+
throw new TransportError(`the API answered with an invalid JSON envelope (HTTP ${response.status})`);
|
|
177
|
+
}
|
|
178
|
+
return parsed;
|
|
174
179
|
}
|
|
175
180
|
async toHttpError(response) {
|
|
176
181
|
let parsed = {};
|
|
@@ -255,6 +260,101 @@ ${indent(String(text))}
|
|
|
255
260
|
`).join("");
|
|
256
261
|
return header + fieldLines + sectionText + relationsSection(issue["relations"]) + childrenSection(issue["children"]) + commentsSection(issue["comments"]) + imagesSection(images);
|
|
257
262
|
}
|
|
263
|
+
function activityEntries(entries) {
|
|
264
|
+
if (entries.length === 0) return "No activity.\n";
|
|
265
|
+
return entries.map(activityEntry).join("\n");
|
|
266
|
+
}
|
|
267
|
+
function activityEntry(entry) {
|
|
268
|
+
switch (entry["kind"]) {
|
|
269
|
+
case "comment":
|
|
270
|
+
return activityComment(entry);
|
|
271
|
+
case "state_event":
|
|
272
|
+
return activityStateEvent(entry);
|
|
273
|
+
default:
|
|
274
|
+
throw new CommandError(`Unknown activity entry kind: ${clean(entry["kind"])}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function activityComment(entry) {
|
|
278
|
+
const comment2 = requiredRecord(entry["comment"], "activity comment");
|
|
279
|
+
const authorValue = comment2["author"];
|
|
280
|
+
const author = authorValue === null ? "unknown" : optionalLabel(requiredRecord(authorValue, "comment author")["name"], "unknown", "comment author name");
|
|
281
|
+
const deleted = comment2["deleted"];
|
|
282
|
+
if (typeof deleted !== "boolean") throw new CommandError("Invalid comment deleted flag.");
|
|
283
|
+
const body = activityCommentBody(comment2["body"], deleted);
|
|
284
|
+
const text = cleanMultiline(body).split("\n").map((line) => " " + line).join("\n");
|
|
285
|
+
return `[${clean(requiredString(entry["occurred_at"], "activity occurrence time"))}] ${clean(author)} (${clean(requiredString(comment2["id"], "comment id"))}):
|
|
286
|
+
${text}
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
function activityCommentBody(value, deleted) {
|
|
290
|
+
if (deleted) return "[deleted comment]";
|
|
291
|
+
if (value === null || value === "") return "[comment has no text]";
|
|
292
|
+
return requiredString(value, "comment body");
|
|
293
|
+
}
|
|
294
|
+
function activityStateEvent(entry) {
|
|
295
|
+
const actor = activityActor(requiredRecord(entry["actor"], "activity actor"));
|
|
296
|
+
const provenance = activityProvenance(requiredRecord(entry["provenance"], "activity provenance"));
|
|
297
|
+
let description;
|
|
298
|
+
switch (entry["action"]) {
|
|
299
|
+
case "created":
|
|
300
|
+
description = `${actor} created the issue in ${stateName(entry["to_state"], "created to_state")}`;
|
|
301
|
+
break;
|
|
302
|
+
case "transition":
|
|
303
|
+
description = `${actor} moved the issue from ${nullableStateName(entry["from_state"], "transition from_state")} to ${stateName(entry["to_state"], "transition to_state")}`;
|
|
304
|
+
break;
|
|
305
|
+
case "milestone":
|
|
306
|
+
description = `${actor} recorded the ${stateName(entry["to_state"], "milestone to_state")} milestone`;
|
|
307
|
+
break;
|
|
308
|
+
default:
|
|
309
|
+
throw new CommandError(`Unknown activity action: ${clean(entry["action"])}`);
|
|
310
|
+
}
|
|
311
|
+
return `[${clean(requiredString(entry["occurred_at"], "activity occurrence time"))}] ${description} \xB7 ${provenance}
|
|
312
|
+
`;
|
|
313
|
+
}
|
|
314
|
+
function activityActor(actor) {
|
|
315
|
+
switch (actor["kind"]) {
|
|
316
|
+
case "user":
|
|
317
|
+
return optionalLabel(actor["name"], "Unknown actor", "activity actor name");
|
|
318
|
+
case "system":
|
|
319
|
+
return "System";
|
|
320
|
+
case "deleted_user":
|
|
321
|
+
return "Deleted user";
|
|
322
|
+
case "unknown":
|
|
323
|
+
return "Unknown actor";
|
|
324
|
+
default:
|
|
325
|
+
throw new CommandError(`Unknown activity actor kind: ${clean(actor["kind"])}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function activityProvenance(provenance) {
|
|
329
|
+
switch (provenance["kind"]) {
|
|
330
|
+
case "live":
|
|
331
|
+
case "import":
|
|
332
|
+
case "backfill":
|
|
333
|
+
return String(provenance["kind"]);
|
|
334
|
+
default:
|
|
335
|
+
throw new CommandError(`Unknown activity provenance: ${clean(provenance["kind"])}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function stateName(value, field2) {
|
|
339
|
+
return optionalLabel(requiredRecord(value, field2)["name"], "Unknown state", `${field2} name`);
|
|
340
|
+
}
|
|
341
|
+
function nullableStateName(value, field2) {
|
|
342
|
+
return value === null ? "Unknown state" : stateName(value, field2);
|
|
343
|
+
}
|
|
344
|
+
function optionalLabel(value, fallback, field2) {
|
|
345
|
+
if (value === null || value === "") return fallback;
|
|
346
|
+
return clean(requiredString(value, field2));
|
|
347
|
+
}
|
|
348
|
+
function requiredRecord(value, field2) {
|
|
349
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
350
|
+
throw new CommandError(`Invalid ${field2}.`);
|
|
351
|
+
}
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
function requiredString(value, field2) {
|
|
355
|
+
if (typeof value !== "string" || value === "") throw new CommandError(`Invalid ${field2}.`);
|
|
356
|
+
return value;
|
|
357
|
+
}
|
|
258
358
|
function stats(data) {
|
|
259
359
|
const teams = data["teams"] ?? [];
|
|
260
360
|
const totals = data["totals"] ?? {};
|
|
@@ -594,6 +694,61 @@ async function show(ctx, identifier, opts) {
|
|
|
594
694
|
`).join("");
|
|
595
695
|
return notice === "" ? { json: json2, human } : { json: json2, human, notice };
|
|
596
696
|
}
|
|
697
|
+
var ACTIVITY_MAX_LIMIT = 100;
|
|
698
|
+
async function activity(ctx, identifier, opts) {
|
|
699
|
+
requireIdentifiers([identifier]);
|
|
700
|
+
let page = await activityPage(ctx, identifier, opts.limit);
|
|
701
|
+
let entries = page.entries;
|
|
702
|
+
if (opts.all === true) {
|
|
703
|
+
const pages = [page.entries];
|
|
704
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
705
|
+
while (page.hasMore) {
|
|
706
|
+
const cursor = page.nextCursor;
|
|
707
|
+
if (seenCursors.has(cursor)) {
|
|
708
|
+
throw new CommandError("Activity response repeated a cursor.");
|
|
709
|
+
}
|
|
710
|
+
seenCursors.add(cursor);
|
|
711
|
+
page = await activityPage(ctx, identifier, opts.limit, cursor);
|
|
712
|
+
pages.push(page.entries);
|
|
713
|
+
}
|
|
714
|
+
entries = pages.reverse().flat();
|
|
715
|
+
}
|
|
716
|
+
const notice = opts.all !== true && page.hasMore ? `harness: showing the newest ${entries.length} activity entries \u2014 pass --all for older activity.
|
|
717
|
+
` : void 0;
|
|
718
|
+
return { json: entries, human: activityEntries(entries), notice };
|
|
719
|
+
}
|
|
720
|
+
async function activityPage(ctx, identifier, limit, before) {
|
|
721
|
+
const body = await ctx.client.get(`/api/v1/issues/${identifier}/activity`, { limit, before });
|
|
722
|
+
if (!Array.isArray(body.data)) throw new CommandError("Activity response data is not an array.");
|
|
723
|
+
if (body.data.some((entry) => typeof entry !== "object" || entry === null || Array.isArray(entry))) {
|
|
724
|
+
throw new CommandError("Activity response contains a non-object entry.");
|
|
725
|
+
}
|
|
726
|
+
const meta = body.meta;
|
|
727
|
+
if (typeof meta !== "object" || meta === null) {
|
|
728
|
+
throw new CommandError("Activity response has no pagination metadata.");
|
|
729
|
+
}
|
|
730
|
+
const hasMore = meta.has_more;
|
|
731
|
+
const nextCursor = meta.next_cursor;
|
|
732
|
+
if (typeof hasMore !== "boolean") {
|
|
733
|
+
throw new CommandError("Activity response has an invalid has_more value.");
|
|
734
|
+
}
|
|
735
|
+
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
736
|
+
throw new CommandError("Activity response has an invalid next_cursor value.");
|
|
737
|
+
}
|
|
738
|
+
if (hasMore) {
|
|
739
|
+
if (nextCursor === null || nextCursor === "") {
|
|
740
|
+
throw new CommandError("Activity response says older entries exist but provides no cursor.");
|
|
741
|
+
}
|
|
742
|
+
if (body.data.length === 0) {
|
|
743
|
+
throw new CommandError("Activity response says older entries exist but contains no entries.");
|
|
744
|
+
}
|
|
745
|
+
return { entries: body.data, hasMore: true, nextCursor };
|
|
746
|
+
}
|
|
747
|
+
if (nextCursor !== null) {
|
|
748
|
+
throw new CommandError("Activity response provides a cursor when no older entries exist.");
|
|
749
|
+
}
|
|
750
|
+
return { entries: body.data, hasMore: false, nextCursor: null };
|
|
751
|
+
}
|
|
597
752
|
async function stats2(ctx) {
|
|
598
753
|
const body = await ctx.client.get("/api/v1/stats");
|
|
599
754
|
return { json: body.data, human: stats(body.data) };
|
|
@@ -992,19 +1147,19 @@ async function claimFirst(ctx, issues) {
|
|
|
992
1147
|
}
|
|
993
1148
|
throw new CommandError("No claimable ready issues.");
|
|
994
1149
|
}
|
|
995
|
-
async function setState(ctx, identifier,
|
|
1150
|
+
async function setState(ctx, identifier, stateName2) {
|
|
996
1151
|
const showBody = await ctx.client.get(`/api/v1/issues/${identifier}`);
|
|
997
1152
|
const teamKey = str2((showBody.data["team"] ?? {})["key"]);
|
|
998
1153
|
const statesBody = await ctx.client.get(
|
|
999
1154
|
`/api/v1/teams/${teamKey}/workflow_states`
|
|
1000
1155
|
);
|
|
1001
|
-
const wanted =
|
|
1156
|
+
const wanted = stateName2.toLowerCase();
|
|
1002
1157
|
const state = statesBody.data.find(
|
|
1003
1158
|
(candidate) => str2(candidate["name"]).toLowerCase() === wanted || str2(candidate["type"]) === wanted
|
|
1004
1159
|
);
|
|
1005
1160
|
if (state === void 0) {
|
|
1006
1161
|
const names = statesBody.data.map((candidate) => str2(candidate["name"])).join(", ");
|
|
1007
|
-
throw new CommandError(`No workflow state matches "${
|
|
1162
|
+
throw new CommandError(`No workflow state matches "${stateName2}". States: ${names}`);
|
|
1008
1163
|
}
|
|
1009
1164
|
const body = await ctx.client.put(`/api/v1/issues/${identifier}/state`, { state_id: state["id"] });
|
|
1010
1165
|
return body.data;
|
|
@@ -1951,6 +2106,8 @@ export {
|
|
|
1951
2106
|
list,
|
|
1952
2107
|
search,
|
|
1953
2108
|
show,
|
|
2109
|
+
ACTIVITY_MAX_LIMIT,
|
|
2110
|
+
activity,
|
|
1954
2111
|
stats2 as stats,
|
|
1955
2112
|
prime,
|
|
1956
2113
|
version,
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ACTIVITY_MAX_LIMIT,
|
|
3
4
|
CLI_VERSION,
|
|
4
5
|
Client,
|
|
5
6
|
CommandError,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
TransportError,
|
|
12
13
|
UpgradeRequiredError,
|
|
13
14
|
UsageError,
|
|
15
|
+
activity,
|
|
14
16
|
archive,
|
|
15
17
|
attachImage,
|
|
16
18
|
blocked,
|
|
@@ -80,7 +82,7 @@ import {
|
|
|
80
82
|
version,
|
|
81
83
|
workflowStates,
|
|
82
84
|
writeCredentials
|
|
83
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-KKAAKRKF.js";
|
|
84
86
|
|
|
85
87
|
// src/cli.ts
|
|
86
88
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -1238,6 +1240,15 @@ function integer(value) {
|
|
|
1238
1240
|
throw new InvalidArgumentError("expected a non-negative integer");
|
|
1239
1241
|
return Number.parseInt(value, 10);
|
|
1240
1242
|
}
|
|
1243
|
+
function activityLimit(value) {
|
|
1244
|
+
const limit = integer(value);
|
|
1245
|
+
if (limit < 1 || limit > ACTIVITY_MAX_LIMIT) {
|
|
1246
|
+
throw new InvalidArgumentError(
|
|
1247
|
+
`expected an integer from 1 to ${ACTIVITY_MAX_LIMIT}`
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
return limit;
|
|
1251
|
+
}
|
|
1241
1252
|
function collect(value, previous) {
|
|
1242
1253
|
return [...previous, value];
|
|
1243
1254
|
}
|
|
@@ -1424,6 +1435,17 @@ program.command("show").description("full issue detail; downloads attached image
|
|
|
1424
1435
|
});
|
|
1425
1436
|
}
|
|
1426
1437
|
);
|
|
1438
|
+
program.command("activity").description("mixed issue activity: comments, creation, and state changes").argument("[id]").option(
|
|
1439
|
+
"--limit <n>",
|
|
1440
|
+
`entries per page (default 20, max ${ACTIVITY_MAX_LIMIT})`,
|
|
1441
|
+
activityLimit
|
|
1442
|
+
).option("--all", "read all older activity").option("--json", "emit JSON").action(async (id, opts) => {
|
|
1443
|
+
await withConfig(opts.json === true, false, (ctx) => {
|
|
1444
|
+
if (id === void 0) throw new UsageError("usage: harness activity <id>");
|
|
1445
|
+
requireIdentifiers2([id]);
|
|
1446
|
+
return activity(ctx, id, opts);
|
|
1447
|
+
});
|
|
1448
|
+
});
|
|
1427
1449
|
program.command("stats").description("per-team and total counts").option("--json", "emit JSON").action(async (opts) => {
|
|
1428
1450
|
await withConfig(opts.json === true, false, (ctx) => stats(ctx));
|
|
1429
1451
|
});
|
|
@@ -1889,7 +1911,7 @@ program.command("upload").description("upload a file; prints markdown to referen
|
|
|
1889
1911
|
});
|
|
1890
1912
|
});
|
|
1891
1913
|
program.command("mcp").description("run the MCP server over stdio (for agents)").action(async () => {
|
|
1892
|
-
const { startMcpServer } = await import("./server-
|
|
1914
|
+
const { startMcpServer } = await import("./server-QVYSED3Z.js");
|
|
1893
1915
|
await startMcpServer();
|
|
1894
1916
|
});
|
|
1895
1917
|
program.command("login").description("sign in via the browser (infra.one session)").option("--url <url>", "host to sign in to").option("--no-browser", "print the URL and prompt for a pasted token instead").option(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ACTIVITY_MAX_LIMIT,
|
|
3
4
|
CLI_VERSION,
|
|
4
5
|
Client,
|
|
5
6
|
HttpError,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
RELATION_UPDATE_TYPES,
|
|
8
9
|
TransportError,
|
|
9
10
|
UsageError,
|
|
11
|
+
activity,
|
|
10
12
|
archive,
|
|
11
13
|
blocked,
|
|
12
14
|
close,
|
|
@@ -59,7 +61,7 @@ import {
|
|
|
59
61
|
upload,
|
|
60
62
|
userList,
|
|
61
63
|
workflowStates
|
|
62
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-KKAAKRKF.js";
|
|
63
65
|
|
|
64
66
|
// src/mcp/server.ts
|
|
65
67
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -161,6 +163,17 @@ var TOOLS = [
|
|
|
161
163
|
shape: { id: identifier, images: z.boolean().optional() },
|
|
162
164
|
handler: (ctx, args) => show(ctx, args["id"], { images: args["images"] === true })
|
|
163
165
|
},
|
|
166
|
+
{
|
|
167
|
+
name: "activity_list",
|
|
168
|
+
description: "Mixed issue activity: comments, creation, and state changes. Returns the newest page unless all=true.",
|
|
169
|
+
readOnly: true,
|
|
170
|
+
shape: {
|
|
171
|
+
id: identifier,
|
|
172
|
+
limit: z.number().int().min(1).max(ACTIVITY_MAX_LIMIT).optional().describe(`entries per page (default 20, max ${ACTIVITY_MAX_LIMIT})`),
|
|
173
|
+
all: z.boolean().optional().describe("follow cursors through all older activity")
|
|
174
|
+
},
|
|
175
|
+
handler: (ctx, args) => activity(ctx, args["id"], args)
|
|
176
|
+
},
|
|
164
177
|
{
|
|
165
178
|
name: "issue_create",
|
|
166
179
|
description: "Create an issue. type becomes a type/ label.",
|
package/package.json
CHANGED
package/spec/openapi.json
CHANGED
|
@@ -973,6 +973,9 @@
|
|
|
973
973
|
"type": "string"
|
|
974
974
|
}
|
|
975
975
|
},
|
|
976
|
+
"required": [
|
|
977
|
+
"url"
|
|
978
|
+
],
|
|
976
979
|
"type": "object"
|
|
977
980
|
}
|
|
978
981
|
}
|
|
@@ -1280,6 +1283,361 @@
|
|
|
1280
1283
|
"tags": []
|
|
1281
1284
|
}
|
|
1282
1285
|
},
|
|
1286
|
+
"/api/v1/issues/{identifier}/activity": {
|
|
1287
|
+
"get": {
|
|
1288
|
+
"callbacks": {},
|
|
1289
|
+
"deprecated": false,
|
|
1290
|
+
"description": "Requires the `harness:read` scope. The newest mixed page of comments and issue-state events, returned oldest-first. `has_more` means older entries exist. Pass the opaque `next_cursor` back as `before`; cursors are bound to the issue and viewer and expire after 24 hours.",
|
|
1291
|
+
"parameters": [
|
|
1292
|
+
{
|
|
1293
|
+
"description": "Issue identifier, e.g. ALL-4901 or CLI-12 (issues minted before team-prefixed numbering carry IO-)",
|
|
1294
|
+
"in": "path",
|
|
1295
|
+
"name": "identifier",
|
|
1296
|
+
"required": true,
|
|
1297
|
+
"schema": {
|
|
1298
|
+
"type": "string"
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
"description": "Opaque cursor returned as `meta.next_cursor` by the preceding newer page",
|
|
1303
|
+
"in": "query",
|
|
1304
|
+
"name": "before",
|
|
1305
|
+
"required": false,
|
|
1306
|
+
"schema": {
|
|
1307
|
+
"type": "string"
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
"description": "Entries per page (default 20, max 100)",
|
|
1312
|
+
"in": "query",
|
|
1313
|
+
"name": "limit",
|
|
1314
|
+
"required": false,
|
|
1315
|
+
"schema": {
|
|
1316
|
+
"maximum": 100,
|
|
1317
|
+
"minimum": 1,
|
|
1318
|
+
"type": "integer"
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
],
|
|
1322
|
+
"responses": {
|
|
1323
|
+
"200": {
|
|
1324
|
+
"content": {
|
|
1325
|
+
"application/json": {
|
|
1326
|
+
"schema": {
|
|
1327
|
+
"properties": {
|
|
1328
|
+
"data": {
|
|
1329
|
+
"items": {
|
|
1330
|
+
"oneOf": [
|
|
1331
|
+
{
|
|
1332
|
+
"additionalProperties": false,
|
|
1333
|
+
"properties": {
|
|
1334
|
+
"comment": {
|
|
1335
|
+
"properties": {
|
|
1336
|
+
"author": {
|
|
1337
|
+
"nullable": true,
|
|
1338
|
+
"properties": {
|
|
1339
|
+
"email": {
|
|
1340
|
+
"nullable": true,
|
|
1341
|
+
"type": "string"
|
|
1342
|
+
},
|
|
1343
|
+
"id": {
|
|
1344
|
+
"format": "uuid",
|
|
1345
|
+
"nullable": true,
|
|
1346
|
+
"type": "string"
|
|
1347
|
+
},
|
|
1348
|
+
"name": {
|
|
1349
|
+
"nullable": true,
|
|
1350
|
+
"type": "string"
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
"required": [
|
|
1354
|
+
"id",
|
|
1355
|
+
"name",
|
|
1356
|
+
"email"
|
|
1357
|
+
],
|
|
1358
|
+
"type": "object"
|
|
1359
|
+
},
|
|
1360
|
+
"body": {
|
|
1361
|
+
"description": "Comment text; null may mean a deleted tombstone or no stored text. Use `deleted` to identify tombstones",
|
|
1362
|
+
"nullable": true,
|
|
1363
|
+
"type": "string"
|
|
1364
|
+
},
|
|
1365
|
+
"deleted": {
|
|
1366
|
+
"description": "Whether the comment is a deleted tombstone",
|
|
1367
|
+
"type": "boolean"
|
|
1368
|
+
},
|
|
1369
|
+
"id": {
|
|
1370
|
+
"format": "uuid",
|
|
1371
|
+
"type": "string"
|
|
1372
|
+
},
|
|
1373
|
+
"inserted_at": {
|
|
1374
|
+
"format": "date-time",
|
|
1375
|
+
"type": "string"
|
|
1376
|
+
},
|
|
1377
|
+
"mentions": {
|
|
1378
|
+
"items": {
|
|
1379
|
+
"properties": {
|
|
1380
|
+
"email": {
|
|
1381
|
+
"nullable": true,
|
|
1382
|
+
"type": "string"
|
|
1383
|
+
},
|
|
1384
|
+
"name": {
|
|
1385
|
+
"nullable": true,
|
|
1386
|
+
"type": "string"
|
|
1387
|
+
},
|
|
1388
|
+
"user_id": {
|
|
1389
|
+
"format": "uuid",
|
|
1390
|
+
"nullable": true,
|
|
1391
|
+
"type": "string"
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
"required": [
|
|
1395
|
+
"user_id",
|
|
1396
|
+
"name",
|
|
1397
|
+
"email"
|
|
1398
|
+
],
|
|
1399
|
+
"type": "object"
|
|
1400
|
+
},
|
|
1401
|
+
"type": "array"
|
|
1402
|
+
},
|
|
1403
|
+
"reactions": {
|
|
1404
|
+
"items": {
|
|
1405
|
+
"properties": {
|
|
1406
|
+
"count": {
|
|
1407
|
+
"type": "integer"
|
|
1408
|
+
},
|
|
1409
|
+
"emoji": {
|
|
1410
|
+
"type": "string"
|
|
1411
|
+
},
|
|
1412
|
+
"reacted_by_current_actor": {
|
|
1413
|
+
"type": "boolean"
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
"required": [
|
|
1417
|
+
"emoji",
|
|
1418
|
+
"count",
|
|
1419
|
+
"reacted_by_current_actor"
|
|
1420
|
+
],
|
|
1421
|
+
"type": "object"
|
|
1422
|
+
},
|
|
1423
|
+
"type": "array"
|
|
1424
|
+
},
|
|
1425
|
+
"updated_at": {
|
|
1426
|
+
"format": "date-time",
|
|
1427
|
+
"type": "string"
|
|
1428
|
+
}
|
|
1429
|
+
},
|
|
1430
|
+
"required": [
|
|
1431
|
+
"id",
|
|
1432
|
+
"body",
|
|
1433
|
+
"deleted",
|
|
1434
|
+
"author",
|
|
1435
|
+
"mentions",
|
|
1436
|
+
"reactions",
|
|
1437
|
+
"inserted_at",
|
|
1438
|
+
"updated_at"
|
|
1439
|
+
],
|
|
1440
|
+
"type": "object"
|
|
1441
|
+
},
|
|
1442
|
+
"id": {
|
|
1443
|
+
"type": "string"
|
|
1444
|
+
},
|
|
1445
|
+
"kind": {
|
|
1446
|
+
"enum": [
|
|
1447
|
+
"comment"
|
|
1448
|
+
],
|
|
1449
|
+
"type": "string"
|
|
1450
|
+
},
|
|
1451
|
+
"occurred_at": {
|
|
1452
|
+
"format": "date-time",
|
|
1453
|
+
"type": "string"
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
"required": [
|
|
1457
|
+
"id",
|
|
1458
|
+
"kind",
|
|
1459
|
+
"occurred_at",
|
|
1460
|
+
"comment"
|
|
1461
|
+
],
|
|
1462
|
+
"type": "object"
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
"additionalProperties": false,
|
|
1466
|
+
"properties": {
|
|
1467
|
+
"action": {
|
|
1468
|
+
"enum": [
|
|
1469
|
+
"created",
|
|
1470
|
+
"milestone",
|
|
1471
|
+
"transition"
|
|
1472
|
+
],
|
|
1473
|
+
"type": "string"
|
|
1474
|
+
},
|
|
1475
|
+
"actor": {
|
|
1476
|
+
"properties": {
|
|
1477
|
+
"kind": {
|
|
1478
|
+
"enum": [
|
|
1479
|
+
"system",
|
|
1480
|
+
"user",
|
|
1481
|
+
"deleted_user",
|
|
1482
|
+
"unknown"
|
|
1483
|
+
],
|
|
1484
|
+
"type": "string"
|
|
1485
|
+
},
|
|
1486
|
+
"name": {
|
|
1487
|
+
"nullable": true,
|
|
1488
|
+
"type": "string"
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
"required": [
|
|
1492
|
+
"kind",
|
|
1493
|
+
"name"
|
|
1494
|
+
],
|
|
1495
|
+
"type": "object"
|
|
1496
|
+
},
|
|
1497
|
+
"from_state": {
|
|
1498
|
+
"nullable": true,
|
|
1499
|
+
"properties": {
|
|
1500
|
+
"key": {
|
|
1501
|
+
"nullable": true,
|
|
1502
|
+
"type": "string"
|
|
1503
|
+
},
|
|
1504
|
+
"name": {
|
|
1505
|
+
"nullable": true,
|
|
1506
|
+
"type": "string"
|
|
1507
|
+
},
|
|
1508
|
+
"type": {
|
|
1509
|
+
"nullable": true,
|
|
1510
|
+
"type": "string"
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
"required": [
|
|
1514
|
+
"key",
|
|
1515
|
+
"name",
|
|
1516
|
+
"type"
|
|
1517
|
+
],
|
|
1518
|
+
"type": "object"
|
|
1519
|
+
},
|
|
1520
|
+
"id": {
|
|
1521
|
+
"type": "string"
|
|
1522
|
+
},
|
|
1523
|
+
"kind": {
|
|
1524
|
+
"enum": [
|
|
1525
|
+
"state_event"
|
|
1526
|
+
],
|
|
1527
|
+
"type": "string"
|
|
1528
|
+
},
|
|
1529
|
+
"occurred_at": {
|
|
1530
|
+
"format": "date-time",
|
|
1531
|
+
"type": "string"
|
|
1532
|
+
},
|
|
1533
|
+
"provenance": {
|
|
1534
|
+
"properties": {
|
|
1535
|
+
"kind": {
|
|
1536
|
+
"enum": [
|
|
1537
|
+
"live",
|
|
1538
|
+
"import",
|
|
1539
|
+
"backfill"
|
|
1540
|
+
],
|
|
1541
|
+
"type": "string"
|
|
1542
|
+
},
|
|
1543
|
+
"witnessed": {
|
|
1544
|
+
"type": "boolean"
|
|
1545
|
+
}
|
|
1546
|
+
},
|
|
1547
|
+
"required": [
|
|
1548
|
+
"kind",
|
|
1549
|
+
"witnessed"
|
|
1550
|
+
],
|
|
1551
|
+
"type": "object"
|
|
1552
|
+
},
|
|
1553
|
+
"to_state": {
|
|
1554
|
+
"properties": {
|
|
1555
|
+
"key": {
|
|
1556
|
+
"nullable": true,
|
|
1557
|
+
"type": "string"
|
|
1558
|
+
},
|
|
1559
|
+
"name": {
|
|
1560
|
+
"nullable": true,
|
|
1561
|
+
"type": "string"
|
|
1562
|
+
},
|
|
1563
|
+
"type": {
|
|
1564
|
+
"nullable": true,
|
|
1565
|
+
"type": "string"
|
|
1566
|
+
}
|
|
1567
|
+
},
|
|
1568
|
+
"required": [
|
|
1569
|
+
"key",
|
|
1570
|
+
"name",
|
|
1571
|
+
"type"
|
|
1572
|
+
],
|
|
1573
|
+
"type": "object"
|
|
1574
|
+
}
|
|
1575
|
+
},
|
|
1576
|
+
"required": [
|
|
1577
|
+
"id",
|
|
1578
|
+
"kind",
|
|
1579
|
+
"occurred_at",
|
|
1580
|
+
"action",
|
|
1581
|
+
"actor",
|
|
1582
|
+
"provenance",
|
|
1583
|
+
"from_state",
|
|
1584
|
+
"to_state"
|
|
1585
|
+
],
|
|
1586
|
+
"type": "object"
|
|
1587
|
+
}
|
|
1588
|
+
]
|
|
1589
|
+
},
|
|
1590
|
+
"type": "array"
|
|
1591
|
+
},
|
|
1592
|
+
"meta": {
|
|
1593
|
+
"properties": {
|
|
1594
|
+
"has_more": {
|
|
1595
|
+
"description": "Whether older entries exist",
|
|
1596
|
+
"type": "boolean"
|
|
1597
|
+
},
|
|
1598
|
+
"next_cursor": {
|
|
1599
|
+
"description": "Opaque cursor to pass as `before`; null when no older entries exist",
|
|
1600
|
+
"nullable": true,
|
|
1601
|
+
"type": "string"
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
"required": [
|
|
1605
|
+
"has_more",
|
|
1606
|
+
"next_cursor"
|
|
1607
|
+
],
|
|
1608
|
+
"type": "object"
|
|
1609
|
+
}
|
|
1610
|
+
},
|
|
1611
|
+
"required": [
|
|
1612
|
+
"data",
|
|
1613
|
+
"meta"
|
|
1614
|
+
],
|
|
1615
|
+
"type": "object"
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
"description": "Success"
|
|
1620
|
+
},
|
|
1621
|
+
"400": {
|
|
1622
|
+
"description": "Invalid input. Error code is `invalid_cursor` for an invalid or expired cursor, or `bad_request` for a malformed `before` value or invalid limit."
|
|
1623
|
+
},
|
|
1624
|
+
"401": {
|
|
1625
|
+
"description": "Missing or invalid token"
|
|
1626
|
+
},
|
|
1627
|
+
"403": {
|
|
1628
|
+
"description": "Token not permitted (org-scoped, non-admin, or missing scope)"
|
|
1629
|
+
},
|
|
1630
|
+
"404": {
|
|
1631
|
+
"description": "Not found"
|
|
1632
|
+
},
|
|
1633
|
+
"422": {
|
|
1634
|
+
"description": "Validation error"
|
|
1635
|
+
}
|
|
1636
|
+
},
|
|
1637
|
+
"summary": "List issue activity",
|
|
1638
|
+
"tags": []
|
|
1639
|
+
}
|
|
1640
|
+
},
|
|
1283
1641
|
"/api/v1/users": {
|
|
1284
1642
|
"get": {
|
|
1285
1643
|
"callbacks": {},
|
|
@@ -2479,9 +2837,12 @@
|
|
|
2479
2837
|
"type": "object"
|
|
2480
2838
|
},
|
|
2481
2839
|
"body": {
|
|
2840
|
+
"description": "Comment text; null may mean a deleted tombstone or no stored text. Use `deleted` to identify tombstones",
|
|
2841
|
+
"nullable": true,
|
|
2482
2842
|
"type": "string"
|
|
2483
2843
|
},
|
|
2484
2844
|
"deleted": {
|
|
2845
|
+
"description": "Whether the comment is a deleted tombstone",
|
|
2485
2846
|
"type": "boolean"
|
|
2486
2847
|
},
|
|
2487
2848
|
"id": {
|