2026-01-06 17:51:38 +01:00
|
|
|
import type { Command } from "commander";
|
|
|
|
|
|
|
|
|
|
import { loadConfig } from "../config/config.js";
|
2026-01-11 11:45:25 +00:00
|
|
|
import { resolvePairingIdLabel } from "../pairing/pairing-labels.js";
|
2026-01-06 17:51:38 +01:00
|
|
|
import {
|
|
|
|
|
approveProviderPairingCode,
|
|
|
|
|
listProviderPairingRequests,
|
|
|
|
|
type PairingProvider,
|
|
|
|
|
} from "../pairing/pairing-store.js";
|
2026-01-11 11:45:25 +00:00
|
|
|
import {
|
|
|
|
|
listPairingProviders,
|
|
|
|
|
notifyPairingApproved,
|
|
|
|
|
resolvePairingProvider,
|
|
|
|
|
} from "../providers/plugins/pairing.js";
|
2026-01-06 17:51:38 +01:00
|
|
|
|
2026-01-11 11:45:25 +00:00
|
|
|
const PROVIDERS: PairingProvider[] = listPairingProviders();
|
2026-01-06 17:51:38 +01:00
|
|
|
|
|
|
|
|
function parseProvider(raw: unknown): PairingProvider {
|
2026-01-11 11:45:25 +00:00
|
|
|
return resolvePairingProvider(raw);
|
2026-01-06 17:51:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function notifyApproved(provider: PairingProvider, id: string) {
|
2026-01-11 11:45:25 +00:00
|
|
|
const cfg = loadConfig();
|
|
|
|
|
await notifyPairingApproved({ providerId: provider, id, cfg });
|
2026-01-06 17:51:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function registerPairingCli(program: Command) {
|
|
|
|
|
const pairing = program
|
|
|
|
|
.command("pairing")
|
|
|
|
|
.description("Secure DM pairing (approve inbound requests)");
|
|
|
|
|
|
|
|
|
|
pairing
|
|
|
|
|
.command("list")
|
|
|
|
|
.description("List pending pairing requests")
|
2026-01-10 16:36:43 +01:00
|
|
|
.option("--provider <provider>", `Provider (${PROVIDERS.join(", ")})`)
|
|
|
|
|
.argument("[provider]", `Provider (${PROVIDERS.join(", ")})`)
|
2026-01-06 17:51:38 +01:00
|
|
|
.option("--json", "Print JSON", false)
|
2026-01-10 16:36:43 +01:00
|
|
|
.action(async (providerArg, opts) => {
|
|
|
|
|
const providerRaw = opts.provider ?? providerArg;
|
|
|
|
|
if (!providerRaw) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Provider required. Use --provider <provider> or pass it as the first argument (expected one of: ${PROVIDERS.join(", ")})`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const provider = parseProvider(providerRaw);
|
2026-01-06 17:51:38 +01:00
|
|
|
const requests = await listProviderPairingRequests(provider);
|
|
|
|
|
if (opts.json) {
|
|
|
|
|
console.log(JSON.stringify({ provider, requests }, null, 2));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (requests.length === 0) {
|
|
|
|
|
console.log(`No pending ${provider} pairing requests.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (const r of requests) {
|
|
|
|
|
const meta = r.meta ? JSON.stringify(r.meta) : "";
|
2026-01-11 11:45:25 +00:00
|
|
|
const idLabel = resolvePairingIdLabel(provider);
|
2026-01-06 17:51:38 +01:00
|
|
|
console.log(
|
2026-01-09 00:01:23 +01:00
|
|
|
`${r.code} ${idLabel}=${r.id}${meta ? ` meta=${meta}` : ""} ${r.createdAt}`,
|
2026-01-06 17:51:38 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
pairing
|
|
|
|
|
.command("approve")
|
|
|
|
|
.description("Approve a pairing code and allow that sender")
|
2026-01-10 16:36:43 +01:00
|
|
|
.option("--provider <provider>", `Provider (${PROVIDERS.join(", ")})`)
|
|
|
|
|
.argument(
|
|
|
|
|
"<codeOrProvider>",
|
|
|
|
|
"Pairing code (or provider when using 2 args)",
|
2026-01-06 17:51:38 +01:00
|
|
|
)
|
2026-01-10 16:36:43 +01:00
|
|
|
.argument("[code]", "Pairing code (when provider is passed as the 1st arg)")
|
2026-01-06 17:51:38 +01:00
|
|
|
.option("--notify", "Notify the requester on the same provider", false)
|
2026-01-10 16:36:43 +01:00
|
|
|
.action(async (codeOrProvider, code, opts) => {
|
|
|
|
|
const providerRaw = opts.provider ?? codeOrProvider;
|
|
|
|
|
const resolvedCode = opts.provider ? codeOrProvider : code;
|
|
|
|
|
if (!opts.provider && !code) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Usage: clawdbot pairing approve <provider> <code> (or: clawdbot pairing approve --provider <provider> <code>)`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (opts.provider && code != null) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Too many arguments. Use: clawdbot pairing approve --provider <provider> <code>`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const provider = parseProvider(providerRaw);
|
2026-01-06 17:51:38 +01:00
|
|
|
const approved = await approveProviderPairingCode({
|
|
|
|
|
provider,
|
2026-01-10 16:36:43 +01:00
|
|
|
code: String(resolvedCode),
|
2026-01-06 17:51:38 +01:00
|
|
|
});
|
|
|
|
|
if (!approved) {
|
2026-01-10 16:36:43 +01:00
|
|
|
throw new Error(
|
|
|
|
|
`No pending pairing request found for code: ${String(resolvedCode)}`,
|
|
|
|
|
);
|
2026-01-06 17:51:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`Approved ${provider} sender ${approved.id}.`);
|
|
|
|
|
|
|
|
|
|
if (!opts.notify) return;
|
|
|
|
|
await notifyApproved(provider, approved.id).catch((err) => {
|
|
|
|
|
console.log(`Failed to notify requester: ${String(err)}`);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|