Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/cli/src/commands/verify/approve.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags } from '@oclif/core'
import { BeeperCommand, ensureWritable } from '../../lib/command.js'
import { createClient } from '../../lib/client.js'
import { resolveVerificationID } from '../../lib/app-state.js'
import { printData } from '../../lib/output.js'
export default class AuthVerifyApprove extends BeeperCommand {
static override summary = 'Approve a pending device verification request'
Expand All @@ -11,6 +12,6 @@ export default class AuthVerifyApprove extends BeeperCommand {
const { flags } = await this.parse(AuthVerifyApprove)
ensureWritable(flags)
const client = await createClient(flags)
await printData(await client.app.verifications.accept(flags.id ?? 'active'), flags.json ? 'json' : 'human')
await printData(await client.app.verifications.accept(await resolveVerificationID(client, flags.id)), flags.json ? 'json' : 'human')
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/verify/cancel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags } from '@oclif/core'
import { BeeperCommand, ensureWritable } from '../../lib/command.js'
import { createClient } from '../../lib/client.js'
import { resolveVerificationID } from '../../lib/app-state.js'
import { printData } from '../../lib/output.js'
export default class AuthVerifyCancel extends BeeperCommand {
static override summary = 'Cancel an in-progress device verification'
Expand All @@ -11,6 +12,6 @@ export default class AuthVerifyCancel extends BeeperCommand {
const { flags } = await this.parse(AuthVerifyCancel)
ensureWritable(flags)
const client = await createClient(flags)
await printData(await client.app.verifications.cancel(flags.id ?? 'active', {}), flags.json ? 'json' : 'human')
await printData(await client.app.verifications.cancel(await resolveVerificationID(client, flags.id), {}), flags.json ? 'json' : 'human')
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/verify/qr-confirm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags } from '@oclif/core'
import { BeeperCommand, ensureWritable } from '../../lib/command.js'
import { createClient } from '../../lib/client.js'
import { resolveVerificationID } from '../../lib/app-state.js'
import { printData } from '../../lib/output.js'
export default class AuthVerifyQrConfirm extends BeeperCommand {
static override summary = 'Confirm that the other device scanned your QR code'
Expand All @@ -11,6 +12,6 @@ export default class AuthVerifyQrConfirm extends BeeperCommand {
const { flags } = await this.parse(AuthVerifyQrConfirm)
ensureWritable(flags)
const client = await createClient(flags)
await printData(await client.app.verifications.qr.confirmScanned(flags.id ?? 'active'), flags.json ? 'json' : 'human')
await printData(await client.app.verifications.qr.confirmScanned(await resolveVerificationID(client, flags.id)), flags.json ? 'json' : 'human')
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/verify/sas-confirm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags } from '@oclif/core'
import { BeeperCommand, ensureWritable } from '../../lib/command.js'
import { createClient } from '../../lib/client.js'
import { resolveVerificationID } from '../../lib/app-state.js'
import { printData } from '../../lib/output.js'
export default class AuthVerifySasConfirm extends BeeperCommand {
static override summary = 'Confirm matching emoji verification'
Expand All @@ -11,6 +12,6 @@ export default class AuthVerifySasConfirm extends BeeperCommand {
const { flags } = await this.parse(AuthVerifySasConfirm)
ensureWritable(flags)
const client = await createClient(flags)
await printData(await client.app.verifications.sas.confirm(flags.id ?? 'active'), flags.json ? 'json' : 'human')
await printData(await client.app.verifications.sas.confirm(await resolveVerificationID(client, flags.id)), flags.json ? 'json' : 'human')
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/verify/sas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags } from '@oclif/core'
import { BeeperCommand, ensureWritable } from '../../lib/command.js'
import { createClient } from '../../lib/client.js'
import { resolveVerificationID } from '../../lib/app-state.js'
import { printData } from '../../lib/output.js'
export default class AuthVerifySas extends BeeperCommand {
static override summary = 'Start emoji verification'
Expand All @@ -11,6 +12,6 @@ export default class AuthVerifySas extends BeeperCommand {
const { flags } = await this.parse(AuthVerifySas)
ensureWritable(flags)
const client = await createClient(flags)
await printData(await client.app.verifications.sas.start(flags.id ?? 'active'), flags.json ? 'json' : 'human')
await printData(await client.app.verifications.sas.start(await resolveVerificationID(client, flags.id)), flags.json ? 'json' : 'human')
}
}
10 changes: 10 additions & 0 deletions packages/cli/src/lib/app-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ export async function getAppState(options: { baseURL?: string; target?: string;
return appRequest<AppState>('GET', '/v1/app/setup', options)
}

type VerificationLister = { app: { verifications: { list(): PromiseLike<{ items: Array<{ id: string }> }> } } }

// The API has no "active" alias: action routes 409 unless the path carries the real verification ID.
export async function resolveVerificationID(client: VerificationLister, id?: string): Promise<string> {
if (id) return id
const active = (await client.app.verifications.list()).items[0]
if (!active) throw new Error('No active verification. Start one with `beeper verify`.')
return active.id
}

export async function driveVerification(options: { baseURL?: string; target?: string; userID?: string; yes?: boolean } = {}): Promise<AppState> {
let state = await getAppState(options)
if (state.state === 'ready') return state
Expand Down
25 changes: 25 additions & 0 deletions packages/cli/test/verify-id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it, mock } from 'bun:test'
import { resolveVerificationID } from '../src/lib/app-state.js'

const clientWith = (items: Array<{ id: string }>) => {
const list = mock(async () => ({ items }))
return { list, client: { app: { verifications: { list } } } }
}

describe('resolveVerificationID', () => {
it('uses the active verification ID from the API when no --id is given', async () => {
const { client } = clientWith([{ id: 'txn-123' }])
expect(await resolveVerificationID(client)).toBe('txn-123')
})

it('uses an explicit --id without listing', async () => {
const { client, list } = clientWith([{ id: 'txn-123' }])
expect(await resolveVerificationID(client, 'txn-explicit')).toBe('txn-explicit')
expect(list).not.toHaveBeenCalled()
})

it('fails with a next step when there is no active verification', async () => {
const { client } = clientWith([])
await expect(resolveVerificationID(client)).rejects.toThrow('No active verification. Start one with `beeper verify`.')
})
})
Loading