@kendoo.agentdesk/agentdesk 0.18.0 → 0.18.2

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/CHANGELOG.md CHANGED
@@ -8,6 +8,16 @@ All user-facing changes to AgentDesk. Each entry is tagged:
8
8
 
9
9
  Internal refactors, infrastructure changes, and architectural notes are not listed here.
10
10
 
11
+ ## [0.18.2] — 2026-04-18
12
+
13
+ ### Fixed
14
+ - `[CLI]` Jira tracker check no longer blocks on a 404 from the `mypermissions` endpoint (some tenants don't expose it). Auth verification now lives in the `/myself` call; `mypermissions` is best-effort and only reports missing permissions if the tenant returns them. A 404 on `/myself` itself still fails loudly — that's a real configuration problem.
15
+
16
+ ## [0.18.1] — 2026-04-18
17
+
18
+ ### Fixed
19
+ - `[CLI]` Jira permission check now trims a trailing slash from the stored base URL (a leftover double-slash could produce a spurious 404) and falls back to REST API v2 if v3 isn't exposed. The error message now includes the exact URL called so misconfigs are easier to diagnose.
20
+
11
21
  ## [0.18.0] — 2026-04-18
12
22
 
13
23
  ### Changed
@@ -94,59 +94,83 @@ async function checkJira({ baseUrl, project }, creds) {
94
94
  let identity = null;
95
95
  const auth = "Basic " + Buffer.from(`${email}:${token}`).toString("base64");
96
96
 
97
- // Fetch the authenticated user so we can report "Posting as X".
98
- try {
99
- const meRes = await fetch(`${baseUrl}/rest/api/3/myself`, {
100
- headers: { Authorization: auth, Accept: "application/json" },
101
- signal: AbortSignal.timeout(10000),
102
- });
103
- if (meRes.ok) {
104
- const me = await meRes.json();
105
- identity = { name: me.displayName || email, email: me.emailAddress || email, id: me.accountId };
97
+ // Normalize the base URL: trim trailing slashes, so `...net/` doesn't
98
+ // produce `...net//rest/...` which some Atlassian paths reject.
99
+ const cleanBase = String(baseUrl || "").replace(/\/+$/, "");
100
+
101
+ // Primary check: /myself. If this works, the tenant + auth are fine.
102
+ // Try v3 first, fall back to v2 on 404. This is what we report to the
103
+ // user as "auth works"; mypermissions is now a best-effort follow-up.
104
+ let myselfRes = null;
105
+ let lastUrl = "";
106
+ for (const v of ["3", "2"]) {
107
+ lastUrl = `${cleanBase}/rest/api/${v}/myself`;
108
+ try {
109
+ myselfRes = await fetch(lastUrl, {
110
+ headers: { Authorization: auth, Accept: "application/json" },
111
+ signal: AbortSignal.timeout(10000),
112
+ });
113
+ if (myselfRes.status !== 404) break;
114
+ } catch (e) {
115
+ if (e.name === "AbortError" || e.name === "TimeoutError") {
116
+ return { ok: false, errors: [`Cannot reach Jira at ${cleanBase} — request timed out`] };
117
+ }
118
+ return { ok: false, errors: [`Cannot reach Jira at ${cleanBase}: ${e.message}`] };
106
119
  }
120
+ }
121
+ if (!myselfRes) return { ok: false, errors: [`Cannot reach Jira at ${cleanBase}`] };
122
+ if (myselfRes.status === 401) {
123
+ return { ok: false, errors: ["Jira authentication failed — check JIRA_EMAIL and JIRA_API_TOKEN"] };
124
+ }
125
+ if (myselfRes.status === 403) {
126
+ return { ok: false, errors: ["Jira access forbidden — your account may lack access to this tenant"] };
127
+ }
128
+ if (myselfRes.status === 404) {
129
+ return { ok: false, errors: [`Jira API not found at ${cleanBase} (tried /rest/api/3/myself and /rest/api/2/myself). Confirm the base URL is the Atlassian tenant root and Jira is enabled on this tenant.`] };
130
+ }
131
+ if (!myselfRes.ok) {
132
+ return { ok: false, errors: [`Jira API error (${myselfRes.status}) calling ${lastUrl}`] };
133
+ }
134
+ try {
135
+ const me = await myselfRes.json();
136
+ identity = { name: me.displayName || email, email: me.emailAddress || email, id: me.accountId };
107
137
  } catch {}
108
138
 
109
- // Use Jira's mypermissions endpoint to check all permissions at once
110
- const permissionsToCheck = "BROWSE_PROJECTS,CREATE_ISSUES,EDIT_ISSUES,ADD_COMMENTS,TRANSITION_ISSUES";
139
+ // Best-effort permission check. If it 404s or otherwise fails, we log the
140
+ // specific perms we COULD verify and return ok — the user's auth works;
141
+ // any missing permission will surface when the agent tries the operation.
111
142
  try {
112
- const url = `${baseUrl}/rest/api/3/mypermissions?permissions=${permissionsToCheck}` +
113
- (project ? `&projectKey=${project}` : "");
114
- const res = await fetch(url, {
115
- headers: { Authorization: auth, Accept: "application/json" },
116
- signal: AbortSignal.timeout(10000),
117
- });
118
-
119
- if (res.status === 401) {
120
- return { ok: false, errors: ["Jira authentication failed — check JIRA_EMAIL and JIRA_API_TOKEN"] };
143
+ const permissionsToCheck = "BROWSE_PROJECTS,CREATE_ISSUES,EDIT_ISSUES,ADD_COMMENTS,TRANSITION_ISSUES";
144
+ let permRes = null;
145
+ for (const v of ["3", "2"]) {
146
+ const url = `${cleanBase}/rest/api/${v}/mypermissions?permissions=${permissionsToCheck}` +
147
+ (project ? `&projectKey=${project}` : "");
148
+ permRes = await fetch(url, {
149
+ headers: { Authorization: auth, Accept: "application/json" },
150
+ signal: AbortSignal.timeout(10000),
151
+ });
152
+ if (permRes.status !== 404) break;
121
153
  }
122
- if (res.status === 403) {
123
- return { ok: false, errors: ["Jira access forbidden — your account may lack access to this project"] };
124
- }
125
- if (!res.ok) {
126
- return { ok: false, errors: [`Jira API error (${res.status}) — check your base URL: ${baseUrl}`] };
127
- }
128
-
129
- const data = await res.json();
130
- const perms = data.permissions || {};
131
-
132
- const permMap = {
133
- BROWSE_PROJECTS: "read tasks",
134
- CREATE_ISSUES: "create tasks",
135
- EDIT_ISSUES: "update tasks",
136
- ADD_COMMENTS: "add comments",
137
- TRANSITION_ISSUES: "change task status",
138
- };
139
-
140
- for (const [key, label] of Object.entries(permMap)) {
141
- if (perms[key] && !perms[key].havePermission) {
142
- errors.push(`Missing permission: ${label} (${key})`);
154
+ if (permRes && permRes.ok) {
155
+ const data = await permRes.json();
156
+ const perms = data.permissions || {};
157
+ const permMap = {
158
+ BROWSE_PROJECTS: "read tasks",
159
+ CREATE_ISSUES: "create tasks",
160
+ EDIT_ISSUES: "update tasks",
161
+ ADD_COMMENTS: "add comments",
162
+ TRANSITION_ISSUES: "change task status",
163
+ };
164
+ for (const [key, label] of Object.entries(permMap)) {
165
+ if (perms[key] && !perms[key].havePermission) {
166
+ errors.push(`Missing permission: ${label} (${key})`);
167
+ }
143
168
  }
144
169
  }
145
- } catch (e) {
146
- if (e.name === "AbortError" || e.name === "TimeoutError") {
147
- return { ok: false, errors: [`Cannot reach Jira at ${baseUrl} — request timed out`] };
148
- }
149
- return { ok: false, errors: [`Cannot reach Jira at ${baseUrl}: ${e.message}`] };
170
+ // If mypermissions failed, silently skip — agent will hit real errors
171
+ // when it tries an operation, which is more accurate anyway.
172
+ } catch {
173
+ // Ignore — auth already verified via /myself above.
150
174
  }
151
175
 
152
176
  return { ok: errors.length === 0, errors, identity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {