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.
Files changed (160) hide show
  1. package/README.md +145 -0
  2. package/WORKFLOW_README.md +65 -0
  3. package/bin/alfred-entrypoint +54 -0
  4. package/dist/commands/BaseCommand.js +159 -0
  5. package/dist/commands/BaseCommand.js.map +1 -0
  6. package/dist/commands/BaseCommandHandler.js +80 -0
  7. package/dist/commands/BaseCommandHandler.js.map +1 -0
  8. package/dist/commands/BuildCommandHandler.js +28 -0
  9. package/dist/commands/BuildCommandHandler.js.map +1 -0
  10. package/dist/commands/HelloCommandHandler.js +6 -0
  11. package/dist/commands/HelloCommandHandler.js.map +1 -0
  12. package/dist/commands/ListAnnotations.js +60 -0
  13. package/dist/commands/ListAnnotations.js.map +1 -0
  14. package/dist/commands/ListBuilds.js +137 -0
  15. package/dist/commands/ListBuilds.js.map +1 -0
  16. package/dist/commands/ListOrganizations.js +27 -0
  17. package/dist/commands/ListOrganizations.js.map +1 -0
  18. package/dist/commands/ListPipelines.js +114 -0
  19. package/dist/commands/ListPipelines.js.map +1 -0
  20. package/dist/commands/ManageToken.js +180 -0
  21. package/dist/commands/ManageToken.js.map +1 -0
  22. package/dist/commands/OrganizationCommandHandler.js +53 -0
  23. package/dist/commands/OrganizationCommandHandler.js.map +1 -0
  24. package/dist/commands/PipelineCommandHandler.js +142 -0
  25. package/dist/commands/PipelineCommandHandler.js.map +1 -0
  26. package/dist/commands/ShowViewer.js +26 -0
  27. package/dist/commands/ShowViewer.js.map +1 -0
  28. package/dist/commands/UserBuildsCommandHandler.js +61 -0
  29. package/dist/commands/UserBuildsCommandHandler.js.map +1 -0
  30. package/dist/commands/ViewerBuildsCommandHandler.js +176 -0
  31. package/dist/commands/ViewerBuildsCommandHandler.js.map +1 -0
  32. package/dist/commands/ViewerCommandHandler.js +46 -0
  33. package/dist/commands/ViewerCommandHandler.js.map +1 -0
  34. package/dist/commands/index.js +8 -0
  35. package/dist/commands/index.js.map +1 -0
  36. package/dist/formatters/BaseFormatter.js +14 -0
  37. package/dist/formatters/BaseFormatter.js.map +1 -0
  38. package/dist/formatters/FormatterFactory.js +48 -0
  39. package/dist/formatters/FormatterFactory.js.map +1 -0
  40. package/dist/formatters/annotations/Formatter.js +10 -0
  41. package/dist/formatters/annotations/Formatter.js.map +1 -0
  42. package/dist/formatters/annotations/JsonFormatter.js +20 -0
  43. package/dist/formatters/annotations/JsonFormatter.js.map +1 -0
  44. package/dist/formatters/annotations/PlainTextFormatter.js +35 -0
  45. package/dist/formatters/annotations/PlainTextFormatter.js.map +1 -0
  46. package/dist/formatters/annotations/index.js +23 -0
  47. package/dist/formatters/annotations/index.js.map +1 -0
  48. package/dist/formatters/builds/AlfredFormatter.js +135 -0
  49. package/dist/formatters/builds/AlfredFormatter.js.map +1 -0
  50. package/dist/formatters/builds/Formatter.js +10 -0
  51. package/dist/formatters/builds/Formatter.js.map +1 -0
  52. package/dist/formatters/builds/JsonFormatter.js +44 -0
  53. package/dist/formatters/builds/JsonFormatter.js.map +1 -0
  54. package/dist/formatters/builds/PlainTextFormatter.js +113 -0
  55. package/dist/formatters/builds/PlainTextFormatter.js.map +1 -0
  56. package/dist/formatters/builds/index.js +26 -0
  57. package/dist/formatters/builds/index.js.map +1 -0
  58. package/dist/formatters/errors/AlfredFormatter.js +110 -0
  59. package/dist/formatters/errors/AlfredFormatter.js.map +1 -0
  60. package/dist/formatters/errors/Formatter.js +98 -0
  61. package/dist/formatters/errors/Formatter.js.map +1 -0
  62. package/dist/formatters/errors/JsonFormatter.js +63 -0
  63. package/dist/formatters/errors/JsonFormatter.js.map +1 -0
  64. package/dist/formatters/errors/PlainTextFormatter.js +52 -0
  65. package/dist/formatters/errors/PlainTextFormatter.js.map +1 -0
  66. package/dist/formatters/errors/index.js +26 -0
  67. package/dist/formatters/errors/index.js.map +1 -0
  68. package/dist/formatters/index.js +9 -0
  69. package/dist/formatters/index.js.map +1 -0
  70. package/dist/formatters/organizations/Formatter.js +10 -0
  71. package/dist/formatters/organizations/Formatter.js.map +1 -0
  72. package/dist/formatters/organizations/JsonFormatter.js +16 -0
  73. package/dist/formatters/organizations/JsonFormatter.js.map +1 -0
  74. package/dist/formatters/organizations/PlainTextFormatter.js +15 -0
  75. package/dist/formatters/organizations/PlainTextFormatter.js.map +1 -0
  76. package/dist/formatters/organizations/index.js +21 -0
  77. package/dist/formatters/organizations/index.js.map +1 -0
  78. package/dist/formatters/pipelines/AlfredFormatter.js +42 -0
  79. package/dist/formatters/pipelines/AlfredFormatter.js.map +1 -0
  80. package/dist/formatters/pipelines/Formatter.js +10 -0
  81. package/dist/formatters/pipelines/Formatter.js.map +1 -0
  82. package/dist/formatters/pipelines/JsonFormatter.js +13 -0
  83. package/dist/formatters/pipelines/JsonFormatter.js.map +1 -0
  84. package/dist/formatters/pipelines/PlainTextFormatter.js +47 -0
  85. package/dist/formatters/pipelines/PlainTextFormatter.js.map +1 -0
  86. package/dist/formatters/pipelines/index.js +28 -0
  87. package/dist/formatters/pipelines/index.js.map +1 -0
  88. package/dist/formatters/token/AlfredFormatter.js +191 -0
  89. package/dist/formatters/token/AlfredFormatter.js.map +1 -0
  90. package/dist/formatters/token/Formatter.js +13 -0
  91. package/dist/formatters/token/Formatter.js.map +1 -0
  92. package/dist/formatters/token/JsonFormatter.js +211 -0
  93. package/dist/formatters/token/JsonFormatter.js.map +1 -0
  94. package/dist/formatters/token/PlainTextFormatter.js +184 -0
  95. package/dist/formatters/token/PlainTextFormatter.js.map +1 -0
  96. package/dist/formatters/token/index.js +26 -0
  97. package/dist/formatters/token/index.js.map +1 -0
  98. package/dist/formatters/viewer/Formatter.js +10 -0
  99. package/dist/formatters/viewer/Formatter.js.map +1 -0
  100. package/dist/formatters/viewer/JsonFormatter.js +20 -0
  101. package/dist/formatters/viewer/JsonFormatter.js.map +1 -0
  102. package/dist/formatters/viewer/PlainTextFormatter.js +20 -0
  103. package/dist/formatters/viewer/PlainTextFormatter.js.map +1 -0
  104. package/dist/formatters/viewer/index.js +21 -0
  105. package/dist/formatters/viewer/index.js.map +1 -0
  106. package/dist/graphql/generated/fragment-masking.js +17 -0
  107. package/dist/graphql/generated/fragment-masking.js.map +1 -0
  108. package/dist/graphql/generated/gql.js +13 -0
  109. package/dist/graphql/generated/gql.js.map +1 -0
  110. package/dist/graphql/generated/graphql.js +852 -0
  111. package/dist/graphql/generated/graphql.js.map +1 -0
  112. package/dist/graphql/generated/index.js +3 -0
  113. package/dist/graphql/generated/index.js.map +1 -0
  114. package/dist/graphql/generated/sdk.js +872 -0
  115. package/dist/graphql/generated/sdk.js.map +1 -0
  116. package/dist/graphql/queries.js +138 -0
  117. package/dist/graphql/queries.js.map +1 -0
  118. package/dist/index.js +271 -0
  119. package/dist/index.js.map +1 -0
  120. package/dist/services/BuildkiteClient.js +520 -0
  121. package/dist/services/BuildkiteClient.js.map +1 -0
  122. package/dist/services/BuildkiteRestClient.js +244 -0
  123. package/dist/services/BuildkiteRestClient.js.map +1 -0
  124. package/dist/services/CacheManager.js +221 -0
  125. package/dist/services/CacheManager.js.map +1 -0
  126. package/dist/services/CredentialManager.js +158 -0
  127. package/dist/services/CredentialManager.js.map +1 -0
  128. package/dist/services/EnhancedBuildkiteClient.js +297 -0
  129. package/dist/services/EnhancedBuildkiteClient.js.map +1 -0
  130. package/dist/services/logger.js +107 -0
  131. package/dist/services/logger.js.map +1 -0
  132. package/dist/types/buildkite.js +5 -0
  133. package/dist/types/buildkite.js.map +1 -0
  134. package/dist/types/credentials.js +2 -0
  135. package/dist/types/credentials.js.map +1 -0
  136. package/dist/types/index.js +3 -0
  137. package/dist/types/index.js.map +1 -0
  138. package/dist/utils/cli-error-handler.js +172 -0
  139. package/dist/utils/cli-error-handler.js.map +1 -0
  140. package/dist/utils/errorUtils.js +59 -0
  141. package/dist/utils/errorUtils.js.map +1 -0
  142. package/dist/utils/parseBuildRef.js +31 -0
  143. package/dist/utils/parseBuildRef.js.map +1 -0
  144. package/dist/utils/textFormatter.js +53 -0
  145. package/dist/utils/textFormatter.js.map +1 -0
  146. package/dist/utils/xdgPaths.js +95 -0
  147. package/dist/utils/xdgPaths.js.map +1 -0
  148. package/env.example +66 -0
  149. package/icons/README.md +68 -0
  150. package/icons/blocked.png +0 -0
  151. package/icons/buildkite.png +0 -0
  152. package/icons/failed.png +0 -0
  153. package/icons/failing.png +0 -0
  154. package/icons/passed.png +0 -0
  155. package/icons/running.png +0 -0
  156. package/icons/scheduled.png +0 -0
  157. package/icons/skipped.png +0 -0
  158. package/icons/unknown.png +0 -0
  159. package/info.plist +734 -0
  160. package/package.json +87 -0
