@allocator-one/harness-cli 1.4.1 → 1.5.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/dist/{chunk-MF7MQ2L6.js → chunk-FGBXAAKW.js} +154 -6
- package/dist/cli.js +59 -8
- package/dist/{server-LJXJLWNK.js → server-ZRD4RTFU.js} +126 -1
- package/package.json +1 -1
- package/spec/openapi.json +1122 -464
|
@@ -1238,8 +1238,13 @@ async function linkRm(ctx, issue, idOrUrl) {
|
|
|
1238
1238
|
async function labelList(ctx) {
|
|
1239
1239
|
const body = await ctx.client.get("/api/v1/labels");
|
|
1240
1240
|
const human = body.data.length === 0 ? "No labels.\n" : table(
|
|
1241
|
-
["NAME", "COLOR", "ID"],
|
|
1242
|
-
body.data.map((label) => [
|
|
1241
|
+
["NAME", "COLOR", "ISSUES", "ID"],
|
|
1242
|
+
body.data.map((label) => [
|
|
1243
|
+
str3(label["name"]),
|
|
1244
|
+
str3(label["color"]) || "-",
|
|
1245
|
+
str3(label["issue_count"] ?? 0),
|
|
1246
|
+
str3(label["id"])
|
|
1247
|
+
])
|
|
1243
1248
|
);
|
|
1244
1249
|
return { json: body.data, human };
|
|
1245
1250
|
}
|
|
@@ -1272,8 +1277,13 @@ async function teamCreate(ctx, opts) {
|
|
|
1272
1277
|
async function teamList(ctx) {
|
|
1273
1278
|
const body = await ctx.client.get("/api/v1/teams");
|
|
1274
1279
|
const human = body.data.length === 0 ? "No teams.\n" : table(
|
|
1275
|
-
["KEY", "NAME"],
|
|
1276
|
-
body.data.map((team) => [
|
|
1280
|
+
["KEY", "NAME", "ISSUES", "DEFAULT"],
|
|
1281
|
+
body.data.map((team) => [
|
|
1282
|
+
str3(team["key"]),
|
|
1283
|
+
str3(team["name"]),
|
|
1284
|
+
str3(team["issue_count"] ?? 0),
|
|
1285
|
+
team["is_default"] === true ? "yes" : ""
|
|
1286
|
+
])
|
|
1277
1287
|
);
|
|
1278
1288
|
return { json: body.data, human };
|
|
1279
1289
|
}
|
|
@@ -1281,8 +1291,13 @@ async function workflowStates(ctx, team) {
|
|
|
1281
1291
|
const key = requiredTeam({ client: ctx.client, config: ctx.config }, team);
|
|
1282
1292
|
const body = await ctx.client.get(`/api/v1/teams/${key}/workflow_states`);
|
|
1283
1293
|
const human = body.data.length === 0 ? "No workflow states.\n" : table(
|
|
1284
|
-
["NAME", "TYPE"],
|
|
1285
|
-
body.data.map((state) => [
|
|
1294
|
+
["NAME", "TYPE", "COLOR", "ID"],
|
|
1295
|
+
body.data.map((state) => [
|
|
1296
|
+
str3(state["name"]),
|
|
1297
|
+
str3(state["type"]),
|
|
1298
|
+
str3(state["color"]) || "-",
|
|
1299
|
+
str3(state["id"])
|
|
1300
|
+
])
|
|
1286
1301
|
);
|
|
1287
1302
|
return { json: body.data, human };
|
|
1288
1303
|
}
|
|
@@ -1294,6 +1309,128 @@ async function userList(ctx) {
|
|
|
1294
1309
|
);
|
|
1295
1310
|
return { json: body.data, human };
|
|
1296
1311
|
}
|
|
1312
|
+
async function teamRename(ctx, key, name) {
|
|
1313
|
+
if (key === void 0 || name === void 0) {
|
|
1314
|
+
throw new UsageError("usage: harness team rename <key> --name <name>");
|
|
1315
|
+
}
|
|
1316
|
+
const body = await ctx.client.patch(`/api/v1/teams/${encodeURIComponent(key)}`, { name });
|
|
1317
|
+
return { json: body.data, human: `Renamed ${str3(body.data["key"])} to ${clean(body.data["name"])}
|
|
1318
|
+
` };
|
|
1319
|
+
}
|
|
1320
|
+
async function teamSetDefault(ctx, key) {
|
|
1321
|
+
if (key === void 0) throw new UsageError("usage: harness team set-default <key>");
|
|
1322
|
+
const body = await ctx.client.put(`/api/v1/teams/${encodeURIComponent(key)}/default`, {});
|
|
1323
|
+
return {
|
|
1324
|
+
json: body.data,
|
|
1325
|
+
human: `${str3(body.data["key"])} is now the default filing target
|
|
1326
|
+
`
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
async function stateAdd(ctx, opts) {
|
|
1330
|
+
const key = requiredTeam({ client: ctx.client, config: ctx.config }, opts.team);
|
|
1331
|
+
if (opts.name === void 0 || opts.type === void 0) {
|
|
1332
|
+
throw new UsageError("usage: harness state add --name <name> --type <type> [--color <color>] [--team <key>]");
|
|
1333
|
+
}
|
|
1334
|
+
let payload = { name: opts.name, type: opts.type };
|
|
1335
|
+
payload = putOpt2(payload, "color", opts.color);
|
|
1336
|
+
const body = await ctx.client.post(`/api/v1/teams/${key}/workflow_states`, payload);
|
|
1337
|
+
return { json: body.data, human: `Added ${clean(body.data["name"])} to ${key}
|
|
1338
|
+
` };
|
|
1339
|
+
}
|
|
1340
|
+
async function stateUpdate(ctx, id, opts) {
|
|
1341
|
+
const key = requiredTeam({ client: ctx.client, config: ctx.config }, opts.team);
|
|
1342
|
+
if (id === void 0) throw new UsageError("usage: harness state update <id> [--name <name>] [--type <type>] [--color <color>]");
|
|
1343
|
+
if (opts.name === void 0 && opts.type === void 0 && opts.color === void 0) {
|
|
1344
|
+
throw new UsageError("nothing to change \u2014 pass at least one of --name, --type, --color");
|
|
1345
|
+
}
|
|
1346
|
+
let payload = {};
|
|
1347
|
+
payload = putOpt2(payload, "name", opts.name);
|
|
1348
|
+
payload = putOpt2(payload, "type", opts.type);
|
|
1349
|
+
payload = putOpt2(payload, "color", opts.color);
|
|
1350
|
+
const body = await ctx.client.patch(`/api/v1/teams/${key}/workflow_states/${encodeURIComponent(id)}`, payload);
|
|
1351
|
+
return { json: body.data, human: `Updated ${clean(body.data["name"])}
|
|
1352
|
+
` };
|
|
1353
|
+
}
|
|
1354
|
+
async function stateOrder(ctx, ids, opts) {
|
|
1355
|
+
const key = requiredTeam({ client: ctx.client, config: ctx.config }, opts.team);
|
|
1356
|
+
if (ids === void 0) {
|
|
1357
|
+
throw new UsageError("usage: harness state order <id,id,id> [--team <key>] \u2014 every state id, in the order you want");
|
|
1358
|
+
}
|
|
1359
|
+
const state_ids = ids.split(",").map((id) => id.trim()).filter((id) => id !== "");
|
|
1360
|
+
if (state_ids.length === 0) throw new UsageError("no state ids given");
|
|
1361
|
+
const body = await ctx.client.put(`/api/v1/teams/${key}/workflow_states/order`, { state_ids });
|
|
1362
|
+
const human = table(
|
|
1363
|
+
["NAME", "TYPE"],
|
|
1364
|
+
body.data.map((state) => [str3(state["name"]), str3(state["type"])])
|
|
1365
|
+
);
|
|
1366
|
+
return { json: body.data, human };
|
|
1367
|
+
}
|
|
1368
|
+
async function stateDelete(ctx, id, opts) {
|
|
1369
|
+
const key = requiredTeam({ client: ctx.client, config: ctx.config }, opts.team);
|
|
1370
|
+
if (id === void 0) throw new UsageError("usage: harness state delete <id> --yes [--team <key>]");
|
|
1371
|
+
requireYes("state delete", id, opts.yes);
|
|
1372
|
+
await ctx.client.delete(`/api/v1/teams/${key}/workflow_states/${encodeURIComponent(id)}`);
|
|
1373
|
+
return { json: { deleted: id }, human: `Deleted workflow state ${id}
|
|
1374
|
+
` };
|
|
1375
|
+
}
|
|
1376
|
+
async function labelUpdate(ctx, id, opts) {
|
|
1377
|
+
if (id === void 0) throw new UsageError("usage: harness label update <id> [--name <name>] [--color <color>]");
|
|
1378
|
+
if (opts.name === void 0 && opts.color === void 0) {
|
|
1379
|
+
throw new UsageError("nothing to change \u2014 pass --name, --color, or both");
|
|
1380
|
+
}
|
|
1381
|
+
let payload = {};
|
|
1382
|
+
payload = putOpt2(payload, "name", opts.name);
|
|
1383
|
+
payload = putOpt2(payload, "color", opts.color);
|
|
1384
|
+
const body = await ctx.client.patch(`/api/v1/labels/${encodeURIComponent(id)}`, payload);
|
|
1385
|
+
return { json: body.data, human: `Updated label ${clean(body.data["name"])}
|
|
1386
|
+
` };
|
|
1387
|
+
}
|
|
1388
|
+
async function labelDelete(ctx, id, opts) {
|
|
1389
|
+
if (id === void 0) throw new UsageError("usage: harness label delete <id> --yes");
|
|
1390
|
+
requireYes("label delete", id, opts.yes);
|
|
1391
|
+
await ctx.client.delete(`/api/v1/labels/${encodeURIComponent(id)}`);
|
|
1392
|
+
return { json: { deleted: id }, human: `Deleted label ${id}
|
|
1393
|
+
` };
|
|
1394
|
+
}
|
|
1395
|
+
async function memberList(ctx) {
|
|
1396
|
+
const body = await ctx.client.get("/api/v1/members");
|
|
1397
|
+
const human = body.data.length === 0 ? "No members.\n" : table(
|
|
1398
|
+
["NAME", "EMAIL", "RANK", "STANDING", "ID"],
|
|
1399
|
+
body.data.map((member) => [
|
|
1400
|
+
str3(field(member, "user", "name")),
|
|
1401
|
+
str3(field(member, "user", "email")),
|
|
1402
|
+
str3(member["role"]) || "-",
|
|
1403
|
+
str3(member["standing"]),
|
|
1404
|
+
str3(field(member, "user", "id"))
|
|
1405
|
+
])
|
|
1406
|
+
);
|
|
1407
|
+
return { json: body.data, human };
|
|
1408
|
+
}
|
|
1409
|
+
async function memberAdd(ctx, opts) {
|
|
1410
|
+
if (opts.email === void 0 || opts.role === void 0) {
|
|
1411
|
+
throw new UsageError("usage: harness member add --email <email> --role <admin|staff|collaborator>");
|
|
1412
|
+
}
|
|
1413
|
+
const body = await ctx.client.post("/api/v1/members", { email: opts.email, role: opts.role });
|
|
1414
|
+
return {
|
|
1415
|
+
json: body.data,
|
|
1416
|
+
human: `Added ${str3(field(body.data, "user", "email"))} as ${str3(body.data["role"])}
|
|
1417
|
+
`
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
async function memberSetRole(ctx, userId, opts) {
|
|
1421
|
+
if (userId === void 0 || opts.role === void 0) {
|
|
1422
|
+
throw new UsageError("usage: harness member set-role <user-id> --role <admin|staff|collaborator|none>");
|
|
1423
|
+
}
|
|
1424
|
+
const body = await ctx.client.put(`/api/v1/members/${encodeURIComponent(userId)}/role`, { role: opts.role });
|
|
1425
|
+
const rank = str3(body.data["role"]);
|
|
1426
|
+
const human = rank === "" ? `Revoked the rank for ${str3(field(body.data, "user", "email"))}
|
|
1427
|
+
` : `${str3(field(body.data, "user", "email"))} is now ${rank}
|
|
1428
|
+
`;
|
|
1429
|
+
return { json: body.data, human };
|
|
1430
|
+
}
|
|
1431
|
+
function requireYes(command, id, yes) {
|
|
1432
|
+
if (yes !== true) throw new UsageError(`${command} ${id} is destructive \u2014 pass --yes to confirm`);
|
|
1433
|
+
}
|
|
1297
1434
|
async function upload(ctx, path2) {
|
|
1298
1435
|
return attachImage(ctx, path2);
|
|
1299
1436
|
}
|
|
@@ -1586,6 +1723,17 @@ export {
|
|
|
1586
1723
|
teamList,
|
|
1587
1724
|
workflowStates,
|
|
1588
1725
|
userList,
|
|
1726
|
+
teamRename,
|
|
1727
|
+
teamSetDefault,
|
|
1728
|
+
stateAdd,
|
|
1729
|
+
stateUpdate,
|
|
1730
|
+
stateOrder,
|
|
1731
|
+
stateDelete,
|
|
1732
|
+
labelUpdate,
|
|
1733
|
+
labelDelete,
|
|
1734
|
+
memberList,
|
|
1735
|
+
memberAdd,
|
|
1736
|
+
memberSetRole,
|
|
1589
1737
|
upload,
|
|
1590
1738
|
defaultCredentialsPath,
|
|
1591
1739
|
readCredentials,
|
package/dist/cli.js
CHANGED
|
@@ -28,12 +28,17 @@ import {
|
|
|
28
28
|
isHostTrusted,
|
|
29
29
|
json,
|
|
30
30
|
labelCreate,
|
|
31
|
+
labelDelete,
|
|
31
32
|
labelList,
|
|
33
|
+
labelUpdate,
|
|
32
34
|
linkAdd,
|
|
33
35
|
linkList,
|
|
34
36
|
linkRm,
|
|
35
37
|
list,
|
|
36
38
|
loginHelp,
|
|
39
|
+
memberAdd,
|
|
40
|
+
memberList,
|
|
41
|
+
memberSetRole,
|
|
37
42
|
milestoneCreate,
|
|
38
43
|
milestoneList,
|
|
39
44
|
prime,
|
|
@@ -48,9 +53,15 @@ import {
|
|
|
48
53
|
resolveConfig,
|
|
49
54
|
search,
|
|
50
55
|
show,
|
|
56
|
+
stateAdd,
|
|
57
|
+
stateDelete,
|
|
58
|
+
stateOrder,
|
|
59
|
+
stateUpdate,
|
|
51
60
|
stats,
|
|
52
61
|
teamCreate,
|
|
53
62
|
teamList,
|
|
63
|
+
teamRename,
|
|
64
|
+
teamSetDefault,
|
|
54
65
|
trustHost,
|
|
55
66
|
update,
|
|
56
67
|
upload,
|
|
@@ -59,7 +70,7 @@ import {
|
|
|
59
70
|
version,
|
|
60
71
|
workflowStates,
|
|
61
72
|
writeCredentials
|
|
62
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-FGBXAAKW.js";
|
|
63
74
|
|
|
64
75
|
// src/cli.ts
|
|
65
76
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -150,7 +161,7 @@ function isSet(value) {
|
|
|
150
161
|
}
|
|
151
162
|
async function loopbackLogin(baseUrl, options) {
|
|
152
163
|
const notify = options.notify ?? defaultNotify;
|
|
153
|
-
const
|
|
164
|
+
const state2 = generateState();
|
|
154
165
|
const verifier = generateVerifier();
|
|
155
166
|
const challenge = challengeFor(verifier);
|
|
156
167
|
const server = createServer();
|
|
@@ -165,13 +176,13 @@ async function loopbackLogin(baseUrl, options) {
|
|
|
165
176
|
return { ok: false, message: "could not open a loopback port for the browser handoff" };
|
|
166
177
|
}
|
|
167
178
|
try {
|
|
168
|
-
const query = new URLSearchParams({ port: String(port), state, challenge });
|
|
179
|
+
const query = new URLSearchParams({ port: String(port), state: state2, challenge });
|
|
169
180
|
const url = `${baseUrl}/cli/login?${query.toString()}`;
|
|
170
181
|
const openBrowser = options.openBrowser ?? defaultOpenBrowser;
|
|
171
182
|
openBrowser(url);
|
|
172
183
|
notify(`Opening ${url}`);
|
|
173
184
|
notify("Waiting for the browser authorization (Ctrl-C to abort)\u2026");
|
|
174
|
-
const code = await awaitCallback(server,
|
|
185
|
+
const code = await awaitCallback(server, state2, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
175
186
|
if (!code.ok) return code;
|
|
176
187
|
const token = await exchangeCode(baseUrl, code.code, verifier, options);
|
|
177
188
|
if (!token.ok) return token;
|
|
@@ -181,7 +192,7 @@ async function loopbackLogin(baseUrl, options) {
|
|
|
181
192
|
server.close();
|
|
182
193
|
}
|
|
183
194
|
}
|
|
184
|
-
function awaitCallback(server,
|
|
195
|
+
function awaitCallback(server, state2, timeoutMs) {
|
|
185
196
|
return new Promise((resolve) => {
|
|
186
197
|
const timer = setTimeout(() => {
|
|
187
198
|
resolve({ ok: false, message: "timed out waiting for the browser authorization" });
|
|
@@ -203,7 +214,7 @@ function awaitCallback(server, state, timeoutMs) {
|
|
|
203
214
|
return;
|
|
204
215
|
}
|
|
205
216
|
const code = url.searchParams.get("code");
|
|
206
|
-
if (url.searchParams.get("state") ===
|
|
217
|
+
if (url.searchParams.get("state") === state2 && code !== null && code !== "") {
|
|
207
218
|
respond(200, "Signed in \u2014 you can close this tab and return to the terminal.");
|
|
208
219
|
clearTimeout(timer);
|
|
209
220
|
resolve({ ok: true, code });
|
|
@@ -1297,6 +1308,12 @@ label.command("create").argument("[name]").option("--color <color>", "label colo
|
|
|
1297
1308
|
);
|
|
1298
1309
|
}
|
|
1299
1310
|
);
|
|
1311
|
+
label.command("update").description("rename or recolor a label").argument("[id]").option("--name <name>", "new name").option("--color <color>", "swatch name (red, blue, \u2026) or a hex code").option("--json", "emit JSON").action(async (id, opts) => {
|
|
1312
|
+
await withConfig(opts.json === true, true, (ctx) => labelUpdate(ctx, id, opts));
|
|
1313
|
+
});
|
|
1314
|
+
label.command("delete").description("delete a label (refused while issues carry it)").argument("[id]").option("--yes", "confirm the deletion").option("--json", "emit JSON").action(async (id, opts) => {
|
|
1315
|
+
await withConfig(opts.json === true, true, (ctx) => labelDelete(ctx, id, opts));
|
|
1316
|
+
});
|
|
1300
1317
|
var team = program.command("team").description("teams");
|
|
1301
1318
|
team.command("list").option("--json", "emit JSON").action(async (opts) => {
|
|
1302
1319
|
await withConfig(
|
|
@@ -1315,7 +1332,41 @@ team.command("create").description("create a team, seeded with the default workf
|
|
|
1315
1332
|
(ctx) => teamCreate(ctx, opts)
|
|
1316
1333
|
);
|
|
1317
1334
|
});
|
|
1318
|
-
|
|
1335
|
+
team.command("rename").description("rename a team (the key is create-only)").argument("[key]").option("--name <name>", "new display name").option("--json", "emit JSON").action(async (key, opts) => {
|
|
1336
|
+
await withConfig(opts.json === true, true, (ctx) => teamRename(ctx, key, opts.name));
|
|
1337
|
+
});
|
|
1338
|
+
team.command("set-default").description("make this team the organization's default filing target").argument("[key]").option("--json", "emit JSON").action(async (key, opts) => {
|
|
1339
|
+
await withConfig(opts.json === true, true, (ctx) => teamSetDefault(ctx, key));
|
|
1340
|
+
});
|
|
1341
|
+
var state = program.command("state").description("a team's workflow states");
|
|
1342
|
+
state.command("list").description("list a team's workflow states").option("--team <key>", "team key (or HARNESS_TEAM)").option("--json", "emit JSON").action(async (opts) => {
|
|
1343
|
+
await withConfig(opts.json === true, false, (ctx) => workflowStates(ctx, opts.team));
|
|
1344
|
+
});
|
|
1345
|
+
state.command("add").description("add a workflow state, appended after the team's existing ones").option("--team <key>", "team key (or HARNESS_TEAM)").option("--name <name>", "display name (required)").option("--type <type>", "backlog | unstarted | started | completed | canceled (required)").option("--color <color>", "swatch name").option("--json", "emit JSON").action(async (opts) => {
|
|
1346
|
+
await withConfig(opts.json === true, true, (ctx) => stateAdd(ctx, opts));
|
|
1347
|
+
});
|
|
1348
|
+
state.command("update").description("rename, retype or recolor a workflow state").argument("[id]").option("--team <key>", "team key (or HARNESS_TEAM)").option("--name <name>", "new name").option("--type <type>", "new type").option("--color <color>", "new swatch name").option("--json", "emit JSON").action(
|
|
1349
|
+
async (id, opts) => {
|
|
1350
|
+
await withConfig(opts.json === true, true, (ctx) => stateUpdate(ctx, id, opts));
|
|
1351
|
+
}
|
|
1352
|
+
);
|
|
1353
|
+
state.command("order").description("reorder a team's states \u2014 pass EVERY state id, comma separated, in the order you want").argument("[ids]").option("--team <key>", "team key (or HARNESS_TEAM)").option("--json", "emit JSON").action(async (ids, opts) => {
|
|
1354
|
+
await withConfig(opts.json === true, true, (ctx) => stateOrder(ctx, ids, opts));
|
|
1355
|
+
});
|
|
1356
|
+
state.command("delete").description("delete a workflow state (refused while issues sit in it)").argument("[id]").option("--team <key>", "team key (or HARNESS_TEAM)").option("--yes", "confirm the deletion").option("--json", "emit JSON").action(async (id, opts) => {
|
|
1357
|
+
await withConfig(opts.json === true, true, (ctx) => stateDelete(ctx, id, opts));
|
|
1358
|
+
});
|
|
1359
|
+
var member = program.command("member").description("the organization's tracker roster");
|
|
1360
|
+
member.command("list").description("who can reach this organization's tracker, and at what rank").option("--json", "emit JSON").action(async (opts) => {
|
|
1361
|
+
await withConfig(opts.json === true, false, (ctx) => memberList(ctx));
|
|
1362
|
+
});
|
|
1363
|
+
member.command("add").description("grant a rank to an existing account, minting the membership").option("--email <email>", "address of an existing account (required)").option("--role <role>", "admin | staff | collaborator (required)").option("--json", "emit JSON").action(async (opts) => {
|
|
1364
|
+
await withConfig(opts.json === true, true, (ctx) => memberAdd(ctx, opts));
|
|
1365
|
+
});
|
|
1366
|
+
member.command("set-role").description("change or revoke a member's rank; none revokes").argument("[user-id]").option("--role <role>", "admin | staff | collaborator | none (required)").option("--json", "emit JSON").action(async (userId, opts) => {
|
|
1367
|
+
await withConfig(opts.json === true, true, (ctx) => memberSetRole(ctx, userId, opts));
|
|
1368
|
+
});
|
|
1369
|
+
program.command("states").description("a team's workflow states (alias of `state list`)").option("--team <key>", "team key (or HARNESS_TEAM)").option("--json", "emit JSON").action(async (opts) => {
|
|
1319
1370
|
await withConfig(
|
|
1320
1371
|
opts.json === true,
|
|
1321
1372
|
false,
|
|
@@ -1340,7 +1391,7 @@ program.command("upload").description("upload a file; prints markdown to referen
|
|
|
1340
1391
|
});
|
|
1341
1392
|
});
|
|
1342
1393
|
program.command("mcp").description("run the MCP server over stdio (for agents)").action(async () => {
|
|
1343
|
-
const { startMcpServer } = await import("./server-
|
|
1394
|
+
const { startMcpServer } = await import("./server-ZRD4RTFU.js");
|
|
1344
1395
|
await startMcpServer();
|
|
1345
1396
|
});
|
|
1346
1397
|
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(
|
|
@@ -17,11 +17,16 @@ import {
|
|
|
17
17
|
docShow,
|
|
18
18
|
docUpdate,
|
|
19
19
|
labelCreate,
|
|
20
|
+
labelDelete,
|
|
20
21
|
labelList,
|
|
22
|
+
labelUpdate,
|
|
21
23
|
linkAdd,
|
|
22
24
|
linkList,
|
|
23
25
|
linkRm,
|
|
24
26
|
list,
|
|
27
|
+
memberAdd,
|
|
28
|
+
memberList,
|
|
29
|
+
memberSetRole,
|
|
25
30
|
milestoneCreate,
|
|
26
31
|
milestoneList,
|
|
27
32
|
projectCreate,
|
|
@@ -32,14 +37,20 @@ import {
|
|
|
32
37
|
resolveConfig,
|
|
33
38
|
search,
|
|
34
39
|
show,
|
|
40
|
+
stateAdd,
|
|
41
|
+
stateDelete,
|
|
42
|
+
stateOrder,
|
|
43
|
+
stateUpdate,
|
|
35
44
|
stats,
|
|
36
45
|
teamCreate,
|
|
37
46
|
teamList,
|
|
47
|
+
teamRename,
|
|
48
|
+
teamSetDefault,
|
|
38
49
|
update,
|
|
39
50
|
upload,
|
|
40
51
|
userList,
|
|
41
52
|
workflowStates
|
|
42
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-FGBXAAKW.js";
|
|
43
54
|
|
|
44
55
|
// src/mcp/server.ts
|
|
45
56
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -416,6 +427,120 @@ var TOOLS = [
|
|
|
416
427
|
shape: { team: z.string().optional().describe("team key; defaults to HARNESS_TEAM") },
|
|
417
428
|
handler: (ctx, args) => workflowStates(ctx, args["team"])
|
|
418
429
|
},
|
|
430
|
+
{
|
|
431
|
+
name: "team_rename",
|
|
432
|
+
description: "Rename a team. The key is create-only \u2014 it is the identifier prefix of every issue filed under the team \u2014 so this changes the display name only. Needs Harness administrator standing.",
|
|
433
|
+
readOnly: false,
|
|
434
|
+
shape: {
|
|
435
|
+
key: z.string().describe("team key, e.g. IOS"),
|
|
436
|
+
name: z.string().describe("new display name")
|
|
437
|
+
},
|
|
438
|
+
handler: (ctx, args) => teamRename(ctx, args["key"], args["name"])
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "team_set_default",
|
|
442
|
+
description: "Make a team the organization's default filing target, clearing the previous default. Needs Harness administrator standing.",
|
|
443
|
+
readOnly: false,
|
|
444
|
+
shape: { key: z.string().describe("team key") },
|
|
445
|
+
handler: (ctx, args) => teamSetDefault(ctx, args["key"])
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "state_add",
|
|
449
|
+
description: "Add a workflow state to a team, appended after its existing states. Type is one of backlog, unstarted, started, completed, canceled. Needs Harness administrator standing.",
|
|
450
|
+
readOnly: false,
|
|
451
|
+
shape: {
|
|
452
|
+
name: z.string().describe("display name"),
|
|
453
|
+
type: z.string().describe("backlog | unstarted | started | completed | canceled"),
|
|
454
|
+
color: z.string().optional().describe("swatch name"),
|
|
455
|
+
team: z.string().optional().describe("team key; defaults to HARNESS_TEAM")
|
|
456
|
+
},
|
|
457
|
+
handler: (ctx, args) => stateAdd(ctx, { team: args["team"], name: args["name"], type: args["type"], color: args["color"] })
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: "state_update",
|
|
461
|
+
description: "Rename, retype or recolor one of a team's workflow states. Pass at least one of name, type, color. Needs Harness administrator standing.",
|
|
462
|
+
readOnly: false,
|
|
463
|
+
shape: {
|
|
464
|
+
id: z.string().describe("workflow state id (from workflow_states)"),
|
|
465
|
+
name: z.string().optional(),
|
|
466
|
+
type: z.string().optional().describe("backlog | unstarted | started | completed | canceled"),
|
|
467
|
+
color: z.string().optional().describe("swatch name"),
|
|
468
|
+
team: z.string().optional().describe("team key; defaults to HARNESS_TEAM")
|
|
469
|
+
},
|
|
470
|
+
handler: (ctx, args) => stateUpdate(ctx, args["id"], {
|
|
471
|
+
team: args["team"],
|
|
472
|
+
name: args["name"],
|
|
473
|
+
type: args["type"],
|
|
474
|
+
color: args["color"]
|
|
475
|
+
})
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: "state_order",
|
|
479
|
+
description: "Reorder a team's workflow states. Send EVERY state id, in the order you want them \u2014 a partial list is refused rather than half-applied, because renumbering part of the order would collide with the positions it left alone. Call workflow_states first to get the ids. Needs Harness administrator standing.",
|
|
480
|
+
readOnly: false,
|
|
481
|
+
shape: {
|
|
482
|
+
state_ids: z.string().describe("comma-separated state ids, in the desired order \u2014 all of them"),
|
|
483
|
+
team: z.string().optional().describe("team key; defaults to HARNESS_TEAM")
|
|
484
|
+
},
|
|
485
|
+
handler: (ctx, args) => stateOrder(ctx, args["state_ids"], { team: args["team"] })
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: "state_delete",
|
|
489
|
+
description: "Delete a workflow state. Refused while issues still sit in it \u2014 move them out first. Needs Harness administrator standing.",
|
|
490
|
+
readOnly: false,
|
|
491
|
+
shape: {
|
|
492
|
+
id: z.string().describe("workflow state id"),
|
|
493
|
+
team: z.string().optional().describe("team key; defaults to HARNESS_TEAM")
|
|
494
|
+
},
|
|
495
|
+
// `yes` is the CLI's typo guard for a human at a shell; an MCP call is
|
|
496
|
+
// already an explicit, named request to delete this id.
|
|
497
|
+
handler: (ctx, args) => stateDelete(ctx, args["id"], { team: args["team"], yes: true })
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: "label_update",
|
|
501
|
+
description: "Rename or recolor a label. Color is a swatch name (purple, green, red, yellow, pink, blue, orange, gray) or a hex code. Needs Harness administrator standing.",
|
|
502
|
+
readOnly: false,
|
|
503
|
+
shape: {
|
|
504
|
+
id: z.string().describe("label id (from label_list)"),
|
|
505
|
+
name: z.string().optional(),
|
|
506
|
+
color: z.string().optional().describe("swatch name or hex code")
|
|
507
|
+
},
|
|
508
|
+
handler: (ctx, args) => labelUpdate(ctx, args["id"], { name: args["name"], color: args["color"] })
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "label_delete",
|
|
512
|
+
description: "Delete a label. Refused while issues still carry it, or while other labels are nested under it. Needs Harness administrator standing.",
|
|
513
|
+
readOnly: false,
|
|
514
|
+
shape: { id: z.string().describe("label id") },
|
|
515
|
+
handler: (ctx, args) => labelDelete(ctx, args["id"], { yes: true })
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: "member_list",
|
|
519
|
+
description: "Who can reach THIS organization's tracker, and at what rank \u2014 its grant holders plus its own admins. Not the same as user_list, which is the deployment-wide assignee picker and never lists a collaborator. Each entry carries both role (the granted rank, absent for an org admin) and standing (what that rank resolves to). Needs Harness administrator standing.",
|
|
520
|
+
readOnly: true,
|
|
521
|
+
shape: {},
|
|
522
|
+
handler: (ctx) => memberList(ctx)
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "member_add",
|
|
526
|
+
description: "Grant a tracker rank to an existing account by email, minting the organization membership if they have none. The account must already exist. Refused if they are already on the roster \u2014 use member_set_role to change a rank. Needs Harness administrator standing.",
|
|
527
|
+
readOnly: false,
|
|
528
|
+
shape: {
|
|
529
|
+
email: z.string().describe("address of an existing account"),
|
|
530
|
+
role: z.string().describe("admin | staff | collaborator")
|
|
531
|
+
},
|
|
532
|
+
handler: (ctx, args) => memberAdd(ctx, { email: args["email"], role: args["role"] })
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: "member_set_role",
|
|
536
|
+
description: "Change or revoke a member's tracker rank; 'none' revokes. Refused when it would remove the organization's last Harness administrator. Needs Harness administrator standing.",
|
|
537
|
+
readOnly: false,
|
|
538
|
+
shape: {
|
|
539
|
+
user_id: z.string().describe("the member's user id (from member_list)"),
|
|
540
|
+
role: z.string().describe("admin | staff | collaborator | none")
|
|
541
|
+
},
|
|
542
|
+
handler: (ctx, args) => memberSetRole(ctx, args["user_id"], { role: args["role"] })
|
|
543
|
+
},
|
|
419
544
|
{
|
|
420
545
|
name: "user_list",
|
|
421
546
|
description: "List users (for assignee resolution).",
|