@tmcayden/youtrack-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 +107 -0
- package/bin/youtrack-axi.js +8 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +91 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/admin.d.ts +7 -0
- package/dist/commands/admin.js +96 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/issues.d.ts +10 -0
- package/dist/commands/issues.js +117 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/mutate.d.ts +9 -0
- package/dist/commands/mutate.js +115 -0
- package/dist/commands/mutate.js.map +1 -0
- package/dist/commands/setup.d.ts +4 -0
- package/dist/commands/setup.js +101 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/work.d.ts +5 -0
- package/dist/commands/work.js +145 -0
- package/dist/commands/work.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/flags.d.ts +25 -0
- package/dist/flags.js +123 -0
- package/dist/flags.js.map +1 -0
- package/dist/format.d.ts +41 -0
- package/dist/format.js +143 -0
- package/dist/format.js.map +1 -0
- package/dist/render.d.ts +10 -0
- package/dist/render.js +13 -0
- package/dist/render.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/youtrack.d.ts +55 -0
- package/dist/youtrack.js +116 -0
- package/dist/youtrack.js.map +1 -0
- package/package.json +46 -0
package/dist/format.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
export const DESCRIPTION_PREVIEW_CHARS = 800;
|
|
3
|
+
/**
|
|
4
|
+
* YouTrack returns every custom field as {name, value:{name|login|...}}. Agents
|
|
5
|
+
* pay for that nesting twice - once in tokens, once in traversal - so it is
|
|
6
|
+
* flattened to plain scalars here and never reaches stdout in nested form.
|
|
7
|
+
*/
|
|
8
|
+
export function flattenCustomFields(fields) {
|
|
9
|
+
const flat = {};
|
|
10
|
+
for (const field of fields ?? []) {
|
|
11
|
+
if (!field?.name)
|
|
12
|
+
continue;
|
|
13
|
+
flat[field.name] = scalarize(field.value);
|
|
14
|
+
}
|
|
15
|
+
return flat;
|
|
16
|
+
}
|
|
17
|
+
function scalarize(value) {
|
|
18
|
+
if (value === null || value === undefined)
|
|
19
|
+
return "";
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return value.map(scalarize).filter(Boolean).join(", ");
|
|
22
|
+
if (typeof value === "object") {
|
|
23
|
+
const record = value;
|
|
24
|
+
for (const key of ["name", "presentation", "fullName", "login", "text"]) {
|
|
25
|
+
const candidate = record[key];
|
|
26
|
+
if (typeof candidate === "string" && candidate.length > 0)
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
const minutes = record["minutes"];
|
|
30
|
+
return typeof minutes === "number" ? formatMinutes(minutes) : "";
|
|
31
|
+
}
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
export function issueRow(issue) {
|
|
35
|
+
const fields = flattenCustomFields(issue.customFields);
|
|
36
|
+
return {
|
|
37
|
+
id: issue.idReadable ?? "",
|
|
38
|
+
summary: issue.summary ?? "",
|
|
39
|
+
state: fields["State"] ?? "",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function issueRowWithFields(issue, extraFields) {
|
|
43
|
+
const flat = flattenCustomFields(issue.customFields);
|
|
44
|
+
const row = { ...issueRow(issue) };
|
|
45
|
+
for (const name of extraFields) {
|
|
46
|
+
row[fieldKey(name)] = flat[name] ?? nativeField(issue, name) ?? "";
|
|
47
|
+
}
|
|
48
|
+
return row;
|
|
49
|
+
}
|
|
50
|
+
function fieldKey(name) {
|
|
51
|
+
return name.toLowerCase().replace(/\s+/g, "_");
|
|
52
|
+
}
|
|
53
|
+
function nativeField(issue, name) {
|
|
54
|
+
switch (fieldKey(name)) {
|
|
55
|
+
case "project":
|
|
56
|
+
return issue.project?.shortName ?? undefined;
|
|
57
|
+
case "reporter":
|
|
58
|
+
return issue.reporter?.login ?? undefined;
|
|
59
|
+
case "created":
|
|
60
|
+
return issue.created ? formatDate(issue.created) : undefined;
|
|
61
|
+
case "updated":
|
|
62
|
+
return issue.updated ? formatDate(issue.updated) : undefined;
|
|
63
|
+
default:
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function truncate(value, limit = DESCRIPTION_PREVIEW_CHARS) {
|
|
68
|
+
const text = (value ?? "").trim();
|
|
69
|
+
if (text.length <= limit)
|
|
70
|
+
return { text, truncated: false, totalChars: text.length };
|
|
71
|
+
return { text: `${text.slice(0, limit)}...`, truncated: true, totalChars: text.length };
|
|
72
|
+
}
|
|
73
|
+
export function formatDate(epochMs) {
|
|
74
|
+
return new Date(epochMs).toISOString().slice(0, 10);
|
|
75
|
+
}
|
|
76
|
+
export function formatMinutes(minutes) {
|
|
77
|
+
if (minutes <= 0)
|
|
78
|
+
return "0m";
|
|
79
|
+
const hours = Math.floor(minutes / 60);
|
|
80
|
+
const rest = minutes % 60;
|
|
81
|
+
return [hours > 0 ? `${hours}h` : undefined, rest > 0 ? `${rest}m` : undefined].filter(Boolean).join(" ");
|
|
82
|
+
}
|
|
83
|
+
export function workRow(item) {
|
|
84
|
+
return {
|
|
85
|
+
date: item.date ? formatDate(item.date) : "",
|
|
86
|
+
duration: item.duration?.presentation ?? formatMinutes(item.duration?.minutes ?? 0),
|
|
87
|
+
author: item.author?.login ?? "",
|
|
88
|
+
type: item.type?.name ?? "",
|
|
89
|
+
text: (item.text ?? "").replace(/\s+/g, " ").trim(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function totalMinutes(items) {
|
|
93
|
+
return items.reduce((sum, item) => sum + (item.duration?.minutes ?? 0), 0);
|
|
94
|
+
}
|
|
95
|
+
const DURATION_TOKENS = /^(?:\d+w\s*)?(?:\d+d\s*)?(?:\d+h\s*)?(?:\d+m\s*)?$/i;
|
|
96
|
+
/** YouTrack parses `presentation` itself; a bare number is taken as minutes. */
|
|
97
|
+
export function parseDuration(raw, usage) {
|
|
98
|
+
const value = raw.trim();
|
|
99
|
+
if (/^\d+$/.test(value)) {
|
|
100
|
+
const minutes = Number(value);
|
|
101
|
+
if (minutes < 1)
|
|
102
|
+
throw durationError(raw, usage);
|
|
103
|
+
return { minutes };
|
|
104
|
+
}
|
|
105
|
+
if (!DURATION_TOKENS.test(value) || !/\d/.test(value))
|
|
106
|
+
throw durationError(raw, usage);
|
|
107
|
+
return { presentation: value };
|
|
108
|
+
}
|
|
109
|
+
function durationError(raw, usage) {
|
|
110
|
+
return new AxiError(`Cannot read "${raw}" as a duration`, "VALIDATION_ERROR", [
|
|
111
|
+
"Use YouTrack duration units: 30m, 1h, 1h 30m, 2d, or a bare number of minutes",
|
|
112
|
+
`Run \`youtrack-axi ${usage}\``,
|
|
113
|
+
]);
|
|
114
|
+
}
|
|
115
|
+
/** Anchored to local noon so a date never slips a day when converted to UTC. */
|
|
116
|
+
export function parseWorkDate(raw, usage) {
|
|
117
|
+
if (raw === undefined) {
|
|
118
|
+
const today = new Date();
|
|
119
|
+
return new Date(today.getFullYear(), today.getMonth(), today.getDate(), 12).getTime();
|
|
120
|
+
}
|
|
121
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
|
|
122
|
+
if (!match) {
|
|
123
|
+
throw new AxiError(`Cannot read "${raw}" as a date`, "VALIDATION_ERROR", [
|
|
124
|
+
"Use YYYY-MM-DD, e.g. --date 2026-09-08",
|
|
125
|
+
`Run \`youtrack-axi ${usage}\``,
|
|
126
|
+
]);
|
|
127
|
+
}
|
|
128
|
+
const [, year, month, day] = match;
|
|
129
|
+
const date = new Date(Number(year), Number(month) - 1, Number(day), 12);
|
|
130
|
+
if (date.getMonth() !== Number(month) - 1 || date.getDate() !== Number(day)) {
|
|
131
|
+
throw new AxiError(`"${raw}" is not a real calendar date`, "VALIDATION_ERROR", [
|
|
132
|
+
"Use YYYY-MM-DD, e.g. --date 2026-09-08",
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
return date.getTime();
|
|
136
|
+
}
|
|
137
|
+
export function splitFieldList(raw) {
|
|
138
|
+
return (raw ?? "")
|
|
139
|
+
.split(",")
|
|
140
|
+
.map((s) => s.trim())
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAiC;IACnE,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,EAAE,IAAI;YAAE,SAAS;QAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;YACxE,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,SAAS,CAAC;QAC9E,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAQD,MAAM,UAAU,QAAQ,CAAC,KAAY;IACnC,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACvD,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;QAC5B,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;KAC7B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAY,EAAE,WAAqB;IACpE,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACrD,MAAM,GAAG,GAA2B,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,KAAY,EAAE,IAAY;IAC7C,QAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,SAAS,CAAC;QAC/C,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,QAAQ,EAAE,KAAK,IAAI,SAAS,CAAC;QAC5C,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAQD,MAAM,UAAU,QAAQ,CAAC,KAAgC,EAAE,KAAK,GAAG,yBAAyB;IAC1F,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACrF,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,GAAG,EAAE,CAAC;IAC1B,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5G,CAAC;AAUD,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;QAC5C,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,CAAC;QACnF,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE;QAC3B,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;KACpD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,eAAe,GAAG,qDAAqD,CAAC;AAE9E,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,KAAa;IACtD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,OAAO,GAAG,CAAC;YAAE,MAAM,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACvF,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,KAAa;IAC/C,OAAO,IAAI,QAAQ,CAAC,gBAAgB,GAAG,iBAAiB,EAAE,kBAAkB,EAAE;QAC5E,+EAA+E;QAC/E,sBAAsB,KAAK,IAAI;KAChC,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,GAAuB,EAAE,KAAa;IAClE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;QACzB,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,KAAK,GAAG,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,gBAAgB,GAAG,aAAa,EAAE,kBAAkB,EAAE;YACvE,wCAAwC;YACxC,sBAAsB,KAAK,IAAI;SAChC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,KAAoD,CAAC;IAClF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IACxE,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,QAAQ,CAAC,IAAI,GAAG,+BAA+B,EAAE,kBAAkB,EAAE;YAC7E,wCAAwC;SACzC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAuB;IACpD,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK renders handler return values itself, but does not export its
|
|
3
|
+
* renderer, so the few strings this CLI builds directly - top-level help,
|
|
4
|
+
* per-command help, the unknown-command error - encode through the same
|
|
5
|
+
* TOON library the SDK uses.
|
|
6
|
+
*/
|
|
7
|
+
export type StructuredOutput = Record<string, unknown>;
|
|
8
|
+
export declare function renderOutput(output: StructuredOutput): string;
|
|
9
|
+
export declare function renderError(message: string, code: string, suggestions?: string[]): string;
|
|
10
|
+
export declare function collapseHome(path: string): string;
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { encode } from "@toon-format/toon";
|
|
3
|
+
export function renderOutput(output) {
|
|
4
|
+
return encode(output);
|
|
5
|
+
}
|
|
6
|
+
export function renderError(message, code, suggestions = []) {
|
|
7
|
+
return renderOutput({ error: message, code, ...(suggestions.length > 0 ? { help: suggestions } : {}) });
|
|
8
|
+
}
|
|
9
|
+
export function collapseHome(path) {
|
|
10
|
+
const home = homedir();
|
|
11
|
+
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAU3C,MAAM,UAAU,YAAY,CAAC,MAAwB;IACnD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAY,EAAE,cAAwB,EAAE;IACnF,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC1G,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.0";
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Config } from "./config.js";
|
|
2
|
+
export interface CustomField {
|
|
3
|
+
name?: string;
|
|
4
|
+
value?: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface Issue {
|
|
7
|
+
idReadable?: string;
|
|
8
|
+
summary?: string;
|
|
9
|
+
description?: string | null;
|
|
10
|
+
created?: number;
|
|
11
|
+
updated?: number;
|
|
12
|
+
resolved?: number | null;
|
|
13
|
+
reporter?: {
|
|
14
|
+
login?: string;
|
|
15
|
+
} | null;
|
|
16
|
+
project?: {
|
|
17
|
+
shortName?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
} | null;
|
|
20
|
+
customFields?: CustomField[];
|
|
21
|
+
comments?: unknown[];
|
|
22
|
+
}
|
|
23
|
+
export interface WorkItem {
|
|
24
|
+
id?: string;
|
|
25
|
+
date?: number;
|
|
26
|
+
duration?: {
|
|
27
|
+
minutes?: number;
|
|
28
|
+
presentation?: string;
|
|
29
|
+
} | null;
|
|
30
|
+
text?: string | null;
|
|
31
|
+
type?: {
|
|
32
|
+
name?: string;
|
|
33
|
+
} | null;
|
|
34
|
+
author?: {
|
|
35
|
+
login?: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
} | null;
|
|
38
|
+
}
|
|
39
|
+
export declare class YouTrackClient {
|
|
40
|
+
private readonly config;
|
|
41
|
+
constructor(config: Config);
|
|
42
|
+
get baseUrl(): string;
|
|
43
|
+
get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
44
|
+
post<T>(path: string, body: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
45
|
+
private request;
|
|
46
|
+
private translateError;
|
|
47
|
+
/**
|
|
48
|
+
* `issuesGetter/count` answers -1 while it computes the total, so a single call
|
|
49
|
+
* cannot be trusted. Returns undefined rather than ever surfacing -1.
|
|
50
|
+
*/
|
|
51
|
+
countIssues(query: string): Promise<number | undefined>;
|
|
52
|
+
}
|
|
53
|
+
export declare const ISSUE_LIST_FIELDS = "idReadable,summary,customFields(name,value(name,login))";
|
|
54
|
+
export declare const ISSUE_DETAIL_FIELDS: string;
|
|
55
|
+
export declare const WORK_ITEM_FIELDS = "id,date,duration(minutes,presentation),text,type(name),author(login,name)";
|
package/dist/youtrack.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
3
|
+
export class YouTrackClient {
|
|
4
|
+
config;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
}
|
|
8
|
+
get baseUrl() {
|
|
9
|
+
return this.config.url;
|
|
10
|
+
}
|
|
11
|
+
async get(path, params = {}) {
|
|
12
|
+
return this.request("GET", path, params);
|
|
13
|
+
}
|
|
14
|
+
async post(path, body, params = {}) {
|
|
15
|
+
return this.request("POST", path, params, body);
|
|
16
|
+
}
|
|
17
|
+
async request(method, path, params, body) {
|
|
18
|
+
const url = new URL(`${this.config.url}/api/${path.replace(/^\/+/, "")}`);
|
|
19
|
+
for (const [key, value] of Object.entries(params)) {
|
|
20
|
+
if (value !== undefined)
|
|
21
|
+
url.searchParams.set(key, String(value));
|
|
22
|
+
}
|
|
23
|
+
let response;
|
|
24
|
+
try {
|
|
25
|
+
response = await fetch(url, {
|
|
26
|
+
method,
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
29
|
+
Accept: "application/json",
|
|
30
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
31
|
+
},
|
|
32
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
33
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const reason = error instanceof Error && error.name === "TimeoutError" ? "timed out" : "is unreachable";
|
|
38
|
+
throw new AxiError(`YouTrack at ${this.config.url} ${reason}`, "NETWORK_ERROR", [
|
|
39
|
+
"Check network access and that the configured URL is correct",
|
|
40
|
+
`Current URL came from ${this.config.source}`,
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
const text = await response.text();
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw this.translateError(response.status, text, path);
|
|
46
|
+
}
|
|
47
|
+
if (text.length === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(text);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new AxiError(`YouTrack returned a non-JSON response for ${path}`, "API_ERROR", [
|
|
54
|
+
"Verify the configured URL points at a YouTrack instance, not a proxy or login page",
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
translateError(status, text, path) {
|
|
59
|
+
let parsed = {};
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(text);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
/* fall through to status-based messages */
|
|
65
|
+
}
|
|
66
|
+
const detail = [parsed.error_description, ...(parsed.error_children ?? []).map((c) => c.error)]
|
|
67
|
+
.filter((s) => Boolean(s))
|
|
68
|
+
.join("; ");
|
|
69
|
+
if (status === 401 || status === 403) {
|
|
70
|
+
return new AxiError(status === 401 ? "YouTrack rejected the token" : `Token lacks permission for ${path}`, "AUTH_ERROR", [
|
|
71
|
+
status === 401
|
|
72
|
+
? `Issue a new permanent token in YouTrack and run \`youtrack-axi setup --url "${this.config.url}" --token "<permanent-token>"\``
|
|
73
|
+
: "Ask a YouTrack administrator to grant this account access, or use a token with wider scope",
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
if (status === 404) {
|
|
77
|
+
return new AxiError(detail || `Not found: ${path}`, "NOT_FOUND", [
|
|
78
|
+
"Run `youtrack-axi issues` to list issues you can see",
|
|
79
|
+
"Run `youtrack-axi projects` to list project short names",
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
if (status === 400 && parsed.error === "invalid_query") {
|
|
83
|
+
return new AxiError(`Invalid YouTrack query: ${detail}`, "VALIDATION_ERROR", [
|
|
84
|
+
'Use YouTrack query syntax, e.g. `youtrack-axi issues "for:me #Unresolved"`',
|
|
85
|
+
"Run `youtrack-axi fields <PROJECT>` to see valid field names and values",
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
if (status === 400) {
|
|
89
|
+
return new AxiError(detail || `YouTrack rejected the request to ${path}`, "VALIDATION_ERROR", [
|
|
90
|
+
"Check the values passed; run the command with --help for its accepted flags",
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
return new AxiError(`YouTrack returned HTTP ${status}${detail ? `: ${detail}` : ""}`, "API_ERROR", [
|
|
94
|
+
"Retry; if it persists, check YouTrack instance health",
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `issuesGetter/count` answers -1 while it computes the total, so a single call
|
|
99
|
+
* cannot be trusted. Returns undefined rather than ever surfacing -1.
|
|
100
|
+
*/
|
|
101
|
+
async countIssues(query) {
|
|
102
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
103
|
+
const result = await this.post("issuesGetter/count", { query }, { fields: "count" });
|
|
104
|
+
const count = result?.count;
|
|
105
|
+
if (typeof count === "number" && count >= 0)
|
|
106
|
+
return count;
|
|
107
|
+
await new Promise((resolve) => setTimeout(resolve, 150 * (attempt + 1)));
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export const ISSUE_LIST_FIELDS = "idReadable,summary,customFields(name,value(name,login))";
|
|
113
|
+
export const ISSUE_DETAIL_FIELDS = "idReadable,summary,description,created,updated,resolved,reporter(login),project(shortName,name)," +
|
|
114
|
+
"customFields(name,value(name,login,minutes,presentation)),comments(id)";
|
|
115
|
+
export const WORK_ITEM_FIELDS = "id,date,duration(minutes,presentation),text,type(name),author(login,name)";
|
|
116
|
+
//# sourceMappingURL=youtrack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtrack.js","sourceRoot":"","sources":["../src/youtrack.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tmcayden/youtrack-axi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Query, update, and log time against YouTrack issues from the shell",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"youtrack-axi": "bin/youtrack-axi.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "npm run build && vitest run && npm run skill:check",
|
|
22
|
+
"prepare": "npm run build",
|
|
23
|
+
"skill:build": "node scripts/skill.mjs",
|
|
24
|
+
"skill:check": "node scripts/skill.mjs --check"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@toon-format/toon": "^2.1.0",
|
|
28
|
+
"axi-sdk-js": "0.1.11"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.0.0",
|
|
32
|
+
"typescript": "^5.7.0",
|
|
33
|
+
"vitest": "^3.0.0"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+ssh://git@github.com/tmcayden/youtrack-axi.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/tmcayden/youtrack-axi#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/tmcayden/youtrack-axi/issues"
|
|
45
|
+
}
|
|
46
|
+
}
|