@sascha384/tic 2.1.0 → 3.0.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.
Files changed (39) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +32 -17
  3. package/dist/app.js +7 -2
  4. package/dist/app.js.map +1 -1
  5. package/dist/auth/ado.d.ts +31 -0
  6. package/dist/auth/ado.js +136 -0
  7. package/dist/auth/ado.js.map +1 -0
  8. package/dist/auth/index.d.ts +2 -0
  9. package/dist/auth/index.js +1 -0
  10. package/dist/auth/index.js.map +1 -1
  11. package/dist/backends/ado/api.d.ts +19 -0
  12. package/dist/backends/ado/api.js +110 -0
  13. package/dist/backends/ado/api.js.map +1 -0
  14. package/dist/backends/ado/index.d.ts +6 -12
  15. package/dist/backends/ado/index.js +201 -349
  16. package/dist/backends/ado/index.js.map +1 -1
  17. package/dist/backends/factory.js +1 -1
  18. package/dist/backends/factory.js.map +1 -1
  19. package/dist/cli/commands/auth.d.ts +5 -3
  20. package/dist/cli/commands/auth.js +39 -4
  21. package/dist/cli/commands/auth.js.map +1 -1
  22. package/dist/cli/commands/mcp.js +17 -17
  23. package/dist/cli/commands/mcp.js.map +1 -1
  24. package/dist/cli/index.js +5 -4
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/components/AuthPrompt.d.ts +1 -0
  27. package/dist/components/AuthPrompt.js +37 -0
  28. package/dist/components/AuthPrompt.js.map +1 -0
  29. package/dist/components/Header.js +10 -2
  30. package/dist/components/Header.js.map +1 -1
  31. package/dist/components/StatusScreen.js +2 -1
  32. package/dist/components/StatusScreen.js.map +1 -1
  33. package/dist/stores/backendDataStore.d.ts +15 -0
  34. package/dist/stores/backendDataStore.js +81 -5
  35. package/dist/stores/backendDataStore.js.map +1 -1
  36. package/package.json +1 -1
  37. package/dist/backends/ado/az.d.ts +0 -28
  38. package/dist/backends/ado/az.js +0 -162
  39. package/dist/backends/ado/az.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tic",
3
3
  "description": "Issue tracking skills for Claude Code",
4
- "version": "2.1.0",
4
+ "version": "3.0.0",
5
5
  "author": {
6
6
  "name": "Sascha Krug"
7
7
  },
