@sinoia/hubdoc-tools 1.8.2 → 1.9.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/cli.js +941 -52
- package/docs/llm-usage.md +95 -0
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -95034,7 +95034,7 @@ var require_bignumber = __commonJS({
|
|
|
95034
95034
|
"node_modules/bignumber.js/bignumber.js"(exports2, module2) {
|
|
95035
95035
|
(function(globalObject) {
|
|
95036
95036
|
"use strict";
|
|
95037
|
-
var BigNumber, isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ",
|
|
95037
|
+
var BigNumber, isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE4 = 1e14, LOG_BASE = 14, MAX_SAFE_INTEGER = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX = 1e9;
|
|
95038
95038
|
function clone(configObject) {
|
|
95039
95039
|
var div, convertBase, parseNumeric, P = BigNumber2.prototype = { constructor: BigNumber2, toString: null, valueOf: null }, ONE = new BigNumber2(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
|
|
95040
95040
|
prefix: "",
|
|
@@ -95286,7 +95286,7 @@ var require_bignumber = __commonJS({
|
|
|
95286
95286
|
if (String(c3[0]).length == i3) {
|
|
95287
95287
|
for (i3 = 0; i3 < c3.length; i3++) {
|
|
95288
95288
|
n3 = c3[i3];
|
|
95289
|
-
if (n3 < 0 || n3 >=
|
|
95289
|
+
if (n3 < 0 || n3 >= BASE4 || n3 !== mathfloor(n3)) break out;
|
|
95290
95290
|
}
|
|
95291
95291
|
if (n3 !== 0) return true;
|
|
95292
95292
|
}
|
|
@@ -95503,7 +95503,7 @@ var require_bignumber = __commonJS({
|
|
|
95503
95503
|
e3 = x3.e - y2.e;
|
|
95504
95504
|
s3 = dp + e3 + 1;
|
|
95505
95505
|
if (!base) {
|
|
95506
|
-
base =
|
|
95506
|
+
base = BASE4;
|
|
95507
95507
|
e3 = bitFloor(x3.e / LOG_BASE) - bitFloor(y2.e / LOG_BASE);
|
|
95508
95508
|
s3 = s3 / LOG_BASE | 0;
|
|
95509
95509
|
}
|
|
@@ -95582,7 +95582,7 @@ var require_bignumber = __commonJS({
|
|
|
95582
95582
|
more = rem[0] != null;
|
|
95583
95583
|
if (!qc[0]) qc.splice(0, 1);
|
|
95584
95584
|
}
|
|
95585
|
-
if (base ==
|
|
95585
|
+
if (base == BASE4) {
|
|
95586
95586
|
for (i3 = 1, s3 = qc[0]; s3 >= 10; s3 /= 10, i3++) ;
|
|
95587
95587
|
round(q3, dp + (q3.e = i3 + e3 * LOG_BASE - 1) + 1, rm, more);
|
|
95588
95588
|
} else {
|
|
@@ -95636,7 +95636,7 @@ var require_bignumber = __commonJS({
|
|
|
95636
95636
|
}
|
|
95637
95637
|
return x3;
|
|
95638
95638
|
}
|
|
95639
|
-
function
|
|
95639
|
+
function normalise2(n3, c3, e3) {
|
|
95640
95640
|
var i3 = 1, j3 = c3.length;
|
|
95641
95641
|
for (; !c3[--j3]; c3.pop()) ;
|
|
95642
95642
|
for (j3 = c3[0]; j3 >= 10; j3 /= 10, i3++) ;
|
|
@@ -95741,12 +95741,12 @@ var require_bignumber = __commonJS({
|
|
|
95741
95741
|
for (k3 = 1; j3 >= 10; j3 /= 10, k3++) ;
|
|
95742
95742
|
if (i3 != k3) {
|
|
95743
95743
|
x3.e++;
|
|
95744
|
-
if (xc[0] ==
|
|
95744
|
+
if (xc[0] == BASE4) xc[0] = 1;
|
|
95745
95745
|
}
|
|
95746
95746
|
break;
|
|
95747
95747
|
} else {
|
|
95748
95748
|
xc[ni] += k3;
|
|
95749
|
-
if (xc[ni] !=
|
|
95749
|
+
if (xc[ni] != BASE4) break;
|
|
95750
95750
|
xc[ni--] = 0;
|
|
95751
95751
|
k3 = 1;
|
|
95752
95752
|
}
|
|
@@ -95955,12 +95955,12 @@ var require_bignumber = __commonJS({
|
|
|
95955
95955
|
}
|
|
95956
95956
|
b3 = (j3 = yc.length) - (i3 = xc.length);
|
|
95957
95957
|
if (b3 > 0) for (; b3--; xc[i3++] = 0) ;
|
|
95958
|
-
b3 =
|
|
95958
|
+
b3 = BASE4 - 1;
|
|
95959
95959
|
for (; j3 > a3; ) {
|
|
95960
95960
|
if (xc[--j3] < yc[j3]) {
|
|
95961
95961
|
for (i3 = j3; i3 && !xc[--i3]; xc[i3] = b3) ;
|
|
95962
95962
|
--xc[i3];
|
|
95963
|
-
xc[j3] +=
|
|
95963
|
+
xc[j3] += BASE4;
|
|
95964
95964
|
}
|
|
95965
95965
|
xc[j3] -= yc[j3];
|
|
95966
95966
|
}
|
|
@@ -95970,7 +95970,7 @@ var require_bignumber = __commonJS({
|
|
|
95970
95970
|
y2.c = [y2.e = 0];
|
|
95971
95971
|
return y2;
|
|
95972
95972
|
}
|
|
95973
|
-
return
|
|
95973
|
+
return normalise2(y2, xc, ye);
|
|
95974
95974
|
};
|
|
95975
95975
|
P.modulo = P.mod = function(y2, b3) {
|
|
95976
95976
|
var q3, s3, x3 = this;
|
|
@@ -96022,7 +96022,7 @@ var require_bignumber = __commonJS({
|
|
|
96022
96022
|
ycL = i3;
|
|
96023
96023
|
}
|
|
96024
96024
|
for (i3 = xcL + ycL, zc = []; i3--; zc.push(0)) ;
|
|
96025
|
-
base =
|
|
96025
|
+
base = BASE4;
|
|
96026
96026
|
sqrtBase = SQRT_BASE;
|
|
96027
96027
|
for (i3 = ycL; --i3 >= 0; ) {
|
|
96028
96028
|
c3 = 0;
|
|
@@ -96043,7 +96043,7 @@ var require_bignumber = __commonJS({
|
|
|
96043
96043
|
} else {
|
|
96044
96044
|
zc.splice(0, 1);
|
|
96045
96045
|
}
|
|
96046
|
-
return
|
|
96046
|
+
return normalise2(y2, zc, e3);
|
|
96047
96047
|
};
|
|
96048
96048
|
P.negated = function() {
|
|
96049
96049
|
var x3 = new BigNumber2(this);
|
|
@@ -96088,14 +96088,14 @@ var require_bignumber = __commonJS({
|
|
|
96088
96088
|
b3 = a3;
|
|
96089
96089
|
}
|
|
96090
96090
|
for (a3 = 0; b3; ) {
|
|
96091
|
-
a3 = (xc[--b3] = xc[b3] + yc[b3] + a3) /
|
|
96092
|
-
xc[b3] =
|
|
96091
|
+
a3 = (xc[--b3] = xc[b3] + yc[b3] + a3) / BASE4 | 0;
|
|
96092
|
+
xc[b3] = BASE4 === xc[b3] ? 0 : xc[b3] % BASE4;
|
|
96093
96093
|
}
|
|
96094
96094
|
if (a3) {
|
|
96095
96095
|
xc = [a3].concat(xc);
|
|
96096
96096
|
++ye;
|
|
96097
96097
|
}
|
|
96098
|
-
return
|
|
96098
|
+
return normalise2(y2, xc, ye);
|
|
96099
96099
|
};
|
|
96100
96100
|
P.precision = P.sd = function(sd, rm) {
|
|
96101
96101
|
var c3, n3, v6, x3 = this;
|
|
@@ -622232,10 +622232,10 @@ var require_tasks = __commonJS({
|
|
|
622232
622232
|
exports2.VERSIONS = {
|
|
622233
622233
|
v1: v1_1.tasks_v1.Tasks
|
|
622234
622234
|
};
|
|
622235
|
-
function
|
|
622235
|
+
function tasks2(versionOrOptions) {
|
|
622236
622236
|
return (0, googleapis_common_1.getAPI)("tasks", versionOrOptions, exports2.VERSIONS, this);
|
|
622237
622237
|
}
|
|
622238
|
-
exports2.tasks =
|
|
622238
|
+
exports2.tasks = tasks2;
|
|
622239
622239
|
var auth = new googleapis_common_1.AuthPlus();
|
|
622240
622240
|
exports2.auth = auth;
|
|
622241
622241
|
var googleapis_common_2 = require_src7();
|
|
@@ -682521,19 +682521,19 @@ var HubDocExportService = class {
|
|
|
682521
682521
|
const mappings = [];
|
|
682522
682522
|
for (const doc of documents) {
|
|
682523
682523
|
const detailedDoc = await this.getDocumentDetails(doc.id);
|
|
682524
|
-
const
|
|
682524
|
+
const projects2 = [];
|
|
682525
682525
|
if (detailedDoc.project && detailedDoc.folder_path) {
|
|
682526
|
-
|
|
682526
|
+
projects2.push({
|
|
682527
682527
|
projectName: detailedDoc.project.name,
|
|
682528
682528
|
folderPath: detailedDoc.folder_path,
|
|
682529
682529
|
fullPath: `${detailedDoc.project.name}/${detailedDoc.folder_path}`
|
|
682530
682530
|
});
|
|
682531
682531
|
}
|
|
682532
|
-
const defaultTargetPath =
|
|
682532
|
+
const defaultTargetPath = projects2.length > 0 ? projects2[0].fullPath.replace(/[/\\]/g, import_path5.default.sep) : "Exported Documents";
|
|
682533
682533
|
const mapping = {
|
|
682534
682534
|
documentId: doc.id,
|
|
682535
682535
|
documentName: doc.name,
|
|
682536
|
-
projects,
|
|
682536
|
+
projects: projects2,
|
|
682537
682537
|
downloadUrl: doc.download_url,
|
|
682538
682538
|
metadata: doc.metadata,
|
|
682539
682539
|
targetPath: defaultTargetPath
|
|
@@ -688590,22 +688590,22 @@ var GlobUtil = class {
|
|
|
688590
688590
|
return;
|
|
688591
688591
|
}
|
|
688592
688592
|
processor.processPatterns(target, patterns);
|
|
688593
|
-
let
|
|
688593
|
+
let tasks2 = 1;
|
|
688594
688594
|
const next = () => {
|
|
688595
|
-
if (--
|
|
688595
|
+
if (--tasks2 === 0)
|
|
688596
688596
|
cb();
|
|
688597
688597
|
};
|
|
688598
688598
|
for (const [m3, absolute, ifDir] of processor.matches.entries()) {
|
|
688599
688599
|
if (this.#ignored(m3))
|
|
688600
688600
|
continue;
|
|
688601
|
-
|
|
688601
|
+
tasks2++;
|
|
688602
688602
|
this.match(m3, absolute, ifDir).then(() => next());
|
|
688603
688603
|
}
|
|
688604
688604
|
for (const t3 of processor.subwalkTargets()) {
|
|
688605
688605
|
if (this.maxDepth !== Infinity && t3.depth() >= this.maxDepth) {
|
|
688606
688606
|
continue;
|
|
688607
688607
|
}
|
|
688608
|
-
|
|
688608
|
+
tasks2++;
|
|
688609
688609
|
const childrenCached = t3.readdirCached();
|
|
688610
688610
|
if (t3.calledReaddir())
|
|
688611
688611
|
this.walkCB3(t3, childrenCached, processor, next);
|
|
@@ -688617,19 +688617,19 @@ var GlobUtil = class {
|
|
|
688617
688617
|
}
|
|
688618
688618
|
walkCB3(target, entries, processor, cb) {
|
|
688619
688619
|
processor = processor.filterEntries(target, entries);
|
|
688620
|
-
let
|
|
688620
|
+
let tasks2 = 1;
|
|
688621
688621
|
const next = () => {
|
|
688622
|
-
if (--
|
|
688622
|
+
if (--tasks2 === 0)
|
|
688623
688623
|
cb();
|
|
688624
688624
|
};
|
|
688625
688625
|
for (const [m3, absolute, ifDir] of processor.matches.entries()) {
|
|
688626
688626
|
if (this.#ignored(m3))
|
|
688627
688627
|
continue;
|
|
688628
|
-
|
|
688628
|
+
tasks2++;
|
|
688629
688629
|
this.match(m3, absolute, ifDir).then(() => next());
|
|
688630
688630
|
}
|
|
688631
688631
|
for (const [target2, patterns] of processor.subwalks.entries()) {
|
|
688632
|
-
|
|
688632
|
+
tasks2++;
|
|
688633
688633
|
this.walkCB2(target2, patterns, processor.child(), next);
|
|
688634
688634
|
}
|
|
688635
688635
|
next();
|
|
@@ -688649,9 +688649,9 @@ var GlobUtil = class {
|
|
|
688649
688649
|
return;
|
|
688650
688650
|
}
|
|
688651
688651
|
processor.processPatterns(target, patterns);
|
|
688652
|
-
let
|
|
688652
|
+
let tasks2 = 1;
|
|
688653
688653
|
const next = () => {
|
|
688654
|
-
if (--
|
|
688654
|
+
if (--tasks2 === 0)
|
|
688655
688655
|
cb();
|
|
688656
688656
|
};
|
|
688657
688657
|
for (const [m3, absolute, ifDir] of processor.matches.entries()) {
|
|
@@ -688663,7 +688663,7 @@ var GlobUtil = class {
|
|
|
688663
688663
|
if (this.maxDepth !== Infinity && t3.depth() >= this.maxDepth) {
|
|
688664
688664
|
continue;
|
|
688665
688665
|
}
|
|
688666
|
-
|
|
688666
|
+
tasks2++;
|
|
688667
688667
|
const children2 = t3.readdirSync();
|
|
688668
688668
|
this.walkCB3Sync(t3, children2, processor, next);
|
|
688669
688669
|
}
|
|
@@ -688671,9 +688671,9 @@ var GlobUtil = class {
|
|
|
688671
688671
|
}
|
|
688672
688672
|
walkCB3Sync(target, entries, processor, cb) {
|
|
688673
688673
|
processor = processor.filterEntries(target, entries);
|
|
688674
|
-
let
|
|
688674
|
+
let tasks2 = 1;
|
|
688675
688675
|
const next = () => {
|
|
688676
|
-
if (--
|
|
688676
|
+
if (--tasks2 === 0)
|
|
688677
688677
|
cb();
|
|
688678
688678
|
};
|
|
688679
688679
|
for (const [m3, absolute, ifDir] of processor.matches.entries()) {
|
|
@@ -688682,7 +688682,7 @@ var GlobUtil = class {
|
|
|
688682
688682
|
this.matchSync(m3, absolute, ifDir);
|
|
688683
688683
|
}
|
|
688684
688684
|
for (const [target2, patterns] of processor.subwalks.entries()) {
|
|
688685
|
-
|
|
688685
|
+
tasks2++;
|
|
688686
688686
|
this.walkCB2Sync(target2, patterns, processor.child(), next);
|
|
688687
688687
|
}
|
|
688688
688688
|
next();
|
|
@@ -689884,25 +689884,25 @@ var ExportCsvManager = class {
|
|
|
689884
689884
|
}).on("end", () => resolve(results)).on("error", reject);
|
|
689885
689885
|
});
|
|
689886
689886
|
}
|
|
689887
|
-
serializeProjects(
|
|
689888
|
-
return
|
|
689887
|
+
serializeProjects(projects2) {
|
|
689888
|
+
return projects2.map((p3) => `${p3.projectName}:${p3.folderPath}`).join("|");
|
|
689889
689889
|
}
|
|
689890
689890
|
parseProjects(projectsStr) {
|
|
689891
689891
|
if (!projectsStr || projectsStr.trim() === "") return [];
|
|
689892
689892
|
try {
|
|
689893
|
-
const
|
|
689893
|
+
const projects2 = [];
|
|
689894
689894
|
const projectEntries = projectsStr.split("|");
|
|
689895
689895
|
for (const entry of projectEntries) {
|
|
689896
689896
|
const [projectName, folderPath] = entry.split(":");
|
|
689897
689897
|
if (projectName && folderPath) {
|
|
689898
|
-
|
|
689898
|
+
projects2.push({
|
|
689899
689899
|
projectName: projectName.trim(),
|
|
689900
689900
|
folderPath: folderPath.trim(),
|
|
689901
689901
|
fullPath: `${projectName.trim()}/${folderPath.trim()}`
|
|
689902
689902
|
});
|
|
689903
689903
|
}
|
|
689904
689904
|
}
|
|
689905
|
-
return
|
|
689905
|
+
return projects2;
|
|
689906
689906
|
} catch {
|
|
689907
689907
|
console.warn(import_chalk14.default.yellow(`Warning: Invalid projects format: ${projectsStr}`));
|
|
689908
689908
|
return [];
|
|
@@ -732813,6 +732813,34 @@ var HubdocFs = class {
|
|
|
732813
732813
|
async uploadUpdate(node) {
|
|
732814
732814
|
await this.uploadNode(node, "update");
|
|
732815
732815
|
}
|
|
732816
|
+
/**
|
|
732817
|
+
* If `node` has unpushed writes, upload them now and clear the dirty flag.
|
|
732818
|
+
* Called from `flush` / `fsync` / `release` so that `close(fd)` (or an
|
|
732819
|
+
* explicit `fsync(fd)`) in userspace blocks until the server has the new
|
|
732820
|
+
* bytes — makes read-after-write via the API (from another process, or
|
|
732821
|
+
* from the same one after close()) immediately consistent.
|
|
732822
|
+
*
|
|
732823
|
+
* The FUSE contract:
|
|
732824
|
+
* - `flush` fires on every `close(2)` in userspace (once per close, even
|
|
732825
|
+
* for dup'd fds).
|
|
732826
|
+
* - `release` fires only after the last reference to the file is dropped
|
|
732827
|
+
* by the kernel — potentially long after `close(2)` returned.
|
|
732828
|
+
* So `flush` (not `release`) is where the upload has to happen for the
|
|
732829
|
+
* close→read-back sequence to be reliable.
|
|
732830
|
+
*/
|
|
732831
|
+
async flushDirty(node) {
|
|
732832
|
+
if (!node.dirty) return;
|
|
732833
|
+
if (this.opts.readOnly) return;
|
|
732834
|
+
if (node.localOnly) {
|
|
732835
|
+
node.dirty = false;
|
|
732836
|
+
node.mtime = /* @__PURE__ */ new Date();
|
|
732837
|
+
return;
|
|
732838
|
+
}
|
|
732839
|
+
if (node.isNew || !node.id) await this.uploadNew(node);
|
|
732840
|
+
else await this.uploadUpdate(node);
|
|
732841
|
+
node.dirty = false;
|
|
732842
|
+
node.mtime = /* @__PURE__ */ new Date();
|
|
732843
|
+
}
|
|
732816
732844
|
invalidateParent(p3) {
|
|
732817
732845
|
const parent = this.nodes.get(parentPath(p3));
|
|
732818
732846
|
if (parent) parent.loadedAt = 0;
|
|
@@ -732956,19 +732984,21 @@ var HubdocFs = class {
|
|
|
732956
732984
|
await import_fs_extra32.default.close(h3.localFd);
|
|
732957
732985
|
} catch {
|
|
732958
732986
|
}
|
|
732959
|
-
|
|
732960
|
-
|
|
732961
|
-
|
|
732962
|
-
|
|
732963
|
-
|
|
732964
|
-
|
|
732965
|
-
|
|
732966
|
-
|
|
732967
|
-
|
|
732987
|
+
await this.flushDirty(h3.node);
|
|
732988
|
+
cb(0);
|
|
732989
|
+
}),
|
|
732990
|
+
flush: wrap(async (_p, fd, cb) => {
|
|
732991
|
+
const h3 = this.handles.get(fd);
|
|
732992
|
+
if (!h3) return cb(0);
|
|
732993
|
+
await this.flushDirty(h3.node);
|
|
732994
|
+
cb(0);
|
|
732995
|
+
}),
|
|
732996
|
+
fsync: wrap(async (_p, fd, _datasync, cb) => {
|
|
732997
|
+
const h3 = this.handles.get(fd);
|
|
732998
|
+
if (!h3) return cb(0);
|
|
732999
|
+
await this.flushDirty(h3.node);
|
|
732968
733000
|
cb(0);
|
|
732969
733001
|
}),
|
|
732970
|
-
flush: (_p, _fd, cb) => cb(0),
|
|
732971
|
-
fsync: (_p, _fd, _datasync, cb) => cb(0),
|
|
732972
733002
|
unlink: wrap(async (p3, cb) => {
|
|
732973
733003
|
if (ro) return cb(Fuse.EACCES);
|
|
732974
733004
|
const node = await this.resolve(p3);
|
|
@@ -733923,10 +733953,769 @@ async function handlePermissionsShow(id, opts) {
|
|
|
733923
733953
|
}
|
|
733924
733954
|
}
|
|
733925
733955
|
|
|
733956
|
+
// apps/cli/cli/handlers/_pagination.ts
|
|
733957
|
+
function extractPagination(headers, itemsLength = 0) {
|
|
733958
|
+
const h3 = normalise(headers);
|
|
733959
|
+
const parseInt10 = (v6, fallback) => {
|
|
733960
|
+
if (typeof v6 === "number") return Number.isFinite(v6) ? v6 : fallback;
|
|
733961
|
+
if (typeof v6 === "string" && v6.trim() !== "") {
|
|
733962
|
+
const n3 = Number(v6);
|
|
733963
|
+
if (Number.isFinite(n3)) return n3;
|
|
733964
|
+
}
|
|
733965
|
+
return fallback;
|
|
733966
|
+
};
|
|
733967
|
+
const page = parseInt10(h3["x-page"], 1);
|
|
733968
|
+
const per_page = parseInt10(h3["x-per-page"], itemsLength);
|
|
733969
|
+
const count = parseInt10(h3["x-total-count"], itemsLength);
|
|
733970
|
+
const pages = parseInt10(h3["x-total-pages"], 1);
|
|
733971
|
+
const rawHasMore = h3["x-has-more"];
|
|
733972
|
+
const has_more = typeof rawHasMore === "string" ? rawHasMore.toLowerCase() === "true" : Boolean(rawHasMore);
|
|
733973
|
+
return { page, per_page, pages, count, has_more };
|
|
733974
|
+
}
|
|
733975
|
+
function normalise(headers) {
|
|
733976
|
+
if (!headers) return {};
|
|
733977
|
+
const out = {};
|
|
733978
|
+
for (const key of Object.keys(headers)) {
|
|
733979
|
+
out[key.toLowerCase()] = headers[key];
|
|
733980
|
+
}
|
|
733981
|
+
return out;
|
|
733982
|
+
}
|
|
733983
|
+
|
|
733984
|
+
// apps/cli/cli/handlers/project-handlers.ts
|
|
733985
|
+
var BASE3 = "/api/v1/projects";
|
|
733986
|
+
var LIST_COLUMNS2 = [
|
|
733987
|
+
{ header: "ID", key: "id" },
|
|
733988
|
+
{ header: "Name", key: "name", maxWidth: 40 },
|
|
733989
|
+
{ header: "Type", key: "project_type" },
|
|
733990
|
+
{ header: "Status", key: "status" },
|
|
733991
|
+
{ header: "Workspace", key: "workspace_id" },
|
|
733992
|
+
{ header: "Progress", key: "completion_percentage", format: (v6) => v6 == null ? "" : `${v6}%` },
|
|
733993
|
+
{ header: "Updated", key: "updated_at" }
|
|
733994
|
+
];
|
|
733995
|
+
function parseJsonOpt5(name, raw, out) {
|
|
733996
|
+
if (raw == null) return void 0;
|
|
733997
|
+
try {
|
|
733998
|
+
return JSON.parse(raw);
|
|
733999
|
+
} catch (err) {
|
|
734000
|
+
out.fail(new Error(`Invalid JSON for --${name}: ${err.message}`));
|
|
734001
|
+
}
|
|
734002
|
+
}
|
|
734003
|
+
async function handleProjectsList(opts) {
|
|
734004
|
+
const out = output(opts);
|
|
734005
|
+
const ctx = await buildApiContext(out);
|
|
734006
|
+
const params = {};
|
|
734007
|
+
if (opts.workspace !== void 0) {
|
|
734008
|
+
params.workspace_id = opts.workspace === "null" ? "null" : opts.workspace;
|
|
734009
|
+
}
|
|
734010
|
+
if (opts.type) params.project_type = opts.type;
|
|
734011
|
+
if (opts.status) params.status = opts.status;
|
|
734012
|
+
if (opts.page) params.page = Number(opts.page);
|
|
734013
|
+
if (opts.perPage) params.per_page = Number(opts.perPage);
|
|
734014
|
+
try {
|
|
734015
|
+
const res = await ctx.axios.get(BASE3, { params });
|
|
734016
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734017
|
+
const pagination = extractPagination(res.headers, items.length);
|
|
734018
|
+
if (opts.json) {
|
|
734019
|
+
out.show({ items, pagination });
|
|
734020
|
+
return;
|
|
734021
|
+
}
|
|
734022
|
+
out.list(items, LIST_COLUMNS2);
|
|
734023
|
+
if (pagination.pages > 1) {
|
|
734024
|
+
out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
|
|
734025
|
+
}
|
|
734026
|
+
} catch (err) {
|
|
734027
|
+
out.fail(err);
|
|
734028
|
+
}
|
|
734029
|
+
}
|
|
734030
|
+
async function handleProjectsShow(id, opts) {
|
|
734031
|
+
const out = output(opts);
|
|
734032
|
+
const ctx = await buildApiContext(out);
|
|
734033
|
+
try {
|
|
734034
|
+
const res = await ctx.axios.get(`${BASE3}/${encodeURIComponent(id)}`);
|
|
734035
|
+
out.show(res.data?.project ?? res.data, { title: `Project ${id}` });
|
|
734036
|
+
} catch (err) {
|
|
734037
|
+
out.fail(err);
|
|
734038
|
+
}
|
|
734039
|
+
}
|
|
734040
|
+
async function handleProjectsCreate(opts) {
|
|
734041
|
+
const out = output(opts);
|
|
734042
|
+
const ctx = await buildApiContext(out);
|
|
734043
|
+
if (!opts.name) out.fail(new Error("--name is required"));
|
|
734044
|
+
const body2 = { name: opts.name };
|
|
734045
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734046
|
+
if (opts.workspace !== void 0) {
|
|
734047
|
+
body2.workspace_id = opts.workspace === "null" ? null : opts.workspace;
|
|
734048
|
+
}
|
|
734049
|
+
if (opts.typeId) body2.project_type_id = opts.typeId;
|
|
734050
|
+
if (opts.folder) body2.documents_folder_id = opts.folder;
|
|
734051
|
+
const settings = parseJsonOpt5("settings", opts.settings, out);
|
|
734052
|
+
if (settings !== void 0) body2.settings = settings;
|
|
734053
|
+
const metadata2 = parseJsonOpt5("metadata", opts.metadata, out);
|
|
734054
|
+
if (metadata2 !== void 0) body2.metadata_user = metadata2;
|
|
734055
|
+
try {
|
|
734056
|
+
const res = await ctx.axios.post(BASE3, body2);
|
|
734057
|
+
const project = res.data?.project ?? res.data;
|
|
734058
|
+
out.ok(`Project created (id=${project?.id ?? "unknown"})`, project);
|
|
734059
|
+
} catch (err) {
|
|
734060
|
+
out.fail(err);
|
|
734061
|
+
}
|
|
734062
|
+
}
|
|
734063
|
+
async function handleProjectsUpdate(id, opts) {
|
|
734064
|
+
const out = output(opts);
|
|
734065
|
+
const ctx = await buildApiContext(out);
|
|
734066
|
+
const body2 = {};
|
|
734067
|
+
if (opts.name !== void 0) body2.name = opts.name;
|
|
734068
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734069
|
+
if (opts.status !== void 0) body2.status = opts.status;
|
|
734070
|
+
const settings = parseJsonOpt5("settings", opts.settings, out);
|
|
734071
|
+
if (settings !== void 0) body2.settings = settings;
|
|
734072
|
+
const metadata2 = parseJsonOpt5("metadata", opts.metadata, out);
|
|
734073
|
+
if (metadata2 !== void 0) body2.metadata_user = metadata2;
|
|
734074
|
+
if (Object.keys(body2).length === 0) out.fail(new Error("No fields to update"));
|
|
734075
|
+
try {
|
|
734076
|
+
const res = await ctx.axios.patch(`${BASE3}/${encodeURIComponent(id)}`, body2);
|
|
734077
|
+
out.ok(`Project ${id} updated`, res.data?.project ?? res.data);
|
|
734078
|
+
} catch (err) {
|
|
734079
|
+
out.fail(err);
|
|
734080
|
+
}
|
|
734081
|
+
}
|
|
734082
|
+
async function handleProjectsDelete(id, opts) {
|
|
734083
|
+
const out = output(opts);
|
|
734084
|
+
const ctx = await buildApiContext(out);
|
|
734085
|
+
if (!opts.yes && !opts.json) {
|
|
734086
|
+
const { confirm } = await lib_default.prompt([
|
|
734087
|
+
{
|
|
734088
|
+
type: "confirm",
|
|
734089
|
+
name: "confirm",
|
|
734090
|
+
message: `Delete project ${id}? This also drops its sprints and tickets.`,
|
|
734091
|
+
default: false
|
|
734092
|
+
}
|
|
734093
|
+
]);
|
|
734094
|
+
if (!confirm) {
|
|
734095
|
+
out.note("Aborted");
|
|
734096
|
+
return;
|
|
734097
|
+
}
|
|
734098
|
+
} else if (!opts.yes && opts.json) {
|
|
734099
|
+
out.fail(new Error("Refusing to delete without --yes in --json mode"));
|
|
734100
|
+
}
|
|
734101
|
+
try {
|
|
734102
|
+
await ctx.axios.delete(`${BASE3}/${encodeURIComponent(id)}`);
|
|
734103
|
+
out.ok(`Project ${id} deleted`);
|
|
734104
|
+
} catch (err) {
|
|
734105
|
+
out.fail(err);
|
|
734106
|
+
}
|
|
734107
|
+
}
|
|
734108
|
+
async function handleProjectsArchive(id, opts) {
|
|
734109
|
+
const out = output(opts);
|
|
734110
|
+
const ctx = await buildApiContext(out);
|
|
734111
|
+
try {
|
|
734112
|
+
const res = await ctx.axios.post(`${BASE3}/${encodeURIComponent(id)}/archive`);
|
|
734113
|
+
out.ok(`Project ${id} archived`, res.data?.project ?? res.data);
|
|
734114
|
+
} catch (err) {
|
|
734115
|
+
out.fail(err);
|
|
734116
|
+
}
|
|
734117
|
+
}
|
|
734118
|
+
async function handleProjectsPublish(id, opts) {
|
|
734119
|
+
const out = output(opts);
|
|
734120
|
+
const ctx = await buildApiContext(out);
|
|
734121
|
+
try {
|
|
734122
|
+
const res = await ctx.axios.post(`${BASE3}/${encodeURIComponent(id)}/publish`);
|
|
734123
|
+
out.ok(`Project ${id} published`, res.data?.project ?? res.data);
|
|
734124
|
+
} catch (err) {
|
|
734125
|
+
out.fail(err);
|
|
734126
|
+
}
|
|
734127
|
+
}
|
|
734128
|
+
|
|
734129
|
+
// apps/cli/cli/handlers/sprint-handlers.ts
|
|
734130
|
+
var sprintsBase = (pid) => `/api/v1/projects/${encodeURIComponent(pid)}/sprints`;
|
|
734131
|
+
var SPRINT_COLUMNS = [
|
|
734132
|
+
{ header: "ID", key: "id" },
|
|
734133
|
+
{ header: "Name", key: "name", maxWidth: 30 },
|
|
734134
|
+
{ header: "Status", key: "status" },
|
|
734135
|
+
{ header: "Start", key: "start_date" },
|
|
734136
|
+
{ header: "End", key: "end_date" },
|
|
734137
|
+
{ header: "Position", key: "position" }
|
|
734138
|
+
];
|
|
734139
|
+
function requireProject(opts, out) {
|
|
734140
|
+
if (!opts.project) out.fail(new Error("--project <id> is required"));
|
|
734141
|
+
}
|
|
734142
|
+
async function handleSprintsList(opts) {
|
|
734143
|
+
const out = output(opts);
|
|
734144
|
+
requireProject(opts, out);
|
|
734145
|
+
const ctx = await buildApiContext(out);
|
|
734146
|
+
const params = {};
|
|
734147
|
+
if (opts.status) params.status = opts.status;
|
|
734148
|
+
if (opts.page) params.page = Number(opts.page);
|
|
734149
|
+
if (opts.perPage) params.per_page = Number(opts.perPage);
|
|
734150
|
+
try {
|
|
734151
|
+
const res = await ctx.axios.get(sprintsBase(opts.project), { params });
|
|
734152
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734153
|
+
const pagination = extractPagination(res.headers, items.length);
|
|
734154
|
+
if (opts.json) {
|
|
734155
|
+
out.show({ items, pagination });
|
|
734156
|
+
return;
|
|
734157
|
+
}
|
|
734158
|
+
out.list(items, SPRINT_COLUMNS);
|
|
734159
|
+
if (pagination.pages > 1) {
|
|
734160
|
+
out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
|
|
734161
|
+
}
|
|
734162
|
+
} catch (err) {
|
|
734163
|
+
out.fail(err);
|
|
734164
|
+
}
|
|
734165
|
+
}
|
|
734166
|
+
async function handleSprintsShow(id, opts) {
|
|
734167
|
+
const out = output(opts);
|
|
734168
|
+
requireProject(opts, out);
|
|
734169
|
+
const ctx = await buildApiContext(out);
|
|
734170
|
+
try {
|
|
734171
|
+
const res = await ctx.axios.get(`${sprintsBase(opts.project)}/${encodeURIComponent(id)}`);
|
|
734172
|
+
out.show(res.data?.sprint ?? res.data, { title: `Sprint ${id}` });
|
|
734173
|
+
} catch (err) {
|
|
734174
|
+
out.fail(err);
|
|
734175
|
+
}
|
|
734176
|
+
}
|
|
734177
|
+
async function handleSprintsCreate(opts) {
|
|
734178
|
+
const out = output(opts);
|
|
734179
|
+
requireProject(opts, out);
|
|
734180
|
+
const ctx = await buildApiContext(out);
|
|
734181
|
+
if (!opts.name) out.fail(new Error("--name is required"));
|
|
734182
|
+
const body2 = { name: opts.name };
|
|
734183
|
+
if (opts.goal !== void 0) body2.goal = opts.goal;
|
|
734184
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734185
|
+
if (opts.start !== void 0) body2.start_date = opts.start;
|
|
734186
|
+
if (opts.end !== void 0) body2.end_date = opts.end;
|
|
734187
|
+
try {
|
|
734188
|
+
const res = await ctx.axios.post(sprintsBase(opts.project), body2);
|
|
734189
|
+
const sprint = res.data?.sprint ?? res.data;
|
|
734190
|
+
out.ok(`Sprint created (id=${sprint?.id ?? "unknown"})`, sprint);
|
|
734191
|
+
} catch (err) {
|
|
734192
|
+
out.fail(err);
|
|
734193
|
+
}
|
|
734194
|
+
}
|
|
734195
|
+
async function handleSprintsUpdate(id, opts) {
|
|
734196
|
+
const out = output(opts);
|
|
734197
|
+
requireProject(opts, out);
|
|
734198
|
+
const ctx = await buildApiContext(out);
|
|
734199
|
+
const body2 = {};
|
|
734200
|
+
if (opts.name !== void 0) body2.name = opts.name;
|
|
734201
|
+
if (opts.goal !== void 0) body2.goal = opts.goal;
|
|
734202
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734203
|
+
if (opts.start !== void 0) body2.start_date = opts.start;
|
|
734204
|
+
if (opts.end !== void 0) body2.end_date = opts.end;
|
|
734205
|
+
if (opts.status !== void 0) body2.status = opts.status;
|
|
734206
|
+
if (Object.keys(body2).length === 0) out.fail(new Error("No fields to update"));
|
|
734207
|
+
try {
|
|
734208
|
+
const res = await ctx.axios.patch(
|
|
734209
|
+
`${sprintsBase(opts.project)}/${encodeURIComponent(id)}`,
|
|
734210
|
+
body2
|
|
734211
|
+
);
|
|
734212
|
+
out.ok(`Sprint ${id} updated`, res.data?.sprint ?? res.data);
|
|
734213
|
+
} catch (err) {
|
|
734214
|
+
out.fail(err);
|
|
734215
|
+
}
|
|
734216
|
+
}
|
|
734217
|
+
async function handleSprintsDelete(id, opts) {
|
|
734218
|
+
const out = output(opts);
|
|
734219
|
+
requireProject(opts, out);
|
|
734220
|
+
const ctx = await buildApiContext(out);
|
|
734221
|
+
if (!opts.yes && !opts.json) {
|
|
734222
|
+
const { confirm } = await lib_default.prompt([
|
|
734223
|
+
{
|
|
734224
|
+
type: "confirm",
|
|
734225
|
+
name: "confirm",
|
|
734226
|
+
message: `Delete sprint ${id}? Tickets in this sprint will be returned to the backlog.`,
|
|
734227
|
+
default: false
|
|
734228
|
+
}
|
|
734229
|
+
]);
|
|
734230
|
+
if (!confirm) {
|
|
734231
|
+
out.note("Aborted");
|
|
734232
|
+
return;
|
|
734233
|
+
}
|
|
734234
|
+
} else if (!opts.yes && opts.json) {
|
|
734235
|
+
out.fail(new Error("Refusing to delete without --yes in --json mode"));
|
|
734236
|
+
}
|
|
734237
|
+
try {
|
|
734238
|
+
await ctx.axios.delete(`${sprintsBase(opts.project)}/${encodeURIComponent(id)}`);
|
|
734239
|
+
out.ok(`Sprint ${id} deleted`);
|
|
734240
|
+
} catch (err) {
|
|
734241
|
+
out.fail(err);
|
|
734242
|
+
}
|
|
734243
|
+
}
|
|
734244
|
+
async function handleSprintsStart(id, opts) {
|
|
734245
|
+
const out = output(opts);
|
|
734246
|
+
requireProject(opts, out);
|
|
734247
|
+
const ctx = await buildApiContext(out);
|
|
734248
|
+
try {
|
|
734249
|
+
const res = await ctx.axios.post(
|
|
734250
|
+
`${sprintsBase(opts.project)}/${encodeURIComponent(id)}/start`
|
|
734251
|
+
);
|
|
734252
|
+
out.ok(`Sprint ${id} started`, res.data?.sprint ?? res.data);
|
|
734253
|
+
} catch (err) {
|
|
734254
|
+
out.fail(err);
|
|
734255
|
+
}
|
|
734256
|
+
}
|
|
734257
|
+
async function handleSprintsComplete(id, opts) {
|
|
734258
|
+
const out = output(opts);
|
|
734259
|
+
requireProject(opts, out);
|
|
734260
|
+
const ctx = await buildApiContext(out);
|
|
734261
|
+
try {
|
|
734262
|
+
const res = await ctx.axios.post(
|
|
734263
|
+
`${sprintsBase(opts.project)}/${encodeURIComponent(id)}/complete`
|
|
734264
|
+
);
|
|
734265
|
+
out.ok(`Sprint ${id} completed`, res.data?.sprint ?? res.data);
|
|
734266
|
+
} catch (err) {
|
|
734267
|
+
out.fail(err);
|
|
734268
|
+
}
|
|
734269
|
+
}
|
|
734270
|
+
|
|
734271
|
+
// apps/cli/cli/handlers/ticket-handlers.ts
|
|
734272
|
+
var ticketsBase = (pid) => `/api/v1/projects/${encodeURIComponent(pid)}/tickets`;
|
|
734273
|
+
var TICKET_COLUMNS = [
|
|
734274
|
+
{ header: "ID", key: "id" },
|
|
734275
|
+
{ header: "Title", key: "title", maxWidth: 40 },
|
|
734276
|
+
{ header: "Type", key: "ticket_type" },
|
|
734277
|
+
{ header: "Status", key: "status" },
|
|
734278
|
+
{ header: "Priority", key: "priority" },
|
|
734279
|
+
{ header: "Sprint", key: "sprint_id" },
|
|
734280
|
+
{ header: "Assignee", key: "assignee_id" },
|
|
734281
|
+
{ header: "Due", key: "due_date" }
|
|
734282
|
+
];
|
|
734283
|
+
function requireProject2(opts, out) {
|
|
734284
|
+
if (!opts.project) out.fail(new Error("--project <id> is required"));
|
|
734285
|
+
}
|
|
734286
|
+
function parseLabels(raw) {
|
|
734287
|
+
if (raw == null) return void 0;
|
|
734288
|
+
return raw.split(",").map((s3) => s3.trim()).filter(Boolean);
|
|
734289
|
+
}
|
|
734290
|
+
async function handleTicketsList(opts) {
|
|
734291
|
+
const out = output(opts);
|
|
734292
|
+
requireProject2(opts, out);
|
|
734293
|
+
const ctx = await buildApiContext(out);
|
|
734294
|
+
const params = {};
|
|
734295
|
+
if (opts.backlog) {
|
|
734296
|
+
params.backlog = true;
|
|
734297
|
+
} else if (opts.sprint !== void 0) {
|
|
734298
|
+
params.sprint_id = opts.sprint === "null" ? "null" : opts.sprint;
|
|
734299
|
+
}
|
|
734300
|
+
if (opts.assignee !== void 0) {
|
|
734301
|
+
params.assignee_id = opts.assignee === "null" ? "null" : opts.assignee;
|
|
734302
|
+
}
|
|
734303
|
+
if (opts.status) params.status = opts.status;
|
|
734304
|
+
if (opts.priority) params.priority = opts.priority;
|
|
734305
|
+
if (opts.label) params.label = opts.label;
|
|
734306
|
+
if (opts.page) params.page = Number(opts.page);
|
|
734307
|
+
if (opts.perPage) params.per_page = Number(opts.perPage);
|
|
734308
|
+
try {
|
|
734309
|
+
const res = await ctx.axios.get(ticketsBase(opts.project), { params });
|
|
734310
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734311
|
+
const pagination = extractPagination(res.headers, items.length);
|
|
734312
|
+
if (opts.json) {
|
|
734313
|
+
out.show({ items, pagination });
|
|
734314
|
+
return;
|
|
734315
|
+
}
|
|
734316
|
+
out.list(items, TICKET_COLUMNS);
|
|
734317
|
+
if (pagination.pages > 1) {
|
|
734318
|
+
out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
|
|
734319
|
+
}
|
|
734320
|
+
} catch (err) {
|
|
734321
|
+
out.fail(err);
|
|
734322
|
+
}
|
|
734323
|
+
}
|
|
734324
|
+
async function handleTicketsShow(id, opts) {
|
|
734325
|
+
const out = output(opts);
|
|
734326
|
+
requireProject2(opts, out);
|
|
734327
|
+
const ctx = await buildApiContext(out);
|
|
734328
|
+
try {
|
|
734329
|
+
const res = await ctx.axios.get(`${ticketsBase(opts.project)}/${encodeURIComponent(id)}`);
|
|
734330
|
+
out.show(res.data?.ticket ?? res.data, { title: `Ticket ${id}` });
|
|
734331
|
+
} catch (err) {
|
|
734332
|
+
out.fail(err);
|
|
734333
|
+
}
|
|
734334
|
+
}
|
|
734335
|
+
function ticketBodyFromOpts(opts, includeAll) {
|
|
734336
|
+
const body2 = {};
|
|
734337
|
+
if (opts.title !== void 0) body2.title = opts.title;
|
|
734338
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734339
|
+
if (opts.type !== void 0) body2.ticket_type = opts.type;
|
|
734340
|
+
if (opts.sprint !== void 0) {
|
|
734341
|
+
body2.sprint_id = opts.sprint === "null" ? null : opts.sprint;
|
|
734342
|
+
}
|
|
734343
|
+
if (opts.assignee !== void 0) {
|
|
734344
|
+
body2.assignee_id = opts.assignee === "null" ? null : opts.assignee;
|
|
734345
|
+
}
|
|
734346
|
+
if (opts.priority !== void 0) body2.priority = opts.priority;
|
|
734347
|
+
if (opts.status !== void 0) body2.status = opts.status;
|
|
734348
|
+
if (opts.parent !== void 0) {
|
|
734349
|
+
body2.parent_ticket_id = opts.parent === "null" ? null : opts.parent;
|
|
734350
|
+
}
|
|
734351
|
+
if (opts.due !== void 0) body2.due_date = opts.due;
|
|
734352
|
+
if (opts.storyPoints !== void 0) body2.story_points = Number(opts.storyPoints);
|
|
734353
|
+
const labels = parseLabels(opts.labels);
|
|
734354
|
+
if (labels !== void 0) body2.labels = labels;
|
|
734355
|
+
if (opts.column !== void 0) body2.column_status = opts.column;
|
|
734356
|
+
return includeAll ? body2 : body2;
|
|
734357
|
+
}
|
|
734358
|
+
async function handleTicketsCreate(opts) {
|
|
734359
|
+
const out = output(opts);
|
|
734360
|
+
requireProject2(opts, out);
|
|
734361
|
+
const ctx = await buildApiContext(out);
|
|
734362
|
+
if (!opts.title) out.fail(new Error("--title is required"));
|
|
734363
|
+
const body2 = ticketBodyFromOpts(opts, true);
|
|
734364
|
+
try {
|
|
734365
|
+
const res = await ctx.axios.post(ticketsBase(opts.project), body2);
|
|
734366
|
+
const ticket = res.data?.ticket ?? res.data;
|
|
734367
|
+
out.ok(`Ticket created (id=${ticket?.id ?? "unknown"})`, ticket);
|
|
734368
|
+
} catch (err) {
|
|
734369
|
+
out.fail(err);
|
|
734370
|
+
}
|
|
734371
|
+
}
|
|
734372
|
+
async function handleTicketsUpdate(id, opts) {
|
|
734373
|
+
const out = output(opts);
|
|
734374
|
+
requireProject2(opts, out);
|
|
734375
|
+
const ctx = await buildApiContext(out);
|
|
734376
|
+
const body2 = ticketBodyFromOpts(opts, true);
|
|
734377
|
+
if (Object.keys(body2).length === 0) out.fail(new Error("No fields to update"));
|
|
734378
|
+
try {
|
|
734379
|
+
const res = await ctx.axios.patch(
|
|
734380
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}`,
|
|
734381
|
+
body2
|
|
734382
|
+
);
|
|
734383
|
+
out.ok(`Ticket ${id} updated`, res.data?.ticket ?? res.data);
|
|
734384
|
+
} catch (err) {
|
|
734385
|
+
out.fail(err);
|
|
734386
|
+
}
|
|
734387
|
+
}
|
|
734388
|
+
async function handleTicketsDelete(id, opts) {
|
|
734389
|
+
const out = output(opts);
|
|
734390
|
+
requireProject2(opts, out);
|
|
734391
|
+
const ctx = await buildApiContext(out);
|
|
734392
|
+
if (!opts.yes && !opts.json) {
|
|
734393
|
+
const { confirm } = await lib_default.prompt([
|
|
734394
|
+
{
|
|
734395
|
+
type: "confirm",
|
|
734396
|
+
name: "confirm",
|
|
734397
|
+
message: `Delete ticket ${id}? This also drops its tasks, subtasks, deliverables and activities.`,
|
|
734398
|
+
default: false
|
|
734399
|
+
}
|
|
734400
|
+
]);
|
|
734401
|
+
if (!confirm) {
|
|
734402
|
+
out.note("Aborted");
|
|
734403
|
+
return;
|
|
734404
|
+
}
|
|
734405
|
+
} else if (!opts.yes && opts.json) {
|
|
734406
|
+
out.fail(new Error("Refusing to delete without --yes in --json mode"));
|
|
734407
|
+
}
|
|
734408
|
+
try {
|
|
734409
|
+
await ctx.axios.delete(`${ticketsBase(opts.project)}/${encodeURIComponent(id)}`);
|
|
734410
|
+
out.ok(`Ticket ${id} deleted`);
|
|
734411
|
+
} catch (err) {
|
|
734412
|
+
out.fail(err);
|
|
734413
|
+
}
|
|
734414
|
+
}
|
|
734415
|
+
async function handleTicketsAssign(id, opts) {
|
|
734416
|
+
const out = output(opts);
|
|
734417
|
+
requireProject2(opts, out);
|
|
734418
|
+
const ctx = await buildApiContext(out);
|
|
734419
|
+
if (opts.to === void 0) out.fail(new Error("--to <user-id|null> is required"));
|
|
734420
|
+
const body2 = { assignee_id: opts.to === "null" ? null : opts.to };
|
|
734421
|
+
try {
|
|
734422
|
+
const res = await ctx.axios.patch(
|
|
734423
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/assign`,
|
|
734424
|
+
body2
|
|
734425
|
+
);
|
|
734426
|
+
const t3 = res.data?.ticket ?? res.data;
|
|
734427
|
+
out.ok(
|
|
734428
|
+
body2.assignee_id === null ? `Ticket ${id} unassigned` : `Ticket ${id} assigned to ${body2.assignee_id}`,
|
|
734429
|
+
t3
|
|
734430
|
+
);
|
|
734431
|
+
} catch (err) {
|
|
734432
|
+
out.fail(err);
|
|
734433
|
+
}
|
|
734434
|
+
}
|
|
734435
|
+
async function handleTicketsStatus(id, opts) {
|
|
734436
|
+
const out = output(opts);
|
|
734437
|
+
requireProject2(opts, out);
|
|
734438
|
+
const ctx = await buildApiContext(out);
|
|
734439
|
+
if (!opts.to) {
|
|
734440
|
+
out.fail(new Error("--to <open|in_progress|done|blocked|awaiting_human|cancelled|failed> is required"));
|
|
734441
|
+
}
|
|
734442
|
+
const body2 = { status: opts.to };
|
|
734443
|
+
if (opts.column !== void 0) body2.column_status = opts.column;
|
|
734444
|
+
try {
|
|
734445
|
+
const res = await ctx.axios.patch(
|
|
734446
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/change_status`,
|
|
734447
|
+
body2
|
|
734448
|
+
);
|
|
734449
|
+
out.ok(`Ticket ${id} status \u2192 ${opts.to}`, res.data?.ticket ?? res.data);
|
|
734450
|
+
} catch (err) {
|
|
734451
|
+
out.fail(err);
|
|
734452
|
+
}
|
|
734453
|
+
}
|
|
734454
|
+
async function handleTicketsMove(id, opts) {
|
|
734455
|
+
const out = output(opts);
|
|
734456
|
+
requireProject2(opts, out);
|
|
734457
|
+
const ctx = await buildApiContext(out);
|
|
734458
|
+
const body2 = {};
|
|
734459
|
+
if (opts.backlog) {
|
|
734460
|
+
body2.sprint_id = null;
|
|
734461
|
+
} else if (opts.sprint !== void 0) {
|
|
734462
|
+
body2.sprint_id = opts.sprint === "null" ? null : opts.sprint;
|
|
734463
|
+
}
|
|
734464
|
+
if (opts.column !== void 0) body2.column_status = opts.column;
|
|
734465
|
+
if (opts.position !== void 0) body2.position = Number(opts.position);
|
|
734466
|
+
if (Object.keys(body2).length === 0) {
|
|
734467
|
+
out.fail(new Error("Nothing to move \u2014 pass --sprint, --backlog, --column or --position"));
|
|
734468
|
+
}
|
|
734469
|
+
try {
|
|
734470
|
+
const res = await ctx.axios.patch(
|
|
734471
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/move`,
|
|
734472
|
+
body2
|
|
734473
|
+
);
|
|
734474
|
+
out.ok(`Ticket ${id} moved`, res.data?.ticket ?? res.data);
|
|
734475
|
+
} catch (err) {
|
|
734476
|
+
out.fail(err);
|
|
734477
|
+
}
|
|
734478
|
+
}
|
|
734479
|
+
var ACTIVITY_COLUMNS = [
|
|
734480
|
+
{ header: "ID", key: "id" },
|
|
734481
|
+
{ header: "Kind", key: "kind" },
|
|
734482
|
+
{ header: "Actor", key: "actor_id" },
|
|
734483
|
+
{ header: "At", key: "created_at" },
|
|
734484
|
+
{ header: "Detail", key: "summary", maxWidth: 60 }
|
|
734485
|
+
];
|
|
734486
|
+
async function handleTicketsActivities(id, opts) {
|
|
734487
|
+
const out = output(opts);
|
|
734488
|
+
requireProject2(opts, out);
|
|
734489
|
+
const ctx = await buildApiContext(out);
|
|
734490
|
+
const params = {};
|
|
734491
|
+
if (opts.page) params.page = Number(opts.page);
|
|
734492
|
+
if (opts.perPage) params.per_page = Number(opts.perPage);
|
|
734493
|
+
try {
|
|
734494
|
+
const res = await ctx.axios.get(
|
|
734495
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/activities`,
|
|
734496
|
+
{ params }
|
|
734497
|
+
);
|
|
734498
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734499
|
+
const pagination = extractPagination(res.headers, items.length);
|
|
734500
|
+
if (opts.json) {
|
|
734501
|
+
out.show({ items, pagination });
|
|
734502
|
+
return;
|
|
734503
|
+
}
|
|
734504
|
+
out.list(items, ACTIVITY_COLUMNS);
|
|
734505
|
+
if (pagination.pages > 1) {
|
|
734506
|
+
out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
|
|
734507
|
+
}
|
|
734508
|
+
} catch (err) {
|
|
734509
|
+
out.fail(err);
|
|
734510
|
+
}
|
|
734511
|
+
}
|
|
734512
|
+
async function handleTicketsDeliverable(id, opts) {
|
|
734513
|
+
const out = output(opts);
|
|
734514
|
+
requireProject2(opts, out);
|
|
734515
|
+
const ctx = await buildApiContext(out);
|
|
734516
|
+
if (!opts.deliverable) out.fail(new Error("--deliverable <id> is required"));
|
|
734517
|
+
try {
|
|
734518
|
+
const res = await ctx.axios.post(
|
|
734519
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/publish_deliverable`,
|
|
734520
|
+
{ deliverable_id: opts.deliverable }
|
|
734521
|
+
);
|
|
734522
|
+
out.ok(
|
|
734523
|
+
`Deliverable ${opts.deliverable} published on ticket ${id}`,
|
|
734524
|
+
res.data?.deliverable ?? res.data
|
|
734525
|
+
);
|
|
734526
|
+
} catch (err) {
|
|
734527
|
+
out.fail(err);
|
|
734528
|
+
}
|
|
734529
|
+
}
|
|
734530
|
+
async function handleTicketsDispatch(id, opts) {
|
|
734531
|
+
const out = output(opts);
|
|
734532
|
+
requireProject2(opts, out);
|
|
734533
|
+
const ctx = await buildApiContext(out);
|
|
734534
|
+
try {
|
|
734535
|
+
const res = await ctx.axios.post(
|
|
734536
|
+
`${ticketsBase(opts.project)}/${encodeURIComponent(id)}/dispatch_agent`
|
|
734537
|
+
);
|
|
734538
|
+
out.ok(`Agent dispatched on ticket ${id}`, res.data?.ticket ?? res.data);
|
|
734539
|
+
} catch (err) {
|
|
734540
|
+
out.fail(err);
|
|
734541
|
+
}
|
|
734542
|
+
}
|
|
734543
|
+
|
|
734544
|
+
// apps/cli/cli/handlers/task-handlers.ts
|
|
734545
|
+
var tasksBase = (pid, tid) => `/api/v1/projects/${encodeURIComponent(pid)}/tickets/${encodeURIComponent(tid)}/tasks`;
|
|
734546
|
+
var TASK_COLUMNS = [
|
|
734547
|
+
{ header: "ID", key: "id" },
|
|
734548
|
+
{ header: "Title", key: "title", maxWidth: 40 },
|
|
734549
|
+
{ header: "Done", key: "completed", format: (v6) => v6 ? "\u2714" : "" },
|
|
734550
|
+
{ header: "Priority", key: "priority" },
|
|
734551
|
+
{ header: "Assignee", key: "assignee_id" },
|
|
734552
|
+
{ header: "Due", key: "due_date" },
|
|
734553
|
+
{ header: "Position", key: "position" }
|
|
734554
|
+
];
|
|
734555
|
+
function requireParents(opts, out) {
|
|
734556
|
+
if (!opts.project) out.fail(new Error("--project <id> is required"));
|
|
734557
|
+
if (!opts.ticket) out.fail(new Error("--ticket <id> is required"));
|
|
734558
|
+
}
|
|
734559
|
+
function parseLabels2(raw) {
|
|
734560
|
+
if (raw == null) return void 0;
|
|
734561
|
+
return raw.split(",").map((s3) => s3.trim()).filter(Boolean);
|
|
734562
|
+
}
|
|
734563
|
+
async function handleTasksList(opts) {
|
|
734564
|
+
const out = output(opts);
|
|
734565
|
+
requireParents(opts, out);
|
|
734566
|
+
const ctx = await buildApiContext(out);
|
|
734567
|
+
try {
|
|
734568
|
+
const res = await ctx.axios.get(tasksBase(opts.project, opts.ticket));
|
|
734569
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734570
|
+
const pagination = extractPagination(res.headers, items.length);
|
|
734571
|
+
if (opts.json) {
|
|
734572
|
+
out.show({ items, pagination });
|
|
734573
|
+
return;
|
|
734574
|
+
}
|
|
734575
|
+
out.list(items, TASK_COLUMNS);
|
|
734576
|
+
if (pagination.pages > 1) {
|
|
734577
|
+
out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
|
|
734578
|
+
}
|
|
734579
|
+
} catch (err) {
|
|
734580
|
+
out.fail(err);
|
|
734581
|
+
}
|
|
734582
|
+
}
|
|
734583
|
+
async function handleTasksShow(id, opts) {
|
|
734584
|
+
const out = output(opts);
|
|
734585
|
+
requireParents(opts, out);
|
|
734586
|
+
const ctx = await buildApiContext(out);
|
|
734587
|
+
try {
|
|
734588
|
+
const res = await ctx.axios.get(
|
|
734589
|
+
`${tasksBase(opts.project, opts.ticket)}/${encodeURIComponent(id)}`
|
|
734590
|
+
);
|
|
734591
|
+
out.show(res.data?.task ?? res.data, { title: `Task ${id}` });
|
|
734592
|
+
} catch (err) {
|
|
734593
|
+
out.fail(err);
|
|
734594
|
+
}
|
|
734595
|
+
}
|
|
734596
|
+
function taskBodyFromOpts(opts) {
|
|
734597
|
+
const body2 = {};
|
|
734598
|
+
if (opts.title !== void 0) body2.title = opts.title;
|
|
734599
|
+
if (opts.description !== void 0) body2.description = opts.description;
|
|
734600
|
+
if (opts.priority !== void 0) body2.priority = opts.priority;
|
|
734601
|
+
if (opts.assignee !== void 0) {
|
|
734602
|
+
body2.assignee_id = opts.assignee === "null" ? null : opts.assignee;
|
|
734603
|
+
}
|
|
734604
|
+
if (opts.due !== void 0) body2.due_date = opts.due;
|
|
734605
|
+
if (opts.estimate !== void 0) body2.estimate = Number(opts.estimate);
|
|
734606
|
+
const labels = parseLabels2(opts.labels);
|
|
734607
|
+
if (labels !== void 0) body2.labels = labels;
|
|
734608
|
+
return body2;
|
|
734609
|
+
}
|
|
734610
|
+
async function handleTasksCreate(opts) {
|
|
734611
|
+
const out = output(opts);
|
|
734612
|
+
requireParents(opts, out);
|
|
734613
|
+
const ctx = await buildApiContext(out);
|
|
734614
|
+
if (!opts.title) out.fail(new Error("--title is required"));
|
|
734615
|
+
const body2 = taskBodyFromOpts(opts);
|
|
734616
|
+
try {
|
|
734617
|
+
const res = await ctx.axios.post(tasksBase(opts.project, opts.ticket), body2);
|
|
734618
|
+
const task = res.data?.task ?? res.data;
|
|
734619
|
+
out.ok(`Task created (id=${task?.id ?? "unknown"})`, task);
|
|
734620
|
+
} catch (err) {
|
|
734621
|
+
out.fail(err);
|
|
734622
|
+
}
|
|
734623
|
+
}
|
|
734624
|
+
async function handleTasksUpdate(id, opts) {
|
|
734625
|
+
const out = output(opts);
|
|
734626
|
+
requireParents(opts, out);
|
|
734627
|
+
const ctx = await buildApiContext(out);
|
|
734628
|
+
const body2 = taskBodyFromOpts(opts);
|
|
734629
|
+
if (Object.keys(body2).length === 0) out.fail(new Error("No fields to update"));
|
|
734630
|
+
try {
|
|
734631
|
+
const res = await ctx.axios.patch(
|
|
734632
|
+
`${tasksBase(opts.project, opts.ticket)}/${encodeURIComponent(id)}`,
|
|
734633
|
+
body2
|
|
734634
|
+
);
|
|
734635
|
+
out.ok(`Task ${id} updated`, res.data?.task ?? res.data);
|
|
734636
|
+
} catch (err) {
|
|
734637
|
+
out.fail(err);
|
|
734638
|
+
}
|
|
734639
|
+
}
|
|
734640
|
+
async function handleTasksDelete(id, opts) {
|
|
734641
|
+
const out = output(opts);
|
|
734642
|
+
requireParents(opts, out);
|
|
734643
|
+
const ctx = await buildApiContext(out);
|
|
734644
|
+
if (!opts.yes && !opts.json) {
|
|
734645
|
+
const { confirm } = await lib_default.prompt([
|
|
734646
|
+
{
|
|
734647
|
+
type: "confirm",
|
|
734648
|
+
name: "confirm",
|
|
734649
|
+
message: `Delete task ${id}?`,
|
|
734650
|
+
default: false
|
|
734651
|
+
}
|
|
734652
|
+
]);
|
|
734653
|
+
if (!confirm) {
|
|
734654
|
+
out.note("Aborted");
|
|
734655
|
+
return;
|
|
734656
|
+
}
|
|
734657
|
+
} else if (!opts.yes && opts.json) {
|
|
734658
|
+
out.fail(new Error("Refusing to delete without --yes in --json mode"));
|
|
734659
|
+
}
|
|
734660
|
+
try {
|
|
734661
|
+
await ctx.axios.delete(
|
|
734662
|
+
`${tasksBase(opts.project, opts.ticket)}/${encodeURIComponent(id)}`
|
|
734663
|
+
);
|
|
734664
|
+
out.ok(`Task ${id} deleted`);
|
|
734665
|
+
} catch (err) {
|
|
734666
|
+
out.fail(err);
|
|
734667
|
+
}
|
|
734668
|
+
}
|
|
734669
|
+
async function handleTasksComplete(id, opts) {
|
|
734670
|
+
const out = output(opts);
|
|
734671
|
+
requireParents(opts, out);
|
|
734672
|
+
const ctx = await buildApiContext(out);
|
|
734673
|
+
const body2 = { completed: !opts.uncomplete };
|
|
734674
|
+
try {
|
|
734675
|
+
const res = await ctx.axios.patch(
|
|
734676
|
+
`${tasksBase(opts.project, opts.ticket)}/${encodeURIComponent(id)}/complete`,
|
|
734677
|
+
body2
|
|
734678
|
+
);
|
|
734679
|
+
out.ok(
|
|
734680
|
+
body2.completed ? `Task ${id} completed` : `Task ${id} re-opened`,
|
|
734681
|
+
res.data?.task ?? res.data
|
|
734682
|
+
);
|
|
734683
|
+
} catch (err) {
|
|
734684
|
+
out.fail(err);
|
|
734685
|
+
}
|
|
734686
|
+
}
|
|
734687
|
+
async function handleTasksReorder(opts) {
|
|
734688
|
+
const out = output(opts);
|
|
734689
|
+
requireParents(opts, out);
|
|
734690
|
+
const ctx = await buildApiContext(out);
|
|
734691
|
+
if (!opts.order || opts.order.trim() === "") {
|
|
734692
|
+
out.fail(new Error("--order is required (comma-separated task ids)"));
|
|
734693
|
+
}
|
|
734694
|
+
const task_ids = opts.order.split(",").map((s3) => s3.trim()).filter(Boolean);
|
|
734695
|
+
if (task_ids.length === 0) {
|
|
734696
|
+
out.fail(new Error("--order must contain at least one task id"));
|
|
734697
|
+
}
|
|
734698
|
+
try {
|
|
734699
|
+
const res = await ctx.axios.patch(
|
|
734700
|
+
`${tasksBase(opts.project, opts.ticket)}/reorder`,
|
|
734701
|
+
{ task_ids }
|
|
734702
|
+
);
|
|
734703
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
734704
|
+
if (opts.json) {
|
|
734705
|
+
out.show({ ok: true, message: "Tasks reordered", tasks: items });
|
|
734706
|
+
return;
|
|
734707
|
+
}
|
|
734708
|
+
out.ok("Tasks reordered");
|
|
734709
|
+
out.list(items, TASK_COLUMNS);
|
|
734710
|
+
} catch (err) {
|
|
734711
|
+
out.fail(err);
|
|
734712
|
+
}
|
|
734713
|
+
}
|
|
734714
|
+
|
|
733926
734715
|
// apps/cli/cli.ts
|
|
733927
734716
|
var getVersion = () => {
|
|
733928
734717
|
try {
|
|
733929
|
-
if (true) return "1.
|
|
734718
|
+
if (true) return "1.9.0";
|
|
733930
734719
|
} catch {
|
|
733931
734720
|
}
|
|
733932
734721
|
for (const candidate of [
|
|
@@ -734220,6 +735009,106 @@ permissions.command("grant").description("Grant a permission on a resource to a
|
|
|
734220
735009
|
permissions.command("revoke <id>").description("Revoke a permission by id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
|
|
734221
735010
|
await handlePermissionsRevoke(id, withGlobals(options));
|
|
734222
735011
|
});
|
|
735012
|
+
var projects = program2.command("projects").description("Manage projects (attached to a workspace or standalone)");
|
|
735013
|
+
projects.command("ls").alias("list").description("List projects").option("--workspace <id>", 'Filter by workspace id; use "null" for standalone projects').option("--type <slug>", "Filter by project_type").option("--status <status>", "active | completed | archived | draft | failed | paused").option("--page <n>").option("--per-page <n>").action(async (options) => {
|
|
735014
|
+
await handleProjectsList(withGlobals(options));
|
|
735015
|
+
});
|
|
735016
|
+
projects.command("show <id>").description("Show a project (with sprints and ticket stats)").action(async (id, options) => {
|
|
735017
|
+
await handleProjectsShow(id, withGlobals(options));
|
|
735018
|
+
});
|
|
735019
|
+
projects.command("create").description("Create a project").requiredOption("--name <name>", "Project name").option("--description <text>").option("--workspace <id>", "Attach to a workspace (omit for standalone)").option("--type-id <id>", "Project type id (UUID)").option("--folder <id>", "Link to a documents folder").option("--settings <json>", "Kanban columns / icon / color as JSON").option("--metadata <json>", "Custom metadata as JSON object").action(async (options) => {
|
|
735020
|
+
await handleProjectsCreate(withGlobals(options));
|
|
735021
|
+
});
|
|
735022
|
+
projects.command("update <id>").description("Update a project (name / description / status / settings / metadata only \u2014 workspace and folder cannot be changed via API)").option("--name <name>").option("--description <text>").option("--status <status>").option("--settings <json>").option("--metadata <json>").action(async (id, options) => {
|
|
735023
|
+
await handleProjectsUpdate(id, withGlobals(options));
|
|
735024
|
+
});
|
|
735025
|
+
projects.command("delete <id>").description("Delete a project (drops its sprints and tickets)").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
|
|
735026
|
+
await handleProjectsDelete(id, withGlobals(options));
|
|
735027
|
+
});
|
|
735028
|
+
projects.command("archive <id>").description("Archive a project (transition to `archived` status)").action(async (id, options) => {
|
|
735029
|
+
await handleProjectsArchive(id, withGlobals(options));
|
|
735030
|
+
});
|
|
735031
|
+
projects.command("publish <id>").description("Publish a project (transition from `draft` to `active`)").action(async (id, options) => {
|
|
735032
|
+
await handleProjectsPublish(id, withGlobals(options));
|
|
735033
|
+
});
|
|
735034
|
+
var sprints = program2.command("sprints").description("Manage sprints of a project (requires --project)");
|
|
735035
|
+
sprints.command("ls").alias("list").description("List sprints of a project").requiredOption("--project <id>", "Parent project id").option("--status <status>", "planning | active | completed | failed | pending").option("--page <n>").option("--per-page <n>").action(async (options) => {
|
|
735036
|
+
await handleSprintsList(withGlobals(options));
|
|
735037
|
+
});
|
|
735038
|
+
sprints.command("show <id>").description("Show a sprint (with tickets summary)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
|
|
735039
|
+
await handleSprintsShow(id, withGlobals(options));
|
|
735040
|
+
});
|
|
735041
|
+
sprints.command("create").description("Create a sprint").requiredOption("--project <id>", "Parent project id").requiredOption("--name <name>").option("--goal <text>").option("--description <text>").option("--start <YYYY-MM-DD>").option("--end <YYYY-MM-DD>").action(async (options) => {
|
|
735042
|
+
await handleSprintsCreate(withGlobals(options));
|
|
735043
|
+
});
|
|
735044
|
+
sprints.command("update <id>").description("Update a sprint").requiredOption("--project <id>", "Parent project id").option("--name <name>").option("--goal <text>").option("--description <text>").option("--start <YYYY-MM-DD>").option("--end <YYYY-MM-DD>").option("--status <status>").action(async (id, options) => {
|
|
735045
|
+
await handleSprintsUpdate(id, withGlobals(options));
|
|
735046
|
+
});
|
|
735047
|
+
sprints.command("delete <id>").description("Delete a sprint (its tickets return to the backlog)").requiredOption("--project <id>", "Parent project id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
|
|
735048
|
+
await handleSprintsDelete(id, withGlobals(options));
|
|
735049
|
+
});
|
|
735050
|
+
sprints.command("start <id>").description("Start a sprint (planning \u2192 active)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
|
|
735051
|
+
await handleSprintsStart(id, withGlobals(options));
|
|
735052
|
+
});
|
|
735053
|
+
sprints.command("complete <id>").description("Complete a sprint (active \u2192 completed)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
|
|
735054
|
+
await handleSprintsComplete(id, withGlobals(options));
|
|
735055
|
+
});
|
|
735056
|
+
var tickets = program2.command("tickets").description("Manage tickets of a project (requires --project)");
|
|
735057
|
+
tickets.command("ls").alias("list").description("List tickets").requiredOption("--project <id>", "Parent project id").option("--sprint <id>", 'Filter by sprint id; use "null" or --backlog for backlog').option("--backlog", "Alias for --sprint null").option("--assignee <id>", 'Filter by assignee id; use "null" for unassigned').option("--status <status>").option("--priority <priority>").option("--label <label>").option("--page <n>").option("--per-page <n>").action(async (options) => {
|
|
735058
|
+
await handleTicketsList(withGlobals(options));
|
|
735059
|
+
});
|
|
735060
|
+
tickets.command("show <id>").description("Show a ticket (with tasks, subtasks, deliverables)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
|
|
735061
|
+
await handleTicketsShow(id, withGlobals(options));
|
|
735062
|
+
});
|
|
735063
|
+
tickets.command("create").description("Create a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--title <text>").option("--description <text>").option("--type <ticket_type>", "task (default) | bug | story | epic | \u2026").option("--sprint <id>", "Attach to a sprint; omit to leave in backlog").option("--assignee <id>", "Assign to a user id").option("--priority <priority>", "lowest | low | medium | high | highest").option("--status <status>").option("--parent <id>", "Parent ticket id (for subtasks)").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>", "Comma-separated labels").option("--column <column_status>").action(async (options) => {
|
|
735064
|
+
await handleTicketsCreate(withGlobals(options));
|
|
735065
|
+
});
|
|
735066
|
+
tickets.command("update <id>").description("Update a ticket").requiredOption("--project <id>", "Parent project id").option("--title <text>").option("--description <text>").option("--type <ticket_type>").option("--sprint <id>", 'Move to a sprint; use "null" for backlog').option("--assignee <id>", 'Reassign; use "null" to unassign').option("--priority <priority>").option("--status <status>").option("--parent <id>").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>").option("--column <column_status>").action(async (id, options) => {
|
|
735067
|
+
await handleTicketsUpdate(id, withGlobals(options));
|
|
735068
|
+
});
|
|
735069
|
+
tickets.command("delete <id>").description("Delete a ticket (drops its tasks, deliverables, activities)").requiredOption("--project <id>", "Parent project id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
|
|
735070
|
+
await handleTicketsDelete(id, withGlobals(options));
|
|
735071
|
+
});
|
|
735072
|
+
tickets.command("assign <id>").description("Assign a ticket to a user; --to null to unassign").requiredOption("--project <id>", "Parent project id").requiredOption("--to <user-id|null>").action(async (id, options) => {
|
|
735073
|
+
await handleTicketsAssign(id, withGlobals(options));
|
|
735074
|
+
});
|
|
735075
|
+
tickets.command("status <id>").description("Change a ticket status (optionally pin the kanban column with --column)").requiredOption("--project <id>", "Parent project id").requiredOption("--to <status>", "open | in_progress | done | blocked | awaiting_human | cancelled | failed").option("--column <column_status>", "Optionally set the kanban column at the same time").action(async (id, options) => {
|
|
735076
|
+
await handleTicketsStatus(id, withGlobals(options));
|
|
735077
|
+
});
|
|
735078
|
+
tickets.command("move <id>").description("Move a ticket to another sprint / column / position").requiredOption("--project <id>", "Parent project id").option("--sprint <id>", 'Target sprint id; use "null" or --backlog for backlog').option("--backlog", "Alias for --sprint null").option("--column <column_status>").option("--position <n>").action(async (id, options) => {
|
|
735079
|
+
await handleTicketsMove(id, withGlobals(options));
|
|
735080
|
+
});
|
|
735081
|
+
tickets.command("activities <id>").description("List activities of a ticket (audit log)").requiredOption("--project <id>", "Parent project id").option("--page <n>").option("--per-page <n>").action(async (id, options) => {
|
|
735082
|
+
await handleTicketsActivities(id, withGlobals(options));
|
|
735083
|
+
});
|
|
735084
|
+
tickets.command("deliverable <id>").description("Publish an existing deliverable already attached to a ticket (see corex#588 for the create-then-publish flow)").requiredOption("--project <id>", "Parent project id").requiredOption("--deliverable <id>", "Deliverable id to publish").action(async (id, options) => {
|
|
735085
|
+
await handleTicketsDeliverable(id, withGlobals(options));
|
|
735086
|
+
});
|
|
735087
|
+
tickets.command("dispatch <id>").description("Dispatch the ticket's already-configured agent (agent_config is read from the ticket, not passed in body)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
|
|
735088
|
+
await handleTicketsDispatch(id, withGlobals(options));
|
|
735089
|
+
});
|
|
735090
|
+
var tasks = program2.command("tasks").description("Manage sub-tasks of a ticket (requires --project + --ticket)");
|
|
735091
|
+
tasks.command("ls").alias("list").description("List sub-tasks of a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").action(async (options) => {
|
|
735092
|
+
await handleTasksList(withGlobals(options));
|
|
735093
|
+
});
|
|
735094
|
+
tasks.command("show <id>").description("Show a task").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").action(async (id, options) => {
|
|
735095
|
+
await handleTasksShow(id, withGlobals(options));
|
|
735096
|
+
});
|
|
735097
|
+
tasks.command("create").description("Create a task under a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").requiredOption("--title <text>").option("--description <text>").option("--priority <priority>").option("--assignee <id>", 'Assign to a user id; use "null" for unassigned').option("--due <YYYY-MM-DD>").option("--estimate <n>", "Effort estimate (integer)").option("--labels <a,b,c>", "Comma-separated labels").action(async (options) => {
|
|
735098
|
+
await handleTasksCreate(withGlobals(options));
|
|
735099
|
+
});
|
|
735100
|
+
tasks.command("update <id>").description("Update a task").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").option("--title <text>").option("--description <text>").option("--priority <priority>").option("--assignee <id>").option("--due <YYYY-MM-DD>").option("--estimate <n>").option("--labels <a,b,c>").action(async (id, options) => {
|
|
735101
|
+
await handleTasksUpdate(id, withGlobals(options));
|
|
735102
|
+
});
|
|
735103
|
+
tasks.command("delete <id>").description("Delete a task").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
|
|
735104
|
+
await handleTasksDelete(id, withGlobals(options));
|
|
735105
|
+
});
|
|
735106
|
+
tasks.command("complete <id>").description("Mark a task as completed (or re-open with --uncomplete)").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").option("--uncomplete", "Re-open the task instead of completing it").action(async (id, options) => {
|
|
735107
|
+
await handleTasksComplete(id, withGlobals(options));
|
|
735108
|
+
});
|
|
735109
|
+
tasks.command("reorder").description("Reorder tasks under a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").requiredOption("--order <ids>", "Comma-separated task ids in the desired order").action(async (options) => {
|
|
735110
|
+
await handleTasksReorder(withGlobals(options));
|
|
735111
|
+
});
|
|
734223
735112
|
program2.parse();
|
|
734224
735113
|
/*! Bundled license information:
|
|
734225
735114
|
|
package/docs/llm-usage.md
CHANGED
|
@@ -362,6 +362,101 @@ hubdoc permissions ls --on="$WS" --on-type=workspace --json \
|
|
|
362
362
|
| jq '.[] | {actor: .actor.display_name, level}'
|
|
363
363
|
```
|
|
364
364
|
|
|
365
|
+
### Projects / Sprints / Tickets / Tasks (corex#588)
|
|
366
|
+
|
|
367
|
+
Manage projects (attached to a workspace or standalone), their sprints,
|
|
368
|
+
tickets and sub-tasks — the same domain Synapse agents drive when they
|
|
369
|
+
consume incoming emails, produce deliverables, or hand off work between
|
|
370
|
+
themselves. All commands support `--json` for machine parsing.
|
|
371
|
+
|
|
372
|
+
`hubdoc projects` (top-level: attached to a workspace or standalone) —
|
|
373
|
+
`ls | show | create | update | delete | archive | publish`.
|
|
374
|
+
`--workspace null` filters the standalone (unattached) projects.
|
|
375
|
+
|
|
376
|
+
`hubdoc sprints --project=<pid>` — `ls | show | create | update | delete
|
|
377
|
+
| start | complete`.
|
|
378
|
+
|
|
379
|
+
`hubdoc tickets --project=<pid>` — `ls | show | create | update | delete
|
|
380
|
+
| assign | status | move | activities | deliverable | dispatch`. Every
|
|
381
|
+
verb requires `--project=<pid>`. `ls --backlog` (or `ls --sprint null`)
|
|
382
|
+
lists the backlog. `assign --to null` unassigns. `status --to <state>
|
|
383
|
+
[--column <col>]` uses the canonical statuses: `open | in_progress | done
|
|
384
|
+
| blocked | awaiting_human | cancelled | failed` and optionally pins the
|
|
385
|
+
kanban column at the same time.
|
|
386
|
+
|
|
387
|
+
`hubdoc tasks --project=<pid> --ticket=<tid>` — `ls | show | create |
|
|
388
|
+
update | delete | complete | reorder`. Sub-tasks live under a ticket;
|
|
389
|
+
both parents are required on every call.
|
|
390
|
+
|
|
391
|
+
**Pagination**: all `ls`-shaped verbs return `{ items, pagination:{ page,
|
|
392
|
+
per_page, pages, count, has_more } }` in `--json`. The transport under
|
|
393
|
+
the hood is a bare JSON array plus `X-Total-*` headers (Pagy), same
|
|
394
|
+
convention as `/api/v1/documents`; the CLI normalises so agents don't
|
|
395
|
+
need to touch headers.
|
|
396
|
+
|
|
397
|
+
**Idempotence caveat**: `sprints start` / `sprints complete` follow the
|
|
398
|
+
interactor semantics — re-triggering on a sprint already in the target
|
|
399
|
+
state returns **422** (`unprocessable_entity` with an
|
|
400
|
+
`already_active` / `already_completed` message), not 200.
|
|
401
|
+
|
|
402
|
+
**Deliverables**: `tickets deliverable <id> --deliverable <did>`
|
|
403
|
+
publishes an EXISTING deliverable already attached to the ticket (via
|
|
404
|
+
web UI or a future create endpoint). The API does not yet support
|
|
405
|
+
creating + publishing a deliverable in one CLI call — track corex#588
|
|
406
|
+
follow-ups.
|
|
407
|
+
|
|
408
|
+
**Dispatch**: `tickets dispatch <id>` fires the agent already configured
|
|
409
|
+
on the ticket (`agent_config` is read from the ticket's persisted
|
|
410
|
+
state). To change the agent config, set it on the ticket via the web UI
|
|
411
|
+
or a future `update` extension — the CLI cannot pass it in the request
|
|
412
|
+
body today.
|
|
413
|
+
|
|
414
|
+
#### End-to-end example: agent turns an incoming email into a mission
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# 0. The Synapse poller has already dropped the raw .eml + attachments
|
|
418
|
+
# into Hubdoc and passed us the email doc id.
|
|
419
|
+
EMAIL_ID="doc-42"
|
|
420
|
+
|
|
421
|
+
# 1. Detach attachments so the agent can classify them separately.
|
|
422
|
+
hubdoc files detach "$EMAIL_ID" --ext pdf,docx --json
|
|
423
|
+
|
|
424
|
+
# 2. Pick the standalone project used for triage (no workspace linkage).
|
|
425
|
+
PID=$(hubdoc projects ls --workspace null --type-id "$TRIAGE_PT_ID" --json \
|
|
426
|
+
| jq -r '.items[0].id')
|
|
427
|
+
|
|
428
|
+
# 3. Create a ticket in the current sprint, priority high, due next week.
|
|
429
|
+
SID=$(hubdoc sprints ls --project "$PID" --status active --json \
|
|
430
|
+
| jq -r '.items[0].id')
|
|
431
|
+
TID=$(hubdoc tickets create --project "$PID" \
|
|
432
|
+
--title "Client X – devis à produire" \
|
|
433
|
+
--sprint "$SID" --assignee "u-1" \
|
|
434
|
+
--priority high --due 2026-07-18 \
|
|
435
|
+
--labels "commercial,urgent" --json | jq -r '.data.id')
|
|
436
|
+
|
|
437
|
+
# 4. Break it down into tasks the agent will chew through.
|
|
438
|
+
hubdoc tasks create --project "$PID" --ticket "$TID" \
|
|
439
|
+
--title "Lire pièces jointes" --priority high --json
|
|
440
|
+
hubdoc tasks create --project "$PID" --ticket "$TID" \
|
|
441
|
+
--title "Rédiger devis" --priority high --due 2026-07-16 --json
|
|
442
|
+
|
|
443
|
+
# 5. Dispatch the agent already configured on the ticket.
|
|
444
|
+
hubdoc tickets dispatch "$TID" --project "$PID" --json
|
|
445
|
+
|
|
446
|
+
# 6. Later — publish an already-attached deliverable and close the ticket.
|
|
447
|
+
hubdoc tickets deliverable "$TID" --project "$PID" \
|
|
448
|
+
--deliverable "$DELIVERABLE_ID" --json
|
|
449
|
+
hubdoc tickets status "$TID" --project "$PID" \
|
|
450
|
+
--to done --column done --json
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Common patterns:
|
|
454
|
+
- **Move a ticket to the backlog**: `hubdoc tickets move <id> --project=<pid>
|
|
455
|
+
--backlog` (equivalent to `--sprint null`).
|
|
456
|
+
- **Un-assign**: `hubdoc tickets assign <id> --project=<pid> --to null`.
|
|
457
|
+
- **Reorder a ticket's tasks** (drag-and-drop from the UI equivalent):
|
|
458
|
+
`hubdoc tasks reorder --project=<pid> --ticket=<tid> --order=k3,k1,k2`.
|
|
459
|
+
|
|
365
460
|
## Out of scope (planned)
|
|
366
461
|
|
|
367
462
|
- `hubdoc templates create|update|delete` — depends on corex#417 backend
|