@marcusrbrown/infra 0.13.0 → 0.13.2

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 (108) hide show
  1. package/dist/cli.js +4977 -0
  2. package/package.json +7 -3
  3. package/src/__snapshots__/cli.test.ts.snap +0 -219
  4. package/src/__test__/mcp-ctx-fixture.test.ts +0 -108
  5. package/src/__test__/mcp-ctx-fixture.ts +0 -104
  6. package/src/cli.test.ts +0 -66
  7. package/src/cli.ts +0 -43
  8. package/src/commands/cliproxy/config.test.ts +0 -475
  9. package/src/commands/cliproxy/config.ts +0 -238
  10. package/src/commands/cliproxy/deploy.test.ts +0 -181
  11. package/src/commands/cliproxy/deploy.ts +0 -157
  12. package/src/commands/cliproxy/host.test.ts +0 -73
  13. package/src/commands/cliproxy/host.ts +0 -31
  14. package/src/commands/cliproxy/index.ts +0 -21
  15. package/src/commands/cliproxy/keys.test.ts +0 -326
  16. package/src/commands/cliproxy/keys.ts +0 -201
  17. package/src/commands/cliproxy/login.test.ts +0 -415
  18. package/src/commands/cliproxy/login.ts +0 -131
  19. package/src/commands/cliproxy/models.test.ts +0 -592
  20. package/src/commands/cliproxy/models.ts +0 -237
  21. package/src/commands/cliproxy/open.test.ts +0 -132
  22. package/src/commands/cliproxy/open.ts +0 -78
  23. package/src/commands/cliproxy/setup/gh.test.ts +0 -218
  24. package/src/commands/cliproxy/setup/gh.ts +0 -250
  25. package/src/commands/cliproxy/setup/preview.test.ts +0 -159
  26. package/src/commands/cliproxy/setup/preview.ts +0 -41
  27. package/src/commands/cliproxy/setup/prompts.test.ts +0 -58
  28. package/src/commands/cliproxy/setup/prompts.ts +0 -99
  29. package/src/commands/cliproxy/setup/providers.test.ts +0 -245
  30. package/src/commands/cliproxy/setup/providers.ts +0 -136
  31. package/src/commands/cliproxy/setup/smoke-test.test.ts +0 -821
  32. package/src/commands/cliproxy/setup/smoke-test.ts +0 -223
  33. package/src/commands/cliproxy/setup/templates.test.ts +0 -357
  34. package/src/commands/cliproxy/setup/templates.ts +0 -145
  35. package/src/commands/cliproxy/setup/validation.test.ts +0 -567
  36. package/src/commands/cliproxy/setup/validation.ts +0 -209
  37. package/src/commands/cliproxy/setup/workflow-analyzer.test.ts +0 -363
  38. package/src/commands/cliproxy/setup/workflow-analyzer.ts +0 -136
  39. package/src/commands/cliproxy/setup.test.ts +0 -2366
  40. package/src/commands/cliproxy/setup.ts +0 -809
  41. package/src/commands/cliproxy/shared.test.ts +0 -118
  42. package/src/commands/cliproxy/shared.ts +0 -84
  43. package/src/commands/cliproxy/status.test.ts +0 -1157
  44. package/src/commands/cliproxy/status.ts +0 -597
  45. package/src/commands/dashboard/deploy.test.ts +0 -215
  46. package/src/commands/dashboard/deploy.ts +0 -135
  47. package/src/commands/dashboard/host.test.ts +0 -79
  48. package/src/commands/dashboard/host.ts +0 -33
  49. package/src/commands/dashboard/index.ts +0 -13
  50. package/src/commands/dashboard/logs.test.ts +0 -317
  51. package/src/commands/dashboard/logs.ts +0 -180
  52. package/src/commands/dashboard/status.test.ts +0 -708
  53. package/src/commands/dashboard/status.ts +0 -293
  54. package/src/commands/gateway/backup.test.ts +0 -426
  55. package/src/commands/gateway/backup.ts +0 -205
  56. package/src/commands/gateway/deploy.test.ts +0 -491
  57. package/src/commands/gateway/deploy.ts +0 -173
  58. package/src/commands/gateway/host.test.ts +0 -89
  59. package/src/commands/gateway/host.ts +0 -33
  60. package/src/commands/gateway/index.ts +0 -17
  61. package/src/commands/gateway/logs.test.ts +0 -263
  62. package/src/commands/gateway/logs.ts +0 -160
  63. package/src/commands/gateway/restore.test.ts +0 -650
  64. package/src/commands/gateway/restore.ts +0 -308
  65. package/src/commands/gateway/status.test.ts +0 -755
  66. package/src/commands/gateway/status.ts +0 -255
  67. package/src/commands/keeweb/deploy.test.ts +0 -177
  68. package/src/commands/keeweb/deploy.ts +0 -191
  69. package/src/commands/keeweb/index.ts +0 -11
  70. package/src/commands/keeweb/open.test.ts +0 -54
  71. package/src/commands/keeweb/open.ts +0 -30
  72. package/src/commands/keeweb/status.test.ts +0 -509
  73. package/src/commands/keeweb/status.ts +0 -359
  74. package/src/commands/mcp.test.ts +0 -260
  75. package/src/commands/mcp.ts +0 -58
  76. package/src/commands/status.test.ts +0 -222
  77. package/src/commands/status.ts +0 -161
  78. package/src/commands/umami/deploy.test.ts +0 -202
  79. package/src/commands/umami/deploy.ts +0 -132
  80. package/src/commands/umami/host.test.ts +0 -79
  81. package/src/commands/umami/host.ts +0 -33
  82. package/src/commands/umami/index.ts +0 -13
  83. package/src/commands/umami/logs.test.ts +0 -191
  84. package/src/commands/umami/logs.ts +0 -163
  85. package/src/commands/umami/status.test.ts +0 -633
  86. package/src/commands/umami/status.ts +0 -285
  87. package/src/commands/vpn/client.test.ts +0 -949
  88. package/src/commands/vpn/client.ts +0 -467
  89. package/src/commands/vpn/deploy.test.ts +0 -208
  90. package/src/commands/vpn/deploy.ts +0 -143
  91. package/src/commands/vpn/host.test.ts +0 -89
  92. package/src/commands/vpn/host.ts +0 -36
  93. package/src/commands/vpn/index.ts +0 -15
  94. package/src/commands/vpn/logs.test.ts +0 -234
  95. package/src/commands/vpn/logs.ts +0 -136
  96. package/src/commands/vpn/peers.test.ts +0 -478
  97. package/src/commands/vpn/shared.test.ts +0 -454
  98. package/src/commands/vpn/shared.ts +0 -170
  99. package/src/commands/vpn/status.test.ts +0 -142
  100. package/src/commands/vpn/status.ts +0 -78
  101. package/src/conventions.test.ts +0 -1180
  102. package/src/lib/action-ctx.ts +0 -25
  103. package/src/lib/known-hosts.test.ts +0 -192
  104. package/src/lib/known-hosts.ts +0 -76
  105. package/src/lib/redact.ts +0 -21
  106. package/src/lib/ssh-identity.test.ts +0 -134
  107. package/src/lib/ssh-identity.ts +0 -76
  108. /package/{src/resources/known_hosts → dist/known_hosts-tk724cp0.} +0 -0