package/README.md CHANGED
@@ -7,7 +7,8 @@ Built with TypeScript and [Ink](https://github.com/vadimdemedes/ink).
7
7
  ## Features
8
8
 
9
9
  - **Keyboard-driven TUI** — browse, create, edit, and manage work items without leaving the terminal
10
- - **Multiple backends** — GitHub (via `gh`), GitLab (via `glab`), Azure DevOps (via `az`), Jira (via REST API)
10
+ - **Multiple backends** — GitHub (REST/GraphQL API), GitLab (via `glab`), Azure DevOps (REST API), Jira (REST API)
11
+ - **Built-in authentication** — OAuth device flow for GitHub and Azure DevOps, with PAT fallback
11
12
  - **Automatic backend detection** — selects backend based on git remote, or configure manually
12
13
  - **SQLite storage** — all data stored locally in `.tic/tic.db` with optional sync to remote backends
13
14
  - **CLI commands** — scriptable commands for all operations (`tic item list`, `tic item create`, etc.)
@@ -186,40 +187,54 @@ tic iteration list # List iterations
186
187
  tic iteration set sprint-2 # Set current iteration
187
188
  tic config get backend # Get config value
188
189
  tic config set backend github # Set config value
190
+ tic auth login github # Authenticate with GitHub (OAuth device flow)
191
+ tic auth login azure # Authenticate with Azure DevOps (Entra ID)
192
+ tic auth login azure --pat # Authenticate with a Personal Access Token
193
+ tic auth status # Show authentication status
194
+ tic auth logout github # Remove stored credentials
189
195
  ```
190
196
 
191
197
  Add `--json` to any command for machine-readable output, or `--quiet` to suppress non-essential output.
192
198
 
193
199
  ## Backends
194
200
 
195
- | Backend | CLI Tool | Detection |
196
- |---------|----------|-----------|
201
+ | Backend | Auth Method | Detection |
202
+ |---------|-------------|-----------|
197
203
  | Local only (SQLite) | — | Default fallback |
198
- | GitHub Issues | [`gh`](https://cli.github.com/) | `github.com` in git remote |
199
- | GitLab Issues | [`glab`](https://gitlab.com/gitlab-org/cli) | `gitlab.com` in git remote |
200
- | Azure DevOps Work Items | [`az`](https://learn.microsoft.com/en-us/cli/azure/) | `dev.azure.com` or `visualstudio.com` in git remote |
201
- | Jira | REST API | Configured via settings |
204
+ | GitHub Issues | `tic auth login github` (OAuth) or existing `gh` token | `github.com` in git remote |
205
+ | GitLab Issues | [`glab`](https://gitlab.com/gitlab-org/cli) CLI | `gitlab.com` in git remote |
206
+ | Azure DevOps Work Items | `tic auth login azure` (Entra ID) or `--pat` | `dev.azure.com` or `visualstudio.com` in git remote |
207
+ | Jira | REST API (configured in settings) | Configured via settings |
202
208
 
203
209
  Each backend supports a different set of capabilities (types, statuses, iterations, relationships, etc.). The TUI and CLI automatically adapt to show only what the active backend supports.
204
210
 
205
211
  You can switch backends from within the TUI by pressing `,` to open settings. For Jira, you'll need to configure your site URL, project key, and optionally a board ID.
206
212
 
207
- ### Azure DevOps Authentication
213
+ ### Authentication
208
214
 
209
- Azure DevOps requires the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/) (`az`) with the `azure-devops` extension. There are two authentication methods, and **both are recommended** for full functionality:
215
+ GitHub and Azure DevOps use tic's built-in authentication with credentials stored securely in your OS keychain. If not already authenticated, the TUI will prompt you to log in when a remote backend is detected.
210
216
 
211
- | Method | Command | What it enables |
212
- |--------|---------|-----------------|
213
- | PAT (Personal Access Token) | `az devops login` | Work items, queries, iterations, relations |
214
- | Azure AD / Entra ID | `az login` | Comments (read and write) |
217
+ **GitHub:**
215
218
 
216
- The Work Item Comments API is a preview endpoint that requires Azure AD tokens. If you only use `az devops login`, everything works except comments — they will silently be unavailable when viewing items and explicitly fail when adding comments.
219
+ ```bash
220
+ tic auth login github # Opens browser for OAuth device flow
221
+ ```
222
+
223
+ Falls back to an existing `gh` CLI token if available.
224
+
225
+ **Azure DevOps:**
226
+
227
+ ```bash
228
+ tic auth login azure # Entra ID device code flow (recommended)
229
+ tic auth login azure --pat # Personal Access Token
230
+ ```
217
231
 
218
- For full functionality:
232
+ **Managing credentials:**
219
233
 
220
234
  ```bash
221
- az login # Azure AD needed for comments
222
- az devops login # PAT needed for work items
235
+ tic auth status # Show auth status for all providers
236
+ tic auth logout github # Remove stored credentials
237
+ tic auth logout azure
223
238
  ```
224
239
 
225
240
  ## Contributing
package/dist/app.js CHANGED
@@ -1,9 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { lazy, Suspense, useEffect } from 'react';
3
3
  import { Box } from 'ink';
4
4
  import { WorkItemList } from './components/WorkItemList.js';
5
5
  import { Header } from './components/Header.js';
6
6
  import { useConfigStore } from './stores/configStore.js';
7
+ import { useBackendDataStore } from './stores/backendDataStore.js';
7
8
  import { navigationStore, useNavigationStore, } from './stores/navigationStore.js';
8
9
  // Lazy — loaded on demand when screen changes
9
10
  const WorkItemForm = lazy(() => import('./components/WorkItemForm.js').then((m) => ({
@@ -19,10 +20,14 @@ const StatusScreen = lazy(() => import('./components/StatusScreen.js').then((m)
19
20
  const HelpScreen = lazy(() => import('./components/HelpScreen.js').then((m) => ({
20
21
  default: m.HelpScreen,
21
22
  })));
23
+ const AuthPrompt = lazy(() => import('./components/AuthPrompt.js').then((m) => ({
24
+ default: m.AuthPrompt,
25
+ })));
22
26
  export function App() {
23
27
  const screen = useNavigationStore((s) => s.screen);
24
28
  const previousScreen = useNavigationStore((s) => s.previousScreen);
25
29
  const autoUpdate = useConfigStore((s) => s.config.autoUpdate);
30
+ const authPrompt = useBackendDataStore((s) => s.authPrompt);
26
31
  // Update check on mount
27
32
  useEffect(() => {
28
33
  if (autoUpdate !== false) {
@@ -32,6 +37,6 @@ export function App() {
32
37
  }));
33
38
  }
34
39
  }, [autoUpdate]);
35
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, {}), screen === 'list' && _jsx(WorkItemList, {}), _jsxs(Suspense, { fallback: null, children: [screen === 'form' && _jsx(WorkItemForm, {}), screen === 'iteration-picker' && _jsx(IterationPicker, {}), screen === 'settings' && _jsx(Settings, {}), screen === 'status' && _jsx(StatusScreen, {}), screen === 'help' && _jsx(HelpScreen, { sourceScreen: previousScreen })] })] }));
40
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Header, {}), authPrompt ? (_jsx(Suspense, { fallback: null, children: _jsx(AuthPrompt, {}) })) : (_jsxs(_Fragment, { children: [screen === 'list' && _jsx(WorkItemList, {}), _jsxs(Suspense, { fallback: null, children: [screen === 'form' && _jsx(WorkItemForm, {}), screen === 'iteration-picker' && _jsx(IterationPicker, {}), screen === 'settings' && _jsx(Settings, {}), screen === 'status' && _jsx(StatusScreen, {}), screen === 'help' && _jsx(HelpScreen, { sourceScreen: previousScreen })] })] }))] }));
36
41
  }
37
42
  //# sourceMappingURL=app.js.map
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export declare const ADO_ACCOUNT = "dev.azure.com";
2
+ export declare const ADO_REFRESH_ACCOUNT = "dev.azure.com:refresh";
3
+ export declare const ADO_PAT_ACCOUNT = "dev.azure.com:pat";
4
+ export declare const AZURE_CLI_CLIENT_ID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
5
+ export declare function getAdoToken(): string | null;
6
+ export declare function getAdoRefreshToken(): string | null;
7
+ export declare function getAdoPat(): string | null;
8
+ export declare function setAdoPat(pat: string): void;
9
+ export declare function clearAdoTokens(): void;
10
+ export interface AuthenticateAdoOptions {
11
+ onCode?: (userCode: string, verificationUri: string) => void;
12
+ }
13
+ /**
14
+ * Refresh an expired access token using the stored refresh token.
15
+ * Returns the new access token, or null if refresh fails.
16
+ */
17
+ export declare function refreshAdoToken(refreshToken: string): Promise<string | null>;
18
+ /**
19
+ * Run the Entra ID device code flow to authenticate with Azure DevOps.
20
+ *
21
+ * 1. Requests a device code from Entra ID
22
+ * 2. Calls onCode callback so the caller can display the code/URL
23
+ * 3. Polls for the access token at the specified interval
24
+ * 4. Stores access + refresh tokens in keychain on success
25
+ *
26
+ * Note: Entra ID returns HTTP 400 (not 200) for pending/slow_down/denied
27
+ * errors during polling, unlike GitHub which returns 200 with error in body.
28
+ *
29
+ * @returns The access token
30
+ */
31
+ export declare function authenticateAdo(options?: AuthenticateAdoOptions): Promise<string>;
@@ -0,0 +1,136 @@
1
+ import { getToken, setToken, deleteToken } from './keychain.js';
2
+ export const ADO_ACCOUNT = 'dev.azure.com';
3
+ export const ADO_REFRESH_ACCOUNT = 'dev.azure.com:refresh';
4
+ export const ADO_PAT_ACCOUNT = 'dev.azure.com:pat';
5
+ // Azure CLI's well-known public client ID — no app registration needed
6
+ export const AZURE_CLI_CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46';
7
+ const AUTHORITY = 'https://login.microsoftonline.com/organizations';
8
+ const ADO_SCOPE = '499b84ac-1321-427f-aa17-267ca6975798/.default offline_access';
9
+ export function getAdoToken() {
10
+ return getToken(ADO_ACCOUNT);
11
+ }
12
+ export function getAdoRefreshToken() {
13
+ return getToken(ADO_REFRESH_ACCOUNT);
14
+ }
15
+ export function getAdoPat() {
16
+ return getToken(ADO_PAT_ACCOUNT);
17
+ }
18
+ export function setAdoPat(pat) {
19
+ setToken(ADO_PAT_ACCOUNT, pat);
20
+ }
21
+ export function clearAdoTokens() {
22
+ deleteToken(ADO_ACCOUNT);
23
+ deleteToken(ADO_REFRESH_ACCOUNT);
24
+ deleteToken(ADO_PAT_ACCOUNT);
25
+ }
26
+ function isTokenError(response) {
27
+ return 'error' in response;
28
+ }
29
+ function sleep(ms) {
30
+ return new Promise((resolve) => setTimeout(resolve, ms));
31
+ }
32
+ function urlEncode(params) {
33
+ return Object.entries(params)
34
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
35
+ .join('&');
36
+ }
37
+ /**
38
+ * Refresh an expired access token using the stored refresh token.
39
+ * Returns the new access token, or null if refresh fails.
40
+ */
41
+ export async function refreshAdoToken(refreshToken) {
42
+ try {
43
+ const response = await fetch(`${AUTHORITY}/oauth2/v2.0/token`, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
46
+ body: urlEncode({
47
+ client_id: AZURE_CLI_CLIENT_ID,
48
+ grant_type: 'refresh_token',
49
+ refresh_token: refreshToken,
50
+ scope: ADO_SCOPE,
51
+ }),
52
+ });
53
+ if (!response.ok)
54
+ return null;
55
+ const data = (await response.json());
56
+ if (isTokenError(data))
57
+ return null;
58
+ setToken(ADO_ACCOUNT, data.access_token);
59
+ if (data.refresh_token) {
60
+ setToken(ADO_REFRESH_ACCOUNT, data.refresh_token);
61
+ }
62
+ return data.access_token;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Run the Entra ID device code flow to authenticate with Azure DevOps.
70
+ *
71
+ * 1. Requests a device code from Entra ID
72
+ * 2. Calls onCode callback so the caller can display the code/URL
73
+ * 3. Polls for the access token at the specified interval
74
+ * 4. Stores access + refresh tokens in keychain on success
75
+ *
76
+ * Note: Entra ID returns HTTP 400 (not 200) for pending/slow_down/denied
77
+ * errors during polling, unlike GitHub which returns 200 with error in body.
78
+ *
79
+ * @returns The access token
80
+ */
81
+ export async function authenticateAdo(options) {
82
+ // Step 1: Request device code
83
+ const codeResponse = await fetch(`${AUTHORITY}/oauth2/v2.0/devicecode`, {
84
+ method: 'POST',
85
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
86
+ body: urlEncode({
87
+ client_id: AZURE_CLI_CLIENT_ID,
88
+ scope: ADO_SCOPE,
89
+ }),
90
+ });
91
+ if (!codeResponse.ok) {
92
+ throw new Error(`Failed to request device code: ${codeResponse.status} ${codeResponse.statusText}`);
93
+ }
94
+ const deviceCode = (await codeResponse.json());
95
+ // Step 2: Notify caller with user code and verification URL
96
+ options?.onCode?.(deviceCode.user_code, deviceCode.verification_uri);
97
+ // Step 3: Poll for access token
98
+ let interval = deviceCode.interval * 1000;
99
+ while (true) {
100
+ await sleep(interval);
101
+ const tokenResponse = await fetch(`${AUTHORITY}/oauth2/v2.0/token`, {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
104
+ body: urlEncode({
105
+ client_id: AZURE_CLI_CLIENT_ID,
106
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
107
+ device_code: deviceCode.device_code,
108
+ }),
109
+ });
110
+ // Entra ID returns 400 for pending/slow_down/declined/expired errors
111
+ if (!tokenResponse.ok) {
112
+ const data = (await tokenResponse.json());
113
+ switch (data.error) {
114
+ case 'authorization_pending':
115
+ continue;
116
+ case 'slow_down':
117
+ interval += 5000;
118
+ continue;
119
+ case 'authorization_declined':
120
+ throw new Error('Authorization was denied by the user');
121
+ case 'expired_token':
122
+ throw new Error('Device code has expired. Please restart the authentication flow.');
123
+ default:
124
+ throw new Error(`Authentication failed: ${data.error}${data.error_description ? ` - ${data.error_description}` : ''}`);
125
+ }
126
+ }
127
+ const data = (await tokenResponse.json());
128
+ // Success — store both tokens and return
129
+ setToken(ADO_ACCOUNT, data.access_token);
130
+ if (data.refresh_token) {
131
+ setToken(ADO_REFRESH_ACCOUNT, data.refresh_token);
132
+ }
133
+ return data.access_token;
134
+ }
135
+ }
136
+ //# sourceMappingURL=ado.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ado.js","sourceRoot":"","sources":["../../src/auth/ado.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
1
  export { getToken, setToken, deleteToken } from './keychain.js';
2
2
  export { GITHUB_ACCOUNT, DEFAULT_CLIENT_ID, getGitHubToken, clearGitHubToken, authenticateGitHub, } from './github.js';
3
3
  export type { AuthenticateGitHubOptions } from './github.js';
4
+ export { ADO_ACCOUNT, ADO_REFRESH_ACCOUNT, ADO_PAT_ACCOUNT, AZURE_CLI_CLIENT_ID, getAdoToken, getAdoRefreshToken, getAdoPat, setAdoPat, clearAdoTokens, refreshAdoToken, authenticateAdo, } from './ado.js';
5
+ export type { AuthenticateAdoOptions } from './ado.js';
@@ -1,3 +1,4 @@
1
1
  export { getToken, setToken, deleteToken } from './keychain.js';
2
2
  export { GITHUB_ACCOUNT, DEFAULT_CLIENT_ID, getGitHubToken, clearGitHubToken, authenticateGitHub, } from './github.js';
3
+ export { ADO_ACCOUNT, ADO_REFRESH_ACCOUNT, ADO_PAT_ACCOUNT, AZURE_CLI_CLIENT_ID, getAdoToken, getAdoRefreshToken, getAdoPat, setAdoPat, clearAdoTokens, refreshAdoToken, authenticateAdo, } from './ado.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,eAAe,GAChB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { BaseApiClient } from '../shared/api-client.js';
2
+ export type AdoAuth = {
3
+ type: 'bearer';
4
+ token: string;
5
+ } | {
6
+ type: 'basic';
7
+ pat: string;
8
+ };
9
+ export declare class AdoApiClient extends BaseApiClient {
10
+ private auth;
11
+ constructor(auth: AdoAuth, org: string);
12
+ private getAuthHeader;
13
+ private appendApiVersion;
14
+ protected fetch<T>(method: string, path: string, body?: unknown, contentType?: string): Promise<T>;
15
+ rest<T>(method: string, path: string, body?: unknown, contentType?: string): Promise<T>;
16
+ wiql<T>(project: string, query: string): Promise<T>;
17
+ batchGetWorkItems<T>(ids: number[]): Promise<T>;
18
+ paginate<T>(path: string): AsyncGenerator<T[]>;
19
+ }
@@ -0,0 +1,110 @@
1
+ import { BaseApiClient, AuthError } from '../shared/api-client.js';
2
+ import { getAdoRefreshToken, refreshAdoToken } from '../../auth/ado.js';
3
+ const ADO_API_VERSION = '7.1';
4
+ export class AdoApiClient extends BaseApiClient {
5
+ auth;
6
+ constructor(auth, org) {
7
+ const token = auth.type === 'bearer' ? auth.token : auth.pat;
8
+ super(token, `https://dev.azure.com/${org}`);
9
+ this.auth = auth;
10
+ }
11
+ getAuthHeader() {
12
+ if (this.auth.type === 'bearer') {
13
+ return `Bearer ${this.auth.token}`;
14
+ }
15
+ return `Basic ${Buffer.from(`:${this.auth.pat}`).toString('base64')}`;
16
+ }
17
+ appendApiVersion(path) {
18
+ const separator = path.includes('?') ? '&' : '?';
19
+ return `${path}${separator}api-version=${ADO_API_VERSION}`;
20
+ }
21
+ async fetch(method, path, body, contentType) {
22
+ const url = this.baseUrl + this.appendApiVersion(path);
23
+ const headers = {
24
+ Authorization: this.getAuthHeader(),
25
+ Accept: 'application/json',
26
+ };
27
+ const init = { method, headers };
28
+ if (body !== undefined && ['POST', 'PATCH', 'PUT'].includes(method)) {
29
+ headers['Content-Type'] = contentType ?? 'application/json';
30
+ init.body = JSON.stringify(body);
31
+ }
32
+ let response = await globalThis.fetch(url, init);
33
+ this.checkRateLimit(response.headers);
34
+ // Try token refresh on 401 for OAuth auth
35
+ if (response.status === 401 && this.auth.type === 'bearer') {
36
+ const refreshToken = getAdoRefreshToken();
37
+ if (refreshToken) {
38
+ const newToken = await refreshAdoToken(refreshToken);
39
+ if (newToken) {
40
+ this.auth = { type: 'bearer', token: newToken };
41
+ this.token = newToken;
42
+ headers['Authorization'] = `Bearer ${newToken}`;
43
+ response = await globalThis.fetch(url, {
44
+ method,
45
+ headers,
46
+ body: init.body,
47
+ });
48
+ }
49
+ }
50
+ }
51
+ if (response.status === 401) {
52
+ throw new AuthError();
53
+ }
54
+ if (!response.ok) {
55
+ const text = await response.text();
56
+ throw new Error(`HTTP ${response.status}: ${text}`);
57
+ }
58
+ return (await response.json());
59
+ }
60
+ async rest(method, path, body, contentType) {
61
+ return this.retry(() => this.fetch(method, path, body, contentType));
62
+ }
63
+ async wiql(project, query) {
64
+ return this.rest('POST', `/${project}/_apis/wit/wiql`, { query });
65
+ }
66
+ async batchGetWorkItems(ids) {
67
+ const CHUNK_SIZE = 200;
68
+ const allValues = [];
69
+ for (let i = 0; i < ids.length; i += CHUNK_SIZE) {
70
+ const chunk = ids.slice(i, i + CHUNK_SIZE);
71
+ const result = await this.rest('POST', '/_apis/wit/workitemsbatch', { ids: chunk, $expand: 4 });
72
+ allValues.push(...result.value);
73
+ }
74
+ return { value: allValues };
75
+ }
76
+ async *paginate(path) {
77
+ let url = this.baseUrl + this.appendApiVersion(path);
78
+ while (url) {
79
+ const headers = {
80
+ Authorization: this.getAuthHeader(),
81
+ Accept: 'application/json',
82
+ };
83
+ const response = await globalThis.fetch(url, {
84
+ method: 'GET',
85
+ headers,
86
+ });
87
+ this.checkRateLimit(response.headers);
88
+ if (response.status === 401) {
89
+ throw new AuthError();
90
+ }
91
+ if (!response.ok) {
92
+ const text = await response.text();
93
+ throw new Error(`HTTP ${response.status}: ${text}`);
94
+ }
95
+ const json = (await response.json());
96
+ yield json.value;
97
+ const continuationToken = response.headers.get('x-ms-continuationtoken');
98
+ if (continuationToken) {
99
+ const separator = path.includes('?') ? '&' : '?';
100
+ url =
101
+ this.baseUrl +
102
+ this.appendApiVersion(`${path}${separator}continuationToken=${continuationToken}`);
103
+ }
104
+ else {
105
+ url = null;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/backends/ado/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAExE,MAAM,eAAe,GAAG,KAAK,CAAC;AAM9B,MAAM,OAAO,YAAa,SAAQ,aAAa;IACrC,IAAI,CAAU;IAEtB,YAAY,IAAa,EAAE,GAAW;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC7D,KAAK,CAAC,KAAK,EAAE,yBAAyB,GAAG,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACrC,CAAC;QACD,OAAO,SAAS,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;IACxE,CAAC;IAEO,gBAAgB,CAAC,IAAY;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACjD,OAAO,GAAG,IAAI,GAAG,SAAS,eAAe,eAAe,EAAE,CAAC;IAC7D,CAAC;IAEkB,KAAK,CAAC,KAAK,CAC5B,MAAc,EACd,IAAY,EACZ,IAAc,EACd,WAAoB;QAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAEvD,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE;YACnC,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QAEF,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAE9C,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACpE,OAAO,CAAC,cAAc,CAAC,GAAG,WAAW,IAAI,kBAAkB,CAAC;YAC5D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,IAAI,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAEjD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEtC,0CAA0C;QAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3D,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;YAC1C,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,YAAY,CAAC,CAAC;gBACrD,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;oBAChD,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;oBACtB,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,QAAQ,EAAE,CAAC;oBAChD,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;wBACrC,MAAM;wBACN,OAAO;wBACP,IAAI,EAAE,IAAI,CAAC,IAAI;qBAChB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,SAAS,EAAE,CAAC;QACxB,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,IAAI,CACR,MAAc,EACd,IAAY,EACZ,IAAc,EACd,WAAoB;QAEpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,OAAe,EAAE,KAAa;QAC1C,OAAO,IAAI,CAAC,IAAI,CAAI,MAAM,EAAE,IAAI,OAAO,iBAAiB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAI,GAAa;QACtC,MAAM,UAAU,GAAG,GAAG,CAAC;QACvB,MAAM,SAAS,GAAc,EAAE,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAC5B,MAAM,EACN,2BAA2B,EAC3B,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAC3B,CAAC;YACF,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,SAAS,EAAO,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,CAAC,QAAQ,CAAI,IAAY;QAC7B,IAAI,GAAG,GAAkB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAEpE,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,OAAO,GAA2B;gBACtC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE;gBACnC,MAAM,EAAE,kBAAkB;aAC3B,CAAC;YAEF,MAAM,QAAQ,GAAa,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrD,MAAM,EAAE,KAAK;gBACb,OAAO;aACR,CAAC,CAAC;YAEH,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAEtC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,IAAI,SAAS,EAAE,CAAC;YACxB,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmC,CAAC;YACvE,MAAM,IAAI,CAAC,KAAK,CAAC;YAEjB,MAAM,iBAAiB,GAAkB,QAAQ,CAAC,OAAO,CAAC,GAAG,CAC3D,wBAAwB,CACzB,CAAC;YACF,IAAI,iBAAiB,EAAE,CAAC;gBACtB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBACjD,GAAG;oBACD,IAAI,CAAC,OAAO;wBACZ,IAAI,CAAC,gBAAgB,CACnB,GAAG,IAAI,GAAG,SAAS,qBAAqB,iBAAiB,EAAE,CAC5D,CAAC;YACN,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,IAAI,CAAC;YACb,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -1,12 +1,16 @@
1
1
  import { BaseBackend } from '../types.js';
2
2
  import type { BackendCapabilities } from '../types.js';
3
3
  import type { WorkItem, NewWorkItem, NewComment, Comment, Template } from '../../types.js';
4
+ export interface AzureDevOpsBackendOptions {
5
+ skipAuth?: boolean;
6
+ }
4
7
  export declare class AzureDevOpsBackend extends BaseBackend {
5
- private cwd;
8
+ private api;
6
9
  private org;
7
10
  private project;
8
11
  private types;
9
- constructor(cwd: string);
12
+ private constructor();
13
+ static create(cwd: string, options?: AzureDevOpsBackendOptions): Promise<AzureDevOpsBackend>;
10
14
  getCapabilities(): BackendCapabilities;
11
15
  getStatuses(): Promise<string[]>;
12
16
  getWorkItemTypes(): Promise<string[]>;
@@ -22,16 +26,6 @@ export declare class AzureDevOpsBackend extends BaseBackend {
22
26
  createWorkItem(data: NewWorkItem): Promise<WorkItem>;
23
27
  updateWorkItem(id: string, data: Partial<WorkItem>): Promise<WorkItem>;
24
28
  deleteWorkItem(id: string): Promise<void>;
25
- /**
26
- * Add a comment to a work item.
27
- *
28
- * The Work Item Comments API is preview-only (7.1-preview.4) and cannot be
29
- * called via `az devops invoke` (it fails to parse preview version strings),
30
- * so we use `az rest` instead. Unlike `az devops` commands which honor PAT
31
- * auth from `az devops login`, `az rest --resource` requires an Azure AD
32
- * token obtained via `az login`. If only PAT auth is available, this method
33
- * throws a descriptive error directing the user to run `az login`.
34
- */
35
29
  addComment(workItemId: string, comment: NewComment): Promise<Comment>;
36
30
  getChildren(id: string): Promise<WorkItem[]>;
37
31
  getDependents(id: string): Promise<WorkItem[]>;