@copado/agentia-cli 0.15.0-alpha.0 → 0.17.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { CicdGateway } from '../../lib/cicd-gateway.js';
3
+ import { readLastBaseBranch } from '../../lib/defaults-store.js';
4
+ import { getCurrentBranch, hasUnpushedCommits, listPendingChanges } from '../../lib/git-worktree.js';
5
+ import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
6
+ import { extractUserStoryFromFeatureBranch, workDoneEndpoint, } from '../../lib/work-types.js';
7
+ export default class WorkDone extends AgentiaCommand {
8
+ static description = [
9
+ 'Mark the current feature/US-XXXX work item done and promote it to the next environment.',
10
+ 'Pushes and registers commits when the branch is ahead of origin, then calls headless work/done',
11
+ '(Promote Action with validate=false).',
12
+ ].join(' ');
13
+ static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
14
+ async run() {
15
+ await this.parse(WorkDone);
16
+ const branch = await getCurrentBranch();
17
+ const workItem = extractUserStoryFromFeatureBranch(branch);
18
+ if (workItem === undefined) {
19
+ this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
20
+ exit: 1,
21
+ suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
22
+ });
23
+ }
24
+ const pendingFiles = await listPendingChanges();
25
+ if (pendingFiles.length > 0) {
26
+ this.error([
27
+ 'Uncommitted tracked changes present. Commit them, then try again:',
28
+ ...pendingFiles.map((file) => ` ${file}`),
29
+ ].join('\n'), {
30
+ exit: 2,
31
+ suggestions: ['git add -A && git commit', `${this.config.bin} work done`],
32
+ });
33
+ }
34
+ this.display(() => {
35
+ this.log(`Branch: ${branch}`);
36
+ this.log(`Work item: ${workItem}`);
37
+ });
38
+ let pushResult;
39
+ const needsPush = await hasUnpushedCommits(branch);
40
+ if (needsPush) {
41
+ const baseBranch = (await readLastBaseBranch())?.trim();
42
+ if (baseBranch === undefined || baseBranch.length === 0) {
43
+ this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
44
+ exit: 1,
45
+ });
46
+ }
47
+ this.display(() => {
48
+ this.log(`Base branch: ${baseBranch}`);
49
+ this.log('Unpushed commits detected; pushing before done.');
50
+ });
51
+ try {
52
+ pushResult = await performWorkPush({
53
+ baseBranch,
54
+ branch,
55
+ hooks: {
56
+ display: (render) => this.display(render),
57
+ log: (message) => this.log(message),
58
+ withSpinner: (message, action) => this.withSpinner(message, action),
59
+ },
60
+ workItem,
61
+ });
62
+ }
63
+ catch (error) {
64
+ if (error instanceof WorkPushValidationError) {
65
+ this.error(error.message, {
66
+ exit: error.exit,
67
+ suggestions: error.suggestions,
68
+ });
69
+ }
70
+ throw error;
71
+ }
72
+ }
73
+ else {
74
+ this.display(() => {
75
+ this.log('Branch is up to date with origin; skipping push.');
76
+ });
77
+ }
78
+ const doneBody = { workItem };
79
+ const done = await this.withSpinner('Marking work item done', () => CicdGateway.call({
80
+ body: doneBody,
81
+ endpoint: workDoneEndpoint(),
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ },
85
+ method: 'POST',
86
+ }));
87
+ this.display(() => {
88
+ this.log(done.message);
89
+ });
90
+ return {
91
+ branch,
92
+ done,
93
+ push: pushResult,
94
+ pushed: needsPush,
95
+ workItem,
96
+ };
97
+ }
98
+ }
99
+ //# sourceMappingURL=done.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"done.js","sourceRoot":"","sources":["../../../src/commands/work/done.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { type WorkPushResult } from '../../lib/work-types.js';
3
+ export default class WorkPush extends AgentiaCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ run(): Promise<WorkPushResult>;
7
+ }
@@ -0,0 +1,67 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { readLastBaseBranch } from '../../lib/defaults-store.js';
3
+ import { getCurrentBranch, listPendingChanges } from '../../lib/git-worktree.js';
4
+ import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
5
+ import { extractUserStoryFromFeatureBranch } from '../../lib/work-types.js';
6
+ export default class WorkPush extends AgentiaCommand {
7
+ static description = [
8
+ 'Push the current feature/US-XXXX branch and register commits with Copado.',
9
+ 'Requires a clean working tree and a base branch from `agentia work set`.',
10
+ 'Locks the tip commit, runs `git push origin <branch>`, then calls headless work/push.',
11
+ ].join(' ');
12
+ static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
13
+ async run() {
14
+ await this.parse(WorkPush);
15
+ const branch = await getCurrentBranch();
16
+ const workItem = extractUserStoryFromFeatureBranch(branch);
17
+ if (workItem === undefined) {
18
+ this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
19
+ exit: 1,
20
+ suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
21
+ });
22
+ }
23
+ const pendingFiles = await listPendingChanges();
24
+ if (pendingFiles.length > 0) {
25
+ this.error([
26
+ 'Uncommitted tracked changes present. Commit them, then try again:',
27
+ ...pendingFiles.map((file) => ` ${file}`),
28
+ ].join('\n'), {
29
+ exit: 2,
30
+ suggestions: ['git add -A && git commit', `${this.config.bin} work push`],
31
+ });
32
+ }
33
+ const baseBranch = (await readLastBaseBranch())?.trim();
34
+ if (baseBranch === undefined || baseBranch.length === 0) {
35
+ this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
36
+ exit: 1,
37
+ });
38
+ }
39
+ this.display(() => {
40
+ this.log(`Branch: ${branch}`);
41
+ this.log(`Work item: ${workItem}`);
42
+ this.log(`Base branch: ${baseBranch}`);
43
+ });
44
+ try {
45
+ return await performWorkPush({
46
+ baseBranch,
47
+ branch,
48
+ hooks: {
49
+ display: (render) => this.display(render),
50
+ log: (message) => this.log(message),
51
+ withSpinner: (message, action) => this.withSpinner(message, action),
52
+ },
53
+ workItem,
54
+ });
55
+ }
56
+ catch (error) {
57
+ if (error instanceof WorkPushValidationError) {
58
+ this.error(error.message, {
59
+ exit: error.exit,
60
+ suggestions: error.suggestions,
61
+ });
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/commands/work/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { type WorkSetResult } from '../../lib/work-types.js';
3
+ export default class WorkSet extends AgentiaCommand {
4
+ static args: {
5
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ 'base-branch': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<WorkSetResult>;
13
+ }
@@ -0,0 +1,98 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
3
+ import { CicdGateway } from '../../lib/cicd-gateway.js';
4
+ import { writeLastWorkSelection } from '../../lib/defaults-store.js';
5
+ import { checkoutFeatureBranch, fetchOriginBranch, listPendingChanges } from '../../lib/git-worktree.js';
6
+ import { featureBranchName, workSetEndpoint, } from '../../lib/work-types.js';
7
+ export default class WorkSet extends AgentiaCommand {
8
+ static args = {
9
+ id: Args.string({
10
+ description: 'User story ID or name',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = [
15
+ 'Set the active work item and prepare a local feature branch.',
16
+ 'Calls the CICD gateway work/set endpoint, requires a clean tracked working tree,',
17
+ 'fetches the work item base branch from origin, then creates or checks out',
18
+ 'feature/<user-story-name>.',
19
+ ].join(' ');
20
+ static examples = [
21
+ '<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU',
22
+ '<%= config.bin %> <%= command.id %> US-0001 --json',
23
+ '<%= config.bin %> <%= command.id %> US-0001 --base-branch develop',
24
+ ];
25
+ static flags = {
26
+ 'base-branch': Flags.string({
27
+ description: 'Override the base branch returned by the work/set endpoint',
28
+ }),
29
+ };
30
+ async run() {
31
+ const { args, flags } = await this.parse(WorkSet);
32
+ const setResponse = await this.withSpinner('Setting work item', () => CicdGateway.call({
33
+ body: { workItem: args.id },
34
+ endpoint: workSetEndpoint(),
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ },
38
+ method: 'POST',
39
+ }));
40
+ this.display(() => {
41
+ this.log(setResponse.message);
42
+ });
43
+ const overrideBaseBranch = flags['base-branch']?.trim();
44
+ const baseBranch = (overrideBaseBranch !== undefined && overrideBaseBranch.length > 0
45
+ ? overrideBaseBranch
46
+ : setResponse.baseBranch?.trim());
47
+ if (baseBranch === undefined || baseBranch.length === 0) {
48
+ this.error('Work set response did not include a base branch.', { exit: 1 });
49
+ }
50
+ const userStoryName = setResponse.workItem.name?.trim();
51
+ if (userStoryName === undefined || userStoryName.length === 0) {
52
+ this.error('Work set response did not include a user story name for the feature branch.', { exit: 1 });
53
+ }
54
+ const branch = featureBranchName(userStoryName);
55
+ const pendingFiles = await listPendingChanges();
56
+ if (pendingFiles.length > 0) {
57
+ this.error([
58
+ 'Pending tracked changes present. Stash or commit them, then try again:',
59
+ ...pendingFiles.map((file) => ` ${file}`),
60
+ ].join('\n'), {
61
+ exit: 2,
62
+ suggestions: [`git stash push && ${this.config.bin} work set ${args.id}`],
63
+ });
64
+ }
65
+ this.display(() => {
66
+ this.log('Working tree is clean.');
67
+ });
68
+ await this.withSpinner(`Fetching origin/${baseBranch}`, () => fetchOriginBranch(baseBranch));
69
+ this.display(() => {
70
+ this.log(`Fetched origin/${baseBranch}.`);
71
+ });
72
+ const checkout = await this.withSpinner(`Checking out ${branch}`, () => checkoutFeatureBranch(branch, baseBranch));
73
+ this.display(() => {
74
+ if (checkout.created) {
75
+ this.log(`Created and checked out ${checkout.branch} from ${baseBranch}.`);
76
+ }
77
+ else {
78
+ this.log(`Checked out existing branch ${checkout.branch}.`);
79
+ }
80
+ });
81
+ await writeLastWorkSelection({
82
+ lastBaseBranch: baseBranch,
83
+ lastWorkItem: userStoryName,
84
+ });
85
+ return {
86
+ baseBranch,
87
+ command: setResponse.command,
88
+ git: {
89
+ branch: checkout.branch,
90
+ branchCreated: checkout.created,
91
+ fetched: true,
92
+ },
93
+ message: setResponse.message,
94
+ workItem: setResponse.workItem,
95
+ };
96
+ }
97
+ }
98
+ //# sourceMappingURL=set.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/commands/work/set.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { type WorkSubmitResult } from '../../lib/work-types.js';
3
+ export default class WorkSubmit extends AgentiaCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ run(): Promise<WorkSubmitResult>;
7
+ }
@@ -0,0 +1,99 @@
1
+ import { AgentiaCommand } from '../../lib/agentia-command.js';
2
+ import { CicdGateway } from '../../lib/cicd-gateway.js';
3
+ import { readLastBaseBranch } from '../../lib/defaults-store.js';
4
+ import { getCurrentBranch, hasUnpushedCommits, listPendingChanges } from '../../lib/git-worktree.js';
5
+ import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
6
+ import { extractUserStoryFromFeatureBranch, workSubmitEndpoint, } from '../../lib/work-types.js';
7
+ export default class WorkSubmit extends AgentiaCommand {
8
+ static description = [
9
+ 'Submit the current feature/US-XXXX work item for pipeline quality gates and validation.',
10
+ 'Pushes and registers commits when the branch is ahead of origin, then calls headless work/submit',
11
+ '(Promote Action with validate=true).',
12
+ ].join(' ');
13
+ static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
14
+ async run() {
15
+ await this.parse(WorkSubmit);
16
+ const branch = await getCurrentBranch();
17
+ const workItem = extractUserStoryFromFeatureBranch(branch);
18
+ if (workItem === undefined) {
19
+ this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
20
+ exit: 1,
21
+ suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
22
+ });
23
+ }
24
+ const pendingFiles = await listPendingChanges();
25
+ if (pendingFiles.length > 0) {
26
+ this.error([
27
+ 'Uncommitted tracked changes present. Commit them, then try again:',
28
+ ...pendingFiles.map((file) => ` ${file}`),
29
+ ].join('\n'), {
30
+ exit: 2,
31
+ suggestions: ['git add -A && git commit', `${this.config.bin} work submit`],
32
+ });
33
+ }
34
+ this.display(() => {
35
+ this.log(`Branch: ${branch}`);
36
+ this.log(`Work item: ${workItem}`);
37
+ });
38
+ let pushResult;
39
+ const needsPush = await hasUnpushedCommits(branch);
40
+ if (needsPush) {
41
+ const baseBranch = (await readLastBaseBranch())?.trim();
42
+ if (baseBranch === undefined || baseBranch.length === 0) {
43
+ this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
44
+ exit: 1,
45
+ });
46
+ }
47
+ this.display(() => {
48
+ this.log(`Base branch: ${baseBranch}`);
49
+ this.log('Unpushed commits detected; pushing before submit.');
50
+ });
51
+ try {
52
+ pushResult = await performWorkPush({
53
+ baseBranch,
54
+ branch,
55
+ hooks: {
56
+ display: (render) => this.display(render),
57
+ log: (message) => this.log(message),
58
+ withSpinner: (message, action) => this.withSpinner(message, action),
59
+ },
60
+ workItem,
61
+ });
62
+ }
63
+ catch (error) {
64
+ if (error instanceof WorkPushValidationError) {
65
+ this.error(error.message, {
66
+ exit: error.exit,
67
+ suggestions: error.suggestions,
68
+ });
69
+ }
70
+ throw error;
71
+ }
72
+ }
73
+ else {
74
+ this.display(() => {
75
+ this.log('Branch is up to date with origin; skipping push.');
76
+ });
77
+ }
78
+ const submitBody = { workItem };
79
+ const submit = await this.withSpinner('Submitting work item', () => CicdGateway.call({
80
+ body: submitBody,
81
+ endpoint: workSubmitEndpoint(),
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ },
85
+ method: 'POST',
86
+ }));
87
+ this.display(() => {
88
+ this.log(submit.message);
89
+ });
90
+ return {
91
+ branch,
92
+ push: pushResult,
93
+ pushed: needsPush,
94
+ submit,
95
+ workItem,
96
+ };
97
+ }
98
+ }
99
+ //# sourceMappingURL=submit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submit.js","sourceRoot":"","sources":["../../../src/commands/work/submit.ts"],"names":[],"mappings":"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"}
@@ -44,13 +44,20 @@ export type CicdEndpointOptions = {
44
44
  region?: CicdRegion;
45
45
  };
