@atlassian-dc-mcp/common 0.17.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/build/__tests__/describe-error.test.d.ts +2 -0
  3. package/build/__tests__/describe-error.test.d.ts.map +1 -0
  4. package/build/__tests__/describe-error.test.js +54 -0
  5. package/build/__tests__/describe-error.test.js.map +1 -0
  6. package/build/__tests__/setup-cli.test.js +151 -44
  7. package/build/__tests__/setup-cli.test.js.map +1 -1
  8. package/build/config/source.d.ts +1 -0
  9. package/build/config/source.d.ts.map +1 -1
  10. package/build/config/source.js.map +1 -1
  11. package/build/index.d.ts +1 -0
  12. package/build/index.d.ts.map +1 -1
  13. package/build/index.js +1 -0
  14. package/build/index.js.map +1 -1
  15. package/build/setup/describe-error.d.ts +2 -0
  16. package/build/setup/describe-error.d.ts.map +1 -0
  17. package/build/setup/describe-error.js +50 -0
  18. package/build/setup/describe-error.js.map +1 -0
  19. package/build/setup/value-validator.d.ts +8 -0
  20. package/build/setup/value-validator.d.ts.map +1 -0
  21. package/build/setup/value-validator.js +57 -0
  22. package/build/setup/value-validator.js.map +1 -0
  23. package/build/setup-cli.d.ts +15 -0
  24. package/build/setup-cli.d.ts.map +1 -1
  25. package/build/setup-cli.js +140 -27
  26. package/build/setup-cli.js.map +1 -1
  27. package/jest.config.js +1 -0
  28. package/package.json +2 -2
  29. package/src/__tests__/describe-error.test.ts +70 -0
  30. package/src/__tests__/setup-cli.test.ts +181 -38
  31. package/src/config/source.ts +1 -0
  32. package/src/index.ts +1 -0
  33. package/src/setup/describe-error.ts +67 -0
  34. package/src/setup/value-validator.ts +59 -0
  35. package/src/setup-cli.ts +191 -30
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,9 @@ import { buildDefaultRegistry } from './config/registry.js';
3
3
  import { getProductRuntimeConfig } from './config/runtime-config.js';
4
4
  import { HomeFileSource, getHomeFilePath } from './config/sources/home-file.js';
5
5
  import { MacosKeychainSource } from './config/sources/macos-keychain.js';
6
+ import { SetupValueValidator } from './setup/value-validator.js';
6
7
  const FALLBACK_PAGE_SIZE = 25;
