atlassian-axi 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/adf.d.ts +13 -0
- package/dist/adf.js +188 -0
- package/dist/adf.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +124 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/conf/create.d.ts +14 -0
- package/dist/commands/conf/create.js +45 -0
- package/dist/commands/conf/create.js.map +1 -0
- package/dist/commands/conf/mine.d.ts +13 -0
- package/dist/commands/conf/mine.js +28 -0
- package/dist/commands/conf/mine.js.map +1 -0
- package/dist/commands/conf/update.d.ts +12 -0
- package/dist/commands/conf/update.js +53 -0
- package/dist/commands/conf/update.js.map +1 -0
- package/dist/commands/jira/assign.d.ts +9 -0
- package/dist/commands/jira/assign.js +20 -0
- package/dist/commands/jira/assign.js.map +1 -0
- package/dist/commands/jira/create.d.ts +16 -0
- package/dist/commands/jira/create.js +44 -0
- package/dist/commands/jira/create.js.map +1 -0
- package/dist/commands/jira/mine.d.ts +13 -0
- package/dist/commands/jira/mine.js +32 -0
- package/dist/commands/jira/mine.js.map +1 -0
- package/dist/commands/jira/move.d.ts +9 -0
- package/dist/commands/jira/move.js +17 -0
- package/dist/commands/jira/move.js.map +1 -0
- package/dist/commands/jira/search.d.ts +12 -0
- package/dist/commands/jira/search.js +30 -0
- package/dist/commands/jira/search.js.map +1 -0
- package/dist/commands/jira/sprint.d.ts +11 -0
- package/dist/commands/jira/sprint.js +38 -0
- package/dist/commands/jira/sprint.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +46 -0
- package/dist/config.js.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +14 -0
- package/dist/format.js.map +1 -0
- package/dist/http.d.ts +19 -0
- package/dist/http.js +80 -0
- package/dist/http.js.map +1 -0
- package/dist/resolvers.d.ts +12 -0
- package/dist/resolvers.js +64 -0
- package/dist/resolvers.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { api } from "../../http.js";
|
|
3
|
+
import { AxiHttpError } from "../../http.js";
|
|
4
|
+
import { markdownToStorage } from "../../adf.js";
|
|
5
|
+
export async function confUpdate(pageId, opts, ctx) {
|
|
6
|
+
let newContent = "";
|
|
7
|
+
if (opts.body === "-") {
|
|
8
|
+
const md = readFileSync(0, "utf-8");
|
|
9
|
+
newContent = markdownToStorage(md);
|
|
10
|
+
}
|
|
11
|
+
else if (opts.body) {
|
|
12
|
+
newContent = markdownToStorage(opts.body);
|
|
13
|
+
}
|
|
14
|
+
await doUpdate(ctx.site, pageId, newContent, !!opts.append, ctx.json);
|
|
15
|
+
}
|
|
16
|
+
async function doUpdate(site, pageId, newContent, append, json, retry = true) {
|
|
17
|
+
const page = await api(site, `/wiki/api/v2/pages/${pageId}?body-format=storage`);
|
|
18
|
+
const currentBody = page.body?.storage?.value || "";
|
|
19
|
+
const finalBody = append ? currentBody + "\n" + newContent : newContent;
|
|
20
|
+
const nextVersion = (page.version?.number || 1) + 1;
|
|
21
|
+
const payload = {
|
|
22
|
+
id: pageId,
|
|
23
|
+
status: "current",
|
|
24
|
+
title: page.title,
|
|
25
|
+
body: {
|
|
26
|
+
representation: "storage",
|
|
27
|
+
value: finalBody,
|
|
28
|
+
},
|
|
29
|
+
version: {
|
|
30
|
+
number: nextVersion,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
const result = await api(site, `/wiki/api/v2/pages/${pageId}`, {
|
|
35
|
+
method: "PUT",
|
|
36
|
+
body: payload,
|
|
37
|
+
});
|
|
38
|
+
if (json) {
|
|
39
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
process.stdout.write(`updated ${pageId} → v${nextVersion}\n`);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof AxiHttpError && err.status === 409 && retry) {
|
|
46
|
+
await doUpdate(site, pageId, newContent, append, json, false);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/commands/conf/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAOjD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,IAAyC,EACzC,GAAe;IAEf,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACpC,UAAU,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACrB,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;AACxE,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,IAAgB,EAChB,MAAc,EACd,UAAkB,EAClB,MAAe,EACf,IAAa,EACb,KAAK,GAAG,IAAI;IAEZ,MAAM,IAAI,GAAG,MAAM,GAAG,CACpB,IAAI,EACJ,sBAAsB,MAAM,sBAAsB,CACnD,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,GAAG,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IACxE,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAEpD,MAAM,OAAO,GAAG;QACd,EAAE,EAAE,MAAM;QACV,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE;YACJ,cAAc,EAAE,SAAS;YACzB,KAAK,EAAE,SAAS;SACjB;QACD,OAAO,EAAE;YACP,MAAM,EAAE,WAAW;SACpB;KACF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAM,IAAI,EAAE,sBAAsB,MAAM,EAAE,EAAE;YAClE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,MAAM,OAAO,WAAW,IAAI,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;YAC/D,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SiteConfig } from "../../config.js";
|
|
2
|
+
interface CmdContext {
|
|
3
|
+
site: SiteConfig & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
json: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function jiraAssign(key: string, name: string, _opts: Record<string, unknown>, ctx: CmdContext): Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { api } from "../../http.js";
|
|
2
|
+
import { resolveAccountId } from "../../resolvers.js";
|
|
3
|
+
export async function jiraAssign(key, name, _opts, ctx) {
|
|
4
|
+
const candidates = await resolveAccountId(ctx.site, name);
|
|
5
|
+
if (!candidates.length) {
|
|
6
|
+
process.stderr.write(`error(resolve): no user matching "${name}"\n`);
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
if (candidates.length > 1) {
|
|
10
|
+
const list = candidates.map((c) => ` ${c.displayName} (${c.accountId})`).join("\n");
|
|
11
|
+
process.stderr.write(`error(resolve): ambiguous name "${name}"\nhint: candidates:\n${list}\n`);
|
|
12
|
+
throw new Error("exit:3");
|
|
13
|
+
}
|
|
14
|
+
await api(ctx.site, `/rest/api/3/issue/${key}/assignee`, {
|
|
15
|
+
method: "PUT",
|
|
16
|
+
body: { accountId: candidates[0].accountId },
|
|
17
|
+
});
|
|
18
|
+
process.stdout.write(`${key} assigned to ${candidates[0].displayName}\n`);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=assign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assign.js","sourceRoot":"","sources":["../../../src/commands/jira/assign.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAOtD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,IAAY,EACZ,KAA8B,EAC9B,GAAe;IAEf,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,KAAK,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mCAAmC,IAAI,yBAAyB,IAAI,IAAI,CACzE,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,GAAG,WAAW,EAAE;QACvD,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE;KAC7C,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,gBAAgB,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SiteConfig } from "../../config.js";
|
|
2
|
+
interface CmdContext {
|
|
3
|
+
site: SiteConfig & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
json: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function jiraCreate(opts: {
|
|
9
|
+
project: string;
|
|
10
|
+
type?: string;
|
|
11
|
+
summary: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
assignee?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}, ctx: CmdContext): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { api } from "../../http.js";
|
|
3
|
+
import { resolveAccountId } from "../../resolvers.js";
|
|
4
|
+
import { markdownToAdf } from "../../adf.js";
|
|
5
|
+
export async function jiraCreate(opts, ctx) {
|
|
6
|
+
const fields = {
|
|
7
|
+
project: { key: opts.project },
|
|
8
|
+
issuetype: { name: opts.type || "Task" },
|
|
9
|
+
summary: opts.summary,
|
|
10
|
+
};
|
|
11
|
+
if (opts.label) {
|
|
12
|
+
fields.labels = opts.label.split(",").map((l) => l.trim());
|
|
13
|
+
}
|
|
14
|
+
if (opts.assignee) {
|
|
15
|
+
const candidates = await resolveAccountId(ctx.site, opts.assignee);
|
|
16
|
+
if (!candidates.length) {
|
|
17
|
+
process.stderr.write(`error(resolve): no user matching "${opts.assignee}"\n`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (candidates.length > 1) {
|
|
21
|
+
const list = candidates.map((c) => ` ${c.displayName} (${c.accountId})`).join("\n");
|
|
22
|
+
process.stderr.write(`error(resolve): ambiguous assignee "${opts.assignee}"\nhint: candidates:\n${list}\n`);
|
|
23
|
+
throw new Error("exit:3");
|
|
24
|
+
}
|
|
25
|
+
fields.assignee = { accountId: candidates[0].accountId };
|
|
26
|
+
}
|
|
27
|
+
if (opts.description === "-") {
|
|
28
|
+
const md = readFileSync(0, "utf-8");
|
|
29
|
+
fields.description = markdownToAdf(md);
|
|
30
|
+
}
|
|
31
|
+
else if (opts.description) {
|
|
32
|
+
fields.description = markdownToAdf(opts.description);
|
|
33
|
+
}
|
|
34
|
+
const body = await api(ctx.site, "/rest/api/3/issue", {
|
|
35
|
+
method: "POST",
|
|
36
|
+
body: { fields },
|
|
37
|
+
});
|
|
38
|
+
if (ctx.json) {
|
|
39
|
+
process.stdout.write(JSON.stringify(body, null, 2) + "\n");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
process.stdout.write(`${body.key} ${ctx.site.baseUrl}/browse/${body.key}\n`);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/jira/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAOC,EACD,GAAe;IAEf,MAAM,MAAM,GAAwB;QAClC,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE;QAC9B,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;IAEF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uCAAuC,IAAI,CAAC,QAAQ,yBAAyB,IAAI,IAAI,CACtF,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QACD,MAAM,CAAC,QAAQ,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,KAAK,GAAG,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAM,GAAG,CAAC,IAAI,EAAE,mBAAmB,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,EAAE,MAAM,EAAE;KACjB,CAAC,CAAC;IAEH,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,WAAW,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SiteConfig } from "../../config.js";
|
|
2
|
+
interface CmdContext {
|
|
3
|
+
site: SiteConfig & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
json: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function jiraMine(opts: {
|
|
9
|
+
done?: boolean;
|
|
10
|
+
since?: string;
|
|
11
|
+
project?: string;
|
|
12
|
+
}, ctx: CmdContext): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { api } from "../../http.js";
|
|
2
|
+
import { tsv, header } from "../../format.js";
|
|
3
|
+
function parseSince(since) {
|
|
4
|
+
const match = since.match(/^(\d+)([dwm])$/);
|
|
5
|
+
if (!match)
|
|
6
|
+
return "-4w";
|
|
7
|
+
return `-${match[1]}${match[2]}`;
|
|
8
|
+
}
|
|
9
|
+
export async function jiraMine(opts, ctx) {
|
|
10
|
+
const since = parseSince(opts.since || "4w");
|
|
11
|
+
let jql = `assignee = currentUser() AND statusCategory = Done AND resolved >= ${since}`;
|
|
12
|
+
if (opts.project)
|
|
13
|
+
jql += ` AND project = "${opts.project}"`;
|
|
14
|
+
jql += " ORDER BY resolved DESC";
|
|
15
|
+
const encodedJql = encodeURIComponent(jql);
|
|
16
|
+
const path = `/rest/api/3/search/jql?jql=${encodedJql}&fields=key,summary,project,resolutiondate&maxResults=50`;
|
|
17
|
+
const body = await api(ctx.site, path);
|
|
18
|
+
if (ctx.json) {
|
|
19
|
+
process.stdout.write(JSON.stringify(body, null, 2) + "\n");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const issues = body.issues || [];
|
|
23
|
+
process.stdout.write(header(`${issues.length} done issues (since ${opts.since || "4w"})`) + "\n");
|
|
24
|
+
const rows = issues.map((i) => [
|
|
25
|
+
i.key,
|
|
26
|
+
(i.fields?.resolutiondate || "").slice(0, 10),
|
|
27
|
+
i.fields?.project?.key || "",
|
|
28
|
+
i.fields?.summary || "",
|
|
29
|
+
]);
|
|
30
|
+
process.stdout.write(tsv(["key", "resolved", "project", "summary"], rows) + "\n");
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=mine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mine.js","sourceRoot":"","sources":["../../../src/commands/jira/mine.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,GAAG,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAO9C,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAA0D,EAC1D,GAAe;IAEf,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAC7C,IAAI,GAAG,GAAG,sEAAsE,KAAK,EAAE,CAAC;IACxF,IAAI,IAAI,CAAC,OAAO;QAAE,GAAG,IAAI,mBAAmB,IAAI,CAAC,OAAO,GAAG,CAAC;IAC5D,GAAG,IAAI,yBAAyB,CAAC;IAEjC,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,8BAA8B,UAAU,0DAA0D,CAAC;IAEhH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAM,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE5C,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,uBAAuB,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAElG,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC;QAClC,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,CAAC,MAAM,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC7C,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE;QAC5B,CAAC,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE;KACxB,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AACpF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SiteConfig } from "../../config.js";
|
|
2
|
+
interface CmdContext {
|
|
3
|
+
site: SiteConfig & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
json: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function jiraMove(key: string, status: string, _opts: Record<string, unknown>, ctx: CmdContext): Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { api } from "../../http.js";
|
|
2
|
+
export async function jiraMove(key, status, _opts, ctx) {
|
|
3
|
+
const body = await api(ctx.site, `/rest/api/3/issue/${key}/transitions`);
|
|
4
|
+
const transitions = body.transitions || [];
|
|
5
|
+
const match = transitions.find((t) => t.name.toLowerCase() === status.toLowerCase());
|
|
6
|
+
if (!match) {
|
|
7
|
+
const valid = transitions.map((t) => t.name).join(", ");
|
|
8
|
+
process.stderr.write(`error(transition): "${status}" is not a valid transition for ${key}\nhint: valid targets: ${valid}\n`);
|
|
9
|
+
throw new Error("exit:3");
|
|
10
|
+
}
|
|
11
|
+
await api(ctx.site, `/rest/api/3/issue/${key}/transitions`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
body: { transition: { id: match.id } },
|
|
14
|
+
});
|
|
15
|
+
process.stdout.write(`${key} → ${match.name}\n`);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=move.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/commands/jira/move.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAOpC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAW,EACX,MAAc,EACd,KAA8B,EAC9B,GAAe;IAEf,MAAM,IAAI,GAAG,MAAM,GAAG,CAAM,GAAG,CAAC,IAAI,EAAE,qBAAqB,GAAG,cAAc,CAAC,CAAC;IAC9E,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;IAE3C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAC5B,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAC1D,CAAC;IAEF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uBAAuB,MAAM,mCAAmC,GAAG,0BAA0B,KAAK,IAAI,CACvG,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,GAAG,cAAc,EAAE;QAC1D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE;KACvC,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SiteConfig } from "../../config.js";
|
|
2
|
+
interface CmdContext {
|
|
3
|
+
site: SiteConfig & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
json: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function jiraSearch(opts: {
|
|
9
|
+
jql: string;
|
|
10
|
+
fields?: string;
|
|
11
|
+
}, ctx: CmdContext): Promise<void>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { api } from "../../http.js";
|
|
2
|
+
import { tsv } from "../../format.js";
|
|
3
|
+
export async function jiraSearch(opts, ctx) {
|
|
4
|
+
const fields = opts.fields || "key,summary,status";
|
|
5
|
+
const fieldList = fields.split(",").map((f) => f.trim());
|
|
6
|
+
const apiFields = fieldList.filter((f) => f !== "key").join(",");
|
|
7
|
+
const encodedJql = encodeURIComponent(opts.jql);
|
|
8
|
+
const path = `/rest/api/3/search/jql?jql=${encodedJql}&fields=${apiFields}&maxResults=50`;
|
|
9
|
+
const body = await api(ctx.site, path);
|
|
10
|
+
if (ctx.json) {
|
|
11
|
+
process.stdout.write(JSON.stringify(body, null, 2) + "\n");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const issues = body.issues || [];
|
|
15
|
+
const rows = issues.map((i) => {
|
|
16
|
+
return fieldList.map((f) => {
|
|
17
|
+
if (f === "key")
|
|
18
|
+
return i.key;
|
|
19
|
+
const val = i.fields?.[f];
|
|
20
|
+
if (val === null || val === undefined)
|
|
21
|
+
return "";
|
|
22
|
+
if (typeof val === "object") {
|
|
23
|
+
return val.name || val.displayName || val.key || JSON.stringify(val);
|
|
24
|
+
}
|
|
25
|
+
return String(val);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
process.stdout.write(tsv(fieldList, rows) + "\n");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/commands/jira/search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAOtC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAsC,EACtC,GAAe;IAEf,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,oBAAoB,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEjE,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,8BAA8B,UAAU,WAAW,SAAS,gBAAgB,CAAC;IAC1F,MAAM,IAAI,GAAG,MAAM,GAAG,CAAM,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE5C,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;QACjC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,IAAI,CAAC,KAAK,KAAK;gBAAE,OAAO,CAAC,CAAC,GAAG,CAAC;YAC9B,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;YACjD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,OAAO,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACvE,CAAC;YACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { api } from "../../http.js";
|
|
2
|
+
import { resolveActiveSprint, resolveBoard } from "../../resolvers.js";
|
|
3
|
+
import { tsv, header } from "../../format.js";
|
|
4
|
+
export async function jiraSprint(opts, ctx) {
|
|
5
|
+
const boardId = opts.board
|
|
6
|
+
? await resolveBoard(ctx.site, opts.board)
|
|
7
|
+
: ctx.site.defaultBoard;
|
|
8
|
+
if (!boardId) {
|
|
9
|
+
process.stderr.write("error(config): no board specified and no defaultBoard in config\nhint: use --board <id|name> or set defaultBoard in config\n");
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
const sprint = await resolveActiveSprint(ctx.site, boardId);
|
|
13
|
+
const body = await api(ctx.site, `/rest/agile/1.0/sprint/${sprint.id}/issue?fields=summary,status,assignee,labels,priority&maxResults=100`);
|
|
14
|
+
if (ctx.json) {
|
|
15
|
+
process.stdout.write(JSON.stringify(body, null, 2) + "\n");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const issues = body.issues || [];
|
|
19
|
+
const counts = { todo: 0, "in-progress": 0, done: 0 };
|
|
20
|
+
for (const issue of issues) {
|
|
21
|
+
const cat = issue.fields?.status?.statusCategory?.key;
|
|
22
|
+
if (cat === "done")
|
|
23
|
+
counts.done++;
|
|
24
|
+
else if (cat === "new")
|
|
25
|
+
counts.todo++;
|
|
26
|
+
else
|
|
27
|
+
counts["in-progress"]++;
|
|
28
|
+
}
|
|
29
|
+
process.stdout.write(header(`${sprint.name} — ${issues.length} issues: ${counts.todo} todo / ${counts["in-progress"]} in-progress / ${counts.done} done`) + "\n");
|
|
30
|
+
const rows = issues.map((i) => [
|
|
31
|
+
i.key,
|
|
32
|
+
i.fields?.status?.name || "",
|
|
33
|
+
i.fields?.assignee?.displayName || "unassigned",
|
|
34
|
+
i.fields?.summary || "",
|
|
35
|
+
]);
|
|
36
|
+
process.stdout.write(tsv(["key", "status", "assignee", "summary"], rows) + "\n");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=sprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sprint.js","sourceRoot":"","sources":["../../../src/commands/jira/sprint.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAO9C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAwB,EAAE,GAAe;IACxE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK;QACxB,CAAC,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC;QAC1C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;IAE1B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8HAA8H,CAAC,CAAC;QACrJ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,MAAM,GAAG,CACpB,GAAG,CAAC,IAAI,EACR,0BAA0B,MAAM,CAAC,EAAE,sEAAsE,CAC1G,CAAC;IAEF,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACtD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC;QACtD,IAAI,GAAG,KAAK,MAAM;YAAE,MAAM,CAAC,IAAI,EAAE,CAAC;aAC7B,IAAI,GAAG,KAAK,KAAK;YAAE,MAAM,CAAC,IAAI,EAAE,CAAC;;YACjC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,MAAM,CACJ,GAAG,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,IAAI,WAAW,MAAM,CAAC,aAAa,CAAC,kBAAkB,MAAM,CAAC,IAAI,OAAO,CAC7H,GAAG,IAAI,CACT,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC;QAClC,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE;QAC5B,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,IAAI,YAAY;QAC/C,CAAC,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE;KACxB,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AACnF,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SiteConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
email: string;
|
|
4
|
+
tokenEnv: string;
|
|
5
|
+
defaultBoard?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface Config {
|
|
8
|
+
defaultSite: string;
|
|
9
|
+
sites: Record<string, SiteConfig>;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadConfig(): Config;
|
|
12
|
+
export declare function resolveSite(config: Config, siteFlag?: string): SiteConfig & {
|
|
13
|
+
name: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function getToken(site: SiteConfig): string;
|
|
16
|
+
export declare function getConfigDir(): string;
|
|
17
|
+
export declare function getCacheDir(): string;
|
|
18
|
+
export declare function initConfig(sites: Config["sites"], defaultSite: string): void;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
const CONFIG_DIR = join(homedir(), ".config", "atlassian-axi");
|
|
5
|
+
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
6
|
+
export function loadConfig() {
|
|
7
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
8
|
+
error(`config not found: ${CONFIG_PATH}\nRun: axi init`);
|
|
9
|
+
}
|
|
10
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
11
|
+
}
|
|
12
|
+
export function resolveSite(config, siteFlag) {
|
|
13
|
+
const name = siteFlag || config.defaultSite;
|
|
14
|
+
const site = config.sites[name];
|
|
15
|
+
if (!site) {
|
|
16
|
+
error(`unknown site "${name}". Available: ${Object.keys(config.sites).join(", ")}`);
|
|
17
|
+
}
|
|
18
|
+
return { ...site, name };
|
|
19
|
+
}
|
|
20
|
+
export function getToken(site) {
|
|
21
|
+
const token = process.env[site.tokenEnv];
|
|
22
|
+
if (!token) {
|
|
23
|
+
error(`env var ${site.tokenEnv} is not set`);
|
|
24
|
+
}
|
|
25
|
+
return token;
|
|
26
|
+
}
|
|
27
|
+
export function getConfigDir() {
|
|
28
|
+
return CONFIG_DIR;
|
|
29
|
+
}
|
|
30
|
+
export function getCacheDir() {
|
|
31
|
+
const dir = join(homedir(), ".cache", "atlassian-axi");
|
|
32
|
+
if (!existsSync(dir))
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
export function initConfig(sites, defaultSite) {
|
|
37
|
+
if (!existsSync(CONFIG_DIR))
|
|
38
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
39
|
+
const config = { defaultSite, sites };
|
|
40
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
41
|
+
}
|
|
42
|
+
function error(msg) {
|
|
43
|
+
process.stderr.write(`error(config): ${msg}\n`);
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAclC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,qBAAqB,WAAW,iBAAiB,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,QAAiB;IAC3D,MAAM,IAAI,GAAG,QAAQ,IAAI,MAAM,CAAC,WAAW,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,KAAK,CAAC,iBAAiB,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAgB;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,CAAC,WAAW,IAAI,CAAC,QAAQ,aAAa,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAsB,EAAE,WAAmB;IACpE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,MAAM,MAAM,GAAW,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC9C,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,KAAK,CAAC,GAAW;IACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/format.d.ts
ADDED
package/dist/format.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function tsv(headers, rows) {
|
|
2
|
+
const lines = rows.map((r) => r.join("\t"));
|
|
3
|
+
return lines.join("\n");
|
|
4
|
+
}
|
|
5
|
+
export function header(text) {
|
|
6
|
+
return text;
|
|
7
|
+
}
|
|
8
|
+
export function formatError(status, message, hint) {
|
|
9
|
+
let out = `error(${status}): ${message}`;
|
|
10
|
+
if (hint)
|
|
11
|
+
out += `\nhint: ${hint}`;
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,CAAC,OAAiB,EAAE,IAAgB;IACrD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe,EAAE,IAAa;IACxE,IAAI,GAAG,GAAG,SAAS,MAAM,MAAM,OAAO,EAAE,CAAC;IACzC,IAAI,IAAI;QAAE,GAAG,IAAI,WAAW,IAAI,EAAE,CAAC;IACnC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SiteConfig } from "./config.js";
|
|
2
|
+
export interface ApiError {
|
|
3
|
+
status: number;
|
|
4
|
+
message: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class AxiHttpError extends Error {
|
|
8
|
+
status: number;
|
|
9
|
+
body: string;
|
|
10
|
+
hint?: string | undefined;
|
|
11
|
+
constructor(status: number, body: string, hint?: string | undefined);
|
|
12
|
+
}
|
|
13
|
+
export interface RequestOpts {
|
|
14
|
+
method?: string;
|
|
15
|
+
body?: unknown;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function api<T = unknown>(site: SiteConfig, path: string, opts?: RequestOpts): Promise<T>;
|
|
19
|
+
export declare function apiPaginated<T>(site: SiteConfig, path: string, extractor: (body: any) => T[]): Promise<T[]>;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getToken } from "./config.js";
|
|
2
|
+
export class AxiHttpError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
body;
|
|
5
|
+
hint;
|
|
6
|
+
constructor(status, body, hint) {
|
|
7
|
+
super(`error(${status}): ${body}`);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.body = body;
|
|
10
|
+
this.hint = hint;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function authHeader(site) {
|
|
14
|
+
const token = getToken(site);
|
|
15
|
+
return "Basic " + Buffer.from(`${site.email}:${token}`).toString("base64");
|
|
16
|
+
}
|
|
17
|
+
export async function api(site, path, opts = {}) {
|
|
18
|
+
const url = `${site.baseUrl}${path}`;
|
|
19
|
+
const method = opts.method || "GET";
|
|
20
|
+
const headers = {
|
|
21
|
+
Authorization: authHeader(site),
|
|
22
|
+
Accept: "application/json",
|
|
23
|
+
...opts.headers,
|
|
24
|
+
};
|
|
25
|
+
if (opts.body) {
|
|
26
|
+
headers["Content-Type"] = "application/json";
|
|
27
|
+
}
|
|
28
|
+
let lastErr;
|
|
29
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
30
|
+
const res = await fetch(url, {
|
|
31
|
+
method,
|
|
32
|
+
headers,
|
|
33
|
+
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
34
|
+
});
|
|
35
|
+
if (res.ok) {
|
|
36
|
+
const text = await res.text();
|
|
37
|
+
return text ? JSON.parse(text) : {};
|
|
38
|
+
}
|
|
39
|
+
if (res.status === 429) {
|
|
40
|
+
const retry = res.headers.get("Retry-After");
|
|
41
|
+
const wait = retry ? parseInt(retry, 10) * 1000 : (attempt + 1) * 2000;
|
|
42
|
+
await sleep(wait);
|
|
43
|
+
lastErr = new AxiHttpError(429, "rate limited");
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const text = await res.text().catch(() => "");
|
|
47
|
+
let msg = `${res.statusText || "request failed"}`;
|
|
48
|
+
try {
|
|
49
|
+
const j = JSON.parse(text);
|
|
50
|
+
if (j.errorMessages?.length)
|
|
51
|
+
msg = j.errorMessages.join("; ");
|
|
52
|
+
else if (j.message)
|
|
53
|
+
msg = j.message;
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
throw new AxiHttpError(res.status, msg);
|
|
57
|
+
}
|
|
58
|
+
throw lastErr || new AxiHttpError(500, "max retries exceeded");
|
|
59
|
+
}
|
|
60
|
+
export async function apiPaginated(site, path, extractor) {
|
|
61
|
+
const results = [];
|
|
62
|
+
let url = path;
|
|
63
|
+
while (url) {
|
|
64
|
+
const body = await api(site, url);
|
|
65
|
+
results.push(...extractor(body));
|
|
66
|
+
if (body.nextPageToken) {
|
|
67
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
68
|
+
const base = url.replace(/[&?]nextPageToken=[^&]*/, "");
|
|
69
|
+
url = `${base}${sep}nextPageToken=${body.nextPageToken}`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
function sleep(ms) {
|
|
78
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SiteConfig } from "./config.js";
|
|
2
|
+
export interface SprintInfo {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
state: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveActiveSprint(site: SiteConfig, boardId: number): Promise<SprintInfo>;
|
|
8
|
+
export declare function resolveAccountId(site: SiteConfig, query: string): Promise<{
|
|
9
|
+
accountId: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
}[]>;
|
|
12
|
+
export declare function resolveBoard(site: SiteConfig, nameOrId: string): Promise<number>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getCacheDir } from "./config.js";
|
|
4
|
+
import { api } from "./http.js";
|
|
5
|
+
function cacheGet(key) {
|
|
6
|
+
const file = join(getCacheDir(), key + ".json");
|
|
7
|
+
if (!existsSync(file))
|
|
8
|
+
return undefined;
|
|
9
|
+
try {
|
|
10
|
+
const entry = JSON.parse(readFileSync(file, "utf-8"));
|
|
11
|
+
if (Date.now() > entry.expires)
|
|
12
|
+
return undefined;
|
|
13
|
+
return entry.value;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function cacheSet(key, value, ttlMs) {
|
|
20
|
+
const file = join(getCacheDir(), key + ".json");
|
|
21
|
+
const entry = { value, expires: Date.now() + ttlMs };
|
|
22
|
+
writeFileSync(file, JSON.stringify(entry));
|
|
23
|
+
}
|
|
24
|
+
const TTL_SPRINT = 10 * 60 * 1000;
|
|
25
|
+
const TTL_USER = 24 * 60 * 60 * 1000;
|
|
26
|
+
export async function resolveActiveSprint(site, boardId) {
|
|
27
|
+
const key = `sprint_${site.baseUrl.replace(/\W/g, "_")}_${boardId}`;
|
|
28
|
+
const cached = cacheGet(key);
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
const body = await api(site, `/rest/agile/1.0/board/${boardId}/sprint?state=active`);
|
|
32
|
+
const sprints = body.values || [];
|
|
33
|
+
if (!sprints.length) {
|
|
34
|
+
throw new Error("no active sprint found for board " + boardId);
|
|
35
|
+
}
|
|
36
|
+
const sprint = sprints[0];
|
|
37
|
+
cacheSet(key, sprint, TTL_SPRINT);
|
|
38
|
+
return sprint;
|
|
39
|
+
}
|
|
40
|
+
export async function resolveAccountId(site, query) {
|
|
41
|
+
const key = `user_${site.baseUrl.replace(/\W/g, "_")}_${query.replace(/\W/g, "_")}`;
|
|
42
|
+
const cached = cacheGet(key);
|
|
43
|
+
if (cached)
|
|
44
|
+
return cached;
|
|
45
|
+
const users = await api(site, `/rest/api/3/user/search?query=${encodeURIComponent(query)}&maxResults=10`);
|
|
46
|
+
const results = users
|
|
47
|
+
.filter((u) => u.accountType === "atlassian")
|
|
48
|
+
.map((u) => ({ accountId: u.accountId, displayName: u.displayName }));
|
|
49
|
+
if (results.length)
|
|
50
|
+
cacheSet(key, results, TTL_USER);
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
export async function resolveBoard(site, nameOrId) {
|
|
54
|
+
const asNum = parseInt(nameOrId, 10);
|
|
55
|
+
if (!isNaN(asNum))
|
|
56
|
+
return asNum;
|
|
57
|
+
const body = await api(site, `/rest/agile/1.0/board?name=${encodeURIComponent(nameOrId)}&maxResults=10`);
|
|
58
|
+
const boards = body.values || [];
|
|
59
|
+
if (!boards.length) {
|
|
60
|
+
throw new Error(`no board matching "${nameOrId}"`);
|
|
61
|
+
}
|
|
62
|
+
return boards[0].id;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=resolvers.js.map
|