@lumoai/cli 1.5.0 → 1.6.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/assets/skill.md +189 -16
- package/dist/cli/src/commands/auth-login.js +4 -3
- package/dist/cli/src/commands/auth-logout.js +2 -1
- package/dist/cli/src/commands/doc-bind.js +3 -3
- package/dist/cli/src/commands/doc-create.js +5 -5
- package/dist/cli/src/commands/doc-delete.js +4 -4
- package/dist/cli/src/commands/doc-import-gdoc.js +82 -0
- package/dist/cli/src/commands/doc-list.js +7 -7
- package/dist/cli/src/commands/doc-move.js +8 -8
- package/dist/cli/src/commands/doc-share-list.js +11 -8
- package/dist/cli/src/commands/doc-share.js +7 -5
- package/dist/cli/src/commands/doc-show.js +6 -6
- package/dist/cli/src/commands/doc-sync.js +44 -0
- package/dist/cli/src/commands/doc-unbind.js +4 -4
- package/dist/cli/src/commands/doc-unshare.js +9 -7
- package/dist/cli/src/commands/doc-update.js +5 -5
- package/dist/cli/src/commands/hook.js +2 -2
- package/dist/cli/src/commands/memory-project-add.js +19 -4
- package/dist/cli/src/commands/memory-project-list.js +1 -2
- package/dist/cli/src/commands/memory-promote.js +3 -3
- package/dist/cli/src/commands/memory-rm.js +1 -2
- package/dist/cli/src/commands/memory-task-add.js +19 -4
- package/dist/cli/src/commands/memory-task-list.js +1 -2
- package/dist/cli/src/commands/milestone-create.js +4 -4
- package/dist/cli/src/commands/milestone-delete.js +5 -5
- package/dist/cli/src/commands/milestone-list.js +3 -3
- package/dist/cli/src/commands/milestone-show.js +5 -5
- package/dist/cli/src/commands/milestone-update.js +6 -5
- package/dist/cli/src/commands/project-list.js +3 -3
- package/dist/cli/src/commands/session-attach.js +5 -5
- package/dist/cli/src/commands/session-detach.js +3 -3
- package/dist/cli/src/commands/session-status.js +3 -3
- package/dist/cli/src/commands/session-wrap.js +32 -0
- package/dist/cli/src/commands/setup.js +33 -7
- package/dist/cli/src/commands/sprint-add.js +3 -3
- package/dist/cli/src/commands/sprint-close.js +5 -5
- package/dist/cli/src/commands/sprint-create.js +4 -4
- package/dist/cli/src/commands/sprint-delete.js +5 -5
- package/dist/cli/src/commands/sprint-list.js +3 -3
- package/dist/cli/src/commands/sprint-remove.js +3 -3
- package/dist/cli/src/commands/sprint-show.js +4 -4
- package/dist/cli/src/commands/sprint-start.js +4 -4
- package/dist/cli/src/commands/sprint-summary.js +7 -7
- package/dist/cli/src/commands/sprint-update.js +6 -5
- package/dist/cli/src/commands/task-artifact-add.js +17 -5
- package/dist/cli/src/commands/task-artifact-list.js +4 -4
- package/dist/cli/src/commands/task-artifact-rm.js +4 -4
- package/dist/cli/src/commands/task-artifact-show.js +8 -8
- package/dist/cli/src/commands/task-artifact-update.js +5 -5
- package/dist/cli/src/commands/task-comment-list.js +111 -0
- package/dist/cli/src/commands/task-comment.js +3 -3
- package/dist/cli/src/commands/task-context.js +29 -12
- package/dist/cli/src/commands/task-create.js +7 -7
- package/dist/cli/src/commands/task-figma-add.js +3 -2
- package/dist/cli/src/commands/task-figma-context.js +61 -0
- package/dist/cli/src/commands/task-figma-list.js +3 -2
- package/dist/cli/src/commands/task-figma-refresh.js +4 -3
- package/dist/cli/src/commands/task-figma-rm.js +3 -2
- package/dist/cli/src/commands/task-list.js +1 -2
- package/dist/cli/src/commands/task-pr-show.js +66 -0
- package/dist/cli/src/commands/task-show.js +8 -7
- package/dist/cli/src/commands/task-slack-show.js +59 -0
- package/dist/cli/src/commands/task-update.js +7 -7
- package/dist/cli/src/commands/task-web-show.js +64 -0
- package/dist/cli/src/commands/whoami.js +4 -3
- package/dist/cli/src/commands/wrap/progress-comment-section.js +81 -0
- package/dist/cli/src/index.js +174 -102
- package/dist/cli/src/lib/agent.js +10 -1
- package/dist/cli/src/lib/api.js +81 -1
- package/dist/cli/src/lib/config.js +2 -1
- package/dist/cli/src/lib/doc-input.js +12 -1
- package/dist/cli/src/lib/editor.js +66 -0
- package/dist/cli/src/lib/figma-api.js +1 -1
- package/dist/cli/src/lib/format.js +3 -2
- package/dist/cli/src/lib/hook-runner.js +64 -19
- package/dist/cli/src/lib/hooks-template.js +52 -7
- package/dist/cli/src/lib/memory-content.js +4 -3
- package/dist/cli/src/lib/path-guard.js +125 -0
- package/dist/cli/src/lib/progress-comment-api.js +47 -0
- package/dist/cli/src/lib/resolve-doc-id.js +2 -1
- package/dist/cli/src/lib/resolve-member.js +2 -1
- package/dist/cli/src/lib/sanitize.js +17 -0
- package/dist/cli/src/lib/tag-resolver.js +2 -1
- package/dist/cli/src/lib/update-check.js +2 -2
- package/dist/cli/src/lib/wrap-panel.js +15 -0
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ const config_1 = require("../lib/config");
|
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
6
|
const doc_input_1 = require("../lib/doc-input");
|
|
7
7
|
const agent_1 = require("../lib/agent");
|
|
8
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
9
|
+
const path_guard_1 = require("../lib/path-guard");
|
|
8
10
|
async function taskArtifactAdd(identifier, options) {
|
|
9
11
|
if (!identifier) {
|
|
10
12
|
console.error('Error: missing <task>. Usage: lumo task artifact add <LUM-42> --kind spec --title "Spec" --file spec.md --source Superpowers');
|
|
@@ -39,9 +41,20 @@ async function taskArtifactAdd(identifier, options) {
|
|
|
39
41
|
console.error('Error: --file <path> is required.');
|
|
40
42
|
return 1;
|
|
41
43
|
}
|
|
44
|
+
const verdict = (0, path_guard_1.checkArtifactFilePath)(options.file);
|
|
45
|
+
if (!verdict.ok) {
|
|
46
|
+
if (verdict.reason === 'unreadable') {
|
|
47
|
+
console.error(`Error: could not read file ${options.file}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.error(`Error: refusing to read ${options.file} — ${verdict.detail}. ` +
|
|
51
|
+
`artifact --file must be a non-sensitive path inside the project directory.`);
|
|
52
|
+
}
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
42
55
|
let content;
|
|
43
56
|
try {
|
|
44
|
-
content = await (0, doc_input_1.readFileUtf8)(
|
|
57
|
+
content = await (0, doc_input_1.readFileUtf8)(verdict.resolved);
|
|
45
58
|
}
|
|
46
59
|
catch {
|
|
47
60
|
console.error(`Error: could not read file ${options.file}`);
|
|
@@ -56,8 +69,7 @@ async function taskArtifactAdd(identifier, options) {
|
|
|
56
69
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
57
70
|
return 1;
|
|
58
71
|
}
|
|
59
|
-
const
|
|
60
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
72
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
61
73
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
62
74
|
const payload = {
|
|
63
75
|
kind,
|
|
@@ -101,10 +113,10 @@ async function taskArtifactAdd(identifier, options) {
|
|
|
101
113
|
/* not JSON */
|
|
102
114
|
}
|
|
103
115
|
console.error(serverMsg
|
|
104
|
-
? `Error: ${serverMsg}`
|
|
116
|
+
? `Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`
|
|
105
117
|
: `Error: artifact add failed (HTTP ${res.status})`);
|
|
106
118
|
return 1;
|
|
107
119
|
}
|
|
108
120
|
const data = (await res.json());
|
|
109
|
-
process.stdout.write(`Added [${data.artifact.kind}] "${data.artifact.title}" to ${identifier}\n`);
|
|
121
|
+
process.stdout.write(`Added [${(0, sanitize_1.sanitizeField)(data.artifact.kind)}] "${(0, sanitize_1.sanitizeField)(data.artifact.title)}" to ${identifier}\n`);
|
|
110
122
|
}
|
|
@@ -4,6 +4,7 @@ exports.taskArtifactList = taskArtifactList;
|
|
|
4
4
|
const config_1 = require("../lib/config");
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
6
|
const agent_1 = require("../lib/agent");
|
|
7
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
7
8
|
async function taskArtifactList(identifier) {
|
|
8
9
|
if (!identifier) {
|
|
9
10
|
console.error('Error: missing <task>. Usage: lumo task artifact list <LUM-42>');
|
|
@@ -14,8 +15,7 @@ async function taskArtifactList(identifier) {
|
|
|
14
15
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
15
16
|
return 1;
|
|
16
17
|
}
|
|
17
|
-
const
|
|
18
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
18
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
19
19
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
20
20
|
let res;
|
|
21
21
|
try {
|
|
@@ -44,7 +44,7 @@ async function taskArtifactList(identifier) {
|
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
for (const a of artifacts) {
|
|
47
|
-
const agentLabel = agent_1.AGENT_LABELS[a.agent] ?? a.agent;
|
|
48
|
-
process.stdout.write(`${a.id} ${a.kind.padEnd(10)} ${agentLabel.padEnd(15)} ${a.source.padEnd(12)} ${a.title}\n`);
|
|
47
|
+
const agentLabel = agent_1.AGENT_LABELS[a.agent] ?? (0, sanitize_1.sanitizeField)(a.agent);
|
|
48
|
+
process.stdout.write(`${a.id} ${(0, sanitize_1.sanitizeField)(a.kind).padEnd(10)} ${agentLabel.padEnd(15)} ${(0, sanitize_1.sanitizeField)(a.source).padEnd(12)} ${(0, sanitize_1.sanitizeField)(a.title)}\n`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.taskArtifactRm = taskArtifactRm;
|
|
4
4
|
const config_1 = require("../lib/config");
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
6
7
|
async function taskArtifactRm(identifier, artifactId, options) {
|
|
7
8
|
if (!identifier) {
|
|
8
9
|
console.error('Error: missing <task>. Usage: lumo task artifact rm <LUM-42> <artifact-id> --yes');
|
|
@@ -21,8 +22,7 @@ async function taskArtifactRm(identifier, artifactId, options) {
|
|
|
21
22
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
22
23
|
return 1;
|
|
23
24
|
}
|
|
24
|
-
const
|
|
25
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
25
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
26
26
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
27
27
|
let res;
|
|
28
28
|
try {
|
|
@@ -50,7 +50,7 @@ async function taskArtifactRm(identifier, artifactId, options) {
|
|
|
50
50
|
catch {
|
|
51
51
|
/* not JSON */
|
|
52
52
|
}
|
|
53
|
-
console.error(`Error: ${serverMsg}`);
|
|
53
|
+
console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
|
|
54
54
|
return 1;
|
|
55
55
|
}
|
|
56
56
|
if (res.status !== 204) {
|
|
@@ -64,7 +64,7 @@ async function taskArtifactRm(identifier, artifactId, options) {
|
|
|
64
64
|
/* not JSON */
|
|
65
65
|
}
|
|
66
66
|
console.error(serverMsg
|
|
67
|
-
? `Error: ${serverMsg}`
|
|
67
|
+
? `Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`
|
|
68
68
|
: `Error: artifact rm failed (HTTP ${res.status})`);
|
|
69
69
|
return 1;
|
|
70
70
|
}
|
|
@@ -4,6 +4,7 @@ exports.taskArtifactShow = taskArtifactShow;
|
|
|
4
4
|
const config_1 = require("../lib/config");
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
6
|
const agent_1 = require("../lib/agent");
|
|
7
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
7
8
|
async function taskArtifactShow(identifier, artifactId) {
|
|
8
9
|
if (!identifier) {
|
|
9
10
|
console.error('Error: missing <task>. Usage: lumo task artifact show <LUM-42> <artifact-id>');
|
|
@@ -18,8 +19,7 @@ async function taskArtifactShow(identifier, artifactId) {
|
|
|
18
19
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
19
20
|
return 1;
|
|
20
21
|
}
|
|
21
|
-
const
|
|
22
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
22
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
23
23
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
24
24
|
let res;
|
|
25
25
|
try {
|
|
@@ -44,7 +44,7 @@ async function taskArtifactShow(identifier, artifactId) {
|
|
|
44
44
|
catch {
|
|
45
45
|
/* not JSON */
|
|
46
46
|
}
|
|
47
|
-
console.error(`Error: ${serverMsg}`);
|
|
47
|
+
console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
|
|
48
48
|
return 1;
|
|
49
49
|
}
|
|
50
50
|
if (!res.ok) {
|
|
@@ -53,11 +53,11 @@ async function taskArtifactShow(identifier, artifactId) {
|
|
|
53
53
|
}
|
|
54
54
|
const { artifact } = (await res.json());
|
|
55
55
|
process.stdout.write(`id: ${artifact.id}\n`);
|
|
56
|
-
process.stdout.write(`kind: ${artifact.kind}\n`);
|
|
57
|
-
process.stdout.write(`title: ${artifact.title}\n`);
|
|
58
|
-
process.stdout.write(`source: ${artifact.source}\n`);
|
|
59
|
-
process.stdout.write(`agent: ${agent_1.AGENT_LABELS[artifact.agent] ?? artifact.agent}\n`);
|
|
56
|
+
process.stdout.write(`kind: ${(0, sanitize_1.sanitizeField)(artifact.kind)}\n`);
|
|
57
|
+
process.stdout.write(`title: ${(0, sanitize_1.sanitizeField)(artifact.title)}\n`);
|
|
58
|
+
process.stdout.write(`source: ${(0, sanitize_1.sanitizeField)(artifact.source)}\n`);
|
|
59
|
+
process.stdout.write(`agent: ${agent_1.AGENT_LABELS[artifact.agent] ?? (0, sanitize_1.sanitizeField)(artifact.agent ?? '')}\n`);
|
|
60
60
|
process.stdout.write(`order: ${artifact.order}\n`);
|
|
61
61
|
process.stdout.write(`task: ${identifier}\n`);
|
|
62
|
-
process.stdout.write(`\n${artifact.content}\n`);
|
|
62
|
+
process.stdout.write(`\n${(0, sanitize_1.sanitizeField)(artifact.content)}\n`);
|
|
63
63
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.taskArtifactUpdate = taskArtifactUpdate;
|
|
4
4
|
const config_1 = require("../lib/config");
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
6
7
|
const agent_1 = require("../lib/agent");
|
|
7
8
|
async function taskArtifactUpdate(identifier, artifactId, options) {
|
|
8
9
|
if (!identifier) {
|
|
@@ -55,8 +56,7 @@ async function taskArtifactUpdate(identifier, artifactId, options) {
|
|
|
55
56
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
56
57
|
return 1;
|
|
57
58
|
}
|
|
58
|
-
const
|
|
59
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
59
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
60
60
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
61
61
|
let res;
|
|
62
62
|
try {
|
|
@@ -88,7 +88,7 @@ async function taskArtifactUpdate(identifier, artifactId, options) {
|
|
|
88
88
|
catch {
|
|
89
89
|
/* not JSON */
|
|
90
90
|
}
|
|
91
|
-
console.error(`Error: ${serverMsg}`);
|
|
91
|
+
console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
|
|
92
92
|
return 1;
|
|
93
93
|
}
|
|
94
94
|
if (res.status !== 200) {
|
|
@@ -102,10 +102,10 @@ async function taskArtifactUpdate(identifier, artifactId, options) {
|
|
|
102
102
|
/* not JSON */
|
|
103
103
|
}
|
|
104
104
|
console.error(serverMsg
|
|
105
|
-
? `Error: ${serverMsg}`
|
|
105
|
+
? `Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`
|
|
106
106
|
: `Error: artifact update failed (HTTP ${res.status})`);
|
|
107
107
|
return 1;
|
|
108
108
|
}
|
|
109
109
|
const data = (await res.json());
|
|
110
|
-
process.stdout.write(`Updated [${data.artifact.kind}] "${data.artifact.title}" (${data.artifact.source}) on ${identifier}\n`);
|
|
110
|
+
process.stdout.write(`Updated [${(0, sanitize_1.sanitizeField)(data.artifact.kind)}] "${(0, sanitize_1.sanitizeField)(data.artifact.title)}" (${(0, sanitize_1.sanitizeField)(data.artifact.source)}) on ${identifier}\n`);
|
|
111
111
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskCommentList = taskCommentList;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
7
|
+
/**
|
|
8
|
+
* Strip TipTap/HTML markup to readable plain text. Comment bodies are stored as
|
|
9
|
+
* HTML; the CLI only needs the visible text. We collapse block tags to newlines
|
|
10
|
+
* and drop the rest, then decode the handful of entities that survive.
|
|
11
|
+
*/
|
|
12
|
+
function htmlToPlainText(html) {
|
|
13
|
+
return html
|
|
14
|
+
.replace(/<\/(p|div|li|h[1-6]|blockquote)>/gi, '\n')
|
|
15
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
16
|
+
.replace(/<[^>]+>/g, '')
|
|
17
|
+
.replace(/ /g, ' ')
|
|
18
|
+
.replace(/&/g, '&')
|
|
19
|
+
.replace(/</g, '<')
|
|
20
|
+
.replace(/>/g, '>')
|
|
21
|
+
.replace(/"/g, '"')
|
|
22
|
+
.replace(/'/g, "'")
|
|
23
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
24
|
+
.trim();
|
|
25
|
+
}
|
|
26
|
+
function printComment(c, indent) {
|
|
27
|
+
const author = (0, sanitize_1.sanitizeField)(c.authorActorId ?? 'unknown');
|
|
28
|
+
console.log(`${indent}${author} · ${c.createdAt}`);
|
|
29
|
+
const text = (0, sanitize_1.sanitizeField)(htmlToPlainText(c.body));
|
|
30
|
+
for (const line of (text || '(empty)').split('\n')) {
|
|
31
|
+
console.log(`${indent}${line}`);
|
|
32
|
+
}
|
|
33
|
+
console.log('');
|
|
34
|
+
for (const reply of c.replies ?? []) {
|
|
35
|
+
printComment(reply, indent + ' ');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `lumo task comments list <LUM-N>`
|
|
40
|
+
*
|
|
41
|
+
* Tier-2 retrieval for the task comment thread. Resolves the LUM-N identifier to
|
|
42
|
+
* its DB id (the comments endpoint is keyed by DB id), then fetches the full
|
|
43
|
+
* thread from `/api/tasks/:id/comments` and prints each top-level comment
|
|
44
|
+
* (replies indented) as `author · time` followed by the plain-text body.
|
|
45
|
+
*/
|
|
46
|
+
async function taskCommentList(identifier) {
|
|
47
|
+
if (!identifier) {
|
|
48
|
+
console.error('Error: usage: lumo task comments list <LUM-42>');
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
const creds = (0, config_1.readCredentials)();
|
|
52
|
+
if (!creds) {
|
|
53
|
+
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
57
|
+
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
58
|
+
// 1. Resolve LUM-N → DB id (the comments endpoint takes the DB id).
|
|
59
|
+
let resolveRes;
|
|
60
|
+
try {
|
|
61
|
+
resolveRes = await fetch(`${base}/api/tasks/resolve/${encodeURIComponent(identifier)}`, { headers: { Authorization: `Bearer ${creds.token}` } });
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
65
|
+
console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
if (resolveRes.status === 401) {
|
|
69
|
+
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
if (resolveRes.status === 404) {
|
|
73
|
+
console.error(`Error: task ${identifier} not found in workspace ${creds.workspaceSlug}`);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
if (!resolveRes.ok) {
|
|
77
|
+
console.error(`Error: resolve failed (HTTP ${resolveRes.status})`);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
const resolved = (await resolveRes.json());
|
|
81
|
+
// 2. Fetch the comment thread.
|
|
82
|
+
let res;
|
|
83
|
+
try {
|
|
84
|
+
res = await fetch(`${base}/api/tasks/${encodeURIComponent(resolved.id)}/comments`, { headers: { Authorization: `Bearer ${creds.token}` } });
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
88
|
+
console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
if (res.status === 401) {
|
|
92
|
+
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
if (res.status === 404) {
|
|
96
|
+
console.error(`Error: task ${identifier} not found`);
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
console.error(`Error: comments list failed (HTTP ${res.status})`);
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
const { comments } = (await res.json());
|
|
104
|
+
if (!comments || comments.length === 0) {
|
|
105
|
+
console.log('(no comments)');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const c of comments) {
|
|
109
|
+
printComment(c, '');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.taskComment = taskComment;
|
|
4
4
|
const config_1 = require("../lib/config");
|
|
5
5
|
const api_1 = require("../lib/api");
|
|
6
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
6
7
|
/**
|
|
7
8
|
* `lumo task comment <LUM-N> <body>`
|
|
8
9
|
*
|
|
@@ -28,8 +29,7 @@ async function taskComment(identifier, body) {
|
|
|
28
29
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
29
30
|
return 1;
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
32
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
33
33
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
34
34
|
// 1. Resolve LUM-N → DB id (the comments endpoint takes DB id).
|
|
35
35
|
let resolveRes;
|
|
@@ -86,7 +86,7 @@ async function taskComment(identifier, body) {
|
|
|
86
86
|
// Body wasn't JSON
|
|
87
87
|
}
|
|
88
88
|
console.error(serverMsg
|
|
89
|
-
? `Error: ${serverMsg}`
|
|
89
|
+
? `Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`
|
|
90
90
|
: `Error: comment create failed (HTTP ${postRes.status})`);
|
|
91
91
|
return 1;
|
|
92
92
|
}
|
|
@@ -4,6 +4,7 @@ exports.taskContext = taskContext;
|
|
|
4
4
|
exports.formatTaskContextMarkdown = formatTaskContextMarkdown;
|
|
5
5
|
const config_1 = require("../lib/config");
|
|
6
6
|
const api_1 = require("../lib/api");
|
|
7
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
7
8
|
async function taskContext(identifier) {
|
|
8
9
|
if (!identifier) {
|
|
9
10
|
console.error('Error: missing <identifier>. Usage: lumo task context <LUM-42>');
|
|
@@ -14,8 +15,7 @@ async function taskContext(identifier) {
|
|
|
14
15
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
15
16
|
return 1;
|
|
16
17
|
}
|
|
17
|
-
const
|
|
18
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
18
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
19
19
|
const url = `${(0, api_1.trimTrailingSlash)(apiUrl)}/api/tasks/context/${encodeURIComponent(identifier)}`;
|
|
20
20
|
let res;
|
|
21
21
|
try {
|
|
@@ -52,39 +52,56 @@ async function taskContext(identifier) {
|
|
|
52
52
|
function formatTaskContextMarkdown(data, now) {
|
|
53
53
|
const lines = [];
|
|
54
54
|
lines.push(`# Task: ${data.task.identifier}`);
|
|
55
|
-
lines.push(`**Title**: ${data.task.title}`);
|
|
55
|
+
lines.push(`**Title**: ${(0, sanitize_1.sanitizeField)(data.task.title)}`);
|
|
56
56
|
lines.push(`**Status**: ${data.task.status}`);
|
|
57
57
|
if (data.task.milestone) {
|
|
58
58
|
const target = data.task.milestone.targetDate
|
|
59
59
|
? `, target ${data.task.milestone.targetDate.slice(0, 10)}`
|
|
60
60
|
: '';
|
|
61
|
-
lines.push(`**Milestone**: ${data.task.milestone.name} (${data.task.milestone.status}${target})`);
|
|
61
|
+
lines.push(`**Milestone**: ${(0, sanitize_1.sanitizeField)(data.task.milestone.name)} (${data.task.milestone.status}${target})`);
|
|
62
62
|
}
|
|
63
63
|
const body = data.task.descriptionMarkdown ?? data.task.description;
|
|
64
64
|
if (body && body.trim().length > 0) {
|
|
65
|
-
lines.push(`**Description**: ${body}`);
|
|
65
|
+
lines.push(`**Description**: ${(0, sanitize_1.sanitizeField)(body)}`);
|
|
66
66
|
}
|
|
67
67
|
lines.push('');
|
|
68
68
|
// Frontload memory before sessions: it's cold context the agent should see
|
|
69
69
|
// first. Server returns "" when empty, in which case we skip the section.
|
|
70
70
|
if (data.memorySection && data.memorySection.trim().length > 0) {
|
|
71
|
-
lines.push(data.memorySection.trimEnd());
|
|
71
|
+
lines.push((0, sanitize_1.sanitizeField)(data.memorySection.trimEnd()));
|
|
72
72
|
lines.push('');
|
|
73
73
|
}
|
|
74
74
|
if (data.slackContextSection && data.slackContextSection.trim().length > 0) {
|
|
75
|
-
lines.push(data.slackContextSection.trimEnd());
|
|
75
|
+
lines.push((0, sanitize_1.sanitizeField)(data.slackContextSection.trimEnd()));
|
|
76
76
|
lines.push('');
|
|
77
77
|
}
|
|
78
78
|
if (data.webLinkSection && data.webLinkSection.trim().length > 0) {
|
|
79
|
-
lines.push(data.webLinkSection.trimEnd());
|
|
79
|
+
lines.push((0, sanitize_1.sanitizeField)(data.webLinkSection.trimEnd()));
|
|
80
80
|
lines.push('');
|
|
81
81
|
}
|
|
82
82
|
if (data.figmaSection && data.figmaSection.trim().length > 0) {
|
|
83
|
-
lines.push(data.figmaSection.trimEnd());
|
|
83
|
+
lines.push((0, sanitize_1.sanitizeField)(data.figmaSection.trimEnd()));
|
|
84
84
|
lines.push('');
|
|
85
85
|
}
|
|
86
86
|
if (data.artifactSection && data.artifactSection.trim().length > 0) {
|
|
87
|
-
lines.push(data.artifactSection.trimEnd());
|
|
87
|
+
lines.push((0, sanitize_1.sanitizeField)(data.artifactSection.trimEnd()));
|
|
88
|
+
lines.push('');
|
|
89
|
+
}
|
|
90
|
+
if (data.docSection && data.docSection.trim().length > 0) {
|
|
91
|
+
lines.push((0, sanitize_1.sanitizeField)(data.docSection.trimEnd()));
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
if (data.commentsSection && data.commentsSection.trim().length > 0) {
|
|
95
|
+
lines.push((0, sanitize_1.sanitizeField)(data.commentsSection.trimEnd()));
|
|
96
|
+
lines.push('');
|
|
97
|
+
}
|
|
98
|
+
if (data.prSection && data.prSection.trim().length > 0) {
|
|
99
|
+
lines.push((0, sanitize_1.sanitizeField)(data.prSection.trimEnd()));
|
|
100
|
+
lines.push('');
|
|
101
|
+
}
|
|
102
|
+
if (data.prReviewTodosSection &&
|
|
103
|
+
data.prReviewTodosSection.trim().length > 0) {
|
|
104
|
+
lines.push((0, sanitize_1.sanitizeField)(data.prReviewTodosSection.trimEnd()));
|
|
88
105
|
lines.push('');
|
|
89
106
|
}
|
|
90
107
|
if (data.sessions.length === 0) {
|
|
@@ -101,11 +118,11 @@ function formatTaskContextMarkdown(data, now) {
|
|
|
101
118
|
const ago = relativeTime(new Date(s.lastActivityAt), now);
|
|
102
119
|
const dur = formatDuration(s.durationMs);
|
|
103
120
|
lines.push(`### Session ${shortId} · ${ago} · ${dur}`);
|
|
104
|
-
lines.push(`**Summary**: ${s.headline}`);
|
|
121
|
+
lines.push(`**Summary**: ${(0, sanitize_1.sanitizeField)(s.headline)}`);
|
|
105
122
|
if (s.unresolved.length > 0) {
|
|
106
123
|
lines.push('**Unresolved**:');
|
|
107
124
|
for (const u of s.unresolved)
|
|
108
|
-
lines.push(`- ${u}`);
|
|
125
|
+
lines.push(`- ${(0, sanitize_1.sanitizeField)(u)}`);
|
|
109
126
|
}
|
|
110
127
|
lines.push('');
|
|
111
128
|
}
|
|
@@ -8,6 +8,7 @@ const config_1 = require("../lib/config");
|
|
|
8
8
|
const api_1 = require("../lib/api");
|
|
9
9
|
const tag_resolver_1 = require("../lib/tag-resolver");
|
|
10
10
|
const resolve_1 = require("../lib/resolve");
|
|
11
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
11
12
|
const ALLOWED_PRIORITIES = ['LOW', 'MEDIUM', 'HIGH', 'URGENT'];
|
|
12
13
|
/**
|
|
13
14
|
* Assert that the sprint belongs to the same team as the newly created task.
|
|
@@ -81,10 +82,10 @@ function normalizePriority(value) {
|
|
|
81
82
|
* present.
|
|
82
83
|
*/
|
|
83
84
|
function formatCreatedTaskLine(task) {
|
|
84
|
-
const escapedTitle = task.title.replace(/"/g, '\\"');
|
|
85
|
+
const escapedTitle = (0, sanitize_1.sanitizeField)(task.title).replace(/"/g, '\\"');
|
|
85
86
|
const head = `Created ${task.identifier} "${escapedTitle}" ${task.url}`;
|
|
86
87
|
if (task.tags && task.tags.length > 0) {
|
|
87
|
-
return `${head}\nTags: ${task.tags.join(', ')}`;
|
|
88
|
+
return `${head}\nTags: ${task.tags.map(sanitize_1.sanitizeField).join(', ')}`;
|
|
88
89
|
}
|
|
89
90
|
return head;
|
|
90
91
|
}
|
|
@@ -108,8 +109,7 @@ async function taskCreate(title, opts) {
|
|
|
108
109
|
}
|
|
109
110
|
priority = normalized;
|
|
110
111
|
}
|
|
111
|
-
const
|
|
112
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
112
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
113
113
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
114
114
|
const url = `${base}/api/tasks`;
|
|
115
115
|
let tagIds;
|
|
@@ -166,7 +166,7 @@ async function taskCreate(title, opts) {
|
|
|
166
166
|
const workspaceSlug = creds.workspaceSlug ?? '';
|
|
167
167
|
try {
|
|
168
168
|
const sprint = await bindTaskToSprint(base, creds.token, workspaceSlug, opts.sprint, { id: data.task.id, teamId: data.task.teamId, identifier: data.task.identifier });
|
|
169
|
-
process.stdout.write(`Sprint: #${sprint.number} "${sprint.name}"\n`);
|
|
169
|
+
process.stdout.write(`Sprint: #${sprint.number} "${(0, sanitize_1.sanitizeField)(sprint.name)}"\n`);
|
|
170
170
|
}
|
|
171
171
|
catch (err) {
|
|
172
172
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -175,7 +175,7 @@ async function taskCreate(title, opts) {
|
|
|
175
175
|
console.error(msg);
|
|
176
176
|
}
|
|
177
177
|
else {
|
|
178
|
-
console.error(`Error: ${msg}`);
|
|
178
|
+
console.error(`Error: ${(0, sanitize_1.sanitizeField)(msg)}`);
|
|
179
179
|
}
|
|
180
180
|
return 1;
|
|
181
181
|
}
|
|
@@ -193,7 +193,7 @@ async function taskCreate(title, opts) {
|
|
|
193
193
|
// Body wasn't JSON; fall through to status-only message
|
|
194
194
|
}
|
|
195
195
|
if (serverMsg) {
|
|
196
|
-
console.error(`Error: ${serverMsg}`);
|
|
196
|
+
console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
|
|
197
197
|
}
|
|
198
198
|
else {
|
|
199
199
|
console.error(`Error: task create failed (HTTP ${res.status})`);
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taskFigmaAdd = taskFigmaAdd;
|
|
4
4
|
const figma_api_1 = require("../lib/figma-api");
|
|
5
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
5
6
|
async function taskFigmaAdd(args) {
|
|
6
7
|
try {
|
|
7
8
|
const { link } = await (0, figma_api_1.addFigmaLink)(args.identifier, args.url);
|
|
8
|
-
const label = link.frameName
|
|
9
|
+
const label = (0, sanitize_1.sanitizeField)(link.frameName
|
|
9
10
|
? `${link.fileName} · ${link.frameName}`
|
|
10
|
-
: `${link.fileName}${link.nodeId === '' ? ' (file-level)' : ''}
|
|
11
|
+
: `${link.fileName}${link.nodeId === '' ? ' (file-level)' : ''}`);
|
|
11
12
|
console.log(`Linked ${args.identifier} ↔ Figma "${label}"`);
|
|
12
13
|
console.log(` ${link.url}`);
|
|
13
14
|
return 0;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskFigmaContext = taskFigmaContext;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
7
|
+
/**
|
|
8
|
+
* `lumo task figma context <LUM-N> <link-id>`
|
|
9
|
+
*
|
|
10
|
+
* Tier-2 retrieval for the cheap inline Figma card. Reads the cached design
|
|
11
|
+
* metadata (file/frame name, url, thumbnail, last sync) from
|
|
12
|
+
* `/api/tasks/:id/figma-links/:linkId/context`. Live design context (layers,
|
|
13
|
+
* variables, code connect) needs the Figma MCP server, so the route degrades to
|
|
14
|
+
* metadata + a `note`; we print the note when present.
|
|
15
|
+
*/
|
|
16
|
+
async function taskFigmaContext(identifier, linkId) {
|
|
17
|
+
if (!identifier || !linkId) {
|
|
18
|
+
console.error('Error: usage: lumo task figma context <LUM-42> <link-id>');
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
const creds = (0, config_1.readCredentials)();
|
|
22
|
+
if (!creds) {
|
|
23
|
+
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
27
|
+
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
28
|
+
let res;
|
|
29
|
+
try {
|
|
30
|
+
res = await fetch(`${base}/api/tasks/${encodeURIComponent(identifier)}/figma-links/${encodeURIComponent(linkId)}/context`, { headers: { Authorization: `Bearer ${creds.token}` } });
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
+
console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
if (res.status === 401) {
|
|
38
|
+
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
if (res.status === 404) {
|
|
42
|
+
console.error(`Error: task ${identifier} or figma link ${linkId} not found in workspace ${creds.workspaceSlug}`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
console.error(`Error: figma context failed (HTTP ${res.status})`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const { metadata, note } = (await res.json());
|
|
50
|
+
if (metadata.fileName)
|
|
51
|
+
console.log(`file: ${(0, sanitize_1.sanitizeField)(metadata.fileName)}`);
|
|
52
|
+
if (metadata.frameName)
|
|
53
|
+
console.log(`frame: ${(0, sanitize_1.sanitizeField)(metadata.frameName)}`);
|
|
54
|
+
console.log(`url: ${metadata.url}`);
|
|
55
|
+
if (metadata.lastSyncedAt)
|
|
56
|
+
console.log(`synced: ${metadata.lastSyncedAt}`);
|
|
57
|
+
if (metadata.lastSyncError)
|
|
58
|
+
console.log(`syncError: ${(0, sanitize_1.sanitizeField)(metadata.lastSyncError)}`);
|
|
59
|
+
if (note)
|
|
60
|
+
console.log(`\nnote: ${(0, sanitize_1.sanitizeField)(note)}`);
|
|
61
|
+
}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taskFigmaList = taskFigmaList;
|
|
4
4
|
const figma_api_1 = require("../lib/figma-api");
|
|
5
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
5
6
|
const STALE_MS = 25 * 24 * 3600 * 1000;
|
|
6
7
|
function formatRow(id, fileName, frame, synced, stale) {
|
|
7
8
|
return [
|
|
8
9
|
id.padEnd(10),
|
|
9
|
-
fileName.padEnd(20),
|
|
10
|
-
frame.padEnd(24),
|
|
10
|
+
(0, sanitize_1.sanitizeField)(fileName).padEnd(20),
|
|
11
|
+
(0, sanitize_1.sanitizeField)(frame).padEnd(24),
|
|
11
12
|
synced.padEnd(12),
|
|
12
13
|
stale ? '⚠ thumbnail stale' : '',
|
|
13
14
|
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taskFigmaRefresh = taskFigmaRefresh;
|
|
4
4
|
const figma_api_1 = require("../lib/figma-api");
|
|
5
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
5
6
|
async function taskFigmaRefresh(args) {
|
|
6
7
|
try {
|
|
7
8
|
const { links } = await (0, figma_api_1.listFigmaLinks)(args.identifier);
|
|
@@ -15,14 +16,14 @@ async function taskFigmaRefresh(args) {
|
|
|
15
16
|
for (const r of results) {
|
|
16
17
|
const link = byId.get(r.id);
|
|
17
18
|
const label = link
|
|
18
|
-
? link.frameName
|
|
19
|
+
? (0, sanitize_1.sanitizeField)(link.frameName
|
|
19
20
|
? `${link.fileName} · ${link.frameName}`
|
|
20
|
-
: `${link.fileName} (file-level)`
|
|
21
|
+
: `${link.fileName} (file-level)`)
|
|
21
22
|
: r.id;
|
|
22
23
|
if (r.ok)
|
|
23
24
|
console.log(` ✓ ${label}`);
|
|
24
25
|
else
|
|
25
|
-
console.log(` ✗ ${label} (${r.error})`);
|
|
26
|
+
console.log(` ✗ ${label} (${(0, sanitize_1.sanitizeField)(r.error ?? '')})`);
|
|
26
27
|
}
|
|
27
28
|
return 0;
|
|
28
29
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taskFigmaRm = taskFigmaRm;
|
|
4
4
|
const figma_api_1 = require("../lib/figma-api");
|
|
5
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
5
6
|
async function taskFigmaRm(args) {
|
|
6
7
|
const { links } = await (0, figma_api_1.listFigmaLinks)(args.identifier);
|
|
7
8
|
const match = links.find(l => l.id === args.linkIdOrUrl || l.url === args.linkIdOrUrl);
|
|
@@ -10,9 +11,9 @@ async function taskFigmaRm(args) {
|
|
|
10
11
|
return 0;
|
|
11
12
|
}
|
|
12
13
|
await (0, figma_api_1.removeFigmaLink)(args.identifier, match.id);
|
|
13
|
-
const label = match.frameName
|
|
14
|
+
const label = (0, sanitize_1.sanitizeField)(match.frameName
|
|
14
15
|
? `${match.fileName} · ${match.frameName}`
|
|
15
|
-
: `${match.fileName} (file-level)
|
|
16
|
+
: `${match.fileName} (file-level)`);
|
|
16
17
|
console.log(`Removed Figma link from ${args.identifier}: "${label}"`);
|
|
17
18
|
return 0;
|
|
18
19
|
}
|
|
@@ -43,8 +43,7 @@ async function taskList(opts) {
|
|
|
43
43
|
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
44
44
|
return 1;
|
|
45
45
|
}
|
|
46
|
-
const
|
|
47
|
-
const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
|
|
46
|
+
const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
|
|
48
47
|
const base = (0, api_1.trimTrailingSlash)(apiUrl);
|
|
49
48
|
let milestoneId;
|
|
50
49
|
if (opts.milestone !== undefined) {
|