8
+ const MAX_VALIDATION_ATTEMPTS = 3;
7
9
  const DEFAULT_PROMPTS = {
8
10
  input: (opts) => inquirerInput(opts),
9
11
  password: (opts) => inquirerPassword(opts),
@@ -19,22 +21,92 @@ export async function runSetup(product, deps = {}) {
19
21
  log('');
20
22
  const current = getProductRuntimeConfig(product);
21
23
  printCurrent(log, registry, product, current);
22
- let answers;
23
- try {
24
- answers = await promptForValues(prompts, product, current);
25
- }
26
- catch (error) {
27
- if (isUserCancel(error)) {
28
- exit(130);
29
- return;
30
- }
31
- throw error;
24
+ const answers = await collectAnswersWithValidation(product, deps, prompts, current, log, exit);
25
+ if (!answers) {
26
+ return;
32
27
  }
33
28
  const homeFile = requireHomeFile(registry);
34
29
  writeNonSecretFields(registry, product, answers, homeFile, log);
35
- const tokenWriter = await writeToken(registry, product, answers.token, homeFile, log, prompts);
30
+ const tokenWriter = await writeToken(registry, product, answers.tokenToWrite, homeFile, log, prompts);
36
31
  printSummary(log, product, answers, tokenWriter);
37
32
  }
33
+ async function collectAnswersWithValidation(product, deps, prompts, current, log, exit) {
34
+ let defaults = current;
35
+ for (let attempt = 1;; attempt++) {
36
+ let answers;
37
+ try {
38
+ answers = await promptForValues(prompts, product, defaults);
39
+ }
40
+ catch (error) {
41
+ if (isUserCancel(error)) {
42
+ exit(130);
43
+ return undefined;
44
+ }
45
+ throw error;
46
+ }
47
+ const answerErrors = validateAnswers(product, answers);
48
+ if (answerErrors.length > 0) {
49
+ for (const message of answerErrors) {
50
+ log(`Validation failed: ${message}`);
51
+ }
52
+ const retry = await confirmRetry(prompts, 'Try again?');
53
+ if (retry) {
54
+ defaults = answersAsDefaults(answers);
55
+ continue;
56
+ }
57
+ exit(1);
58
+ return undefined;
59
+ }
60
+ if (!deps.validateCredentials || !answers.tokenForValidation) {
61
+ return answers;
62
+ }
63
+ const result = await deps.validateCredentials({
64
+ host: answers.host,
65
+ apiBasePath: answers.apiBasePath,
66
+ token: answers.tokenForValidation,
67
+ });
68
+ if (result.ok) {
69
+ log(result.detail ? `Validation succeeded: ${result.detail}` : 'Validation succeeded.');
70
+ return answers;
71
+ }
72
+ log(`Validation failed: ${result.message}`);
73
+ const outcome = await offerRetryAfterFailure(prompts, attempt);
74
+ if (outcome === 'retry') {
75
+ defaults = answersAsDefaults(answers);
76
+ continue;
77
+ }
78
+ if (outcome === 'save-anyway') {
79
+ return answers;
80
+ }
81
+ exit(1);
82
+ return undefined;
83
+ }
84
+ }
85
+ function answersAsDefaults(answers) {
86
+ const pageSize = Number.parseInt(answers.defaultPageSize, 10);
87
+ return {
88
+ host: answers.host,
89
+ apiBasePath: answers.apiBasePath,
90
+ token: answers.tokenForValidation,
91
+ defaultPageSize: Number.isFinite(pageSize) && pageSize > 0 ? pageSize : undefined,
92
+ };
93
+ }
94
+ async function confirmRetry(prompts, message) {
95
+ return prompts.confirm({ message, default: true });
96
+ }
97
+ async function offerRetryAfterFailure(prompts, attempt) {
98
+ if (attempt < MAX_VALIDATION_ATTEMPTS) {
99
+ const retry = await confirmRetry(prompts, 'Try again with different values?');
100
+ if (retry) {
101
+ return 'retry';
102
+ }
103
+ }
104
+ const saveAnyway = await prompts.confirm({
105
+ message: 'Save configuration anyway?',
106
+ default: false,
107
+ });
108
+ return saveAnyway ? 'save-anyway' : 'abort';
109
+ }
38
110
  function requireHomeFile(registry) {
39
111
  const homeFile = registry.getWritableSource((s) => s instanceof HomeFileSource);
40
112
  if (!homeFile) {
@@ -53,36 +125,74 @@ function printCurrent(log, registry, product, current) {
53
125
  }
54
126
  log('');
55
127
  }
56
- async function promptForValues(prompts, product, current) {
128
+ async function promptForValues(prompts, product, defaults) {
57
129
  const host = await prompts.input({
58
130
  message: 'Host (e.g. jira.example.com):',
59
- default: current.host ?? '',
131
+ default: defaults.host ?? '',
132
+ validate: SetupValueValidator.host,
60
133
  });
61
134
  const apiBasePath = await prompts.input({
62
135
  message: 'API base path:',
63
- default: current.apiBasePath ?? product.defaultApiBasePath ?? '',
136
+ default: defaults.apiBasePath ?? product.defaultApiBasePath ?? '',
137
+ validate: SetupValueValidator.apiBasePath,
64
138
  });
65
139
  const defaultPageSize = await prompts.input({
66
140
  message: 'Default page size:',
67
- default: String(current.defaultPageSize ?? FALLBACK_PAGE_SIZE),
68
- validate: (raw) => /^\d+$/.test(raw.trim()) && Number.parseInt(raw.trim(), 10) > 0
69
- ? true
70
- : 'Enter a positive integer',
141
+ default: String(defaults.defaultPageSize ?? FALLBACK_PAGE_SIZE),
142
+ validate: SetupValueValidator.pageSize,
71
143
  });
72
- const token = await promptForToken(prompts, current.token);
73
- return { host: host.trim(), apiBasePath: apiBasePath.trim(), defaultPageSize: defaultPageSize.trim(), token };
144
+ const token = await promptForToken(prompts, defaults.token);
145
+ return {
146
+ host: host.trim(),
147
+ apiBasePath: apiBasePath.trim(),
148
+ defaultPageSize: defaultPageSize.trim(),
149
+ ...token,
150
+ };
74
151
  }
75
152
  async function promptForToken(prompts, existing) {
76
- const entered = await prompts.password({ message: 'API token:', mask: '*' });
153
+ const entered = await prompts.password({
154
+ message: 'API token:',
155
+ mask: '*',
156
+ validate: SetupValueValidator.token,
157
+ });
77
158
  const trimmed = entered.trim();
78
159
  if (trimmed.length > 0) {
79
- return trimmed;
160
+ return { tokenToWrite: trimmed, tokenForValidation: trimmed };
80
161
  }
81
162
  if (!existing) {
82
- return undefined;
163
+ return { tokenToWrite: undefined, tokenForValidation: undefined };
164
+ }
165
+ const keepExisting = await prompts.confirm({ message: 'Keep existing token?', default: true });
166
+ return {
167
+ tokenToWrite: undefined,
168
+ tokenForValidation: keepExisting ? existing : undefined,
169
+ };
170
+ }
171
+ function validateAnswers(product, answers) {
172
+ const errors = [];
173
+ for (const [label, value, validator] of [
174
+ ['host', answers.host, SetupValueValidator.host],
175
+ ['API base path', answers.apiBasePath, SetupValueValidator.apiBasePath],
176
+ ['API token', answers.tokenForValidation ?? '', SetupValueValidator.token],
177
+ ]) {
178
+ const result = validator(value);
179
+ if (result !== true) {
180
+ errors.push(`${label}: ${result}`);
181
+ }
83
182
  }
84
- await prompts.confirm({ message: 'Keep existing token?', default: true });
85
- return undefined;
183
+ const pageSize = SetupValueValidator.pageSize(answers.defaultPageSize);
184
+ if (pageSize !== true) {
185
+ errors.push(`default page size: ${pageSize}`);
186
+ }
187
+ if (!answers.tokenForValidation) {
188
+ errors.push(`API token is required (${product.envVars.token}).`);
189
+ }
190
+ const hasHost = answers.host.length > 0;
191
+ const hasFullApiBasePath = /^https?:\/\//i.test(answers.apiBasePath);
192
+ if (!hasHost && !hasFullApiBasePath) {
193
+ errors.push(`Enter ${product.envVars.host}, or enter a full URL for ${product.envVars.apiBasePath}.`);
194
+ }
195
+ return errors;
86
196
  }
87
197
  function writeNonSecretFields(registry, product, answers, homeFile, log) {
88
198
  for (const key of ['host', 'apiBasePath', 'defaultPageSize']) {
@@ -131,7 +241,10 @@ async function tryWrite(writer, product, token, log, prompts) {
131
241
  message: 'Fall back to plaintext home file with mode 0600?',
132
242
  default: false,
133
243
  });
134
- return !fallback;
244
+ if (fallback) {
245
+ return false;
246
+ }
247
+ throw new Error('Token was not saved because keychain write failed and plaintext fallback was declined');
135
248
  }
136
249
  return false;
137
250
  }
@@ -164,7 +277,7 @@ function printSummary(log, product, answers, tokenWriter) {
164
277
  log(` apiBasePath: ${answers.apiBasePath || '(unchanged)'}`);
165
278
  log(` defaultPageSize: ${answers.defaultPageSize || '(unchanged)'}`);
166
279
  if (tokenWriter) {
167
- log(` token: ${maskToken(answers.token)} (stored in ${describeWriter(tokenWriter, product)})`);
280
+ log(` token: ${maskToken(answers.tokenToWrite)} (stored in ${describeWriter(tokenWriter, product)})`);
168
281
  }
169
282
  else {
170
283
  log(' token: (unchanged)');
@@ -1 +1 @@
1
- {"version":3,"file":"setup-cli.js","sourceRoot":"","sources":["../src/setup-cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"setup-cli.js","sourceRoot":"","sources":["../src/setup-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,KAAK,IAAI,aAAa,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAE,oBAAoB,EAAuB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAMrE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,uBAAuB,GAAG,CAAC,CAAC;AA+ClC,MAAM,eAAe,GAAiB;IACpC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAW,CAAC;IAC3C,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAW,CAAC;IACjD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAW,CAAC;CAChD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAA0B,EAAE,OAAkB,EAAE;IAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,oBAAoB,EAAE,CAAC;IACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC;IAEhD,QAAQ,CAAC,UAAU,EAAE,CAAC;IAEtB,GAAG,CAAC,4BAA4B,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,GAAG,CAAC,EAAE,CAAC,CAAC;IAER,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjD,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAE9C,MAAM,OAAO,GAAG,MAAM,4BAA4B,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC/F,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACtG,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,OAA0B,EAC1B,IAAe,EACf,OAAqB,EACrB,OAAmD,EACnD,GAA8B,EAC9B,IAA4B;IAE5B,IAAI,QAAQ,GAAmB,OAAO,CAAC;IAEvC,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,OAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,CAAC;gBACV,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;gBACnC,GAAG,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACxD,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBACtC,SAAS;YACX,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,CAAC;YACR,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;YAC7D,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC;YAC5C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,KAAK,EAAE,OAAO,CAAC,kBAAkB;SAClC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,yBAAyB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC;YACxF,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,GAAG,CAAC,sBAAsB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YACxB,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;YAC9B,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,CAAC,CAAC,CAAC;QACR,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAqB;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAC9D,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK,EAAE,OAAO,CAAC,kBAAkB;QACjC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KAClF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAAqB,EAAE,OAAe;IAChE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,OAAqB,EACrB,OAAe;IAEf,IAAI,OAAO,GAAG,uBAAuB,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;QAC9E,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;QACvC,OAAO,EAAE,4BAA4B;QACrC,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IACH,OAAO,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,QAAwB;IAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,iBAAiB,CACzC,CAAC,CAAC,EAAuB,EAAE,CAAC,CAAC,YAAY,cAAc,CACxD,CAAC;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CACnB,GAA8B,EAC9B,QAAwB,EACxB,OAA0B,EAC1B,OAAmD;IAEnD,MAAM,IAAI,GAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;IAC9E,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,YAAY,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QACzF,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAClF,GAAG,CAAC,WAAW,KAAK,KAAK,YAAY,KAAK,MAAM,GAAG,CAAC,CAAC;IACvD,CAAC;IACD,GAAG,CAAC,EAAE,CAAC,CAAC;AACV,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,OAAqB,EACrB,OAA0B,EAC1B,QAAwB;IAExB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;QAC/B,OAAO,EAAE,+BAA+B;QACxC,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE;QAC5B,QAAQ,EAAE,mBAAmB,CAAC,IAAI;KACnC,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;QACtC,OAAO,EAAE,gBAAgB;QACzB,OAAO,EAAE,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,kBAAkB,IAAI,EAAE;QACjE,QAAQ,EAAE,mBAAmB,CAAC,WAAW;KAC1C,CAAC,CAAC;IACH,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;QAC1C,OAAO,EAAE,oBAAoB;QAC7B,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,eAAe,IAAI,kBAAkB,CAAC;QAC/D,QAAQ,EAAE,mBAAmB,CAAC,QAAQ;KACvC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QACjB,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE;QAC/B,eAAe,EAAE,eAAe,CAAC,IAAI,EAAE;QACvC,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,OAAqB,EACrB,QAA4B;IAE5B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC;QACrC,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,mBAAmB,CAAC,KAAK;KACpC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC;IACpE,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,OAAO;QACL,YAAY,EAAE,SAAS;QACvB,kBAAkB,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACxD,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,OAA0B,EAAE,OAAqB;IACxE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI;QACtC,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,IAAI,CAAC;QAChD,CAAC,eAAe,EAAE,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC,WAAW,CAAC;QACvE,CAAC,WAAW,EAAE,OAAO,CAAC,kBAAkB,IAAI,EAAE,EAAE,mBAAmB,CAAC,KAAK,CAAC;KAClE,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACvE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,0BAA0B,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACxC,MAAM,kBAAkB,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,IAAI,6BAA6B,OAAO,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC;IACxG,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAAwB,EACxB,OAA0B,EAC1B,OAAqB,EACrB,QAAwB,EACxB,GAA8B;IAE9B,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,iBAAiB,CAAU,EAAE,CAAC;QACtE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,QAAwB,EACxB,OAA0B,EAC1B,KAAyB,EACzB,QAAwB,EACxB,GAA8B,EAC9B,OAAqB;IAErB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,iBAAiB,CACzC,CAAC,CAAC,EAA4B,EAAE,CAAC,CAAC,YAAY,mBAAmB,CAClE,CAAC;IACF,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,IAAI,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE1B,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE/D,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACrE,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,MAAM,YAAY,mBAAmB,EAAE,CAAC;gBAC1C,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAAsB,EACtB,OAA0B,EAC1B,KAAa,EACb,GAA8B,EAC9B,OAAqB;IAErB,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAI,KAAsC,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAChF,GAAG,CAAC,4BAA4B,MAAM,CAAC,QAAQ,EAAE,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAClF,IAAI,MAAM,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,MAAM,YAAY,mBAAmB,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;gBACrC,OAAO,EAAE,kDAAkD;gBAC3D,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;YACH,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACrB,QAAwB,EACxB,OAA0B,EAC1B,GAAc,EACd,MAAsB,EACtB,GAA8B;IAE9B,MAAM,SAAS,GAAG,QAAQ;SACvB,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YACnC,GAAG,CAAC,wBAAwB,MAAM,qEAAqE,MAAM,CAAC,QAAQ,EAAE,kCAAkC,CAAC,CAAC;QAC9J,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,aAAa,MAAM,6DAA6D,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAClJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,QAAwB,EAAE,QAAgB;IAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAC9D,OAAO,KAAK,EAAE,QAAQ,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,YAAY,CACnB,GAA8B,EAC9B,OAA0B,EAC1B,OAAqB,EACrB,WAAuC;IAEvC,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC5B,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE,CAAC,CAAC;IAChD,GAAG,CAAC,kBAAkB,OAAO,CAAC,WAAW,IAAI,aAAa,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,sBAAsB,OAAO,CAAC,eAAe,IAAI,aAAa,EAAE,CAAC,CAAC;IACtE,IAAI,WAAW,EAAE,CAAC;QAChB,GAAG,CAAC,YAAY,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,eAAe,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACzG,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC9B,CAAC;IACD,GAAG,CAAC,cAAc,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,cAAc,CAAC,MAAsB,EAAE,OAA0B;IACxE,IAAI,MAAM,YAAY,mBAAmB,EAAE,CAAC;QAC1C,OAAO,qDAAqD,OAAO,CAAC,EAAE,SAAS,CAAC;IAClF,CAAC;IACD,IAAI,MAAM,YAAY,cAAc,EAAE,CAAC;QACrC,OAAO,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,YAAY,CAAC,GAAc;IAClC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,aAAa;YAChB,OAAO,eAAe,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QACjB,KAAK,iBAAiB;YACpB,OAAO,WAAW,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAkC;IAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,KAAyB;IAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,OAAO,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,KAA4C,CAAC;IACzD,OAAO,GAAG,CAAC,IAAI,KAAK,iBAAiB,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AACnF,CAAC"}
package/jest.config.js CHANGED
@@ -10,6 +10,7 @@ export default {
10
10
  }],
11
11
  },
12
12
  testEnvironment: 'node',
13
+ watchman: false,
13
14
  testMatch: ['**/__tests__/**/*.test.ts'],
14
15
  collectCoverageFrom: [
15
16
  'src/**/*.ts',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlassian-dc-mcp/common",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "main": "build/index.js",
5
5
  "type": "module",
6
6
  "private": false,
@@ -39,5 +39,5 @@
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "eb8dccc2a1ba6eebf65925209f23ed269fec3550"
42
+ "gitHead": "69b3068c96667bf755394cff3de1e40211ed8ea8"
43
43
  }
@@ -0,0 +1,70 @@
1
+ import { describeValidationError } from '../setup/describe-error.js';
2
+
3
+ describe('describeValidationError', () => {
4
+ it('formats HTTP errors with status, URL, and a 401 hint', () => {
5
+ const apiError = {
6
+ name: 'ApiError',
7
+ status: 401,
8
+ statusText: 'Unauthorized',
9
+ url: 'https://jira.example.com/rest/api/2/myself',
10
+ body: {},
11
+ };
12
+ expect(describeValidationError(apiError)).toBe(
13
+ 'https://jira.example.com/rest/api/2/myself returned 401 Unauthorized. Check the host, API base path, and API token.',
14
+ );
15
+ });
16
+
17
+ it('formats non-auth HTTP errors without the auth hint', () => {
18
+ const apiError = {
19
+ name: 'ApiError',
20
+ status: 500,
21
+ statusText: 'Internal Server Error',
22
+ url: 'https://jira.example.com/rest/api/2/myself',
23
+ };
24
+ expect(describeValidationError(apiError)).toBe(
25
+ 'https://jira.example.com/rest/api/2/myself returned 500 Internal Server Error.',
26
+ );
27
+ });
28
+
29
+ it('unwraps Node fetch TypeErrors with ECONNREFUSED via cause', () => {
30
+ const error = Object.assign(new TypeError('fetch failed'), {
31
+ cause: Object.assign(new Error('connect ECONNREFUSED 127.0.0.1:443'), {
32
+ code: 'ECONNREFUSED',
33
+ }),
34
+ });
35
+ expect(describeValidationError(error)).toBe(
36
+ 'connection refused (ECONNREFUSED: connect ECONNREFUSED 127.0.0.1:443)',
37
+ );
38
+ });
39
+
40
+ it('unwraps DNS failures as ENOTFOUND', () => {
41
+ const error = Object.assign(new TypeError('fetch failed'), {
42
+ cause: Object.assign(new Error('getaddrinfo ENOTFOUND jira.nope'), {
43
+ code: 'ENOTFOUND',
44
+ }),
45
+ });
46
+ expect(describeValidationError(error)).toBe(
47
+ 'could not resolve host (ENOTFOUND: getaddrinfo ENOTFOUND jira.nope)',
48
+ );
49
+ });
50
+
51
+ it('recognises self-signed certificate errors', () => {
52
+ const error = Object.assign(new TypeError('fetch failed'), {
53
+ cause: Object.assign(new Error('self-signed certificate'), {
54
+ code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
55
+ }),
56
+ });
57
+ expect(describeValidationError(error)).toBe(
58
+ 'server uses a self-signed TLS certificate (DEPTH_ZERO_SELF_SIGNED_CERT: self-signed certificate)',
59
+ );
60
+ });
61
+
62
+ it('describes aborted requests', () => {
63
+ const error = Object.assign(new Error('aborted'), { name: 'AbortError' });
64
+ expect(describeValidationError(error)).toBe('request was aborted (possibly timed out)');
65
+ });
66
+
67
+ it('falls back to the raw message when nothing else matches', () => {
68
+ expect(describeValidationError(new Error('something weird'))).toBe('something weird');
69
+ });
70
+ });
@@ -2,7 +2,13 @@ import { DefaultConfigRegistry } from '../config/registry.js';
2
2
  import { HomeFileSource } from '../config/sources/home-file.js';
3
3
  import { MacosKeychainSource, type KeychainDeps } from '../config/sources/macos-keychain.js';
4
4
  import { ProcessEnvSource } from '../config/sources/process-env.js';
5
- import { runSetup, type SetupPrompts } from '../setup-cli.js';
5
+ import {
6
+ runSetup,
7
+ type CredentialValidationContext,
8
+ type CredentialValidationResult,
9
+ type SetupPrompts,
10
+ type ValidateCredentials,
11
+ } from '../setup-cli.js';
6
12
  import type { ConfigKey, ProductDefinition } from '../config/source.js';
7
13
 
8
14
  const JIRA: ProductDefinition = {
@@ -72,15 +78,69 @@ class FakeHomeFile extends HomeFileSource {
72
78
  }
73
79
  }
74
80
 
81
+ class StubCredentialValidator {
82
+ readonly calls: CredentialValidationContext[] = [];
83
+ private readonly queue: CredentialValidationResult[] = [];
84
+ private fallback: CredentialValidationResult = { ok: true };
85
+
86
+ returns(result: CredentialValidationResult): this {
87
+ this.fallback = result;
88
+ return this;
89
+ }
90
+
91
+ reject(message: string): this {
92
+ return this.returns({ ok: false, message });
93
+ }
94
+
95
+ enqueue(...results: CredentialValidationResult[]): this {
96
+ this.queue.push(...results);
97
+ return this;
98
+ }
99
+
100
+ asFn(): ValidateCredentials {
101
+ return async (ctx) => {
102
+ this.calls.push(ctx);
103
+ return this.queue.shift() ?? this.fallback;
104
+ };
105
+ }
106
+ }
107
+
108
+ type ConfirmStub = (message: string, fallbackDefault?: boolean) => boolean;
109
+
110
+ function scriptedConfirms(script: Record<string, boolean>): ConfirmStub {
111
+ return (message, fallbackDefault) => {
112
+ for (const [prefix, answer] of Object.entries(script)) {
113
+ if (message.startsWith(prefix)) return answer;
114
+ }
115
+ return fallbackDefault ?? false;
116
+ };
117
+ }
118
+
75
119
  function makeRegistry(keychain: FakeKeychain, home: FakeHomeFile) {
76
120
  return new DefaultConfigRegistry([new ProcessEnvSource(), home, keychain]);
77
121
  }
78
122
 
79
- function makePrompts(overrides: Partial<SetupPrompts> = {}): SetupPrompts {
123
+ const standardAnswers = {
124
+ host: 'j-host',
125
+ apiBasePath: '/rest/api/2',
126
+ pageSize: '25',
127
+ token: 'secret',
128
+ };
129
+
130
+ function makePrompts(
131
+ overrides: Partial<SetupPrompts> = {},
132
+ answers: Partial<typeof standardAnswers> = {},
133
+ confirms?: ConfirmStub,
134
+ ): SetupPrompts {
135
+ const filled = { ...standardAnswers, ...answers };
80
136
  return {
81
- input: async (opts) => opts.default ?? '',
82
- password: async () => '',
83
- confirm: async (opts) => opts.default ?? false,
137
+ input: async (opts) => {
138
+ if (opts.message.startsWith('Host')) return filled.host;
139
+ if (opts.message.startsWith('API base path')) return filled.apiBasePath;
140
+ return filled.pageSize;
141
+ },
142
+ password: async () => filled.token,
143
+ confirm: async (opts) => (confirms ?? ((_m, d) => d ?? false))(opts.message, opts.default),
84
144
  ...overrides,
85
145
  };
86
146
  }
@@ -105,16 +165,13 @@ describe('runSetup', () => {
105
165
  });
106
166
 
107
167
  it('writes the token to keychain first on darwin and clears home file token', async () => {
108
- const prompts = makePrompts({
109
- input: async (opts) => {
110
- if (opts.message.startsWith('Host')) return 'j-host';
111
- if (opts.message.startsWith('API base path')) return '/rest/api/2';
112
- return '25';
113
- },
114
- password: async () => 'secret',
115
- });
116
168
  const registry = makeRegistry(keychain, home);
117
- await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
169
+ await runSetup(JIRA, {
170
+ registry,
171
+ log: (m) => logs.push(m),
172
+ exit: () => undefined,
173
+ prompts: makePrompts(),
174
+ });
118
175
 
119
176
  expect(keychain.writeCalls).toBe(1);
120
177
  expect(keychain.store).toBe('secret');
@@ -124,16 +181,13 @@ describe('runSetup', () => {
124
181
 
125
182
  it('falls back to home file when keychain is unavailable', async () => {
126
183
  keychain.available = false;
127
- const prompts = makePrompts({
128
- input: async (opts) => {
129
- if (opts.message.startsWith('Host')) return 'j-host';
130
- if (opts.message.startsWith('API base path')) return '/rest/api/2';
131
- return '25';
132
- },
133
- password: async () => 'secret',
134
- });
135
184
  const registry = makeRegistry(keychain, home);
136
- await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
185
+ await runSetup(JIRA, {
186
+ registry,
187
+ log: (m) => logs.push(m),
188
+ exit: () => undefined,
189
+ prompts: makePrompts(),
190
+ });
137
191
 
138
192
  expect(keychain.writeCalls).toBe(0);
139
193
  const tokenWrites = home.writes.filter(([, k]) => k === 'token');
@@ -142,32 +196,121 @@ describe('runSetup', () => {
142
196
 
143
197
  it('prints shadowing warning when env var is set higher than target', async () => {
144
198
  process.env.JIRA_API_TOKEN = 'shadow';
145
- const prompts = makePrompts({
146
- input: async (opts) => {
147
- if (opts.message.startsWith('Host')) return 'j-host';
148
- if (opts.message.startsWith('API base path')) return '/rest/api/2';
149
- return '25';
150
- },
151
- password: async () => 'secret',
152
- });
153
199
  const registry = makeRegistry(keychain, home);
154
- await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
200
+ await runSetup(JIRA, {
201
+ registry,
202
+ log: (m) => logs.push(m),
203
+ exit: () => undefined,
204
+ prompts: makePrompts(),
205
+ });
155
206
 
156
207
  expect(logs.some((l) => l.includes('JIRA_API_TOKEN') && l.includes('Warning'))).toBe(true);
157
208
  });
158
209
 
210
+ it('calls validateCredentials with the entered values before saving', async () => {
211
+ const validator = new StubCredentialValidator();
212
+ const registry = makeRegistry(keychain, home);
213
+
214
+ await runSetup(JIRA, {
215
+ registry,
216
+ log: (m) => logs.push(m),
217
+ exit: () => undefined,
218
+ prompts: makePrompts(),
219
+ validateCredentials: validator.asFn(),
220
+ });
221
+
222
+ expect(validator.calls).toEqual([
223
+ { host: 'j-host', apiBasePath: '/rest/api/2', token: 'secret' },
224
+ ]);
225
+ expect(keychain.writeCalls).toBe(1);
226
+ });
227
+
228
+ it('exits without writing when validation fails and user declines retry or save-anyway', async () => {
229
+ const validator = new StubCredentialValidator().reject('401 Unauthorized');
230
+ const exitFn = jest.fn();
231
+ const registry = makeRegistry(keychain, home);
232
+
233
+ await runSetup(JIRA, {
234
+ registry,
235
+ log: (m) => logs.push(m),
236
+ exit: exitFn,
237
+ prompts: makePrompts({}, { token: 'bad-token' }, scriptedConfirms({
238
+ 'Try again': false,
239
+ 'Save configuration anyway': false,
240
+ })),
241
+ validateCredentials: validator.asFn(),
242
+ });
243
+
244
+ expect(validator.calls).toHaveLength(1);
245
+ expect(exitFn).toHaveBeenCalledWith(1);
246
+ expect(keychain.writeCalls).toBe(0);
247
+ expect(home.writes).toHaveLength(0);
248
+ expect(logs.some((l) => l.includes('401 Unauthorized'))).toBe(true);
249
+ });
250
+
251
+ it('retries validation after a failure and writes once it succeeds', async () => {
252
+ const validator = new StubCredentialValidator().enqueue(
253
+ { ok: false, message: '401 Unauthorized' },
254
+ { ok: true },
255
+ );
256
+ const registry = makeRegistry(keychain, home);
257
+
258
+ await runSetup(JIRA, {
259
+ registry,
260
+ log: (m) => logs.push(m),
261
+ exit: () => undefined,
262
+ prompts: makePrompts({}, {}, scriptedConfirms({ 'Try again': true })),
263
+ validateCredentials: validator.asFn(),
264
+ });
265
+
266
+ expect(validator.calls).toHaveLength(2);
267
+ expect(keychain.writeCalls).toBe(1);
268
+ expect(logs.some((l) => l.includes('401 Unauthorized'))).toBe(true);
269
+ expect(logs.some((l) => l.startsWith('Validation succeeded'))).toBe(true);
270
+ });
271
+
272
+ it('stops offering retry after the third failure and lets the user save anyway', async () => {
273
+ const validator = new StubCredentialValidator().reject('401 Unauthorized');
274
+ let retryPromptsShown = 0;
275
+ const confirms: ConfirmStub = (message) => {
276
+ if (message.startsWith('Try again')) {
277
+ retryPromptsShown++;
278
+ return true;
279
+ }
280
+ if (message.startsWith('Save configuration anyway')) return true;
281
+ return false;
282
+ };
283
+ const registry = makeRegistry(keychain, home);
284
+
285
+ await runSetup(JIRA, {
286
+ registry,
287
+ log: (m) => logs.push(m),
288
+ exit: () => undefined,
289
+ prompts: makePrompts({}, {}, confirms),
290
+ validateCredentials: validator.asFn(),
291
+ });
292
+
293
+ expect(validator.calls).toHaveLength(3);
294
+ expect(retryPromptsShown).toBe(2);
295
+ expect(keychain.writeCalls).toBe(1);
296
+ });
297
+
159
298
  it('exits 130 on SIGINT cancellation before any write', async () => {
160
299
  const exitErr: Error & { name: string } = Object.assign(new Error('closed'), {
161
300
  name: 'ExitPromptError',
162
301
  });
163
- const prompts = makePrompts({
164
- input: async () => {
165
- throw exitErr;
166
- },
167
- });
168
302
  const exitFn = jest.fn();
169
303
  const registry = makeRegistry(keychain, home);
170
- await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: exitFn, prompts });
304
+ await runSetup(JIRA, {
305
+ registry,
306
+ log: (m) => logs.push(m),
307
+ exit: exitFn,
308
+ prompts: makePrompts({
309
+ input: async () => {
310
+ throw exitErr;
311
+ },
312
+ }),
313
+ });
171
314
  expect(exitFn).toHaveBeenCalledWith(130);
172
315
  expect(keychain.writeCalls).toBe(0);
173
316
  expect(home.writes).toHaveLength(0);
@@ -8,6 +8,7 @@ export interface ProductDefinition {
8
8
  readonly id: string;
9
9
  readonly envVars: Record<ConfigKey, string>;
10
10
  readonly defaultApiBasePath?: string;
11
+ readonly apiBasePathStrippableSuffixes?: readonly string[];
11
12
  }
12
13
 
13
14
  export interface ReadableSource {
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
4
  export * from './api-error-handler.js'
5
5
  export * from './config/index.js';
6
6
  export { runSetup } from './setup-cli.js';
7
+ export { describeValidationError } from './setup/describe-error.js';
7
8
 
8
9
  // Helper function to format tool responses
9
10
  export const formatToolResponse = (result: unknown) => ({