@@ -0,0 +1,3 @@
1
+ // Buildkite GraphQL API response types
2
+ export {};
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["types/index.ts"],"names":[],"mappings":"AAAA,uCAAuC","sourcesContent":["// Buildkite GraphQL API response types\n\n// Viewer types\nexport interface ViewerUser {\n id: string;\n uuid: string;\n name: string;\n email: string;\n}\n\nexport interface Viewer {\n id: string;\n user?: ViewerUser;\n}\n\nexport interface ViewerData {\n viewer: Viewer;\n}\n\n// Organization types\nexport interface Organization {\n id: string;\n name: string;\n slug: string;\n}\n\n// Pipeline types\nexport interface Repository {\n url: string;\n}\n\nexport interface Pipeline {\n uuid: string;\n id: string;\n name: string;\n slug: string;\n description?: string;\n url?: string;\n organization?: string;\n repository?: Repository;\n}\n\n// Build types\nexport interface BuildPipeline {\n name: string;\n slug: string;\n}\n\nexport interface BuildOrganization {\n name: string;\n slug: string;\n}\n\nexport interface Build {\n id: string;\n number: number;\n url?: string;\n web_url?: string;\n state?: string;\n message?: string;\n commit?: string;\n branch?: string;\n createdAt?: string;\n created_at?: string;\n startedAt?: string;\n started_at?: string;\n finishedAt?: string;\n finished_at?: string;\n pipeline?: BuildPipeline;\n organization?: BuildOrganization;\n}\n\n// Annotation types\nexport interface Annotation {\n context: string;\n style: string;\n body: {\n html: string;\n };\n}\n\n// GraphQL API response structure types\nexport interface GraphQLEdge<T> {\n node: T;\n}\n\nexport interface GraphQLConnection<T> {\n edges: GraphQLEdge<T>[];\n pageInfo?: {\n hasNextPage: boolean;\n endCursor?: string;\n };\n}\n\nexport interface ViewerOrganizationsData {\n viewer: {\n organizations: GraphQLConnection<Organization>;\n };\n}\n\nexport interface PipelineQueryResponse {\n organization: {\n pipelines: GraphQLConnection<Pipeline>;\n };\n}\n\nexport interface ViewerBuildsQueryResponse {\n viewer: {\n builds: GraphQLConnection<Build>;\n };\n}\n\nexport interface OrganizationsQueryResponse {\n viewer: {\n organizations: GraphQLConnection<Organization>;\n };\n} "]}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * CLI Error Handler - Provides error display for CLI applications
3
+ */
4
+ import { logger } from '../services/logger.js';
5
+ import { FormatterFactory, FormatterType } from '../formatters/FormatterFactory.js';
6
+ // Default format to use for error output
7
+ let globalErrorFormat = 'plain';
8
+ /**
9
+ * Set the global error output format
10
+ * @param format The format to use ('plain', 'json', 'alfred')
11
+ */
12
+ export function setErrorFormat(format) {
13
+ const normalizedFormat = format.toLowerCase().trim();
14
+ if (['plain', 'json', 'alfred'].includes(normalizedFormat)) {
15
+ globalErrorFormat = normalizedFormat;
16
+ logger.debug(`Error output format set to ${normalizedFormat}`);
17
+ }
18
+ else {
19
+ logger.warn(`Unknown format '${format}', error output format remains as ${globalErrorFormat}`);
20
+ }
21
+ }
22
+ /**
23
+ * Display a formatted error message
24
+ *
25
+ * @param error The error to display
26
+ * @param debug Whether to include debug information
27
+ * @param format Output format (plain, json, alfred), defaults to global setting
28
+ */
29
+ export function displayCLIError(error, debug = false, format) {
30
+ // Use provided format or fall back to global format
31
+ const outputFormat = format || globalErrorFormat;
32
+ // Get the appropriate formatter based on format
33
+ const formatter = FormatterFactory.getFormatter(FormatterType.ERROR, outputFormat);
34
+ // Format the error using the selected formatter
35
+ const formattedError = formatter.formatError(error, { debug });
36
+ // Print the formatted output
37
+ if (outputFormat === 'plain') {
38
+ logger.error(formattedError);
39
+ }
40
+ else {
41
+ logger.console(formattedError);
42
+ }
43
+ process.exit(1);
44
+ }
45
+ /**
46
+ * Get the current global error output format
47
+ * @returns The current format
48
+ */
49
+ export function getErrorFormat() {
50
+ return globalErrorFormat;
51
+ }
52
+ /**
53
+ * Format an error object for display in the CLI
54
+ * Extracts as much useful information as possible, including stack traces
55
+ *
56
+ * @param error The error object to format
57
+ * @param debug Whether to include debug information
58
+ * @returns Formatted error message
59
+ */
60
+ export function formatErrorForCLI(error, debug = false) {
61
+ let output = '';
62
+ // Add a separator and heading
63
+ output += '\n\x1b[31m════════════════════════ ERROR ════════════════════════\x1b[0m\n\n';
64
+ if (error instanceof Error) {
65
+ // Handle standard Error objects
66
+ output += `\x1b[31m${error.name}: ${error.message}\x1b[0m\n\n`;
67
+ if (error.stack) {
68
+ const stackLines = error.stack.split('\n');
69
+ // First line usually contains the error message, which we've already displayed
70
+ const stackTrace = stackLines.slice(1).join('\n');
71
+ output += `\x1b[33mStack Trace:\x1b[0m\n${stackTrace}\n\n`;
72
+ }
73
+ // Handle additional properties that might be present in API errors
74
+ const apiError = error;
75
+ if (apiError.response?.errors) {
76
+ output += '\x1b[33mAPI Errors:\x1b[0m\n';
77
+ apiError.response.errors.forEach((e, i) => {
78
+ output += ` Error ${i + 1}: ${e.message || 'Unknown error'}\n`;
79
+ if (e.path)
80
+ output += ` Path: ${e.path.join('.')}\n`;
81
+ if (e.locations)
82
+ output += ` Locations: ${JSON.stringify(e.locations)}\n`;
83
+ output += '\n';
84
+ });
85
+ }
86
+ if (debug && apiError.request) {
87
+ output += '\x1b[36mRequest Details:\x1b[0m\n';
88
+ output += ` URL: ${apiError.request.url || 'N/A'}\n`;
89
+ output += ` Method: ${apiError.request.method || 'N/A'}\n\n`;
90
+ }
91
+ // If error has a cause, include it
92
+ if (apiError.cause) {
93
+ output += '\x1b[33mCaused by:\x1b[0m\n';
94
+ output += formatErrorForCLI(apiError.cause, debug);
95
+ }
96
+ }
97
+ else if (error && typeof error === 'object') {
98
+ // Handle non-Error objects
99
+ try {
100
+ output += '\x1b[31mError Object:\x1b[0m\n';
101
+ output += JSON.stringify(error, null, 2) + '\n\n';
102
+ // Try to extract more detailed information
103
+ const errorObj = error;
104
+ if (errorObj.message) {
105
+ output += `Message: ${errorObj.message}\n`;
106
+ }
107
+ if (debug) {
108
+ output += '\x1b[36mProperties:\x1b[0m\n';
109
+ for (const key in errorObj) {
110
+ try {
111
+ if (key !== 'stack' && key !== 'message') {
112
+ const value = errorObj[key];
113
+ output += ` ${key}: ${typeof value === 'object' ? JSON.stringify(value) : value}\n`;
114
+ }
115
+ }
116
+ catch {
117
+ output += ` ${key}: [Cannot stringify]\n`;
118
+ }
119
+ }
120
+ output += '\n';
121
+ }
122
+ }
123
+ catch {
124
+ output += '\x1b[31mUnable to stringify error object\x1b[0m\n\n';
125
+ }
126
+ }
127
+ else {
128
+ // Handle primitive values
129
+ output += `\x1b[31m${String(error)}\x1b[0m\n\n`;
130
+ }
131
+ if (debug) {
132
+ // Add debug information
133
+ output += '\x1b[36mDebug Information:\x1b[0m\n';
134
+ output += ` Timestamp: ${new Date().toISOString()}\n`;
135
+ output += ` Node Version: ${process.version}\n`;
136
+ output += ` Platform: ${process.platform} (${process.arch})\n`;
137
+ try {
138
+ // Get the current call stack
139
+ const stack = new Error().stack;
140
+ if (stack) {
141
+ const stackLines = stack.split('\n').slice(2); // Skip the Error creation line and this function
142
+ output += '\n\x1b[36mCurrent Stack:\x1b[0m\n';
143
+ output += stackLines.join('\n') + '\n';
144
+ }
145
+ }
146
+ catch {
147
+ // Ignore stack trace errors
148
+ }
149
+ }
150
+ // Add a closing separator
151
+ output += '\n\x1b[31m═══════════════════════════════════════════════════════\x1b[0m\n';
152
+ return output;
153
+ }
154
+ /**
155
+ * Wraps a function with CLI error handling
156
+ *
157
+ * @param fn The function to wrap
158
+ * @param options Error handling options
159
+ * @returns A wrapped function with error handling
160
+ */
161
+ export function withCLIErrorHandling(fn, options = {}) {
162
+ return async function (...args) {
163
+ try {
164
+ return await fn(...args);
165
+ }
166
+ catch (error) {
167
+ displayCLIError(error, options.debugMode || false, options.format);
168
+ process.exit(1);
169
+ }
170
+ };
171
+ }
172
+ //# sourceMappingURL=cli-error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-error-handler.js","sourceRoot":"/","sources":["utils/cli-error-handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI Error Handler - Provides error display for CLI applications\n */\n\nimport { logger } from '../services/logger.js';\nimport { FormatterFactory, FormatterType } from '../formatters/FormatterFactory.js';\nimport { ErrorFormatter } from '../formatters/errors/index.js';\n\n// Default format to use for error output\nlet globalErrorFormat = 'plain';\n\n/**\n * Set the global error output format\n * @param format The format to use ('plain', 'json', 'alfred')\n */\nexport function setErrorFormat(format: string): void {\n const normalizedFormat = format.toLowerCase().trim();\n if (['plain', 'json', 'alfred'].includes(normalizedFormat)) {\n globalErrorFormat = normalizedFormat;\n logger.debug(`Error output format set to ${normalizedFormat}`);\n } else {\n logger.warn(`Unknown format '${format}', error output format remains as ${globalErrorFormat}`);\n }\n}\n\n/**\n * Display a formatted error message\n * \n * @param error The error to display\n * @param debug Whether to include debug information\n * @param format Output format (plain, json, alfred), defaults to global setting\n */\nexport function displayCLIError(\n error: unknown, \n debug = false, \n format?: string\n): void {\n // Use provided format or fall back to global format\n const outputFormat = format || globalErrorFormat;\n \n // Get the appropriate formatter based on format\n const formatter = FormatterFactory.getFormatter(FormatterType.ERROR, outputFormat) as ErrorFormatter;\n \n // Format the error using the selected formatter\n const formattedError = formatter.formatError(error, { debug });\n \n // Print the formatted output\n if (outputFormat === 'plain') {\n logger.error(formattedError);\n } else {\n logger.console(formattedError);\n }\n \n process.exit(1);\n}\n\n/**\n * Get the current global error output format\n * @returns The current format\n */\nexport function getErrorFormat(): string {\n return globalErrorFormat;\n}\n\n/**\n * Format an error object for display in the CLI\n * Extracts as much useful information as possible, including stack traces\n * \n * @param error The error object to format\n * @param debug Whether to include debug information\n * @returns Formatted error message\n */\nexport function formatErrorForCLI(error: unknown, debug = false): string {\n let output = '';\n \n // Add a separator and heading\n output += '\\n\\x1b[31m════════════════════════ ERROR ════════════════════════\\x1b[0m\\n\\n';\n \n if (error instanceof Error) {\n // Handle standard Error objects\n output += `\\x1b[31m${error.name}: ${error.message}\\x1b[0m\\n\\n`;\n \n if (error.stack) {\n const stackLines = error.stack.split('\\n');\n // First line usually contains the error message, which we've already displayed\n const stackTrace = stackLines.slice(1).join('\\n');\n output += `\\x1b[33mStack Trace:\\x1b[0m\\n${stackTrace}\\n\\n`;\n }\n \n // Handle additional properties that might be present in API errors\n const apiError = error as any;\n if (apiError.response?.errors) {\n output += '\\x1b[33mAPI Errors:\\x1b[0m\\n';\n apiError.response.errors.forEach((e: any, i: number) => {\n output += ` Error ${i + 1}: ${e.message || 'Unknown error'}\\n`;\n if (e.path) output += ` Path: ${e.path.join('.')}\\n`;\n if (e.locations) output += ` Locations: ${JSON.stringify(e.locations)}\\n`;\n output += '\\n';\n });\n }\n \n if (debug && apiError.request) {\n output += '\\x1b[36mRequest Details:\\x1b[0m\\n';\n output += ` URL: ${apiError.request.url || 'N/A'}\\n`;\n output += ` Method: ${apiError.request.method || 'N/A'}\\n\\n`;\n }\n \n // If error has a cause, include it\n if (apiError.cause) {\n output += '\\x1b[33mCaused by:\\x1b[0m\\n';\n output += formatErrorForCLI(apiError.cause, debug);\n }\n } else if (error && typeof error === 'object') {\n // Handle non-Error objects\n try {\n output += '\\x1b[31mError Object:\\x1b[0m\\n';\n output += JSON.stringify(error, null, 2) + '\\n\\n';\n \n // Try to extract more detailed information\n const errorObj = error as Record<string, any>;\n if (errorObj.message) {\n output += `Message: ${errorObj.message}\\n`;\n }\n \n if (debug) {\n output += '\\x1b[36mProperties:\\x1b[0m\\n';\n for (const key in errorObj) {\n try {\n if (key !== 'stack' && key !== 'message') {\n const value = errorObj[key];\n output += ` ${key}: ${typeof value === 'object' ? JSON.stringify(value) : value}\\n`;\n }\n } catch {\n output += ` ${key}: [Cannot stringify]\\n`;\n }\n }\n output += '\\n';\n }\n } catch {\n output += '\\x1b[31mUnable to stringify error object\\x1b[0m\\n\\n';\n }\n } else {\n // Handle primitive values\n output += `\\x1b[31m${String(error)}\\x1b[0m\\n\\n`;\n }\n \n if (debug) {\n // Add debug information\n output += '\\x1b[36mDebug Information:\\x1b[0m\\n';\n output += ` Timestamp: ${new Date().toISOString()}\\n`;\n output += ` Node Version: ${process.version}\\n`;\n output += ` Platform: ${process.platform} (${process.arch})\\n`;\n \n try {\n // Get the current call stack\n const stack = new Error().stack;\n if (stack) {\n const stackLines = stack.split('\\n').slice(2); // Skip the Error creation line and this function\n output += '\\n\\x1b[36mCurrent Stack:\\x1b[0m\\n';\n output += stackLines.join('\\n') + '\\n';\n }\n } catch {\n // Ignore stack trace errors\n }\n }\n \n // Add a closing separator\n output += '\\n\\x1b[31m═══════════════════════════════════════════════════════\\x1b[0m\\n';\n \n return output;\n}\n\n/**\n * Wraps a function with CLI error handling\n * \n * @param fn The function to wrap\n * @param options Error handling options\n * @returns A wrapped function with error handling\n */\nexport function withCLIErrorHandling<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n options: {\n debugMode?: boolean;\n format?: string;\n } = {}\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async function(...args: Parameters<T>): Promise<ReturnType<T>> {\n try {\n return await fn(...args);\n } catch (error) {\n displayCLIError(\n error, \n options.debugMode || false, \n options.format\n );\n process.exit(1);\n }\n };\n} "]}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Error handling utilities
3
+ */
4
+ import * as sourceMap from 'source-map-support';
5
+ import { logger } from '../services/logger.js';
6
+ import { displayCLIError } from './cli-error-handler.js';
7
+ /**
8
+ * Initialize error handling for the application
9
+ */
10
+ export function initializeErrorHandling() {
11
+ // Install source map support for better stack traces
12
+ sourceMap.install({
13
+ handleUncaughtExceptions: false,
14
+ hookRequire: true
15
+ });
16
+ // Set up handlers for uncaught exceptions and unhandled rejections
17
+ process.on('uncaughtException', (err) => {
18
+ displayCLIError(err, false);
19
+ });
20
+ process.on('unhandledRejection', (reason) => {
21
+ displayCLIError(reason, false);
22
+ });
23
+ logger.debug('Error handling system initialized');
24
+ }
25
+ /**
26
+ * Log an error with proper formatting
27
+ * @param error The error to log
28
+ */
29
+ export function logError(error) {
30
+ if (error instanceof Error) {
31
+ logger.error(error, 'Error occurred');
32
+ }
33
+ else {
34
+ logger.error({ error }, 'Unknown error occurred');
35
+ }
36
+ }
37
+ /**
38
+ * Wrap an async function with better error handling
39
+ *
40
+ * @param fn Function to wrap
41
+ * @returns Wrapped function with better error handling
42
+ */
43
+ export function withErrorHandling(fn) {
44
+ return async function (...args) {
45
+ try {
46
+ return await fn(...args);
47
+ }
48
+ catch (error) {
49
+ if (error instanceof Error) {
50
+ logger.error(error, 'Error occurred');
51
+ }
52
+ else {
53
+ logger.error({ error }, 'Unknown error occurred');
54
+ }
55
+ throw error;
56
+ }
57
+ };
58
+ }
59
+ //# sourceMappingURL=errorUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorUtils.js","sourceRoot":"/","sources":["utils/errorUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,qDAAqD;IACrD,SAAS,CAAC,OAAO,CAAC;QAChB,wBAAwB,EAAE,KAAK;QAC/B,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;QACtC,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;QAC1C,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,wBAAwB,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,EAAK;IAEL,OAAO,KAAK,WAAU,GAAG,IAAmB;QAC1C,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,wBAAwB,CAAC,CAAC;YACpD,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Error handling utilities\n */\nimport * as sourceMap from 'source-map-support';\nimport { logger } from '../services/logger.js';\nimport { displayCLIError } from './cli-error-handler.js';\n\n/**\n * Initialize error handling for the application\n */\nexport function initializeErrorHandling(): void {\n // Install source map support for better stack traces\n sourceMap.install({\n handleUncaughtExceptions: false,\n hookRequire: true\n });\n \n // Set up handlers for uncaught exceptions and unhandled rejections\n process.on('uncaughtException', (err) => {\n displayCLIError(err, false);\n });\n\n process.on('unhandledRejection', (reason) => {\n displayCLIError(reason, false);\n });\n\n logger.debug('Error handling system initialized');\n}\n\n/**\n * Log an error with proper formatting\n * @param error The error to log\n */\nexport function logError(error: unknown): void {\n if (error instanceof Error) {\n logger.error(error, 'Error occurred');\n } else {\n logger.error({ error }, 'Unknown error occurred');\n }\n}\n\n/**\n * Wrap an async function with better error handling\n * \n * @param fn Function to wrap\n * @returns Wrapped function with better error handling\n */\nexport function withErrorHandling<T extends (...args: any[]) => Promise<any>>(\n fn: T\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async function(...args: Parameters<T>): Promise<ReturnType<T>> {\n try {\n return await fn(...args);\n } catch (error) {\n if (error instanceof Error) {\n logger.error(error, 'Error occurred');\n } else {\n logger.error({ error }, 'Unknown error occurred');\n }\n throw error;\n }\n };\n} "]}
@@ -0,0 +1,31 @@
1
+ export function parseBuildRef(input) {
2
+ if (!input) {
3
+ throw new Error('Build reference is required');
4
+ }
5
+ // Remove optional leading @
6
+ const cleanInput = input.startsWith('@') ? input.slice(1) : input;
7
+ // Try URL format first: https://buildkite.com/org/pipeline/builds/number
8
+ const urlRegex = /^https?:\/\/buildkite\.com\/([^\/]+)\/([^\/]+)\/builds\/(\d+)$/;
9
+ const urlMatch = cleanInput.match(urlRegex);
10
+ if (urlMatch) {
11
+ const [, org, pipeline, numberStr] = urlMatch;
12
+ const number = parseInt(numberStr, 10);
13
+ if (isNaN(number)) {
14
+ throw new Error(`Invalid build number: ${numberStr}`);
15
+ }
16
+ return { org, pipeline, number };
17
+ }
18
+ // Try slug format: org/pipeline/number
19
+ const slugRegex = /^([^\/]+)\/([^\/]+)\/(\d+)$/;
20
+ const slugMatch = cleanInput.match(slugRegex);
21
+ if (slugMatch) {
22
+ const [, org, pipeline, numberStr] = slugMatch;
23
+ const number = parseInt(numberStr, 10);
24
+ if (isNaN(number)) {
25
+ throw new Error(`Invalid build number: ${numberStr}`);
26
+ }
27
+ return { org, pipeline, number };
28
+ }
29
+ throw new Error(`Invalid build reference format: ${input}. Expected org/pipeline/number or @https://buildkite.com/org/pipeline/builds/number`);
30
+ }
31
+ //# sourceMappingURL=parseBuildRef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseBuildRef.js","sourceRoot":"/","sources":["utils/parseBuildRef.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,4BAA4B;IAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAElE,yEAAyE;IACzE,MAAM,QAAQ,GAAG,gEAAgE,CAAC;IAClF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC;QAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,yBAAyB,SAAS,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,uCAAuC;IACvC,MAAM,SAAS,GAAG,6BAA6B,CAAC;IAChD,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,GAAG,SAAS,CAAC;QAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,yBAAyB,SAAS,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,mCAAmC,KAAK,qFAAqF,CAAC,CAAC;AACjJ,CAAC","sourcesContent":["export interface BuildRef {\n org: string;\n pipeline: string;\n number: number;\n}\n\nexport function parseBuildRef(input: string): BuildRef {\n if (!input) {\n throw new Error('Build reference is required');\n }\n\n // Remove optional leading @\n const cleanInput = input.startsWith('@') ? input.slice(1) : input;\n\n // Try URL format first: https://buildkite.com/org/pipeline/builds/number\n const urlRegex = /^https?:\\/\\/buildkite\\.com\\/([^\\/]+)\\/([^\\/]+)\\/builds\\/(\\d+)$/;\n const urlMatch = cleanInput.match(urlRegex);\n \n if (urlMatch) {\n const [, org, pipeline, numberStr] = urlMatch;\n const number = parseInt(numberStr, 10);\n \n if (isNaN(number)) {\n throw new Error(`Invalid build number: ${numberStr}`);\n }\n \n return { org, pipeline, number };\n }\n\n // Try slug format: org/pipeline/number\n const slugRegex = /^([^\\/]+)\\/([^\\/]+)\\/(\\d+)$/;\n const slugMatch = cleanInput.match(slugRegex);\n \n if (slugMatch) {\n const [, org, pipeline, numberStr] = slugMatch;\n const number = parseInt(numberStr, 10);\n \n if (isNaN(number)) {\n throw new Error(`Invalid build number: ${numberStr}`);\n }\n \n return { org, pipeline, number };\n }\n\n throw new Error(`Invalid build reference format: ${input}. Expected org/pipeline/number or @https://buildkite.com/org/pipeline/builds/number`);\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import { htmlToText } from 'html-to-text';
2
+ /**
3
+ * Formats HTML text for terminal display
4
+ */
5
+ export function formatTextForTerminal(text, options = {}) {
6
+ const { maxWidth = 80, preserveLinks = true } = options;
7
+ if (!text || typeof text !== 'string') {
8
+ return '';
9
+ }
10
+ // Convert HTML to text with better structure preservation
11
+ const htmlToTextOptions = {
12
+ wordwrap: maxWidth,
13
+ preserveNewlines: true,
14
+ selectors: [
15
+ { selector: 'a', format: 'anchor' }
16
+ ],
17
+ formatters: {
18
+ anchor: (elem, _walk, builder) => {
19
+ const href = elem.attribs?.href;
20
+ const text = elem.children?.[0]?.data || href;
21
+ if (preserveLinks && href) {
22
+ builder.addInline(`${text} (${href})`);
23
+ }
24
+ else {
25
+ builder.addInline(text);
26
+ }
27
+ }
28
+ }
29
+ };
30
+ try {
31
+ const formattedText = htmlToText(text, htmlToTextOptions);
32
+ // Clean up any excessive whitespace while preserving structure
33
+ return formattedText
34
+ .replace(/\n{3,}/g, '\n\n') // Replace 3+ newlines with 2
35
+ .trim();
36
+ }
37
+ catch (error) {
38
+ // If HTML parsing fails, fall back to original text
39
+ console.warn('Failed to parse HTML, using original text:', error);
40
+ return text.trim();
41
+ }
42
+ }
43
+ /**
44
+ * Formats annotation body text specifically for terminal display
45
+ */
46
+ export function formatAnnotationBody(bodyText, options = {}) {
47
+ return formatTextForTerminal(bodyText, {
48
+ maxWidth: 100,
49
+ preserveLinks: true,
50
+ ...options
51
+ });
52
+ }
53
+ //# sourceMappingURL=textFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textFormatter.js","sourceRoot":"/","sources":["utils/textFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO1C;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,UAAiC,EAAE;IACrF,MAAM,EACJ,QAAQ,GAAG,EAAE,EACb,aAAa,GAAG,IAAI,EACrB,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,0DAA0D;IAC1D,MAAM,iBAAiB,GAAG;QACxB,QAAQ,EAAE,QAAQ;QAClB,gBAAgB,EAAE,IAAI;QACtB,SAAS,EAAE;YACT,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE;SACpC;QACD,UAAU,EAAE;YACV,MAAM,EAAE,CAAC,IAAS,EAAE,KAAU,EAAE,OAAY,EAAE,EAAE;gBAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;gBAChC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;gBAC9C,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;oBAC1B,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC;gBACzC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;SACF;KACF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAE1D,+DAA+D;QAC/D,OAAO,aAAa;aACjB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,6BAA6B;aACxD,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,oDAAoD;QACpD,OAAO,CAAC,IAAI,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,UAAiC,EAAE;IACxF,OAAO,qBAAqB,CAAC,QAAQ,EAAE;QACrC,QAAQ,EAAE,GAAG;QACb,aAAa,EAAE,IAAI;QACnB,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { htmlToText } from 'html-to-text';\n\nexport interface TextFormattingOptions {\n maxWidth?: number;\n preserveLinks?: boolean;\n}\n\n/**\n * Formats HTML text for terminal display\n */\nexport function formatTextForTerminal(text: string, options: TextFormattingOptions = {}): string {\n const {\n maxWidth = 80,\n preserveLinks = true\n } = options;\n\n if (!text || typeof text !== 'string') {\n return '';\n }\n\n // Convert HTML to text with better structure preservation\n const htmlToTextOptions = {\n wordwrap: maxWidth,\n preserveNewlines: true,\n selectors: [\n { selector: 'a', format: 'anchor' }\n ],\n formatters: {\n anchor: (elem: any, _walk: any, builder: any) => {\n const href = elem.attribs?.href;\n const text = elem.children?.[0]?.data || href;\n if (preserveLinks && href) {\n builder.addInline(`${text} (${href})`);\n } else {\n builder.addInline(text);\n }\n }\n }\n };\n\n try {\n const formattedText = htmlToText(text, htmlToTextOptions);\n \n // Clean up any excessive whitespace while preserving structure\n return formattedText\n .replace(/\\n{3,}/g, '\\n\\n') // Replace 3+ newlines with 2\n .trim();\n } catch (error) {\n // If HTML parsing fails, fall back to original text\n console.warn('Failed to parse HTML, using original text:', error);\n return text.trim();\n }\n}\n\n/**\n * Formats annotation body text specifically for terminal display\n */\nexport function formatAnnotationBody(bodyText: string, options: TextFormattingOptions = {}): string {\n return formatTextForTerminal(bodyText, {\n maxWidth: 100,\n preserveLinks: true,\n ...options\n });\n}\n"]}
@@ -0,0 +1,95 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ /**
5
+ * XDG Base Directory Specification utility
6
+ * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
7
+ */
8
+ export class XDGPaths {
9
+ /**
10
+ * Get XDG_CONFIG_HOME directory path
11
+ * Defaults to $HOME/.config
12
+ */
13
+ static getConfigHome() {
14
+ return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
15
+ }
16
+ /**
17
+ * Get XDG_DATA_HOME directory path
18
+ * Defaults to $HOME/.local/share
19
+ */
20
+ static getDataHome() {
21
+ return process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
22
+ }
23
+ /**
24
+ * Get XDG_CACHE_HOME directory path
25
+ * Defaults to $HOME/.cache
26
+ */
27
+ static getCacheHome() {
28
+ return process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
29
+ }
30
+ /**
31
+ * Get XDG_STATE_HOME directory path
32
+ * Defaults to $HOME/.local/state
33
+ */
34
+ static getStateHome() {
35
+ return process.env.XDG_STATE_HOME || path.join(os.homedir(), '.local', 'state');
36
+ }
37
+ /**
38
+ * Get the appropriate application directory within an XDG directory
39
+ * @param base XDG base directory
40
+ * @param appName Application name
41
+ * @param create Whether to create the directory if it doesn't exist
42
+ */
43
+ static getAppDir(base, appName, create = true) {
44
+ const dir = path.join(base, appName);
45
+ if (create) {
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ }
48
+ return dir;
49
+ }
50
+ /**
51
+ * Get application's cache directory
52
+ * @param appName Application name
53
+ * @param create Whether to create the directory if it doesn't exist
54
+ */
55
+ static getAppCacheDir(appName, create = true) {
56
+ return this.getAppDir(this.getCacheHome(), appName, create);
57
+ }
58
+ /**
59
+ * Get application's config directory
60
+ * @param appName Application name
61
+ * @param create Whether to create the directory if it doesn't exist
62
+ */
63
+ static getAppConfigDir(appName, create = true) {
64
+ return this.getAppDir(this.getConfigHome(), appName, create);
65
+ }
66
+ /**
67
+ * Get application's data directory
68
+ * @param appName Application name
69
+ * @param create Whether to create the directory if it doesn't exist
70
+ */
71
+ static getAppDataDir(appName, create = true) {
72
+ return this.getAppDir(this.getDataHome(), appName, create);
73
+ }
74
+ /**
75
+ * Get application's state directory (for logs, etc.)
76
+ * @param appName Application name
77
+ * @param create Whether to create the directory if it doesn't exist
78
+ */
79
+ static getAppStateDir(appName, create = true) {
80
+ return this.getAppDir(this.getStateHome(), appName, create);
81
+ }
82
+ /**
83
+ * Get application's log directory (typically in state home)
84
+ * @param appName Application name
85
+ * @param create Whether to create the directory if it doesn't exist
86
+ */
87
+ static getAppLogDir(appName, create = true) {
88
+ const logDir = path.join(this.getAppStateDir(appName, create), 'logs');
89
+ if (create) {
90
+ fs.mkdirSync(logDir, { recursive: true });
91
+ }
92
+ return logDir;
93
+ }
94
+ }
95
+ //# sourceMappingURL=xdgPaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xdgPaths.js","sourceRoot":"/","sources":["utils/xdgPaths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB;;;GAGG;AACH,MAAM,OAAO,QAAQ;IACnB;;;OAGG;IACH,MAAM,CAAC,aAAa;QAClB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC3E,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,WAAW;QAChB,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjF,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IACzE,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClF,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,IAAY,EAAE,OAAe,EAAE,MAAM,GAAG,IAAI;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,MAAM,EAAE,CAAC;YACX,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,OAAe,EAAE,MAAM,GAAG,IAAI;QAClD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,OAAe,EAAE,MAAM,GAAG,IAAI;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,OAAe,EAAE,MAAM,GAAG,IAAI;QACjD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,OAAe,EAAE,MAAM,GAAG,IAAI;QAClD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,OAAe,EAAE,MAAM,GAAG,IAAI;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACvE,IAAI,MAAM,EAAE,CAAC;YACX,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * XDG Base Directory Specification utility\n * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\n */\nexport class XDGPaths {\n /**\n * Get XDG_CONFIG_HOME directory path\n * Defaults to $HOME/.config\n */\n static getConfigHome(): string {\n return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');\n }\n\n /**\n * Get XDG_DATA_HOME directory path\n * Defaults to $HOME/.local/share\n */\n static getDataHome(): string {\n return process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');\n }\n\n /**\n * Get XDG_CACHE_HOME directory path\n * Defaults to $HOME/.cache\n */\n static getCacheHome(): string {\n return process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');\n }\n\n /**\n * Get XDG_STATE_HOME directory path\n * Defaults to $HOME/.local/state\n */\n static getStateHome(): string {\n return process.env.XDG_STATE_HOME || path.join(os.homedir(), '.local', 'state');\n }\n\n /**\n * Get the appropriate application directory within an XDG directory\n * @param base XDG base directory\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppDir(base: string, appName: string, create = true): string {\n const dir = path.join(base, appName);\n if (create) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n }\n\n /**\n * Get application's cache directory\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppCacheDir(appName: string, create = true): string {\n return this.getAppDir(this.getCacheHome(), appName, create);\n }\n\n /**\n * Get application's config directory\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppConfigDir(appName: string, create = true): string {\n return this.getAppDir(this.getConfigHome(), appName, create);\n }\n\n /**\n * Get application's data directory\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppDataDir(appName: string, create = true): string {\n return this.getAppDir(this.getDataHome(), appName, create);\n }\n\n /**\n * Get application's state directory (for logs, etc.)\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppStateDir(appName: string, create = true): string {\n return this.getAppDir(this.getStateHome(), appName, create);\n }\n\n /**\n * Get application's log directory (typically in state home)\n * @param appName Application name\n * @param create Whether to create the directory if it doesn't exist\n */\n static getAppLogDir(appName: string, create = true): string {\n const logDir = path.join(this.getAppStateDir(appName, create), 'logs');\n if (create) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n return logDir;\n }\n} "]}
package/env.example ADDED
@@ -0,0 +1,66 @@
1
+ # bktide Environment Configuration
2
+ # Copy this file to ~/.config/bktide/env or to the workflow directory as .env
3
+
4
+ # Node.js Configuration
5
+ # ===================
6
+
7
+ # Path to the Node.js binary
8
+ # If Node.js is not in your PATH or you want to use a specific version,
9
+ # set this to the full path of the node binary
10
+ # Examples:
11
+ # NODE_BIN=node # Use node from PATH (default)
12
+ # NODE_BIN=/opt/homebrew/bin/node # Homebrew on Apple Silicon
13
+ # NODE_BIN=/usr/local/bin/node # Homebrew on Intel Mac
14
+ # NODE_BIN=/Users/username/.nvm/versions/node/v18.17.0/bin/node # nvm
15
+ #NODE_BIN=node
16
+
17
+ # PATH Configuration
18
+ # =================
19
+
20
+ # You can extend the PATH to include additional directories where Node.js might be located
21
+ # This is useful if your Node.js installation is not in the default system PATH
22
+ # Examples:
23
+ # export PATH="/opt/homebrew/bin:$PATH" # Add Homebrew (Apple Silicon)
24
+ # export PATH="/usr/local/bin:$PATH" # Add Homebrew (Intel)
25
+ # export PATH="/Users/username/.nvm/versions/node/v18.17.0/bin:$PATH" # Add nvm
26
+
27
+ # Buildkite API Configuration
28
+ # ==========================
29
+
30
+ # Buildkite API token
31
+ # This can also be stored securely using: bktide token --store
32
+ # Or passed as a command line argument: --token your_token_here
33
+ #BUILDKITE_API_TOKEN=your_buildkite_api_token_here
34
+
35
+ # Debugging and Development
36
+ # ========================
37
+
38
+ # Enable debug logging (equivalent to --debug flag)
39
+ #DEBUG=true
40
+
41
+ # Disable caching (equivalent to --no-cache flag)
42
+ #NO_CACHE=true
43
+
44
+ # Custom cache directory (defaults to ~/.cache/bktide)
45
+ #CACHE_DIR=/custom/path/to/cache
46
+
47
+ # Custom log directory (defaults to ~/.local/state/bktide/logs)
48
+ #LOG_DIR=/custom/path/to/logs
49
+
50
+ # Example configurations for common setups:
51
+ # ========================================
52
+
53
+ # For Homebrew on Apple Silicon Macs:
54
+ # export PATH="/opt/homebrew/bin:$PATH"
55
+ # NODE_BIN=/opt/homebrew/bin/node
56
+
57
+ # For Homebrew on Intel Macs:
58
+ # export PATH="/usr/local/bin:$PATH"
59
+ # NODE_BIN=/usr/local/bin/node
60
+
61
+ # For nvm users (replace with your Node version):
62
+ # export PATH="/Users/$(whoami)/.nvm/versions/node/v18.17.0/bin:$PATH"
63
+ # NODE_BIN="/Users/$(whoami)/.nvm/versions/node/v18.17.0/bin/node"
64
+
65
+ # For system Node.js installation:
66
+ # NODE_BIN=node
@@ -0,0 +1,68 @@
1
+ [Possible States](https://buildkite.com/user/graphql/documentation/type/BuildStates)
2
+
3
+ SKIPPED
4
+ The build was skipped
5
+
6
+ CREATING
7
+ The build is currently being created
8
+
9
+ SCHEDULED
10
+ The build has yet to start running jobs
11
+
12
+ RUNNING
13
+ The build is currently running jobs
14
+
15
+ PASSED
16
+ The build passed
17
+
18
+ FAILED
19
+ The build failed
20
+
21
+ FAILING
22
+ The build is failing
23
+
24
+ CANCELING
25
+ The build is currently being canceled
26
+
27
+ CANCELED
28
+ The build was canceled
29
+
30
+ BLOCKED
31
+ The build is blocked
32
+
33
+ NOT_RUN
34
+ The build wasn't run
35
+
36
+ Material Icons
37
+ Size: 256x256
38
+
39
+ * icons/pass.png
40
+ * [Check Circle](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:check_circle:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=check&icon.size=256&icon.color=%2375FB4C)
41
+ * icons/fail.png
42
+ * [Error](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:error:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=error&icon.size=256&icon.color=%23BB271A)
43
+ * icons/running.png
44
+ * [Sync](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:sync:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=sync&icon.size=256&icon.color=%23F19E39)
45
+ * icons/cancelled.png
46
+ * [Cancel](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:cancel:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=cancel&icon.size=256&icon.color=%23EA3323)
47
+ * Red
48
+ * icons/cancelling.png
49
+ * [Sync Problem](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:sync_problem:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=sync&icon.size=256&icon.color=%23EA3323)
50
+ * Red
51
+ * icons/failing.png
52
+ * [Sync Problem](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:sync_problem:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=sync&icon.size=256&icon.color=%23EA3323)
53
+ * Red
54
+ * icons/skipped.png
55
+ * [Next Plan](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:next_plan:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=skip&icon.size=256&icon.color=%23999999)
56
+ * Gray
57
+ * icons/blocked.png
58
+ * [Pause](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:pause:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=pause&icon.size=256&icon.color=%238C1AF6)
59
+ * purple
60
+ * icons/scheduled.png
61
+ * [Schedule](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:schedule:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=schedule&icon.size=256&icon.color=%23999999)
62
+ * gray
63
+ * icons/unknown.png
64
+ * [Question Mark](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:question_mark:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=unknown&icon.size=256&icon.color=%23999999)
65
+ * gray
66
+ * icons/building.png
67
+ * https://buildkite.com/about/brand-assets/
68
+ * secondary logo, mark
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file