@marcusrbrown/infra 0.13.1 → 0.13.3

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 +11965 -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,118 +0,0 @@
1
- import {describe, expect, mock, test} from 'bun:test'
2
- import {managementHeaders, parseManagementKeyList, requestJson} from './shared'
3
-
4
- describe('managementHeaders', () => {
5
- test('sets x-management-key header', () => {
6
- const headers = managementHeaders('mgmt-key')
7
- expect(headers.get('x-management-key')).toBe('mgmt-key')
8
- })
9
-
10
- test('sets content-type to application/json', () => {
11
- const headers = managementHeaders('mgmt-key')
12
- expect(headers.get('content-type')).toBe('application/json')
13
- })
14
-
15
- test('does not set Authorization header', () => {
16
- const headers = managementHeaders('mgmt-key')
17
- expect(headers.get('authorization')).toBeNull()
18
- })
19
- })
20
-
21
- describe('requestJson', () => {
22
- test('returns parsed JSON on success', async () => {
23
- const payload = {ok: true, value: 42}
24
- const mockFetch = mock(() =>
25
- Promise.resolve(
26
- new Response(JSON.stringify(payload), {
27
- status: 200,
28
- headers: {'content-type': 'application/json'},
29
- }),
30
- ),
31
- )
32
- const original = globalThis.fetch
33
- globalThis.fetch = mockFetch as unknown as typeof fetch
34
- try {
35
- const result = await requestJson('https://example.com/api', {method: 'GET'})
36
- expect(result).toEqual(payload)
37
- } finally {
38
- globalThis.fetch = original
39
- }
40
- })
41
-
42
- test('throws with HTTP status and body on non-200 response', async () => {
43
- const mockFetch = mock(() => Promise.resolve(new Response('Unauthorized', {status: 401})))
44
- const original = globalThis.fetch
45
- globalThis.fetch = mockFetch as unknown as typeof fetch
46
- try {
47
- await expect(requestJson('https://example.com/api', {method: 'POST'})).rejects.toThrow(
48
- 'POST https://example.com/api failed with HTTP 401: Unauthorized',
49
- )
50
- } finally {
51
- globalThis.fetch = original
52
- }
53
- })
54
-
55
- test('throws on 200 with malformed JSON body so mutating callers fail closed', async () => {
56
- const mockFetch = mock(() =>
57
- Promise.resolve(
58
- new Response('not-json-content', {
59
- status: 200,
60
- headers: {'content-type': 'text/plain'},
61
- }),
62
- ),
63
- )
64
- const original = globalThis.fetch
65
- globalThis.fetch = mockFetch as unknown as typeof fetch
66
- try {
67
- await expect(requestJson('https://example.com/api', {method: 'GET'})).rejects.toThrow(/returned malformed JSON/)
68
- } finally {
69
- globalThis.fetch = original
70
- }
71
- })
72
-
73
- test('returns null on 204 No Content', async () => {
74
- const mockFetch = mock(() => Promise.resolve(new Response(null, {status: 204})))
75
- const original = globalThis.fetch
76
- globalThis.fetch = mockFetch as unknown as typeof fetch
77
- try {
78
- const result = await requestJson('https://example.com/api', {method: 'DELETE'})
79
- expect(result).toBeNull()
80
- } finally {
81
- globalThis.fetch = original
82
- }
83
- })
84
- })
85
-
86
- describe('parseManagementKeyList', () => {
87
- test('accepts top-level string array', () => {
88
- expect(parseManagementKeyList(['k1', 'k2'])).toEqual(['k1', 'k2'])
89
- })
90
-
91
- test('accepts {api-keys: string[]}', () => {
92
- expect(parseManagementKeyList({'api-keys': ['k1']})).toEqual(['k1'])
93
- })
94
-
95
- test('accepts {api_keys: string[]}', () => {
96
- expect(parseManagementKeyList({api_keys: ['k1']})).toEqual(['k1'])
97
- })
98
-
99
- test('throws on null payload so destructive PUTs fail closed', () => {
100
- expect(() => parseManagementKeyList(null)).toThrow(/Unexpected management key-list shape/)
101
- })
102
-
103
- test('throws on empty object', () => {
104
- expect(() => parseManagementKeyList({})).toThrow(/Unexpected management key-list shape/)
105
- })
106
-
107
- test('throws on array of non-strings', () => {
108
- expect(() => parseManagementKeyList([1, 2, 3])).toThrow(/Unexpected management key-list shape/)
109
- })
110
-
111
- test('throws on string scalar', () => {
112
- expect(() => parseManagementKeyList('not-an-array')).toThrow(/Unexpected management key-list shape/)
113
- })
114
-
115
- test('throws on object with non-array api-keys field', () => {
116
- expect(() => parseManagementKeyList({'api-keys': 'k1'})).toThrow(/Unexpected management key-list shape/)
117
- })
118
- })
@@ -1,84 +0,0 @@
1
- // Shared HTTP helpers for cliproxy commands.
2
-
3
- export const HTTP_TIMEOUT_MS = 10_000
4
-
5
- // Permissive parser for /v0/management/api-keys list responses. Returns [] on every
6
- // unknown shape. Use ONLY for display paths (e.g. `cliproxy keys list`) where
7
- // empty-on-malformed is acceptable. Mutating callers (createManagementApiKey,
8
- // deleteManagementApiKey, `cliproxy keys add`) must use parseManagementKeyList
9
- // below — the permissive default would cause a destructive PUT to replace the
10
- // entire key list with just the new key.
11
- export function toStringArray(payload: unknown): string[] {
12
- if (Array.isArray(payload)) {
13
- return payload.filter((item): item is string => typeof item === 'string')
14
- }
15
-
16
- if (payload !== null && typeof payload === 'object') {
17
- const obj = payload as Record<string, unknown>
18
- const value = obj['api-keys'] ?? obj.api_keys
19
- if (Array.isArray(value)) {
20
- return value.filter((item): item is string => typeof item === 'string')
21
- }
22
- }
23
-
24
- return []
25
- }
26
-
27
- // Strict parser for /v0/management/api-keys list responses used by mutating callers.
28
- // Falls back to throw on any unknown shape — never returns [] on malformed input.
29
- // Accepts string[], {api-keys: string[]}, or {api_keys: string[]}. Throws on every other shape.
30
- export function parseManagementKeyList(payload: unknown): string[] {
31
- if (Array.isArray(payload)) {
32
- if (!payload.every((item): item is string => typeof item === 'string')) {
33
- throw new Error(
34
- `Unexpected management key-list shape: top-level array contains non-string entries (got ${JSON.stringify(payload).slice(0, 100)})`,
35
- )
36
- }
37
- return payload
38
- }
39
-
40
- if (payload !== null && typeof payload === 'object') {
41
- const obj = payload as Record<string, unknown>
42
- const value = obj['api-keys'] ?? obj.api_keys
43
- if (Array.isArray(value) && value.every((item): item is string => typeof item === 'string')) {
44
- return value
45
- }
46
- }
47
-
48
- throw new Error(
49
- `Unexpected management key-list shape: expected string[] or {api-keys: string[]} (got ${JSON.stringify(payload).slice(0, 100)})`,
50
- )
51
- }
52
-
53
- export function managementHeaders(key: string): Headers {
54
- const headers = new Headers()
55
- headers.set('x-management-key', key)
56
- headers.set('content-type', 'application/json')
57
- return headers
58
- }
59
-
60
- export async function requestJson(endpoint: string, init: RequestInit): Promise<unknown> {
61
- const response = await fetch(endpoint, {
62
- ...init,
63
- signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
64
- })
65
-
66
- if (!response.ok) {
67
- const body = await response.text()
68
- throw new Error(`${init.method ?? 'GET'} ${endpoint} failed with HTTP ${response.status}: ${body}`)
69
- }
70
-
71
- // 204 No Content is a valid empty response for some mutations.
72
- if (response.status === 204) return null
73
-
74
- // JSON parse failures must surface — permissive parsing here caused a data-loss
75
- // class bug (PR #312 Fro Bot review): bad management JSON would silently become
76
- // null, then toStringArray(null) → [], then a destructive PUT would replace the
77
- // entire key list with just the new key.
78
- try {
79
- return await response.json()
80
- } catch (parseError) {
81
- const message = parseError instanceof Error ? parseError.message : String(parseError)
82
- throw new Error(`${init.method ?? 'GET'} ${endpoint} returned malformed JSON: ${message}`)
83
- }
84
- }