@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.
- package/dist/cli.js +4977 -0
- package/package.json +7 -3
- package/src/__snapshots__/cli.test.ts.snap +0 -219
- package/src/__test__/mcp-ctx-fixture.test.ts +0 -108
- package/src/__test__/mcp-ctx-fixture.ts +0 -104
- package/src/cli.test.ts +0 -66
- package/src/cli.ts +0 -43
- package/src/commands/cliproxy/config.test.ts +0 -475
- package/src/commands/cliproxy/config.ts +0 -238
- package/src/commands/cliproxy/deploy.test.ts +0 -181
- package/src/commands/cliproxy/deploy.ts +0 -157
- package/src/commands/cliproxy/host.test.ts +0 -73
- package/src/commands/cliproxy/host.ts +0 -31
- package/src/commands/cliproxy/index.ts +0 -21
- package/src/commands/cliproxy/keys.test.ts +0 -326
- package/src/commands/cliproxy/keys.ts +0 -201
- package/src/commands/cliproxy/login.test.ts +0 -415
- package/src/commands/cliproxy/login.ts +0 -131
- package/src/commands/cliproxy/models.test.ts +0 -592
- package/src/commands/cliproxy/models.ts +0 -237
- package/src/commands/cliproxy/open.test.ts +0 -132
- package/src/commands/cliproxy/open.ts +0 -78
- package/src/commands/cliproxy/setup/gh.test.ts +0 -218
- package/src/commands/cliproxy/setup/gh.ts +0 -250
- package/src/commands/cliproxy/setup/preview.test.ts +0 -159
- package/src/commands/cliproxy/setup/preview.ts +0 -41
- package/src/commands/cliproxy/setup/prompts.test.ts +0 -58
- package/src/commands/cliproxy/setup/prompts.ts +0 -99
- package/src/commands/cliproxy/setup/providers.test.ts +0 -245
- package/src/commands/cliproxy/setup/providers.ts +0 -136
- package/src/commands/cliproxy/setup/smoke-test.test.ts +0 -821
- package/src/commands/cliproxy/setup/smoke-test.ts +0 -223
- package/src/commands/cliproxy/setup/templates.test.ts +0 -357
- package/src/commands/cliproxy/setup/templates.ts +0 -145
- package/src/commands/cliproxy/setup/validation.test.ts +0 -567
- package/src/commands/cliproxy/setup/validation.ts +0 -209
- package/src/commands/cliproxy/setup/workflow-analyzer.test.ts +0 -363
- package/src/commands/cliproxy/setup/workflow-analyzer.ts +0 -136
- package/src/commands/cliproxy/setup.test.ts +0 -2366
- package/src/commands/cliproxy/setup.ts +0 -809
- package/src/commands/cliproxy/shared.test.ts +0 -118
- package/src/commands/cliproxy/shared.ts +0 -84
- package/src/commands/cliproxy/status.test.ts +0 -1157
- package/src/commands/cliproxy/status.ts +0 -597
- package/src/commands/dashboard/deploy.test.ts +0 -215
- package/src/commands/dashboard/deploy.ts +0 -135
- package/src/commands/dashboard/host.test.ts +0 -79
- package/src/commands/dashboard/host.ts +0 -33
- package/src/commands/dashboard/index.ts +0 -13
- package/src/commands/dashboard/logs.test.ts +0 -317
- package/src/commands/dashboard/logs.ts +0 -180
- package/src/commands/dashboard/status.test.ts +0 -708
- package/src/commands/dashboard/status.ts +0 -293
- package/src/commands/gateway/backup.test.ts +0 -426
- package/src/commands/gateway/backup.ts +0 -205
- package/src/commands/gateway/deploy.test.ts +0 -491
- package/src/commands/gateway/deploy.ts +0 -173
- package/src/commands/gateway/host.test.ts +0 -89
- package/src/commands/gateway/host.ts +0 -33
- package/src/commands/gateway/index.ts +0 -17
- package/src/commands/gateway/logs.test.ts +0 -263
- package/src/commands/gateway/logs.ts +0 -160
- package/src/commands/gateway/restore.test.ts +0 -650
- package/src/commands/gateway/restore.ts +0 -308
- package/src/commands/gateway/status.test.ts +0 -755
- package/src/commands/gateway/status.ts +0 -255
- package/src/commands/keeweb/deploy.test.ts +0 -177
- package/src/commands/keeweb/deploy.ts +0 -191
- package/src/commands/keeweb/index.ts +0 -11
- package/src/commands/keeweb/open.test.ts +0 -54
- package/src/commands/keeweb/open.ts +0 -30
- package/src/commands/keeweb/status.test.ts +0 -509
- package/src/commands/keeweb/status.ts +0 -359
- package/src/commands/mcp.test.ts +0 -260
- package/src/commands/mcp.ts +0 -58
- package/src/commands/status.test.ts +0 -222
- package/src/commands/status.ts +0 -161
- package/src/commands/umami/deploy.test.ts +0 -202
- package/src/commands/umami/deploy.ts +0 -132
- package/src/commands/umami/host.test.ts +0 -79
- package/src/commands/umami/host.ts +0 -33
- package/src/commands/umami/index.ts +0 -13
- package/src/commands/umami/logs.test.ts +0 -191
- package/src/commands/umami/logs.ts +0 -163
- package/src/commands/umami/status.test.ts +0 -633
- package/src/commands/umami/status.ts +0 -285
- package/src/commands/vpn/client.test.ts +0 -949
- package/src/commands/vpn/client.ts +0 -467
- package/src/commands/vpn/deploy.test.ts +0 -208
- package/src/commands/vpn/deploy.ts +0 -143
- package/src/commands/vpn/host.test.ts +0 -89
- package/src/commands/vpn/host.ts +0 -36
- package/src/commands/vpn/index.ts +0 -15
- package/src/commands/vpn/logs.test.ts +0 -234
- package/src/commands/vpn/logs.ts +0 -136
- package/src/commands/vpn/peers.test.ts +0 -478
- package/src/commands/vpn/shared.test.ts +0 -454
- package/src/commands/vpn/shared.ts +0 -170
- package/src/commands/vpn/status.test.ts +0 -142
- package/src/commands/vpn/status.ts +0 -78
- package/src/conventions.test.ts +0 -1180
- package/src/lib/action-ctx.ts +0 -25
- package/src/lib/known-hosts.test.ts +0 -192
- package/src/lib/known-hosts.ts +0 -76
- package/src/lib/redact.ts +0 -21
- package/src/lib/ssh-identity.test.ts +0 -134
- package/src/lib/ssh-identity.ts +0 -76
- /package/{src/resources/known_hosts → dist/known_hosts-tk724cp0.} +0 -0
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import {afterEach, beforeEach, describe, expect, it} from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import {createCapturedCtx, expectCapturedToInclude, MockProcessExit} from '../../__test__/mcp-ctx-fixture'
|
|
4
|
-
import {vpnStatusAction} from './status'
|
|
5
|
-
|
|
6
|
-
// ─── SpawnFn helpers ──────────────────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
type SpawnFn = (
|
|
9
|
-
cmd: string[],
|
|
10
|
-
opts: {env: Record<string, string>; stdout: 'pipe'; stderr: 'pipe'},
|
|
11
|
-
) => {
|
|
12
|
-
stdout: ReadableStream<Uint8Array>
|
|
13
|
-
stderr: ReadableStream<Uint8Array>
|
|
14
|
-
exited: Promise<number>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function makeSpawnOk(output: string): SpawnFn {
|
|
18
|
-
return (_cmd, _opts) => {
|
|
19
|
-
const encoder = new TextEncoder()
|
|
20
|
-
return {
|
|
21
|
-
stdout: new ReadableStream({
|
|
22
|
-
start(controller) {
|
|
23
|
-
controller.enqueue(encoder.encode(output))
|
|
24
|
-
controller.close()
|
|
25
|
-
},
|
|
26
|
-
}),
|
|
27
|
-
stderr: new ReadableStream({
|
|
28
|
-
start(controller) {
|
|
29
|
-
controller.close()
|
|
30
|
-
},
|
|
31
|
-
}),
|
|
32
|
-
exited: Promise.resolve(0),
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function makeSpawnError(message: string): SpawnFn {
|
|
38
|
-
return (_cmd, _opts) => {
|
|
39
|
-
const encoder = new TextEncoder()
|
|
40
|
-
return {
|
|
41
|
-
stdout: new ReadableStream({
|
|
42
|
-
start(controller) {
|
|
43
|
-
controller.close()
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
46
|
-
stderr: new ReadableStream({
|
|
47
|
-
start(controller) {
|
|
48
|
-
controller.enqueue(encoder.encode(message))
|
|
49
|
-
controller.close()
|
|
50
|
-
},
|
|
51
|
-
}),
|
|
52
|
-
exited: Promise.resolve(1),
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ─── vpnStatusAction ctx capture ─────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
describe('vpnStatusAction — ctx capture', () => {
|
|
60
|
-
let originalEnv: Record<string, string | undefined>
|
|
61
|
-
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
originalEnv = {VPN_HOST: process.env.VPN_HOST}
|
|
64
|
-
process.env.VPN_HOST = '1.2.3.4'
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
afterEach(() => {
|
|
68
|
-
if (originalEnv.VPN_HOST === undefined) {
|
|
69
|
-
delete process.env.VPN_HOST
|
|
70
|
-
} else {
|
|
71
|
-
process.env.VPN_HOST = originalEnv.VPN_HOST
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('routes "Status: OK" to ctx.console.log when wg0 is up', async () => {
|
|
76
|
-
const {ctx, captured} = createCapturedCtx()
|
|
77
|
-
|
|
78
|
-
const wgOutput = [
|
|
79
|
-
'interface: wg0',
|
|
80
|
-
' public key: serverpubkey==',
|
|
81
|
-
' private key: (hidden)',
|
|
82
|
-
' listening port: 51820',
|
|
83
|
-
].join('\n')
|
|
84
|
-
|
|
85
|
-
await vpnStatusAction({key: undefined}, ctx, makeSpawnOk(wgOutput))
|
|
86
|
-
|
|
87
|
-
expect(expectCapturedToInclude(captured, 'Status: OK')).toBe(true)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('routes "VPN status" header to ctx.console.log', async () => {
|
|
91
|
-
const {ctx, captured} = createCapturedCtx()
|
|
92
|
-
|
|
93
|
-
const wgOutput = 'interface: wg0\n public key: key==\n private key: (hidden)\n listening port: 51820\n'
|
|
94
|
-
|
|
95
|
-
await vpnStatusAction({key: undefined}, ctx, makeSpawnOk(wgOutput))
|
|
96
|
-
|
|
97
|
-
expect(expectCapturedToInclude(captured, 'VPN status')).toBe(true)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('surfaces the server public key in the output', async () => {
|
|
101
|
-
const {ctx, captured} = createCapturedCtx()
|
|
102
|
-
|
|
103
|
-
const wgOutput = 'interface: wg0\n public key: MYPUBLICKEY==\n private key: (hidden)\n listening port: 51820\n'
|
|
104
|
-
|
|
105
|
-
await vpnStatusAction({key: undefined}, ctx, makeSpawnOk(wgOutput))
|
|
106
|
-
|
|
107
|
-
expect(expectCapturedToInclude(captured, 'MYPUBLICKEY==')).toBe(true)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('routes error to ctx.console.error and calls ctx.process.exit(1) when SSH fails', async () => {
|
|
111
|
-
const {ctx, captured} = createCapturedCtx()
|
|
112
|
-
|
|
113
|
-
await expect(vpnStatusAction({key: undefined}, ctx, makeSpawnError('Connection refused'))).rejects.toBeInstanceOf(
|
|
114
|
-
MockProcessExit,
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
expect(captured.stderr.join('').includes('Error')).toBe(true)
|
|
118
|
-
expect(captured.exit?.code).toBe(1)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('routes error to ctx.console.error and calls ctx.process.exit(1) when VPN_HOST is unset', async () => {
|
|
122
|
-
delete process.env.VPN_HOST
|
|
123
|
-
const {ctx, captured} = createCapturedCtx()
|
|
124
|
-
|
|
125
|
-
await expect(vpnStatusAction({key: undefined}, ctx)).rejects.toBeInstanceOf(MockProcessExit)
|
|
126
|
-
|
|
127
|
-
expect(captured.stderr.join('').includes('VPN_HOST')).toBe(true)
|
|
128
|
-
expect(captured.exit?.code).toBe(1)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('routes "Status: DEGRADED" to ctx.console.log and exits 1 when wg0 is down', async () => {
|
|
132
|
-
const {ctx, captured} = createCapturedCtx()
|
|
133
|
-
|
|
134
|
-
// wg show output without "interface:" line means interface is down
|
|
135
|
-
await expect(
|
|
136
|
-
vpnStatusAction({key: undefined}, ctx, makeSpawnOk('wg: The requested operation is not possible')),
|
|
137
|
-
).rejects.toBeInstanceOf(MockProcessExit)
|
|
138
|
-
|
|
139
|
-
expect(expectCapturedToInclude(captured, 'DEGRADED')).toBe(true)
|
|
140
|
-
expect(captured.exit?.code).toBe(1)
|
|
141
|
-
})
|
|
142
|
-
})
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type {goke} from 'goke'
|
|
2
|
-
import type {ActionCtx} from '../../lib/action-ctx'
|
|
3
|
-
|
|
4
|
-
import {z} from 'zod'
|
|
5
|
-
|
|
6
|
-
import {validateVpnHost} from './host'
|
|
7
|
-
import {getVpnWgStatus, type SpawnFn} from './shared'
|
|
8
|
-
|
|
9
|
-
export {getVpnStatusSummary} from './shared'
|
|
10
|
-
|
|
11
|
-
declare const process: {
|
|
12
|
-
env: Record<string, string | undefined>
|
|
13
|
-
exitCode?: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ─── Action (exported for direct testing) ────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
export async function vpnStatusAction(
|
|
19
|
-
options: {key?: string | undefined},
|
|
20
|
-
ctx: ActionCtx,
|
|
21
|
-
spawn?: SpawnFn,
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
const hostEnvKey = options.key ?? 'VPN_HOST'
|
|
24
|
-
const host = process.env[hostEnvKey]
|
|
25
|
-
|
|
26
|
-
if (!host) {
|
|
27
|
-
ctx.console.error(`VPN host not set. Export ${hostEnvKey} or pass --key <env-name> pointing to a set variable.`)
|
|
28
|
-
ctx.process.exit(1)
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
validateVpnHost(host)
|
|
34
|
-
} catch (error) {
|
|
35
|
-
ctx.console.error(error instanceof Error ? error.message : String(error))
|
|
36
|
-
ctx.process.exit(1)
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
ctx.console.log('VPN status')
|
|
41
|
-
ctx.console.log('')
|
|
42
|
-
|
|
43
|
-
const result = await getVpnWgStatus(host, spawn)
|
|
44
|
-
|
|
45
|
-
if (!result.ok && !result.wgShow) {
|
|
46
|
-
ctx.console.error(`Error: ${result.error ?? 'Unknown error'}`)
|
|
47
|
-
ctx.process.exit(1)
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const wgShow = result.wgShow
|
|
52
|
-
|
|
53
|
-
if (wgShow?.serverPublicKey) {
|
|
54
|
-
ctx.console.log(`Server public key: ${wgShow.serverPublicKey}`)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
ctx.console.log(`Peers: ${wgShow?.peerCount ?? 0}`)
|
|
58
|
-
ctx.console.log('')
|
|
59
|
-
|
|
60
|
-
if (result.ok && wgShow?.interfaceUp) {
|
|
61
|
-
ctx.console.log('Status: OK')
|
|
62
|
-
} else {
|
|
63
|
-
ctx.console.log('Status: DEGRADED (WireGuard interface not up)')
|
|
64
|
-
ctx.process.exit(1)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─── Command registration ─────────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
export function registerVpnStatus(cli: ReturnType<typeof goke>): void {
|
|
71
|
-
cli
|
|
72
|
-
.command('vpn status', 'Show operational health of the VPN box via SSH and wg show.')
|
|
73
|
-
.option(
|
|
74
|
-
'--key [key]',
|
|
75
|
-
z.string().describe('Environment variable name holding the SSH host. Falls back to VPN_HOST when omitted.'),
|
|
76
|
-
)
|
|
77
|
-
.action((options, ctx) => vpnStatusAction(options, ctx))
|
|
78
|
-
}
|