← Public packages
@danj/audible-auth
Locked Audible credential gateway: Connect, token refresh, and narrow Audible calls for other Kody packages.
src/secrets.ts
85 lines · 2.7 KB · TypeScript/**
* Package secrets for the Audible tokens. Code writes values once with
* `secretSet` and afterwards handles only opaque `{{secret:…}}` refs, which
* Kody fills in on the way out to approved hosts.
*/
import { kody, packageSecrets } from 'kody:runtime'
import { NotConnectedError } from './device.ts'
export async function saveSecret(input: {
name: string
value: string
description: string
expiresAt?: string | null
}): Promise<{ packageId: string | null }> {
const saved = (await kody.secretSet({
name: input.name,
value: input.value,
description: input.description,
scope: 'package',
...(input.expiresAt !== undefined ? { expires_at: input.expiresAt } : {}),
})) as { package_id?: unknown } | null
return { packageId: typeof saved?.package_id === 'string' ? saved.package_id : null }
}
/** Delete a package secret. Returns false when Kody refuses or it is already gone. */
export async function deleteSecret(name: string): Promise<boolean> {
try {
const result = (await kody.secretDelete({ name, scope: 'package' })) as { deleted?: unknown } | null
return result?.deleted === true
} catch {
return false
}
}
/** Opaque placeholder for a package secret. Never a value. */
export async function secretRef(name: string): Promise<string> {
let ref: unknown
try {
ref = await packageSecrets.get(name)
} catch {
ref = null
}
if (typeof ref !== 'string' || !ref.startsWith('{{secret')) {
throw new NotConnectedError()
}
return ref
}
export type SecretState = {
exists: boolean
hosts: string[]
expiresAt: string | null
packageId: string | null
}
/**
* Metadata only: which hosts may receive each secret. Returns null when the
* runtime does not list package secrets (the hosted app surface does not).
*/
export async function secretStates(names: string[]): Promise<Record<string, SecretState> | null> {
const states: Record<string, SecretState> = {}
for (const name of names) states[name] = { exists: false, hosts: [], expiresAt: null, packageId: null }
let found = false
try {
const listed = (await kody.secretList({ scope: 'package' })) as
| Array<Record<string, unknown>>
| { secrets?: Array<Record<string, unknown>> }
const rows = Array.isArray(listed) ? listed : (listed?.secrets ?? [])
for (const row of rows) {
const name = String(row.name ?? '')
if (!(name in states)) continue
found = true
states[name] = {
exists: true,
hosts: Array.isArray(row.allowed_hosts) ? row.allowed_hosts.map(String) : [],
expiresAt: typeof row.expires_at === 'string' ? row.expires_at : null,
packageId: typeof row.package_id === 'string' ? row.package_id : null,
}
}
} catch {
return null
}
return found ? states : null
}