@@ -1,475 +0,0 @@
1
- import {existsSync, statSync} from 'node:fs'
2
- import {afterEach, beforeEach, describe, expect, it} from 'bun:test'
3
-
4
- import {createCapturedCtx, expectCapturedToInclude} from '../../__test__/mcp-ctx-fixture'
5
- import {
6
- buildSetRequest,
7
- cliproxyConfigGetAction,
8
- cliproxyConfigSetAction,
9
- formatConfigAsColumns,
10
- parseBoolean,
11
- parseNumber,
12
- resolveManagementKey,
13
- } from './config'
14
- import {toStringArray} from './keys'
15
- import {requireSshAuthSock, resolveHost} from './login'
16
-
17
- const originalFetch = globalThis.fetch
18
-
19
- type FetchReplacement = (url: string, init?: RequestInit) => Promise<Response>
20
-
21
- function createFetchImplementation(handler: FetchReplacement): typeof fetch {
22
- return Object.assign(
23
- (input: string | URL | Request, init?: RequestInit) => {
24
- if (typeof input !== 'string') {
25
- throw new TypeError(`Unexpected non-string fetch input: ${String(input)}`)
26
- }
27
-
28
- return handler(input, init)
29
- },
30
- {preconnect: originalFetch.preconnect},
31
- )
32
- }
33
-
34
- describe('cliproxy config helpers', () => {
35
- describe('parseBoolean', () => {
36
- it('parses true values case-insensitively', () => {
37
- expect(parseBoolean('true')).toBe(true)
38
- expect(parseBoolean('True')).toBe(true)
39
- expect(parseBoolean('TRUE')).toBe(true)
40
- })
41
-
42
- it('parses false values case-insensitively', () => {
43
- expect(parseBoolean('false')).toBe(false)
44
- expect(parseBoolean('False')).toBe(false)
45
- })
46
-
47
- it('throws for invalid values', () => {
48
- expect(() => parseBoolean('wat')).toThrow()
49
- })
50
- })
51
-
52
- describe('parseNumber', () => {
53
- it('parses integers and floats', () => {
54
- expect(parseNumber('42', 'request-retry')).toBe(42)
55
- expect(parseNumber('3.14', 'request-retry')).toBe(3.14)
56
- })
57
-
58
- it('throws TypeError for non-numeric values', () => {
59
- expect(() => parseNumber('abc', 'request-retry')).toThrow(TypeError)
60
- })
61
-
62
- it('throws TypeError for NaN', () => {
63
- expect(() => parseNumber('NaN', 'request-retry')).toThrow(TypeError)
64
- })
65
-
66
- it('throws TypeError for Infinity', () => {
67
- expect(() => parseNumber('Infinity', 'request-retry')).toThrow(TypeError)
68
- })
69
- })
70
-
71
- describe('buildSetRequest', () => {
72
- it('builds a boolean debug request with {value} body', () => {
73
- const request = buildSetRequest('https://cliproxy.example.com', 'debug', 'true')
74
-
75
- expect(request.endpoint).toBe('https://cliproxy.example.com/v0/management/debug')
76
- expect(JSON.parse(request.body)).toEqual({value: true})
77
- })
78
-
79
- it('builds a numeric request-retry request with {value} body', () => {
80
- const request = buildSetRequest('https://cliproxy.example.com', 'request-retry', '3')
81
-
82
- expect(request.endpoint).toBe('https://cliproxy.example.com/v0/management/request-retry')
83
- expect(JSON.parse(request.body)).toEqual({value: 3})
84
- })
85
-
86
- it('builds a string proxy-url request with {value} body', () => {
87
- const request = buildSetRequest('https://cliproxy.example.com', 'proxy-url', 'https://x.com')
88
-
89
- expect(request.endpoint).toBe('https://cliproxy.example.com/v0/management/proxy-url')
90
- expect(JSON.parse(request.body)).toEqual({value: 'https://x.com'})
91
- })
92
-
93
- it('throws for unsupported fields', () => {
94
- expect(() => buildSetRequest('https://cliproxy.example.com', 'provider', 'claude')).toThrow()
95
- })
96
- })
97
-
98
- describe('resolveManagementKey', () => {
99
- const originalManagementKey = process.env.CLIPROXY_MANAGEMENT_KEY
100
-
101
- beforeEach(() => {
102
- delete process.env.CLIPROXY_MANAGEMENT_KEY
103
- })
104
-
105
- afterEach(() => {
106
- if (originalManagementKey === undefined) {
107
- delete process.env.CLIPROXY_MANAGEMENT_KEY
108
- } else {
109
- process.env.CLIPROXY_MANAGEMENT_KEY = originalManagementKey
110
- }
111
- })
112
-
113
- it('returns explicit input when provided', () => {
114
- process.env.CLIPROXY_MANAGEMENT_KEY = 'env-key'
115
-
116
- expect(resolveManagementKey('explicit-key')).toBe('explicit-key')
117
- })
118
-
119
- it('falls back to CLIPROXY_MANAGEMENT_KEY', () => {
120
- process.env.CLIPROXY_MANAGEMENT_KEY = 'env-key'
121
-
122
- expect(resolveManagementKey()).toBe('env-key')
123
- })
124
-
125
- it('throws when no key is available', () => {
126
- expect(() => resolveManagementKey()).toThrow()
127
- })
128
- })
129
- })
130
-
131
- describe('formatConfigAsColumns', () => {
132
- it('formats flat object as aligned key: value lines', () => {
133
- const result = formatConfigAsColumns({debug: true, 'request-retry': 3, 'proxy-url': 'https://x.com'})
134
- const lines = result.split('\n')
135
-
136
- expect(lines[0]).toBe('debug : true')
137
- expect(lines[1]).toBe('request-retry: 3')
138
- expect(lines[2]).toBe('proxy-url : https://x.com')
139
- })
140
-
141
- it('serializes nested objects as JSON', () => {
142
- const result = formatConfigAsColumns({nested: {a: 1}})
143
-
144
- expect(result).toBe('nested: {"a":1}')
145
- })
146
-
147
- it('returns empty string for empty object', () => {
148
- expect(formatConfigAsColumns({})).toBe('')
149
- })
150
-
151
- it('falls back to JSON.stringify for non-objects', () => {
152
- expect(formatConfigAsColumns(null)).toBe('null')
153
- expect(formatConfigAsColumns([1, 2])).toBe('[\n 1,\n 2\n]')
154
- })
155
- })
156
-
157
- describe('cliproxyConfigGetAction (Mode C, Tier-2 ctx capture)', () => {
158
- afterEach(() => {
159
- globalThis.fetch = originalFetch
160
- })
161
-
162
- it('Mode C: captures formatted config to ctx.stdout and returns config object', async () => {
163
- const mockConfig = {debug: true, 'request-retry': 3}
164
- globalThis.fetch = createFetchImplementation(
165
- async () =>
166
- new Response(JSON.stringify(mockConfig), {
167
- status: 200,
168
- headers: {'content-type': 'application/json'},
169
- }),
170
- )
171
-
172
- const {ctx, captured} = createCapturedCtx()
173
- const result = await cliproxyConfigGetAction({url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx)
174
-
175
- // Tier-2: stdout contains formatted config
176
- expect(expectCapturedToInclude(captured, 'debug')).toBe(true)
177
- expect(expectCapturedToInclude(captured, 'request-retry')).toBe(true)
178
-
179
- // Mode C: action returns the config object
180
- expect(result).toEqual(mockConfig)
181
- })
182
-
183
- it('Mode C: security warning goes to ctx.stderr', async () => {
184
- const mockConfig = {debug: false}
185
- globalThis.fetch = createFetchImplementation(
186
- async () =>
187
- new Response(JSON.stringify(mockConfig), {
188
- status: 200,
189
- headers: {'content-type': 'application/json'},
190
- }),
191
- )
192
-
193
- const {ctx, captured} = createCapturedCtx()
194
- await cliproxyConfigGetAction({url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx)
195
-
196
- const stderrText = captured.stderr.join('')
197
- expect(stderrText).toContain('API keys')
198
- })
199
-
200
- it('Mode C: --json flag outputs raw JSON to ctx.stdout', async () => {
201
- const mockConfig = {debug: true}
202
- globalThis.fetch = createFetchImplementation(
203
- async () =>
204
- new Response(JSON.stringify(mockConfig), {
205
- status: 200,
206
- headers: {'content-type': 'application/json'},
207
- }),
208
- )
209
-
210
- const {ctx, captured} = createCapturedCtx()
211
- const result = await cliproxyConfigGetAction(
212
- {url: 'https://cliproxy.example.com', key: 'mgmt-key', json: true},
213
- ctx,
214
- )
215
-
216
- const stdoutText = captured.stdout.join('')
217
- expect(JSON.parse(stdoutText)).toEqual(mockConfig)
218
- expect(result).toEqual(mockConfig)
219
- })
220
-
221
- it('Mode C: --output writes file and prints confirmation to ctx.stdout', async () => {
222
- const testFile = '/tmp/test-cliproxy-config-get-action.json'
223
- const mockConfig = {debug: true, 'api-keys': ['key1', 'key2']}
224
- globalThis.fetch = createFetchImplementation(
225
- async () =>
226
- new Response(JSON.stringify(mockConfig), {
227
- status: 200,
228
- headers: {'content-type': 'application/json'},
229
- }),
230
- )
231
-
232
- const {ctx, captured} = createCapturedCtx()
233
- try {
234
- const result = await cliproxyConfigGetAction(
235
- {url: 'https://cliproxy.example.com', key: 'mgmt-key', output: testFile},
236
- ctx,
237
- )
238
-
239
- expect(existsSync(testFile)).toBe(true)
240
- const {mode} = statSync(testFile)
241
- expect(mode & 0o777).toBe(0o600)
242
- expect(JSON.parse(await Bun.file(testFile).text())).toEqual(mockConfig)
243
- expect(expectCapturedToInclude(captured, '✓ Config written to')).toBe(true)
244
- expect(result).toEqual(mockConfig)
245
- } finally {
246
- if (existsSync(testFile)) {
247
- const fs = await import('node:fs/promises')
248
- await fs.unlink(testFile).catch(() => {})
249
- }
250
- }
251
- })
252
- })
253
-
254
- describe('cliproxyConfigSetAction (Mode A, two positional args, Tier-2 ctx capture)', () => {
255
- afterEach(() => {
256
- globalThis.fetch = originalFetch
257
- })
258
-
259
- it('Mode A: captures PUT response to ctx.stdout', async () => {
260
- globalThis.fetch = createFetchImplementation(
261
- async () =>
262
- new Response(JSON.stringify({value: true}), {
263
- status: 200,
264
- headers: {'content-type': 'application/json'},
265
- }),
266
- )
267
-
268
- const {ctx, captured} = createCapturedCtx()
269
- await cliproxyConfigSetAction('debug', 'true', {url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx)
270
-
271
- expect(expectCapturedToInclude(captured, 'value')).toBe(true)
272
- expect(expectCapturedToInclude(captured, 'true')).toBe(true)
273
- })
274
-
275
- it('Mode A: routes unsupported field error through ctx.console.error + exit(1)', async () => {
276
- const {ctx, captured} = createCapturedCtx()
277
- await expect(
278
- cliproxyConfigSetAction('unsupported-field', 'val', {url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx),
279
- ).rejects.toMatchObject({name: 'MockProcessExit', code: 1})
280
- expect(captured.stderr.join('')).toContain('not a supported mutable field')
281
- expect(captured.exit).toEqual({code: 1})
282
- })
283
- })
284
-
285
- describe('cliproxyConfigGetAction (Tier-2 failure-path parity)', () => {
286
- const originalManagementKey = process.env.CLIPROXY_MANAGEMENT_KEY
287
-
288
- afterEach(() => {
289
- globalThis.fetch = originalFetch
290
- if (originalManagementKey === undefined) {
291
- delete process.env.CLIPROXY_MANAGEMENT_KEY
292
- } else {
293
- process.env.CLIPROXY_MANAGEMENT_KEY = originalManagementKey
294
- }
295
- })
296
-
297
- it('Tier-2: missing CLIPROXY_MANAGEMENT_KEY routes to ctx.console.error + exit(1)', async () => {
298
- delete process.env.CLIPROXY_MANAGEMENT_KEY
299
-
300
- const {ctx, captured} = createCapturedCtx()
301
- await expect(cliproxyConfigGetAction({url: 'https://cliproxy.example.com'}, ctx)).rejects.toMatchObject({
302
- name: 'MockProcessExit',
303
- code: 1,
304
- })
305
- expect(captured.stderr.join('')).toContain('Management API key')
306
- expect(captured.exit).toEqual({code: 1})
307
- })
308
-
309
- it('Tier-2: HTTP 500 response routes to ctx.console.error + exit(1)', async () => {
310
- globalThis.fetch = createFetchImplementation(async () => new Response('server error', {status: 500}))
311
-
312
- const {ctx, captured} = createCapturedCtx()
313
- await expect(
314
- cliproxyConfigGetAction({url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx),
315
- ).rejects.toMatchObject({
316
- name: 'MockProcessExit',
317
- code: 1,
318
- })
319
- expect(captured.stderr.join('')).toContain('HTTP 500')
320
- expect(captured.exit).toEqual({code: 1})
321
- })
322
-
323
- it('Tier-2: --output write failure routes to ctx.console.error + exit(1)', async () => {
324
- const mockConfig = {debug: true}
325
- globalThis.fetch = createFetchImplementation(
326
- async () =>
327
- new Response(JSON.stringify(mockConfig), {
328
- status: 200,
329
- headers: {'content-type': 'application/json'},
330
- }),
331
- )
332
-
333
- // Use a path that cannot be written (directory as file path)
334
- const {ctx, captured} = createCapturedCtx()
335
- await expect(
336
- cliproxyConfigGetAction(
337
- {url: 'https://cliproxy.example.com', key: 'mgmt-key', output: '/dev/null/cannot-write'},
338
- ctx,
339
- ),
340
- ).rejects.toMatchObject({
341
- name: 'MockProcessExit',
342
- code: 1,
343
- })
344
- expect(captured.stderr.join('')).toContain('Failed to write config')
345
- expect(captured.exit).toEqual({code: 1})
346
- })
347
- })
348
-
349
- describe('cliproxyConfigSetAction (Tier-2 failure-path parity)', () => {
350
- const originalManagementKey = process.env.CLIPROXY_MANAGEMENT_KEY
351
-
352
- afterEach(() => {
353
- globalThis.fetch = originalFetch
354
- if (originalManagementKey === undefined) {
355
- delete process.env.CLIPROXY_MANAGEMENT_KEY
356
- } else {
357
- process.env.CLIPROXY_MANAGEMENT_KEY = originalManagementKey
358
- }
359
- })
360
-
361
- it('Tier-2: missing CLIPROXY_MANAGEMENT_KEY routes to ctx.console.error + exit(1)', async () => {
362
- delete process.env.CLIPROXY_MANAGEMENT_KEY
363
-
364
- const {ctx, captured} = createCapturedCtx()
365
- await expect(
366
- cliproxyConfigSetAction('debug', 'true', {url: 'https://cliproxy.example.com'}, ctx),
367
- ).rejects.toMatchObject({
368
- name: 'MockProcessExit',
369
- code: 1,
370
- })
371
- expect(captured.stderr.join('')).toContain('Management API key')
372
- expect(captured.exit).toEqual({code: 1})
373
- })
374
-
375
- it('Tier-2: HTTP 500 response routes to ctx.console.error + exit(1)', async () => {
376
- globalThis.fetch = createFetchImplementation(async () => new Response('server error', {status: 500}))
377
-
378
- const {ctx, captured} = createCapturedCtx()
379
- await expect(
380
- cliproxyConfigSetAction('debug', 'true', {url: 'https://cliproxy.example.com', key: 'mgmt-key'}, ctx),
381
- ).rejects.toMatchObject({
382
- name: 'MockProcessExit',
383
- code: 1,
384
- })
385
- expect(captured.stderr.join('')).toContain('HTTP 500')
386
- expect(captured.exit).toEqual({code: 1})
387
- })
388
- })
389
-
390
- describe('cliproxy keys helpers', () => {
391
- describe('toStringArray', () => {
392
- it('returns string arrays filtered to strings only', () => {
393
- expect(toStringArray(['a', 'b'])).toEqual(['a', 'b'])
394
- expect(toStringArray(['a', 1, 'b', false])).toEqual(['a', 'b'])
395
- })
396
-
397
- it('reads api-keys (hyphenated) from objects', () => {
398
- expect(toStringArray({'api-keys': ['a', 'b']})).toEqual(['a', 'b'])
399
- })
400
-
401
- it('reads api_keys (underscored) from objects', () => {
402
- expect(toStringArray({api_keys: ['a', 'b']})).toEqual(['a', 'b'])
403
- })
404
-
405
- it('prefers api-keys over api_keys', () => {
406
- expect(toStringArray({'api-keys': ['x'], api_keys: ['y']})).toEqual(['x'])
407
- })
408
-
409
- it('returns an empty array for unsupported payloads', () => {
410
- expect(toStringArray(null)).toEqual([])
411
- expect(toStringArray(undefined)).toEqual([])
412
- expect(toStringArray(123)).toEqual([])
413
- })
414
- })
415
- })
416
-
417
- describe('cliproxy login helpers', () => {
418
- describe('resolveHost', () => {
419
- const originalHost = process.env.CLIPROXY_DOMAIN
420
-
421
- beforeEach(() => {
422
- delete process.env.CLIPROXY_DOMAIN
423
- })
424
-
425
- afterEach(() => {
426
- if (originalHost === undefined) {
427
- delete process.env.CLIPROXY_DOMAIN
428
- } else {
429
- process.env.CLIPROXY_DOMAIN = originalHost
430
- }
431
- })
432
-
433
- it('returns explicit input when provided', () => {
434
- process.env.CLIPROXY_DOMAIN = 'env.example.com'
435
-
436
- expect(resolveHost('explicit.example.com')).toBe('explicit.example.com')
437
- })
438
-
439
- it('falls back to CLIPROXY_DOMAIN', () => {
440
- process.env.CLIPROXY_DOMAIN = 'env.example.com'
441
-
442
- expect(resolveHost()).toBe('env.example.com')
443
- })
444
-
445
- it('falls back to the default host', () => {
446
- expect(resolveHost()).toBe('cliproxy.fro.bot')
447
- })
448
- })
449
-
450
- describe('requireSshAuthSock', () => {
451
- const originalSshAuthSock = process.env.SSH_AUTH_SOCK
452
-
453
- beforeEach(() => {
454
- delete process.env.SSH_AUTH_SOCK
455
- })
456
-
457
- afterEach(() => {
458
- if (originalSshAuthSock === undefined) {
459
- delete process.env.SSH_AUTH_SOCK
460
- } else {
461
- process.env.SSH_AUTH_SOCK = originalSshAuthSock
462
- }
463
- })
464
-
465
- it('returns SSH_AUTH_SOCK when set', () => {
466
- process.env.SSH_AUTH_SOCK = '/tmp/agent.sock'
467
-
468
- expect(requireSshAuthSock()).toBe('/tmp/agent.sock')
469
- })
470
-
471
- it('throws when SSH_AUTH_SOCK is missing', () => {
472
- expect(() => requireSshAuthSock()).toThrow()
473
- })
474
- })
475
- })