46
46
  export type HttpRequestFn = (url: URL, method: string, headers: Record<string, string>, body: unknown) => Promise<string>;
47
+ export type CicdGatewayErrorRequest = {
48
+ method?: string;
49
+ url?: string;
50
+ };
47
51
  export declare class CicdGatewayError extends Error {
48
52
  readonly categories: string[];
49
53
  readonly code?: string;
54
+ readonly requestMethod?: string;
55
+ readonly requestUrl?: string;
50
56
  readonly statusCode?: number;
51
57
  constructor(message: string, options?: {
52
58
  categories?: string[];
53
59
  code?: string;
60
+ request?: CicdGatewayErrorRequest;
54
61
  statusCode?: number;
55
62
  });
56
63
  }
@@ -58,7 +65,8 @@ export declare function gatewayUserAgent(): string;
58
65
  export declare function createTransactionId(): string;
59
66
  export declare function normalizeCicdBaseUrl(value: string): string;
60
67
  export declare function sanitizeCicdErrorMessage(message: string): string;
61
- export declare function formatCicdGatewayError(status: number, raw: string): Error;
68
+ export declare function maskUrlSecrets(url: string | URL): string;
69
+ export declare function formatCicdGatewayError(status: number, raw: string, request?: CicdGatewayErrorRequest): Error;
62
70
  export declare function cicdApiUrl(serverUrl: string, apiKey: string): URL;
