bktide 0.0.1
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/README.md +145 -0
- package/WORKFLOW_README.md +65 -0
- package/bin/alfred-entrypoint +54 -0
- package/dist/commands/BaseCommand.js +159 -0
- package/dist/commands/BaseCommand.js.map +1 -0
- package/dist/commands/BaseCommandHandler.js +80 -0
- package/dist/commands/BaseCommandHandler.js.map +1 -0
- package/dist/commands/BuildCommandHandler.js +28 -0
- package/dist/commands/BuildCommandHandler.js.map +1 -0
- package/dist/commands/HelloCommandHandler.js +6 -0
- package/dist/commands/HelloCommandHandler.js.map +1 -0
- package/dist/commands/ListAnnotations.js +60 -0
- package/dist/commands/ListAnnotations.js.map +1 -0
- package/dist/commands/ListBuilds.js +137 -0
- package/dist/commands/ListBuilds.js.map +1 -0
- package/dist/commands/ListOrganizations.js +27 -0
- package/dist/commands/ListOrganizations.js.map +1 -0
- package/dist/commands/ListPipelines.js +114 -0
- package/dist/commands/ListPipelines.js.map +1 -0
- package/dist/commands/ManageToken.js +180 -0
- package/dist/commands/ManageToken.js.map +1 -0
- package/dist/commands/OrganizationCommandHandler.js +53 -0
- package/dist/commands/OrganizationCommandHandler.js.map +1 -0
- package/dist/commands/PipelineCommandHandler.js +142 -0
- package/dist/commands/PipelineCommandHandler.js.map +1 -0
- package/dist/commands/ShowViewer.js +26 -0
- package/dist/commands/ShowViewer.js.map +1 -0
- package/dist/commands/UserBuildsCommandHandler.js +61 -0
- package/dist/commands/UserBuildsCommandHandler.js.map +1 -0
- package/dist/commands/ViewerBuildsCommandHandler.js +176 -0
- package/dist/commands/ViewerBuildsCommandHandler.js.map +1 -0
- package/dist/commands/ViewerCommandHandler.js +46 -0
- package/dist/commands/ViewerCommandHandler.js.map +1 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/formatters/BaseFormatter.js +14 -0
- package/dist/formatters/BaseFormatter.js.map +1 -0
- package/dist/formatters/FormatterFactory.js +48 -0
- package/dist/formatters/FormatterFactory.js.map +1 -0
- package/dist/formatters/annotations/Formatter.js +10 -0
- package/dist/formatters/annotations/Formatter.js.map +1 -0
- package/dist/formatters/annotations/JsonFormatter.js +20 -0
- package/dist/formatters/annotations/JsonFormatter.js.map +1 -0
- package/dist/formatters/annotations/PlainTextFormatter.js +35 -0
- package/dist/formatters/annotations/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/annotations/index.js +23 -0
- package/dist/formatters/annotations/index.js.map +1 -0
- package/dist/formatters/builds/AlfredFormatter.js +135 -0
- package/dist/formatters/builds/AlfredFormatter.js.map +1 -0
- package/dist/formatters/builds/Formatter.js +10 -0
- package/dist/formatters/builds/Formatter.js.map +1 -0
- package/dist/formatters/builds/JsonFormatter.js +44 -0
- package/dist/formatters/builds/JsonFormatter.js.map +1 -0
- package/dist/formatters/builds/PlainTextFormatter.js +113 -0
- package/dist/formatters/builds/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/builds/index.js +26 -0
- package/dist/formatters/builds/index.js.map +1 -0
- package/dist/formatters/errors/AlfredFormatter.js +110 -0
- package/dist/formatters/errors/AlfredFormatter.js.map +1 -0
- package/dist/formatters/errors/Formatter.js +98 -0
- package/dist/formatters/errors/Formatter.js.map +1 -0
- package/dist/formatters/errors/JsonFormatter.js +63 -0
- package/dist/formatters/errors/JsonFormatter.js.map +1 -0
- package/dist/formatters/errors/PlainTextFormatter.js +52 -0
- package/dist/formatters/errors/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/errors/index.js +26 -0
- package/dist/formatters/errors/index.js.map +1 -0
- package/dist/formatters/index.js +9 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/organizations/Formatter.js +10 -0
- package/dist/formatters/organizations/Formatter.js.map +1 -0
- package/dist/formatters/organizations/JsonFormatter.js +16 -0
- package/dist/formatters/organizations/JsonFormatter.js.map +1 -0
- package/dist/formatters/organizations/PlainTextFormatter.js +15 -0
- package/dist/formatters/organizations/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/organizations/index.js +21 -0
- package/dist/formatters/organizations/index.js.map +1 -0
- package/dist/formatters/pipelines/AlfredFormatter.js +42 -0
- package/dist/formatters/pipelines/AlfredFormatter.js.map +1 -0
- package/dist/formatters/pipelines/Formatter.js +10 -0
- package/dist/formatters/pipelines/Formatter.js.map +1 -0
- package/dist/formatters/pipelines/JsonFormatter.js +13 -0
- package/dist/formatters/pipelines/JsonFormatter.js.map +1 -0
- package/dist/formatters/pipelines/PlainTextFormatter.js +47 -0
- package/dist/formatters/pipelines/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/pipelines/index.js +28 -0
- package/dist/formatters/pipelines/index.js.map +1 -0
- package/dist/formatters/token/AlfredFormatter.js +191 -0
- package/dist/formatters/token/AlfredFormatter.js.map +1 -0
- package/dist/formatters/token/Formatter.js +13 -0
- package/dist/formatters/token/Formatter.js.map +1 -0
- package/dist/formatters/token/JsonFormatter.js +211 -0
- package/dist/formatters/token/JsonFormatter.js.map +1 -0
- package/dist/formatters/token/PlainTextFormatter.js +184 -0
- package/dist/formatters/token/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/token/index.js +26 -0
- package/dist/formatters/token/index.js.map +1 -0
- package/dist/formatters/viewer/Formatter.js +10 -0
- package/dist/formatters/viewer/Formatter.js.map +1 -0
- package/dist/formatters/viewer/JsonFormatter.js +20 -0
- package/dist/formatters/viewer/JsonFormatter.js.map +1 -0
- package/dist/formatters/viewer/PlainTextFormatter.js +20 -0
- package/dist/formatters/viewer/PlainTextFormatter.js.map +1 -0
- package/dist/formatters/viewer/index.js +21 -0
- package/dist/formatters/viewer/index.js.map +1 -0
- package/dist/graphql/generated/fragment-masking.js +17 -0
- package/dist/graphql/generated/fragment-masking.js.map +1 -0
- package/dist/graphql/generated/gql.js +13 -0
- package/dist/graphql/generated/gql.js.map +1 -0
- package/dist/graphql/generated/graphql.js +852 -0
- package/dist/graphql/generated/graphql.js.map +1 -0
- package/dist/graphql/generated/index.js +3 -0
- package/dist/graphql/generated/index.js.map +1 -0
- package/dist/graphql/generated/sdk.js +872 -0
- package/dist/graphql/generated/sdk.js.map +1 -0
- package/dist/graphql/queries.js +138 -0
- package/dist/graphql/queries.js.map +1 -0
- package/dist/index.js +271 -0
- package/dist/index.js.map +1 -0
- package/dist/services/BuildkiteClient.js +520 -0
- package/dist/services/BuildkiteClient.js.map +1 -0
- package/dist/services/BuildkiteRestClient.js +244 -0
- package/dist/services/BuildkiteRestClient.js.map +1 -0
- package/dist/services/CacheManager.js +221 -0
- package/dist/services/CacheManager.js.map +1 -0
- package/dist/services/CredentialManager.js +158 -0
- package/dist/services/CredentialManager.js.map +1 -0
- package/dist/services/EnhancedBuildkiteClient.js +297 -0
- package/dist/services/EnhancedBuildkiteClient.js.map +1 -0
- package/dist/services/logger.js +107 -0
- package/dist/services/logger.js.map +1 -0
- package/dist/types/buildkite.js +5 -0
- package/dist/types/buildkite.js.map +1 -0
- package/dist/types/credentials.js +2 -0
- package/dist/types/credentials.js.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/cli-error-handler.js +172 -0
- package/dist/utils/cli-error-handler.js.map +1 -0
- package/dist/utils/errorUtils.js +59 -0
- package/dist/utils/errorUtils.js.map +1 -0
- package/dist/utils/parseBuildRef.js +31 -0
- package/dist/utils/parseBuildRef.js.map +1 -0
- package/dist/utils/textFormatter.js +53 -0
- package/dist/utils/textFormatter.js.map +1 -0
- package/dist/utils/xdgPaths.js +95 -0
- package/dist/utils/xdgPaths.js.map +1 -0
- package/env.example +66 -0
- package/icons/README.md +68 -0
- package/icons/blocked.png +0 -0
- package/icons/buildkite.png +0 -0
- package/icons/failed.png +0 -0
- package/icons/failing.png +0 -0
- package/icons/passed.png +0 -0
- package/icons/running.png +0 -0
- package/icons/scheduled.png +0 -0
- package/icons/skipped.png +0 -0
- package/icons/unknown.png +0 -0
- package/info.plist +734 -0
- package/package.json +87 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Entry } from '@napi-rs/keyring';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
import { BuildkiteClient } from './BuildkiteClient.js';
|
|
4
|
+
import { BuildkiteRestClient } from './BuildkiteRestClient.js';
|
|
5
|
+
const SERVICE_NAME = 'bktide';
|
|
6
|
+
const ACCOUNT_KEY = 'default';
|
|
7
|
+
/**
|
|
8
|
+
* Manages secure storage of credentials using the system's keychain
|
|
9
|
+
*/
|
|
10
|
+
export class CredentialManager {
|
|
11
|
+
entry;
|
|
12
|
+
constructor(serviceName = SERVICE_NAME, accountName = ACCOUNT_KEY) {
|
|
13
|
+
this.entry = new Entry(serviceName, accountName);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Stores a token in the system keychain
|
|
17
|
+
* @param token The Buildkite API token to store
|
|
18
|
+
* @returns true if token was successfully stored
|
|
19
|
+
*/
|
|
20
|
+
async saveToken(token) {
|
|
21
|
+
try {
|
|
22
|
+
await this.entry.setPassword(token);
|
|
23
|
+
logger.debug('Token saved to system keychain');
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger.error('Failed to save token to system keychain', error);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the stored token from the system keychain
|
|
33
|
+
* @returns The stored token or undefined if not found
|
|
34
|
+
*/
|
|
35
|
+
async getToken() {
|
|
36
|
+
try {
|
|
37
|
+
const token = this.entry.getPassword();
|
|
38
|
+
return token || undefined;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Deletes the stored token from the system keychain
|
|
46
|
+
* @returns true if token was successfully deleted
|
|
47
|
+
*/
|
|
48
|
+
async deleteToken() {
|
|
49
|
+
try {
|
|
50
|
+
await this.entry.deletePassword();
|
|
51
|
+
logger.debug('Token deleted from system keychain');
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger.error('Failed to delete token from system keychain', error);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a token exists in the system keychain
|
|
61
|
+
* @returns true if a token exists
|
|
62
|
+
*/
|
|
63
|
+
async hasToken() {
|
|
64
|
+
const token = await this.getToken();
|
|
65
|
+
return !!token;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validates if a token is valid by making test API calls to both GraphQL and REST APIs
|
|
69
|
+
* @param token Optional token to validate. If not provided, will use the stored token.
|
|
70
|
+
* @returns Object containing validation status for both GraphQL and REST APIs
|
|
71
|
+
*/
|
|
72
|
+
async validateToken(token) {
|
|
73
|
+
try {
|
|
74
|
+
// If no token provided, try to get the stored one
|
|
75
|
+
const tokenToValidate = token || await this.getToken();
|
|
76
|
+
if (!tokenToValidate) {
|
|
77
|
+
logger.debug('No token provided for validation');
|
|
78
|
+
return {
|
|
79
|
+
valid: false,
|
|
80
|
+
canListOrganizations: false,
|
|
81
|
+
organizations: {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Create clients with the token
|
|
85
|
+
const graphqlClient = new BuildkiteClient(tokenToValidate, { debug: false, caching: false });
|
|
86
|
+
const restClient = new BuildkiteRestClient(tokenToValidate, { debug: false });
|
|
87
|
+
// First check if we can list organizations
|
|
88
|
+
let orgSlugs = [];
|
|
89
|
+
try {
|
|
90
|
+
orgSlugs = await graphqlClient.getOrganizations().then(orgs => orgs.map(org => org.slug));
|
|
91
|
+
logger.debug('Successfully retrieved organization slugs');
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
logger.debug('Failed to retrieve organization slugs', {
|
|
95
|
+
error: error instanceof Error ? error.message : String(error),
|
|
96
|
+
cause: error instanceof Error && error.cause ? error.cause : undefined
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
canListOrganizations: false,
|
|
101
|
+
organizations: {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const organizations = {};
|
|
105
|
+
let allValid = true;
|
|
106
|
+
// Validate each organization
|
|
107
|
+
for (const orgSlug of orgSlugs) {
|
|
108
|
+
const orgStatus = {
|
|
109
|
+
graphql: false,
|
|
110
|
+
builds: false,
|
|
111
|
+
organizations: false
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
// Check GraphQL access
|
|
115
|
+
await graphqlClient.getViewer();
|
|
116
|
+
orgStatus.graphql = true;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
logger.debug(`GraphQL validation failed for organization ${orgSlug}`, error);
|
|
120
|
+
allValid = false;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
// Check build access
|
|
124
|
+
await restClient.hasBuildAccess(orgSlug);
|
|
125
|
+
orgStatus.builds = true;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
logger.debug(`Build access validation failed for organization ${orgSlug}`, error);
|
|
129
|
+
allValid = false;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
// Check organization access
|
|
133
|
+
await restClient.hasOrganizationAccess(orgSlug);
|
|
134
|
+
orgStatus.organizations = true;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
logger.debug(`Organization access validation failed for organization ${orgSlug}`, error);
|
|
138
|
+
allValid = false;
|
|
139
|
+
}
|
|
140
|
+
organizations[orgSlug] = orgStatus;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
valid: allValid,
|
|
144
|
+
canListOrganizations: true,
|
|
145
|
+
organizations
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
logger.debug('Token validation failed', error);
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
canListOrganizations: false,
|
|
153
|
+
organizations: {}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=CredentialManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialManager.js","sourceRoot":"/","sources":["services/CredentialManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,MAAM,WAAW,GAAG,SAAS,CAAC;AAE9B;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACpB,KAAK,CAAQ;IAErB,YAAY,WAAW,GAAG,YAAY,EAAE,WAAW,GAAG,WAAW;QAC/D,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;YAC/D,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YACvC,OAAO,KAAK,IAAI,SAAS,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpC,OAAO,CAAC,CAAC,KAAK,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,KAAc;QAChC,IAAI,CAAC;YACH,kDAAkD;YAClD,MAAM,eAAe,GAAG,KAAK,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvD,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBACjD,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,oBAAoB,EAAE,KAAK;oBAC3B,aAAa,EAAE,EAAE;iBAClB,CAAC;YACJ,CAAC;YAED,gCAAgC;YAChC,MAAM,aAAa,GAAG,IAAI,eAAe,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7F,MAAM,UAAU,GAAG,IAAI,mBAAmB,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAE9E,2CAA2C;YAC3C,IAAI,QAAQ,GAAa,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,aAAa,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1F,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC5D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,uCAAuC,EAAE;oBACpD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;oBAC7D,KAAK,EAAE,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;iBACvE,CAAC,CAAC;gBACH,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,oBAAoB,EAAE,KAAK;oBAC3B,aAAa,EAAE,EAAE;iBAClB,CAAC;YACJ,CAAC;YAED,MAAM,aAAa,GAAiD,EAAE,CAAC;YACvE,IAAI,QAAQ,GAAG,IAAI,CAAC;YAEpB,6BAA6B;YAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAiC;oBAC9C,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,KAAK;oBACb,aAAa,EAAE,KAAK;iBACrB,CAAC;gBAEF,IAAI,CAAC;oBACH,uBAAuB;oBACvB,MAAM,aAAa,CAAC,SAAS,EAAE,CAAC;oBAChC,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;gBAC3B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,8CAA8C,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;oBAC7E,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBAED,IAAI,CAAC;oBACH,qBAAqB;oBACrB,MAAM,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;oBACzC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC;gBAC1B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,mDAAmD,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;oBAClF,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBAED,IAAI,CAAC;oBACH,4BAA4B;oBAC5B,MAAM,UAAU,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;oBAChD,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC;gBACjC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,0DAA0D,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;oBACzF,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBAED,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;YACrC,CAAC;YAED,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,oBAAoB,EAAE,IAAI;gBAC1B,aAAa;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAC/C,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,oBAAoB,EAAE,KAAK;gBAC3B,aAAa,EAAE,EAAE;aAClB,CAAC;QACJ,CAAC;IACH,CAAC;CACF","sourcesContent":["import { Entry } from '@napi-rs/keyring';\nimport { logger } from './logger.js';\nimport { BuildkiteClient } from './BuildkiteClient.js';\nimport { BuildkiteRestClient } from './BuildkiteRestClient.js';\nimport { TokenValidationStatus, OrganizationValidationStatus } from '../types/credentials.js';\n\nconst SERVICE_NAME = 'bktide';\nconst ACCOUNT_KEY = 'default';\n\n/**\n * Manages secure storage of credentials using the system's keychain\n */\nexport class CredentialManager {\n private entry: Entry;\n\n constructor(serviceName = SERVICE_NAME, accountName = ACCOUNT_KEY) {\n this.entry = new Entry(serviceName, accountName);\n }\n\n /**\n * Stores a token in the system keychain\n * @param token The Buildkite API token to store\n * @returns true if token was successfully stored\n */\n async saveToken(token: string): Promise<boolean> {\n try {\n await this.entry.setPassword(token);\n logger.debug('Token saved to system keychain');\n return true;\n } catch (error) {\n logger.error('Failed to save token to system keychain', error);\n return false;\n }\n }\n\n /**\n * Retrieves the stored token from the system keychain\n * @returns The stored token or undefined if not found\n */\n async getToken(): Promise<string | undefined> {\n try {\n const token = this.entry.getPassword();\n return token || undefined;\n } catch (error) {\n return undefined;\n }\n }\n\n /**\n * Deletes the stored token from the system keychain\n * @returns true if token was successfully deleted\n */\n async deleteToken(): Promise<boolean> {\n try {\n await this.entry.deletePassword();\n logger.debug('Token deleted from system keychain');\n return true;\n } catch (error) {\n logger.error('Failed to delete token from system keychain', error);\n return false;\n }\n }\n\n /**\n * Checks if a token exists in the system keychain\n * @returns true if a token exists\n */\n async hasToken(): Promise<boolean> {\n const token = await this.getToken();\n return !!token;\n }\n\n /**\n * Validates if a token is valid by making test API calls to both GraphQL and REST APIs\n * @param token Optional token to validate. If not provided, will use the stored token.\n * @returns Object containing validation status for both GraphQL and REST APIs\n */\n async validateToken(token?: string): Promise<TokenValidationStatus> {\n try {\n // If no token provided, try to get the stored one\n const tokenToValidate = token || await this.getToken();\n if (!tokenToValidate) {\n logger.debug('No token provided for validation');\n return { \n valid: false, \n canListOrganizations: false,\n organizations: {} \n };\n }\n\n // Create clients with the token\n const graphqlClient = new BuildkiteClient(tokenToValidate, { debug: false, caching: false });\n const restClient = new BuildkiteRestClient(tokenToValidate, { debug: false });\n \n // First check if we can list organizations\n let orgSlugs: string[] = [];\n try {\n orgSlugs = await graphqlClient.getOrganizations().then(orgs => orgs.map(org => org.slug));\n logger.debug('Successfully retrieved organization slugs');\n } catch (error) {\n logger.debug('Failed to retrieve organization slugs', {\n error: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error && error.cause ? error.cause : undefined\n });\n return { \n valid: false, \n canListOrganizations: false,\n organizations: {} \n };\n }\n\n const organizations: Record<string, OrganizationValidationStatus> = {};\n let allValid = true;\n\n // Validate each organization\n for (const orgSlug of orgSlugs) {\n const orgStatus: OrganizationValidationStatus = {\n graphql: false,\n builds: false,\n organizations: false\n };\n\n try {\n // Check GraphQL access\n await graphqlClient.getViewer();\n orgStatus.graphql = true;\n } catch (error) {\n logger.debug(`GraphQL validation failed for organization ${orgSlug}`, error);\n allValid = false;\n }\n\n try {\n // Check build access\n await restClient.hasBuildAccess(orgSlug);\n orgStatus.builds = true;\n } catch (error) {\n logger.debug(`Build access validation failed for organization ${orgSlug}`, error);\n allValid = false;\n }\n\n try {\n // Check organization access\n await restClient.hasOrganizationAccess(orgSlug);\n orgStatus.organizations = true;\n } catch (error) {\n logger.debug(`Organization access validation failed for organization ${orgSlug}`, error);\n allValid = false;\n }\n\n organizations[orgSlug] = orgStatus;\n }\n\n return {\n valid: allValid,\n canListOrganizations: true,\n organizations\n };\n } catch (error) {\n logger.debug('Token validation failed', error);\n return { \n valid: false, \n canListOrganizations: false,\n organizations: {} \n };\n }\n }\n}"]}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { GraphQLClient } from 'graphql-request';
|
|
11
|
+
import { CacheManager } from './CacheManager.js';
|
|
12
|
+
import { GET_VIEWER, GET_ORGANIZATIONS, GET_PIPELINES, GET_BUILDS, GET_VIEWER_BUILDS } from '../graphql/queries.js';
|
|
13
|
+
// These imports will be available after running GraphQL codegen
|
|
14
|
+
import { getSdk } from '../graphql/generated/sdk.js';
|
|
15
|
+
/**
|
|
16
|
+
* EnhancedBuildkiteClient provides strongly-typed methods to interact with the Buildkite GraphQL API
|
|
17
|
+
*/
|
|
18
|
+
export class EnhancedBuildkiteClient {
|
|
19
|
+
/**
|
|
20
|
+
* Create a new EnhancedBuildkiteClient
|
|
21
|
+
* @param token Your Buildkite API token
|
|
22
|
+
* @param options Configuration options
|
|
23
|
+
*/
|
|
24
|
+
constructor(token, options, debug) {
|
|
25
|
+
this.baseUrl = 'https://graphql.buildkite.com/v1';
|
|
26
|
+
this.cacheManager = null;
|
|
27
|
+
this.debug = false;
|
|
28
|
+
this.token = token;
|
|
29
|
+
this.debug = debug || (options === null || options === void 0 ? void 0 : options.debug) || false;
|
|
30
|
+
if (options === null || options === void 0 ? void 0 : options.baseUrl) {
|
|
31
|
+
this.baseUrl = options.baseUrl;
|
|
32
|
+
}
|
|
33
|
+
this.client = new GraphQLClient(this.baseUrl, {
|
|
34
|
+
headers: {
|
|
35
|
+
Authorization: `Bearer ${this.token}`,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Initialize the typed SDK
|
|
39
|
+
this.typedSdk = getSdk(this.client);
|
|
40
|
+
// Initialize cache if caching is enabled
|
|
41
|
+
if ((options === null || options === void 0 ? void 0 : options.caching) !== false) {
|
|
42
|
+
this.cacheManager = new CacheManager(options === null || options === void 0 ? void 0 : options.cacheTTLs);
|
|
43
|
+
// Initialize cache and set token hash (async, but we don't wait)
|
|
44
|
+
this.initCache();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Initialize cache asynchronously
|
|
49
|
+
*/
|
|
50
|
+
initCache() {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
if (this.cacheManager) {
|
|
53
|
+
yield this.cacheManager.init();
|
|
54
|
+
yield this.cacheManager.setTokenHash(this.token);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the current viewer information
|
|
60
|
+
* @returns The viewer data
|
|
61
|
+
*/
|
|
62
|
+
getViewer() {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.debug(`🕒 Starting GraphQL query: GetViewer`);
|
|
66
|
+
}
|
|
67
|
+
// Check if result is in cache
|
|
68
|
+
if (this.cacheManager) {
|
|
69
|
+
const cachedResult = yield this.cacheManager.get(GET_VIEWER.toString(), {});
|
|
70
|
+
if (cachedResult) {
|
|
71
|
+
if (this.debug) {
|
|
72
|
+
console.debug(`✅ Served from cache: GetViewer`);
|
|
73
|
+
}
|
|
74
|
+
return cachedResult;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const startTime = process.hrtime.bigint();
|
|
78
|
+
const result = yield this.typedSdk.GetViewer();
|
|
79
|
+
// Store result in cache if caching is enabled
|
|
80
|
+
if (this.cacheManager) {
|
|
81
|
+
yield this.cacheManager.set(GET_VIEWER.toString(), result, {});
|
|
82
|
+
}
|
|
83
|
+
const endTime = process.hrtime.bigint();
|
|
84
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
85
|
+
if (this.debug) {
|
|
86
|
+
console.debug(`✅ GraphQL query completed: GetViewer (${duration.toFixed(2)}ms)`);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the organizations for the current viewer
|
|
93
|
+
* @returns The organizations data
|
|
94
|
+
*/
|
|
95
|
+
getOrganizations() {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
if (this.debug) {
|
|
98
|
+
console.debug(`🕒 Starting GraphQL query: GetOrganizations`);
|
|
99
|
+
}
|
|
100
|
+
// Check if result is in cache
|
|
101
|
+
if (this.cacheManager) {
|
|
102
|
+
const cachedResult = yield this.cacheManager.get(GET_ORGANIZATIONS.toString(), {});
|
|
103
|
+
if (cachedResult) {
|
|
104
|
+
if (this.debug) {
|
|
105
|
+
console.debug(`✅ Served from cache: GetOrganizations`);
|
|
106
|
+
}
|
|
107
|
+
return cachedResult;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const startTime = process.hrtime.bigint();
|
|
111
|
+
const result = yield this.typedSdk.GetOrganizations();
|
|
112
|
+
// Store result in cache if caching is enabled
|
|
113
|
+
if (this.cacheManager) {
|
|
114
|
+
yield this.cacheManager.set(GET_ORGANIZATIONS.toString(), result, {});
|
|
115
|
+
}
|
|
116
|
+
const endTime = process.hrtime.bigint();
|
|
117
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
118
|
+
if (this.debug) {
|
|
119
|
+
console.debug(`✅ GraphQL query completed: GetOrganizations (${duration.toFixed(2)}ms)`);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get the organization slugs for the current viewer
|
|
126
|
+
* @returns An array of organization slugs the current user belongs to
|
|
127
|
+
*/
|
|
128
|
+
getViewerOrganizationSlugs() {
|
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
var _a, _b;
|
|
131
|
+
try {
|
|
132
|
+
const startTime = process.hrtime.bigint();
|
|
133
|
+
if (this.debug) {
|
|
134
|
+
console.debug(`🕒 Starting GraphQL query: getViewerOrganizationSlugs`);
|
|
135
|
+
}
|
|
136
|
+
const data = yield this.getOrganizations();
|
|
137
|
+
if (!((_b = (_a = data === null || data === void 0 ? void 0 : data.viewer) === null || _a === void 0 ? void 0 : _a.organizations) === null || _b === void 0 ? void 0 : _b.edges)) {
|
|
138
|
+
throw new Error('Failed to fetch organizations from the API');
|
|
139
|
+
}
|
|
140
|
+
const orgs = data.viewer.organizations.edges.map((edge) => edge.node.slug);
|
|
141
|
+
if (orgs.length === 0) {
|
|
142
|
+
throw new Error('No organizations found for the current user');
|
|
143
|
+
}
|
|
144
|
+
const endTime = process.hrtime.bigint();
|
|
145
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
146
|
+
if (this.debug) {
|
|
147
|
+
console.debug(`✅ Found ${orgs.length} organizations (${duration.toFixed(2)}ms)`);
|
|
148
|
+
}
|
|
149
|
+
return orgs;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('Error fetching viewer organizations:', error);
|
|
153
|
+
throw new Error('Failed to determine your organizations');
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get pipelines for an organization
|
|
159
|
+
* @param organizationSlug The organization slug
|
|
160
|
+
* @param first Number of pipelines to retrieve
|
|
161
|
+
* @param after Cursor for pagination
|
|
162
|
+
* @returns The pipelines data
|
|
163
|
+
*/
|
|
164
|
+
getPipelines(organizationSlug, first, after) {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
const variables = {
|
|
167
|
+
organizationSlug,
|
|
168
|
+
first,
|
|
169
|
+
after,
|
|
170
|
+
};
|
|
171
|
+
if (this.debug) {
|
|
172
|
+
console.debug(`🕒 Starting GraphQL query: GetPipelines for ${organizationSlug}`);
|
|
173
|
+
}
|
|
174
|
+
// Check if result is in cache
|
|
175
|
+
if (this.cacheManager) {
|
|
176
|
+
const cachedResult = yield this.cacheManager.get(GET_PIPELINES.toString(), variables);
|
|
177
|
+
if (cachedResult) {
|
|
178
|
+
if (this.debug) {
|
|
179
|
+
console.debug(`✅ Served from cache: GetPipelines`);
|
|
180
|
+
}
|
|
181
|
+
return cachedResult;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const startTime = process.hrtime.bigint();
|
|
185
|
+
const result = yield this.typedSdk.GetPipelines(variables);
|
|
186
|
+
// Store result in cache if caching is enabled
|
|
187
|
+
if (this.cacheManager) {
|
|
188
|
+
yield this.cacheManager.set(GET_PIPELINES.toString(), result, variables);
|
|
189
|
+
}
|
|
190
|
+
const endTime = process.hrtime.bigint();
|
|
191
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
192
|
+
if (this.debug) {
|
|
193
|
+
console.debug(`✅ GraphQL query completed: GetPipelines (${duration.toFixed(2)}ms)`);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get builds for a pipeline
|
|
200
|
+
* @param pipelineSlug The pipeline slug
|
|
201
|
+
* @param organizationSlug The organization slug
|
|
202
|
+
* @param first Number of builds to retrieve
|
|
203
|
+
* @returns The builds data
|
|
204
|
+
*/
|
|
205
|
+
getBuilds(pipelineSlug, organizationSlug, first) {
|
|
206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
const variables = {
|
|
208
|
+
pipelineSlug,
|
|
209
|
+
organizationSlug,
|
|
210
|
+
first,
|
|
211
|
+
};
|
|
212
|
+
if (this.debug) {
|
|
213
|
+
console.debug(`🕒 Starting GraphQL query: GetBuilds for ${pipelineSlug} in ${organizationSlug}`);
|
|
214
|
+
}
|
|
215
|
+
// Check if result is in cache
|
|
216
|
+
if (this.cacheManager) {
|
|
217
|
+
const cachedResult = yield this.cacheManager.get(GET_BUILDS.toString(), variables);
|
|
218
|
+
if (cachedResult) {
|
|
219
|
+
if (this.debug) {
|
|
220
|
+
console.debug(`✅ Served from cache: GetBuilds`);
|
|
221
|
+
}
|
|
222
|
+
return cachedResult;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const startTime = process.hrtime.bigint();
|
|
226
|
+
const result = yield this.typedSdk.GetBuilds(variables);
|
|
227
|
+
// Store result in cache if caching is enabled
|
|
228
|
+
if (this.cacheManager) {
|
|
229
|
+
yield this.cacheManager.set(GET_BUILDS.toString(), result, variables);
|
|
230
|
+
}
|
|
231
|
+
const endTime = process.hrtime.bigint();
|
|
232
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
233
|
+
if (this.debug) {
|
|
234
|
+
console.debug(`✅ GraphQL query completed: GetBuilds (${duration.toFixed(2)}ms)`);
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get builds for the current viewer
|
|
241
|
+
* @param first Number of builds to retrieve
|
|
242
|
+
* @returns The viewer builds data
|
|
243
|
+
*/
|
|
244
|
+
getViewerBuilds(first) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
const variables = {
|
|
247
|
+
first,
|
|
248
|
+
};
|
|
249
|
+
if (this.debug) {
|
|
250
|
+
console.debug(`🕒 Starting GraphQL query: GetViewerBuilds`);
|
|
251
|
+
}
|
|
252
|
+
// Check if result is in cache
|
|
253
|
+
if (this.cacheManager) {
|
|
254
|
+
const cachedResult = yield this.cacheManager.get(GET_VIEWER_BUILDS.toString(), variables);
|
|
255
|
+
if (cachedResult) {
|
|
256
|
+
if (this.debug) {
|
|
257
|
+
console.debug(`✅ Served from cache: GetViewerBuilds`);
|
|
258
|
+
}
|
|
259
|
+
return cachedResult;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const startTime = process.hrtime.bigint();
|
|
263
|
+
const result = yield this.typedSdk.GetViewerBuilds(variables);
|
|
264
|
+
// Store result in cache if caching is enabled
|
|
265
|
+
if (this.cacheManager) {
|
|
266
|
+
yield this.cacheManager.set(GET_VIEWER_BUILDS.toString(), result, variables);
|
|
267
|
+
}
|
|
268
|
+
const endTime = process.hrtime.bigint();
|
|
269
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
270
|
+
if (this.debug) {
|
|
271
|
+
console.debug(`✅ GraphQL query completed: GetViewerBuilds (${duration.toFixed(2)}ms)`);
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Clear all cache entries
|
|
278
|
+
*/
|
|
279
|
+
clearCache() {
|
|
280
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
281
|
+
if (this.cacheManager) {
|
|
282
|
+
yield this.cacheManager.clear();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Invalidate a specific cache type
|
|
288
|
+
*/
|
|
289
|
+
invalidateCache(type) {
|
|
290
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
291
|
+
if (this.cacheManager) {
|
|
292
|
+
yield this.cacheManager.invalidateType(type);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=EnhancedBuildkiteClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnhancedBuildkiteClient.js","sourceRoot":"/","sources":["services/EnhancedBuildkiteClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { GraphQLClient } from 'graphql-request';\nimport { CacheManager } from './CacheManager.js';\nimport { BuildkiteClientOptions } from './BuildkiteClient.js';\nimport { \n GET_VIEWER, \n GET_ORGANIZATIONS, \n GET_PIPELINES, \n GET_BUILDS, \n GET_VIEWER_BUILDS \n} from '../graphql/queries.js';\n// These imports will be available after running GraphQL codegen\nimport { \n GetViewerQuery, \n GetOrganizationsQuery, \n GetPipelinesQuery,\n GetPipelinesQueryVariables,\n GetBuildsQuery,\n GetBuildsQueryVariables,\n GetViewerBuildsQuery,\n GetViewerBuildsQueryVariables,\n getSdk\n} from '../graphql/generated/sdk.js';\n\n/**\n * EnhancedBuildkiteClient provides strongly-typed methods to interact with the Buildkite GraphQL API\n */\nexport class EnhancedBuildkiteClient {\n private client: GraphQLClient;\n private typedSdk: ReturnType<typeof getSdk>;\n private token: string;\n private baseUrl: string = 'https://graphql.buildkite.com/v1';\n private cacheManager: CacheManager | null = null;\n private debug: boolean = false;\n\n /**\n * Create a new EnhancedBuildkiteClient\n * @param token Your Buildkite API token\n * @param options Configuration options\n */\n constructor(token: string, options?: BuildkiteClientOptions, debug?: boolean) {\n this.token = token;\n this.debug = debug || options?.debug || false;\n if (options?.baseUrl) {\n this.baseUrl = options.baseUrl;\n }\n\n this.client = new GraphQLClient(this.baseUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n },\n });\n\n // Initialize the typed SDK\n this.typedSdk = getSdk(this.client);\n\n // Initialize cache if caching is enabled\n if (options?.caching !== false) {\n this.cacheManager = new CacheManager(options?.cacheTTLs);\n // Initialize cache and set token hash (async, but we don't wait)\n this.initCache();\n }\n }\n\n /**\n * Initialize cache asynchronously\n */\n private async initCache(): Promise<void> {\n if (this.cacheManager) {\n await this.cacheManager.init();\n await this.cacheManager.setTokenHash(this.token);\n }\n }\n\n /**\n * Get the current viewer information\n * @returns The viewer data\n */\n public async getViewer(): Promise<GetViewerQuery> {\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: GetViewer`);\n }\n\n // Check if result is in cache\n if (this.cacheManager) {\n const cachedResult = await this.cacheManager.get<GetViewerQuery>(GET_VIEWER.toString(), {});\n if (cachedResult) {\n if (this.debug) {\n console.debug(`✅ Served from cache: GetViewer`);\n }\n return cachedResult;\n }\n }\n\n const startTime = process.hrtime.bigint();\n const result = await this.typedSdk.GetViewer();\n\n // Store result in cache if caching is enabled\n if (this.cacheManager) {\n await this.cacheManager.set(GET_VIEWER.toString(), result, {});\n }\n\n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ GraphQL query completed: GetViewer (${duration.toFixed(2)}ms)`);\n }\n\n return result;\n }\n\n /**\n * Get the organizations for the current viewer\n * @returns The organizations data\n */\n public async getOrganizations(): Promise<GetOrganizationsQuery> {\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: GetOrganizations`);\n }\n\n // Check if result is in cache\n if (this.cacheManager) {\n const cachedResult = await this.cacheManager.get<GetOrganizationsQuery>(GET_ORGANIZATIONS.toString(), {});\n if (cachedResult) {\n if (this.debug) {\n console.debug(`✅ Served from cache: GetOrganizations`);\n }\n return cachedResult;\n }\n }\n\n const startTime = process.hrtime.bigint();\n const result = await this.typedSdk.GetOrganizations();\n\n // Store result in cache if caching is enabled\n if (this.cacheManager) {\n await this.cacheManager.set(GET_ORGANIZATIONS.toString(), result, {});\n }\n\n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ GraphQL query completed: GetOrganizations (${duration.toFixed(2)}ms)`);\n }\n\n return result;\n }\n\n /**\n * Get the organization slugs for the current viewer\n * @returns An array of organization slugs the current user belongs to\n */\n public async getViewerOrganizationSlugs(): Promise<string[]> {\n try {\n const startTime = process.hrtime.bigint();\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: getViewerOrganizationSlugs`);\n }\n \n const data = await this.getOrganizations();\n \n if (!data?.viewer?.organizations?.edges) {\n throw new Error('Failed to fetch organizations from the API');\n }\n \n const orgs = data.viewer.organizations.edges.map((edge: { node: { slug: string } }) => edge.node.slug);\n \n if (orgs.length === 0) {\n throw new Error('No organizations found for the current user');\n }\n \n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ Found ${orgs.length} organizations (${duration.toFixed(2)}ms)`);\n }\n \n return orgs;\n } catch (error) {\n console.error('Error fetching viewer organizations:', error);\n throw new Error('Failed to determine your organizations');\n }\n }\n\n /**\n * Get pipelines for an organization\n * @param organizationSlug The organization slug\n * @param first Number of pipelines to retrieve\n * @param after Cursor for pagination\n * @returns The pipelines data\n */\n public async getPipelines(\n organizationSlug: string, \n first?: number, \n after?: string\n ): Promise<GetPipelinesQuery> {\n const variables: GetPipelinesQueryVariables = {\n organizationSlug,\n first,\n after,\n };\n\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: GetPipelines for ${organizationSlug}`);\n }\n\n // Check if result is in cache\n if (this.cacheManager) {\n const cachedResult = await this.cacheManager.get<GetPipelinesQuery>(GET_PIPELINES.toString(), variables);\n if (cachedResult) {\n if (this.debug) {\n console.debug(`✅ Served from cache: GetPipelines`);\n }\n return cachedResult;\n }\n }\n\n const startTime = process.hrtime.bigint();\n const result = await this.typedSdk.GetPipelines(variables);\n\n // Store result in cache if caching is enabled\n if (this.cacheManager) {\n await this.cacheManager.set(GET_PIPELINES.toString(), result, variables);\n }\n\n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ GraphQL query completed: GetPipelines (${duration.toFixed(2)}ms)`);\n }\n\n return result;\n }\n\n /**\n * Get builds for a pipeline\n * @param pipelineSlug The pipeline slug\n * @param organizationSlug The organization slug\n * @param first Number of builds to retrieve\n * @returns The builds data\n */\n public async getBuilds(\n pipelineSlug: string,\n organizationSlug: string,\n first?: number\n ): Promise<GetBuildsQuery> {\n const variables: GetBuildsQueryVariables = {\n pipelineSlug,\n organizationSlug,\n first,\n };\n\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: GetBuilds for ${pipelineSlug} in ${organizationSlug}`);\n }\n\n // Check if result is in cache\n if (this.cacheManager) {\n const cachedResult = await this.cacheManager.get<GetBuildsQuery>(GET_BUILDS.toString(), variables);\n if (cachedResult) {\n if (this.debug) {\n console.debug(`✅ Served from cache: GetBuilds`);\n }\n return cachedResult;\n }\n }\n\n const startTime = process.hrtime.bigint();\n const result = await this.typedSdk.GetBuilds(variables);\n\n // Store result in cache if caching is enabled\n if (this.cacheManager) {\n await this.cacheManager.set(GET_BUILDS.toString(), result, variables);\n }\n\n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ GraphQL query completed: GetBuilds (${duration.toFixed(2)}ms)`);\n }\n\n return result;\n }\n\n /**\n * Get builds for the current viewer\n * @param first Number of builds to retrieve\n * @returns The viewer builds data\n */\n public async getViewerBuilds(first: number): Promise<GetViewerBuildsQuery> {\n const variables: GetViewerBuildsQueryVariables = {\n first,\n };\n\n if (this.debug) {\n console.debug(`🕒 Starting GraphQL query: GetViewerBuilds`);\n }\n\n // Check if result is in cache\n if (this.cacheManager) {\n const cachedResult = await this.cacheManager.get<GetViewerBuildsQuery>(GET_VIEWER_BUILDS.toString(), variables);\n if (cachedResult) {\n if (this.debug) {\n console.debug(`✅ Served from cache: GetViewerBuilds`);\n }\n return cachedResult;\n }\n }\n\n const startTime = process.hrtime.bigint();\n const result = await this.typedSdk.GetViewerBuilds(variables);\n\n // Store result in cache if caching is enabled\n if (this.cacheManager) {\n await this.cacheManager.set(GET_VIEWER_BUILDS.toString(), result, variables);\n }\n\n const endTime = process.hrtime.bigint();\n const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds\n if (this.debug) {\n console.debug(`✅ GraphQL query completed: GetViewerBuilds (${duration.toFixed(2)}ms)`);\n }\n\n return result;\n }\n\n /**\n * Clear all cache entries\n */\n public async clearCache(): Promise<void> {\n if (this.cacheManager) {\n await this.cacheManager.clear();\n }\n }\n\n /**\n * Invalidate a specific cache type\n */\n public async invalidateCache(type: string): Promise<void> {\n if (this.cacheManager) {\n await this.cacheManager.invalidateType(type);\n }\n }\n} "]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pino } from 'pino';
|
|
4
|
+
import { XDGPaths } from '../utils/xdgPaths.js';
|
|
5
|
+
const logDir = XDGPaths.getAppLogDir('bktide');
|
|
6
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
7
|
+
const logFile = path.join(logDir, 'cli.log');
|
|
8
|
+
export const logger = pino({
|
|
9
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
10
|
+
customLevels: {
|
|
11
|
+
console: 80
|
|
12
|
+
},
|
|
13
|
+
transport: {
|
|
14
|
+
targets: [
|
|
15
|
+
// Direct console output (no pretty formatting)
|
|
16
|
+
{
|
|
17
|
+
target: 'pino-pretty',
|
|
18
|
+
level: 'console',
|
|
19
|
+
options: {
|
|
20
|
+
colorize: false,
|
|
21
|
+
useOnlyCustomProps: true,
|
|
22
|
+
sync: true,
|
|
23
|
+
minimumLevel: 'console',
|
|
24
|
+
ignore: 'level,time,pid,hostname',
|
|
25
|
+
errorProps: 'err,error,stack,message,code,details'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
// Debug output with pretty formatting
|
|
29
|
+
{
|
|
30
|
+
target: 'pino-pretty',
|
|
31
|
+
level: 'trace',
|
|
32
|
+
options: {
|
|
33
|
+
colorize: true,
|
|
34
|
+
sync: true,
|
|
35
|
+
translateTime: 'HH:MM:ss.l',
|
|
36
|
+
ignore: 'time,pid,hostname',
|
|
37
|
+
errorProps: 'err,error,stack,message,code,details'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
// JSON file output for detailed logging
|
|
41
|
+
{
|
|
42
|
+
target: 'pino/file',
|
|
43
|
+
level: 'trace',
|
|
44
|
+
options: {
|
|
45
|
+
destination: logFile,
|
|
46
|
+
mkdir: true,
|
|
47
|
+
sync: false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
dedupe: true
|
|
52
|
+
},
|
|
53
|
+
serializers: {
|
|
54
|
+
err: pino.stdSerializers.err,
|
|
55
|
+
error: pino.stdSerializers.err,
|
|
56
|
+
stack: (stack) => stack,
|
|
57
|
+
details: (details) => details
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Convenience re-exports for existing codebases
|
|
61
|
+
export const { info, warn, error, debug, trace, fatal } = logger;
|
|
62
|
+
/**
|
|
63
|
+
* Changes the logging level of the current logger instance
|
|
64
|
+
* @param level The new log level to set
|
|
65
|
+
*/
|
|
66
|
+
export function setLogLevel(level) {
|
|
67
|
+
logger.level = level;
|
|
68
|
+
debug(`Log level changed to ${level}`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Helper function to measure and log execution time of async functions
|
|
72
|
+
* @param label Description of the operation being timed
|
|
73
|
+
* @param fn Function to execute and time
|
|
74
|
+
* @param level Log level to use for output (defaults to 'debug')
|
|
75
|
+
* @returns Result of the function execution
|
|
76
|
+
*/
|
|
77
|
+
export async function timeIt(label, fn, level = 'debug') {
|
|
78
|
+
const start = process.hrtime.bigint();
|
|
79
|
+
try {
|
|
80
|
+
return await fn();
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
const end = process.hrtime.bigint();
|
|
84
|
+
const duration = Number(end - start) / 1_000_000; // Convert to milliseconds
|
|
85
|
+
switch (level) {
|
|
86
|
+
case 'trace':
|
|
87
|
+
logger.trace({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
88
|
+
break;
|
|
89
|
+
case 'debug':
|
|
90
|
+
logger.debug({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
91
|
+
break;
|
|
92
|
+
case 'info':
|
|
93
|
+
logger.info({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
94
|
+
break;
|
|
95
|
+
case 'warn':
|
|
96
|
+
logger.warn({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
97
|
+
break;
|
|
98
|
+
case 'error':
|
|
99
|
+
logger.error({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
100
|
+
break;
|
|
101
|
+
case 'fatal':
|
|
102
|
+
logger.fatal({ duration }, `${label} completed in ${duration.toFixed(2)}ms`);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"/","sources":["services/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { pino } from 'pino';\nimport { XDGPaths } from '../utils/xdgPaths.js';\n\nconst logDir = XDGPaths.getAppLogDir('bktide');\nfs.mkdirSync(logDir, { recursive: true });\n\nconst logFile = path.join(logDir, 'cli.log');\n\nexport const logger = pino(\n {\n level: process.env.LOG_LEVEL || 'info',\n customLevels: {\n console: 80\n },\n transport: {\n targets: [\n // Direct console output (no pretty formatting)\n {\n target: 'pino-pretty',\n level: 'console',\n options: {\n colorize: false,\n useOnlyCustomProps: true,\n sync: true,\n minimumLevel: 'console',\n ignore: 'level,time,pid,hostname',\n errorProps: 'err,error,stack,message,code,details'\n }\n },\n // Debug output with pretty formatting\n {\n target: 'pino-pretty',\n level: 'trace',\n options: {\n colorize: true,\n sync: true,\n translateTime: 'HH:MM:ss.l',\n ignore: 'time,pid,hostname',\n errorProps: 'err,error,stack,message,code,details'\n }\n },\n // JSON file output for detailed logging\n {\n target: 'pino/file',\n level: 'trace',\n options: { \n destination: logFile,\n mkdir: true,\n sync: false \n }\n }\n ],\n dedupe: true\n },\n serializers: {\n err: pino.stdSerializers.err,\n error: pino.stdSerializers.err,\n stack: (stack: string) => stack,\n details: (details: any) => details\n }\n }\n);\n\n// Convenience re-exports for existing codebases\nexport const { info, warn, error, debug, trace, fatal } = logger;\n\n/**\n * Changes the logging level of the current logger instance\n * @param level The new log level to set\n */\nexport function setLogLevel(level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'console'): void {\n logger.level = level;\n debug(`Log level changed to ${level}`);\n}\n\n/**\n * Helper function to measure and log execution time of async functions\n * @param label Description of the operation being timed\n * @param fn Function to execute and time\n * @param level Log level to use for output (defaults to 'debug')\n * @returns Result of the function execution\n */\nexport async function timeIt<T>(\n label: string, \n fn: () => Promise<T>, \n level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' = 'debug'\n): Promise<T> {\n const start = process.hrtime.bigint();\n try {\n return await fn();\n } finally {\n const end = process.hrtime.bigint();\n const duration = Number(end - start) / 1_000_000; // Convert to milliseconds\n switch(level) {\n case 'trace': logger.trace({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n case 'debug': logger.debug({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n case 'info': logger.info({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n case 'warn': logger.warn({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n case 'error': logger.error({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n case 'fatal': logger.fatal({ duration }, `${label} completed in ${duration.toFixed(2)}ms`); break;\n }\n }\n} "]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildkite.js","sourceRoot":"/","sources":["types/buildkite.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * Common types for Buildkite GraphQL API\n */\n\nexport interface PipelineNode {\n id: string;\n name: string;\n slug: string;\n description: string;\n url: string;\n repository: {\n url: string;\n };\n}\n\nexport interface BuildNode {\n id: string;\n number: number;\n url: string;\n state: string;\n message: string;\n commit: string;\n branch: string;\n createdAt: string;\n startedAt: string | null;\n finishedAt: string | null;\n}\n\nexport interface OrganizationNode {\n id: string;\n name: string;\n slug: string;\n}\n\nexport interface PageInfo {\n hasNextPage: boolean;\n hasPreviousPage: boolean;\n startCursor: string;\n endCursor: string;\n} "]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"/","sources":["types/credentials.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents the validation status of a Buildkite API token\n */\nexport interface OrganizationValidationStatus {\n /** Whether the token is valid for the GraphQL API for this organization */\n graphql: boolean;\n /** Whether the token is valid for the REST API to access builds in this organization */\n builds: boolean;\n /** Whether the token is valid for the REST API to access organization details */\n organizations: boolean;\n}\n\nexport interface TokenValidationStatus {\n /** Combined status of all validation checks */\n valid: boolean;\n /** Whether the token can access the GraphQL API to list organizations */\n canListOrganizations: boolean;\n /** Validation status for each organization */\n organizations: Record<string, OrganizationValidationStatus>;\n}\n\n/**\n * Represents the complete status of a Buildkite API token\n */\nexport interface TokenStatus {\n /** Whether a token exists in the keychain */\n hasToken: boolean;\n /** Whether the token is valid for both GraphQL and REST APIs */\n isValid: boolean;\n /** Detailed validation status for each API */\n validation: TokenValidationStatus;\n}\n\n/**\n * Represents the result of checking a token's status\n */\nexport interface TokenCheckResult {\n /** The token status information */\n status: TokenStatus;\n /** Any errors encountered during validation */\n errors: unknown[];\n}\n\n/**\n * Represents the result of checking or storing a token\n */\nexport interface TokenCheckOrStoreResult {\n /** Whether a token was stored */\n stored: boolean;\n /** Any errors encountered during the process */\n errors: unknown[];\n}\n\n/**\n * Represents the result of storing a token\n */\nexport interface TokenStoreResult {\n /** Whether the token was successfully stored */\n success: boolean;\n /** Any errors encountered during storage */\n errors: unknown[];\n} "]}
|