63
71
  export declare function maskedCicdApiUrl(serverUrl: string): string;
64
72
  export declare function discoverCicdBaseUrl(apiKey: string): Promise<string>;
@@ -15,12 +15,19 @@ let activeHttpRequestOptions = {};
15
15
  export class CicdGatewayError extends Error {
16
16
  categories;
17
17
  code;
18
+ requestMethod;
19
+ requestUrl;
18
20
  statusCode;
19
21
  constructor(message, options = {}) {
20
22
  super(message);
21
23
  this.name = 'CicdGatewayError';
22
24
  this.categories = options.categories ?? [];
23
25
  this.code = options.code;
26
+ this.requestMethod = options.request?.method;
27
+ this.requestUrl =
28
+ options.request?.url !== undefined && options.request.url.trim().length > 0
29
+ ? maskUrlSecrets(options.request.url)
30
+ : undefined;
24
31
  this.statusCode = options.statusCode;
25
32
  }
26
33
  }
@@ -77,15 +84,44 @@ export function sanitizeCicdErrorMessage(message) {
77
84
  }
78
85
  return message.trim();
79
86
  }
80
- export function formatCicdGatewayError(status, raw) {
87
+ export function maskUrlSecrets(url) {
88
+ let parsed;
89
+ try {
90
+ parsed = typeof url === 'string' ? new URL(url) : new URL(url.toString());
91
+ }
92
+ catch {
93
+ return typeof url === 'string' ? url : url.toString();
94
+ }
95
+ if (parsed.searchParams.has('api_key')) {
96
+ parsed.searchParams.set('api_key', '<masked>');
97
+ }
98
+ return parsed.toString();
99
+ }
100
+ function formatRequestSuffix(request) {
101
+ if (request?.url === undefined || request.url.trim().length === 0) {
102
+ return '';
103
+ }
104
+ const url = maskUrlSecrets(request.url);
105
+ const method = request.method !== undefined && request.method.trim().length > 0 ? `${request.method} ` : '';
106
+ return ` Request: ${method}${url}`;
107
+ }
108
+ function withRequestSuffix(message, request) {
109
+ const suffix = formatRequestSuffix(request);
110
+ if (suffix.length === 0) {
111
+ return message;
112
+ }
113
+ return message.endsWith('.') ? `${message}${suffix}` : `${message}.${suffix}`;
114
+ }
115
+ export function formatCicdGatewayError(status, raw, request) {
81
116
  try {
82
117
  const parsed = JSON.parse(raw);
83
118
  const detailMessage = extractCicdErrorDetailMessage(parsed.data?.details);
84
119
  const message = detailMessage ?? parsed.message;
85
120
  if (message !== undefined && message.trim().length > 0) {
86
- return new CicdGatewayError(sanitizeCicdErrorMessage(message), {
121
+ return new CicdGatewayError(withRequestSuffix(sanitizeCicdErrorMessage(message), request), {
87
122
  categories: extractCicdErrorCategories(parsed.data?.details),
88
123
  code: parsed.data?.code,
124
+ request,
89
125
  statusCode: status,
90
126
  });
91
127
  }
@@ -93,20 +129,25 @@ export function formatCicdGatewayError(status, raw) {
93
129
  catch {
94
130
  // Fall through to the generic error below.
95
131
  }
96
- return new CicdGatewayError(`CICD Gateway request failed (${status}).`, { statusCode: status });
132
+ return new CicdGatewayError(withRequestSuffix(`CICD Gateway request failed (${status}).`, request), {
133
+ request,
134
+ statusCode: status,
135
+ });
97
136
  }
98
- function createCicdGatewayError(parsed) {
137
+ function createCicdGatewayError(parsed, request) {
99
138
  const detailMessage = extractCicdErrorDetailMessage(parsed.data?.details);
100
139
  const message = detailMessage ?? parsed.message;
101
140
  if (message !== undefined && message.trim().length > 0) {
102
- return new CicdGatewayError(sanitizeCicdErrorMessage(message), {
141
+ return new CicdGatewayError(withRequestSuffix(sanitizeCicdErrorMessage(message), request), {
103
142
  categories: extractCicdErrorCategories(parsed.data?.details),
104
143
  code: parsed.data?.code,
144
+ request,
105
145
  });
106
146
  }
107
- return new CicdGatewayError('CICD Gateway request failed.', {
147
+ return new CicdGatewayError(withRequestSuffix('CICD Gateway request failed.', request), {
108
148
  categories: extractCicdErrorCategories(parsed.data?.details),
109
149
  code: parsed.data?.code,
150
+ request,
110
151
  });
111
152
  }
112
153
  function buildOuterHeaders(transactionId) {
@@ -229,6 +270,7 @@ function unwrapResponse(parsed) {
229
270
  return data;
230
271
  }
231
272
  async function executeRequest(credentials, request, onDebugRequest, options = {}) {
273
+ const errorRequest = buildErrorRequestInfo(credentials, request, options);
232
274
  const raw = await sendRequest(credentials, request, onDebugRequest, options);
233
275
  if (raw.trim().length === 0) {
234
276
  return undefined;
@@ -237,7 +279,7 @@ async function executeRequest(credentials, request, onDebugRequest, options = {}
237
279
  try {
238
280
  const possibleError = JSON.parse(raw);
239
281
  if (possibleError.status === 'error') {
240
- throw createCicdGatewayError(possibleError);
282
+ throw createCicdGatewayError(possibleError, errorRequest);
241
283
  }
242
284
  }
243
285
  catch (error) {
@@ -255,16 +297,32 @@ async function executeRequest(credentials, request, onDebugRequest, options = {}
255
297
  throw new Error(`CICD Gateway returned non-JSON response: ${raw}`);
256
298
  }
257
299
  if (parsed.status === 'error') {
258
- throw createCicdGatewayError(parsed);
300
+ throw createCicdGatewayError(parsed, errorRequest);
259
301
  }
260
302
  return unwrapResponse(parsed);
261
303
  }
304
+ function buildErrorRequestInfo(credentials, request, options = {}) {
305
+ if (!options.forceRemote && readLocalGatewayConfig().enabled) {
306
+ return {
307
+ method: request.method,
308
+ url: maskUrlSecrets(new URL(request.endpoint, `${credentials.baseUrl}/`)),
309
+ };
310
+ }
311
+ return {
312
+ method: request.method,
313
+ url: `${request.endpoint} via ${maskedCicdApiUrl(credentials.baseUrl)}`,
314
+ };
315
+ }
262
316
  async function sendRemoteRequest(credentials, request, onDebugRequest) {
263
317
  const endpoint = cicdApiUrl(credentials.baseUrl, credentials.apiKey);
264
318
  const transactionId = createTransactionId();
265
319
  const outerHeaders = buildOuterHeaders(transactionId);
266
320
  const gatewayRequest = { ...request };
267
321
  delete gatewayRequest.responseType;
322
+ const errorRequest = {
323
+ method: request.method,
324
+ url: `${request.endpoint} via ${maskedCicdApiUrl(credentials.baseUrl)}`,
325
+ };
268
326
  if (onDebugRequest) {
269
327
  onDebugRequest({
270
328
  body: gatewayRequest,
@@ -277,7 +335,12 @@ async function sendRemoteRequest(credentials, request, onDebugRequest) {
277
335
  url: maskedCicdApiUrl(credentials.baseUrl),
278
336
  });
279
337
  }
280
- return httpRequestFn(endpoint, 'POST', outerHeaders, gatewayRequest);
338
+ try {
339
+ return await httpRequestFn(endpoint, 'POST', outerHeaders, gatewayRequest);
340
+ }
341
+ catch (error) {
342
+ throw enrichCicdGatewayError(error, errorRequest);
343
+ }
281
344
  }
282
345
  async function sendLocalRequest(credentials, request, onDebugRequest) {
283
346
  const localConfig = readLocalGatewayConfig();
@@ -286,6 +349,10 @@ async function sendLocalRequest(credentials, request, onDebugRequest) {
286
349
  const headers = buildLocalGatewayHeaders(credentials.apiKey, sfContext, request.headers, transactionId);
287
350
  const url = new URL(request.endpoint, `${credentials.baseUrl}/`);
288
351
  const { body } = request;
352
+ const errorRequest = {
353
+ method: request.method,
354
+ url: maskUrlSecrets(url),
355
+ };
289
356
  if (onDebugRequest) {
290
357
  onDebugRequest({
291
358
  body: body ?? null,
@@ -304,10 +371,25 @@ async function sendLocalRequest(credentials, request, onDebugRequest) {
304
371
  try {
305
372
  return await httpRequestFn(url, request.method, headers, body);
306
373
  }
374
+ catch (error) {
375
+ throw enrichCicdGatewayError(error, errorRequest);
376
+ }
307
377
  finally {
308
378
  activeHttpRequestOptions = previousOptions;
309
379
  }
310
380
  }
381
+ function enrichCicdGatewayError(error, request) {
382
+ if (!(error instanceof CicdGatewayError)) {
383
+ return error;
384
+ }
385
+ const baseMessage = error.message.replace(/\.\s*Request: .+$/u, '.');
386
+ return new CicdGatewayError(withRequestSuffix(baseMessage, request), {
387
+ categories: error.categories,
388
+ code: error.code,
389
+ request,
390
+ statusCode: error.statusCode,
391
+ });
392
+ }
311
393
  async function sendRequest(credentials, request, onDebugRequest, options = {}) {
312
394
  if (!options.forceRemote && readLocalGatewayConfig().enabled) {
313
395
  return sendLocalRequest(credentials, request, onDebugRequest);