clawdentity 0.0.2 → 0.0.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.
package/dist/bin.js CHANGED
@@ -17062,7 +17062,7 @@ var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
17062
17062
 
17063
17063
  // src/index.ts
17064
17064
  import { createRequire } from "module";
17065
- import { Command as Command9 } from "commander";
17065
+ import { Command as Command10 } from "commander";
17066
17066
 
17067
17067
  // src/commands/admin.ts
17068
17068
  import { Command } from "commander";
@@ -19133,8 +19133,8 @@ var ConnectorClient = class {
19133
19133
  }
19134
19134
  }
19135
19135
  async wait(delayMs) {
19136
- await new Promise((resolve) => {
19137
- setTimeout(resolve, delayMs);
19136
+ await new Promise((resolve2) => {
19137
+ setTimeout(resolve2, delayMs);
19138
19138
  });
19139
19139
  }
19140
19140
  makeFrameId() {
@@ -19378,7 +19378,7 @@ async function buildUpgradeHeaders(input) {
19378
19378
  };
19379
19379
  }
19380
19380
  async function startConnectorRuntime(input) {
19381
- const logger11 = input.logger ?? createLogger({ service: "connector", module: "runtime" });
19381
+ const logger12 = input.logger ?? createLogger({ service: "connector", module: "runtime" });
19382
19382
  const fetchImpl = input.fetchImpl ?? fetch;
19383
19383
  const secretKey = decodeBase64url(
19384
19384
  parseRequiredString(input.credentials.secretKey, "secretKey")
@@ -19413,7 +19413,7 @@ async function startConnectorRuntime(input) {
19413
19413
  openclawHookPath: resolveOpenclawHookPath(input.openclawHookPath),
19414
19414
  openclawHookToken: resolveOpenclawHookToken(input.openclawHookToken),
19415
19415
  fetchImpl,
19416
- logger: logger11,
19416
+ logger: logger12,
19417
19417
  webSocketFactory: createWebSocketFactory()
19418
19418
  });
19419
19419
  const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
@@ -19502,7 +19502,7 @@ async function startConnectorRuntime(input) {
19502
19502
  writeJson(res, 202, { accepted: true, peer: relayRequest.peer });
19503
19503
  } catch (error48) {
19504
19504
  if (error48 instanceof AppError) {
19505
- logger11.warn("connector.outbound.rejected", {
19505
+ logger12.warn("connector.outbound.rejected", {
19506
19506
  code: error48.code,
19507
19507
  status: error48.status,
19508
19508
  message: error48.message
@@ -19515,7 +19515,7 @@ async function startConnectorRuntime(input) {
19515
19515
  });
19516
19516
  return;
19517
19517
  }
19518
- logger11.error("connector.outbound.failed", {
19518
+ logger12.error("connector.outbound.failed", {
19519
19519
  errorName: error48 instanceof Error ? error48.name : "unknown"
19520
19520
  });
19521
19521
  writeJson(res, 500, {
@@ -19527,35 +19527,35 @@ async function startConnectorRuntime(input) {
19527
19527
  }
19528
19528
  });
19529
19529
  let stoppedResolve;
19530
- const stoppedPromise = new Promise((resolve) => {
19531
- stoppedResolve = resolve;
19530
+ const stoppedPromise = new Promise((resolve2) => {
19531
+ stoppedResolve = resolve2;
19532
19532
  });
19533
19533
  const stop = async () => {
19534
19534
  connectorClient.disconnect();
19535
- await new Promise((resolve, reject) => {
19535
+ await new Promise((resolve2, reject) => {
19536
19536
  server.close((error48) => {
19537
19537
  if (error48) {
19538
19538
  reject(error48);
19539
19539
  return;
19540
19540
  }
19541
- resolve();
19541
+ resolve2();
19542
19542
  });
19543
19543
  });
19544
19544
  stoppedResolve?.();
19545
19545
  };
19546
- await new Promise((resolve, reject) => {
19546
+ await new Promise((resolve2, reject) => {
19547
19547
  server.once("error", reject);
19548
19548
  server.listen(
19549
19549
  Number(outboundBaseUrl.port || "80"),
19550
19550
  outboundBaseUrl.hostname,
19551
19551
  () => {
19552
19552
  server.off("error", reject);
19553
- resolve();
19553
+ resolve2();
19554
19554
  }
19555
19555
  );
19556
19556
  });
19557
19557
  connectorClient.connect();
19558
- logger11.info("connector.runtime.started", {
19558
+ logger12.info("connector.runtime.started", {
19559
19559
  outboundUrl,
19560
19560
  websocketUrl: wsUrl,
19561
19561
  agentDid: input.credentials.agentDid
@@ -21794,10 +21794,572 @@ var createOpenclawCommand = () => {
21794
21794
  return openclawCommand;
21795
21795
  };
21796
21796
 
21797
- // src/commands/verify.ts
21798
- import { readFile as readFile5 } from "fs/promises";
21797
+ // src/commands/pair.ts
21798
+ import { randomBytes as randomBytes3 } from "crypto";
21799
+ import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
21800
+ import { dirname as dirname5, join as join7, resolve } from "path";
21799
21801
  import { Command as Command8 } from "commander";
21800
- var logger9 = createLogger({ service: "cli", module: "verify" });
21802
+ import jsQR from "jsqr";
21803
+ import { PNG } from "pngjs";
21804
+ import QRCode from "qrcode";
21805
+ var logger9 = createLogger({ service: "cli", module: "pair" });
21806
+ var AGENTS_DIR_NAME5 = "agents";
21807
+ var AIT_FILE_NAME4 = "ait.jwt";
21808
+ var SECRET_KEY_FILE_NAME3 = "secret.key";
21809
+ var PAIRING_QR_DIR_NAME = "pairing";
21810
+ var PAIR_START_PATH = "/pair/start";
21811
+ var PAIR_CONFIRM_PATH = "/pair/confirm";
21812
+ var OWNER_PAT_HEADER = "x-claw-owner-pat";
21813
+ var NONCE_SIZE2 = 24;
21814
+ var PAIRING_TICKET_PREFIX = "clwpair1_";
21815
+ var isRecord8 = (value) => {
21816
+ return typeof value === "object" && value !== null;
21817
+ };
21818
+ function createCliError6(code, message2) {
21819
+ return new AppError({
21820
+ code,
21821
+ message: message2,
21822
+ status: 400
21823
+ });
21824
+ }
21825
+ function parseNonEmptyString8(value) {
21826
+ if (typeof value !== "string") {
21827
+ return "";
21828
+ }
21829
+ return value.trim();
21830
+ }
21831
+ function parsePairingTicket(value) {
21832
+ const ticket = parseNonEmptyString8(value);
21833
+ if (!ticket.startsWith(PAIRING_TICKET_PREFIX)) {
21834
+ throw createCliError6(
21835
+ "CLI_PAIR_CONFIRM_TICKET_INVALID",
21836
+ "Pairing ticket is invalid"
21837
+ );
21838
+ }
21839
+ return ticket;
21840
+ }
21841
+ function parseTtlSeconds(value) {
21842
+ const raw = parseNonEmptyString8(value);
21843
+ if (raw.length === 0) {
21844
+ return void 0;
21845
+ }
21846
+ const parsed = Number.parseInt(raw, 10);
21847
+ if (!Number.isInteger(parsed) || parsed < 1) {
21848
+ throw createCliError6(
21849
+ "CLI_PAIR_START_INVALID_TTL",
21850
+ "ttlSeconds must be a positive integer"
21851
+ );
21852
+ }
21853
+ return parsed;
21854
+ }
21855
+ function resolveProxyUrl(overrideProxyUrl) {
21856
+ const candidate = parseNonEmptyString8(overrideProxyUrl) || parseNonEmptyString8(process.env.CLAWDENTITY_PROXY_URL);
21857
+ if (candidate.length === 0) {
21858
+ throw createCliError6(
21859
+ "CLI_PAIR_PROXY_URL_REQUIRED",
21860
+ "Proxy URL is required. Pass --proxy-url <url> or set CLAWDENTITY_PROXY_URL."
21861
+ );
21862
+ }
21863
+ try {
21864
+ const parsed = new URL(candidate);
21865
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
21866
+ throw new Error("invalid protocol");
21867
+ }
21868
+ return parsed.toString();
21869
+ } catch {
21870
+ throw createCliError6("CLI_PAIR_INVALID_PROXY_URL", "Proxy URL is invalid");
21871
+ }
21872
+ }
21873
+ function toProxyRequestUrl(proxyUrl, path) {
21874
+ const normalizedBase = proxyUrl.endsWith("/") ? proxyUrl : `${proxyUrl}/`;
21875
+ return new URL(path.slice(1), normalizedBase).toString();
21876
+ }
21877
+ function toPathWithQuery3(url2) {
21878
+ const parsed = new URL(url2);
21879
+ return `${parsed.pathname}${parsed.search}`;
21880
+ }
21881
+ function extractErrorCode(payload) {
21882
+ if (!isRecord8(payload)) {
21883
+ return void 0;
21884
+ }
21885
+ const envelope = payload;
21886
+ if (!envelope.error || typeof envelope.error.code !== "string") {
21887
+ return void 0;
21888
+ }
21889
+ const code = envelope.error.code.trim();
21890
+ return code.length > 0 ? code : void 0;
21891
+ }
21892
+ function extractErrorMessage(payload) {
21893
+ if (!isRecord8(payload)) {
21894
+ return void 0;
21895
+ }
21896
+ const envelope = payload;
21897
+ if (!envelope.error || typeof envelope.error.message !== "string") {
21898
+ return void 0;
21899
+ }
21900
+ const message2 = envelope.error.message.trim();
21901
+ return message2.length > 0 ? message2 : void 0;
21902
+ }
21903
+ async function parseJsonResponse5(response) {
21904
+ try {
21905
+ return await response.json();
21906
+ } catch {
21907
+ return void 0;
21908
+ }
21909
+ }
21910
+ async function executePairRequest(input) {
21911
+ try {
21912
+ return await input.fetchImpl(input.url, input.init);
21913
+ } catch {
21914
+ throw createCliError6(
21915
+ "CLI_PAIR_REQUEST_FAILED",
21916
+ "Unable to connect to proxy URL. Check network access and proxyUrl."
21917
+ );
21918
+ }
21919
+ }
21920
+ function mapStartPairError(status, payload) {
21921
+ const code = extractErrorCode(payload);
21922
+ const message2 = extractErrorMessage(payload);
21923
+ if (code === "PROXY_PAIR_OWNER_PAT_INVALID" || status === 401) {
21924
+ return message2 ? `Owner PAT is invalid (401): ${message2}` : "Owner PAT is invalid or expired (401).";
21925
+ }
21926
+ if (code === "PROXY_PAIR_OWNER_PAT_FORBIDDEN" || status === 403) {
21927
+ return message2 ? `Owner PAT does not control initiator agent DID (403): ${message2}` : "Owner PAT does not control initiator agent DID (403).";
21928
+ }
21929
+ if (status === 400) {
21930
+ return message2 ? `Pair start request is invalid (400): ${message2}` : "Pair start request is invalid (400).";
21931
+ }
21932
+ if (status >= 500) {
21933
+ return `Proxy pairing service is unavailable (${status}).`;
21934
+ }
21935
+ if (message2) {
21936
+ return `Pair start failed (${status}): ${message2}`;
21937
+ }
21938
+ return `Pair start failed (${status})`;
21939
+ }
21940
+ function mapConfirmPairError(status, payload) {
21941
+ const code = extractErrorCode(payload);
21942
+ const message2 = extractErrorMessage(payload);
21943
+ if (code === "PROXY_PAIR_TICKET_NOT_FOUND" || status === 404) {
21944
+ return "Pairing ticket is invalid or expired";
21945
+ }
21946
+ if (code === "PROXY_PAIR_TICKET_EXPIRED" || status === 410) {
21947
+ return "Pairing ticket has expired";
21948
+ }
21949
+ if (status === 400) {
21950
+ return message2 ? `Pair confirm request is invalid (400): ${message2}` : "Pair confirm request is invalid (400).";
21951
+ }
21952
+ if (status >= 500) {
21953
+ return `Proxy pairing service is unavailable (${status}).`;
21954
+ }
21955
+ if (message2) {
21956
+ return `Pair confirm failed (${status}): ${message2}`;
21957
+ }
21958
+ return `Pair confirm failed (${status})`;
21959
+ }
21960
+ function parsePairStartResponse(payload) {
21961
+ if (!isRecord8(payload)) {
21962
+ throw createCliError6(
21963
+ "CLI_PAIR_START_INVALID_RESPONSE",
21964
+ "Pair start response is invalid"
21965
+ );
21966
+ }
21967
+ const ticket = parsePairingTicket(payload.ticket);
21968
+ const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
21969
+ const expiresAt = parseNonEmptyString8(payload.expiresAt);
21970
+ if (initiatorAgentDid.length === 0 || expiresAt.length === 0) {
21971
+ throw createCliError6(
21972
+ "CLI_PAIR_START_INVALID_RESPONSE",
21973
+ "Pair start response is invalid"
21974
+ );
21975
+ }
21976
+ return {
21977
+ ticket,
21978
+ initiatorAgentDid,
21979
+ expiresAt
21980
+ };
21981
+ }
21982
+ function parsePairConfirmResponse(payload) {
21983
+ if (!isRecord8(payload)) {
21984
+ throw createCliError6(
21985
+ "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
21986
+ "Pair confirm response is invalid"
21987
+ );
21988
+ }
21989
+ const paired = payload.paired === true;
21990
+ const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
21991
+ const responderAgentDid = parseNonEmptyString8(payload.responderAgentDid);
21992
+ if (!paired || initiatorAgentDid.length === 0 || responderAgentDid.length === 0) {
21993
+ throw createCliError6(
21994
+ "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
21995
+ "Pair confirm response is invalid"
21996
+ );
21997
+ }
21998
+ return {
21999
+ paired,
22000
+ initiatorAgentDid,
22001
+ responderAgentDid
22002
+ };
22003
+ }
22004
+ async function readAgentProofMaterial(agentName, dependencies) {
22005
+ const readFileImpl = dependencies.readFileImpl ?? readFile5;
22006
+ const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
22007
+ const normalizedAgentName = assertValidAgentName(agentName);
22008
+ const agentDir = join7(
22009
+ getConfigDirImpl(),
22010
+ AGENTS_DIR_NAME5,
22011
+ normalizedAgentName
22012
+ );
22013
+ const aitPath = join7(agentDir, AIT_FILE_NAME4);
22014
+ const secretKeyPath = join7(agentDir, SECRET_KEY_FILE_NAME3);
22015
+ let ait;
22016
+ try {
22017
+ ait = (await readFileImpl(aitPath, "utf-8")).trim();
22018
+ } catch (error48) {
22019
+ const nodeError = error48;
22020
+ if (nodeError.code === "ENOENT") {
22021
+ throw createCliError6(
22022
+ "CLI_PAIR_AGENT_NOT_FOUND",
22023
+ `Agent "${normalizedAgentName}" is missing ${AIT_FILE_NAME4}. Run agent create first.`
22024
+ );
22025
+ }
22026
+ throw error48;
22027
+ }
22028
+ if (ait.length === 0) {
22029
+ throw createCliError6(
22030
+ "CLI_PAIR_AGENT_NOT_FOUND",
22031
+ `Agent "${normalizedAgentName}" has an empty ${AIT_FILE_NAME4}`
22032
+ );
22033
+ }
22034
+ let encodedSecretKey;
22035
+ try {
22036
+ encodedSecretKey = (await readFileImpl(secretKeyPath, "utf-8")).trim();
22037
+ } catch (error48) {
22038
+ const nodeError = error48;
22039
+ if (nodeError.code === "ENOENT") {
22040
+ throw createCliError6(
22041
+ "CLI_PAIR_AGENT_NOT_FOUND",
22042
+ `Agent "${normalizedAgentName}" is missing ${SECRET_KEY_FILE_NAME3}. Run agent create first.`
22043
+ );
22044
+ }
22045
+ throw error48;
22046
+ }
22047
+ if (encodedSecretKey.length === 0) {
22048
+ throw createCliError6(
22049
+ "CLI_PAIR_AGENT_NOT_FOUND",
22050
+ `Agent "${normalizedAgentName}" has an empty ${SECRET_KEY_FILE_NAME3}`
22051
+ );
22052
+ }
22053
+ let secretKey;
22054
+ try {
22055
+ secretKey = decodeBase64url(encodedSecretKey);
22056
+ } catch {
22057
+ throw createCliError6(
22058
+ "CLI_PAIR_AGENT_NOT_FOUND",
22059
+ `Agent "${normalizedAgentName}" has invalid ${SECRET_KEY_FILE_NAME3}`
22060
+ );
22061
+ }
22062
+ return {
22063
+ ait,
22064
+ secretKey
22065
+ };
22066
+ }
22067
+ function resolveOwnerPat(options) {
22068
+ const ownerPat = parseNonEmptyString8(options.explicitOwnerPat) || parseNonEmptyString8(options.config.apiKey);
22069
+ if (ownerPat.length > 0) {
22070
+ return ownerPat;
22071
+ }
22072
+ throw createCliError6(
22073
+ "CLI_PAIR_START_OWNER_PAT_REQUIRED",
22074
+ "Owner PAT is required. Pass --owner-pat <token> or configure API key with `clawdentity invite redeem` / `clawdentity config set apiKey <token>`."
22075
+ );
22076
+ }
22077
+ async function buildSignedHeaders(input) {
22078
+ const signed = await signHttpRequest({
22079
+ method: input.method,
22080
+ pathWithQuery: toPathWithQuery3(input.requestUrl),
22081
+ timestamp: String(input.timestampSeconds),
22082
+ nonce: input.nonce,
22083
+ body: input.bodyBytes,
22084
+ secretKey: input.secretKey
22085
+ });
22086
+ return signed.headers;
22087
+ }
22088
+ async function encodeTicketQrPng(ticket) {
22089
+ const buffer = await QRCode.toBuffer(ticket, {
22090
+ type: "png",
22091
+ width: 512,
22092
+ margin: 2,
22093
+ errorCorrectionLevel: "M"
22094
+ });
22095
+ return new Uint8Array(buffer);
22096
+ }
22097
+ function decodeTicketFromPng(imageBytes) {
22098
+ let decodedPng;
22099
+ try {
22100
+ decodedPng = PNG.sync.read(Buffer.from(imageBytes));
22101
+ } catch {
22102
+ throw createCliError6(
22103
+ "CLI_PAIR_CONFIRM_QR_FILE_INVALID",
22104
+ "QR image file is invalid or unsupported"
22105
+ );
22106
+ }
22107
+ const imageData = new Uint8ClampedArray(
22108
+ decodedPng.data.buffer,
22109
+ decodedPng.data.byteOffset,
22110
+ decodedPng.data.byteLength
22111
+ );
22112
+ const decoded = jsQR(imageData, decodedPng.width, decodedPng.height);
22113
+ if (!decoded || parseNonEmptyString8(decoded.data).length === 0) {
22114
+ throw createCliError6(
22115
+ "CLI_PAIR_CONFIRM_QR_NOT_FOUND",
22116
+ "No pairing QR code was found in the image"
22117
+ );
22118
+ }
22119
+ return parsePairingTicket(decoded.data);
22120
+ }
22121
+ async function persistPairingQr(input) {
22122
+ const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir6;
22123
+ const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile6;
22124
+ const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
22125
+ const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
22126
+ const baseDir = join7(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22127
+ const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join7(
22128
+ baseDir,
22129
+ `${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
22130
+ );
22131
+ await mkdirImpl(dirname5(outputPath), { recursive: true });
22132
+ const imageBytes = await qrEncodeImpl(input.ticket);
22133
+ await writeFileImpl(outputPath, imageBytes);
22134
+ return outputPath;
22135
+ }
22136
+ function resolveConfirmTicketSource(options) {
22137
+ const inlineTicket = parseNonEmptyString8(options.ticket);
22138
+ const qrFile = parseNonEmptyString8(options.qrFile);
22139
+ if (inlineTicket.length > 0 && qrFile.length > 0) {
22140
+ throw createCliError6(
22141
+ "CLI_PAIR_CONFIRM_INPUT_CONFLICT",
22142
+ "Provide either --ticket or --qr-file, not both"
22143
+ );
22144
+ }
22145
+ if (inlineTicket.length > 0) {
22146
+ return {
22147
+ ticket: parsePairingTicket(inlineTicket),
22148
+ source: "ticket"
22149
+ };
22150
+ }
22151
+ if (qrFile.length > 0) {
22152
+ return {
22153
+ ticket: "",
22154
+ source: "qr-file",
22155
+ qrFilePath: resolve(qrFile)
22156
+ };
22157
+ }
22158
+ throw createCliError6(
22159
+ "CLI_PAIR_CONFIRM_TICKET_REQUIRED",
22160
+ "Pairing ticket is required. Pass --ticket <clwpair1_...> or --qr-file <path>."
22161
+ );
22162
+ }
22163
+ async function startPairing(agentName, options, dependencies = {}) {
22164
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
22165
+ const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
22166
+ const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
22167
+ const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
22168
+ const ttlSeconds = parseTtlSeconds(options.ttlSeconds);
22169
+ const proxyUrl = resolveProxyUrl(options.proxyUrl);
22170
+ const config2 = await resolveConfigImpl();
22171
+ const ownerPat = resolveOwnerPat({
22172
+ explicitOwnerPat: options.ownerPat,
22173
+ config: config2
22174
+ });
22175
+ const { ait, secretKey } = await readAgentProofMaterial(
22176
+ agentName,
22177
+ dependencies
22178
+ );
22179
+ const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_START_PATH);
22180
+ const requestBody = JSON.stringify({
22181
+ ttlSeconds
22182
+ });
22183
+ const bodyBytes = new TextEncoder().encode(requestBody);
22184
+ const timestampSeconds = nowSecondsImpl();
22185
+ const nonce = nonceFactoryImpl();
22186
+ const signedHeaders = await buildSignedHeaders({
22187
+ method: "POST",
22188
+ requestUrl,
22189
+ bodyBytes,
22190
+ secretKey,
22191
+ timestampSeconds,
22192
+ nonce
22193
+ });
22194
+ const response = await executePairRequest({
22195
+ fetchImpl,
22196
+ url: requestUrl,
22197
+ init: {
22198
+ method: "POST",
22199
+ headers: {
22200
+ authorization: `Claw ${ait}`,
22201
+ "content-type": "application/json",
22202
+ [OWNER_PAT_HEADER]: ownerPat,
22203
+ ...signedHeaders
22204
+ },
22205
+ body: requestBody
22206
+ }
22207
+ });
22208
+ const responseBody = await parseJsonResponse5(response);
22209
+ if (!response.ok) {
22210
+ throw createCliError6(
22211
+ "CLI_PAIR_START_FAILED",
22212
+ mapStartPairError(response.status, responseBody)
22213
+ );
22214
+ }
22215
+ const parsed = parsePairStartResponse(responseBody);
22216
+ const result = {
22217
+ ...parsed,
22218
+ proxyUrl
22219
+ };
22220
+ if (options.qr === true) {
22221
+ result.qrPath = await persistPairingQr({
22222
+ agentName,
22223
+ qrOutput: options.qrOutput,
22224
+ ticket: parsed.ticket,
22225
+ dependencies,
22226
+ nowSeconds: timestampSeconds
22227
+ });
22228
+ }
22229
+ return result;
22230
+ }
22231
+ async function confirmPairing(agentName, options, dependencies = {}) {
22232
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
22233
+ const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
22234
+ const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
22235
+ const readFileImpl = dependencies.readFileImpl ?? readFile5;
22236
+ const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
22237
+ const ticketSource = resolveConfirmTicketSource(options);
22238
+ const proxyUrl = resolveProxyUrl(options.proxyUrl);
22239
+ let ticket = ticketSource.ticket;
22240
+ if (ticketSource.source === "qr-file") {
22241
+ if (!ticketSource.qrFilePath) {
22242
+ throw createCliError6(
22243
+ "CLI_PAIR_CONFIRM_QR_FILE_REQUIRED",
22244
+ "QR file path is required"
22245
+ );
22246
+ }
22247
+ let imageBytes;
22248
+ try {
22249
+ imageBytes = await readFileImpl(ticketSource.qrFilePath);
22250
+ } catch (error48) {
22251
+ const nodeError = error48;
22252
+ if (nodeError.code === "ENOENT") {
22253
+ throw createCliError6(
22254
+ "CLI_PAIR_CONFIRM_QR_FILE_NOT_FOUND",
22255
+ `QR file not found: ${ticketSource.qrFilePath}`
22256
+ );
22257
+ }
22258
+ throw error48;
22259
+ }
22260
+ ticket = parsePairingTicket(qrDecodeImpl(new Uint8Array(imageBytes)));
22261
+ }
22262
+ const { ait, secretKey } = await readAgentProofMaterial(
22263
+ agentName,
22264
+ dependencies
22265
+ );
22266
+ const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_CONFIRM_PATH);
22267
+ const requestBody = JSON.stringify({ ticket });
22268
+ const bodyBytes = new TextEncoder().encode(requestBody);
22269
+ const timestampSeconds = nowSecondsImpl();
22270
+ const nonce = nonceFactoryImpl();
22271
+ const signedHeaders = await buildSignedHeaders({
22272
+ method: "POST",
22273
+ requestUrl,
22274
+ bodyBytes,
22275
+ secretKey,
22276
+ timestampSeconds,
22277
+ nonce
22278
+ });
22279
+ const response = await executePairRequest({
22280
+ fetchImpl,
22281
+ url: requestUrl,
22282
+ init: {
22283
+ method: "POST",
22284
+ headers: {
22285
+ authorization: `Claw ${ait}`,
22286
+ "content-type": "application/json",
22287
+ ...signedHeaders
22288
+ },
22289
+ body: requestBody
22290
+ }
22291
+ });
22292
+ const responseBody = await parseJsonResponse5(response);
22293
+ if (!response.ok) {
22294
+ throw createCliError6(
22295
+ "CLI_PAIR_CONFIRM_FAILED",
22296
+ mapConfirmPairError(response.status, responseBody)
22297
+ );
22298
+ }
22299
+ const parsed = parsePairConfirmResponse(responseBody);
22300
+ return {
22301
+ ...parsed,
22302
+ proxyUrl
22303
+ };
22304
+ }
22305
+ var createPairCommand = (dependencies = {}) => {
22306
+ const pairCommand = new Command8("pair").description(
22307
+ "Manage proxy trust pairing between agents"
22308
+ );
22309
+ pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option(
22310
+ "--proxy-url <url>",
22311
+ "Initiator proxy base URL (or set CLAWDENTITY_PROXY_URL)"
22312
+ ).option(
22313
+ "--owner-pat <token>",
22314
+ "Owner PAT override (defaults to configured API key)"
22315
+ ).option("--ttl-seconds <seconds>", "Pairing ticket expiry in seconds").option("--qr", "Generate a local QR file for sharing").option("--qr-output <path>", "Write QR PNG to a specific file path").action(
22316
+ withErrorHandling(
22317
+ "pair start",
22318
+ async (agentName, options) => {
22319
+ const result = await startPairing(agentName, options, dependencies);
22320
+ logger9.info("cli.pair_started", {
22321
+ initiatorAgentDid: result.initiatorAgentDid,
22322
+ proxyUrl: result.proxyUrl,
22323
+ expiresAt: result.expiresAt,
22324
+ qrPath: result.qrPath
22325
+ });
22326
+ writeStdoutLine("Pairing ticket created");
22327
+ writeStdoutLine(`Ticket: ${result.ticket}`);
22328
+ writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
22329
+ writeStdoutLine(`Expires At: ${result.expiresAt}`);
22330
+ if (result.qrPath) {
22331
+ writeStdoutLine(`QR File: ${result.qrPath}`);
22332
+ }
22333
+ }
22334
+ )
22335
+ );
22336
+ pairCommand.command("confirm <agentName>").description("Confirm pairing using one-time pairing ticket").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--qr-file <path>", "Path to pairing QR PNG file").option(
22337
+ "--proxy-url <url>",
22338
+ "Responder proxy base URL (or set CLAWDENTITY_PROXY_URL)"
22339
+ ).action(
22340
+ withErrorHandling(
22341
+ "pair confirm",
22342
+ async (agentName, options) => {
22343
+ const result = await confirmPairing(agentName, options, dependencies);
22344
+ logger9.info("cli.pair_confirmed", {
22345
+ initiatorAgentDid: result.initiatorAgentDid,
22346
+ responderAgentDid: result.responderAgentDid,
22347
+ proxyUrl: result.proxyUrl
22348
+ });
22349
+ writeStdoutLine("Pairing confirmed");
22350
+ writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
22351
+ writeStdoutLine(`Responder Agent DID: ${result.responderAgentDid}`);
22352
+ writeStdoutLine(`Paired: ${result.paired ? "true" : "false"}`);
22353
+ }
22354
+ )
22355
+ );
22356
+ return pairCommand;
22357
+ };
22358
+
22359
+ // src/commands/verify.ts
22360
+ import { readFile as readFile6 } from "fs/promises";
22361
+ import { Command as Command9 } from "commander";
22362
+ var logger10 = createLogger({ service: "cli", module: "verify" });
21801
22363
  var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
21802
22364
  var CRL_CLAIMS_CACHE_FILE = "crl-claims.json";
21803
22365
  var REGISTRY_KEYS_CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -21808,7 +22370,7 @@ var VerifyCommandError = class extends Error {
21808
22370
  this.name = "VerifyCommandError";
21809
22371
  }
21810
22372
  };
21811
- var isRecord8 = (value) => {
22373
+ var isRecord9 = (value) => {
21812
22374
  return typeof value === "object" && value !== null;
21813
22375
  };
21814
22376
  var normalizeRegistryUrl = (registryUrl) => {
@@ -21844,7 +22406,7 @@ var resolveToken = async (tokenOrFile) => {
21844
22406
  throw new VerifyCommandError("invalid token (value is empty)");
21845
22407
  }
21846
22408
  try {
21847
- const fileContents = await readFile5(input, "utf-8");
22409
+ const fileContents = await readFile6(input, "utf-8");
21848
22410
  const token = fileContents.trim();
21849
22411
  if (token.length === 0) {
21850
22412
  throw new VerifyCommandError(`invalid token (${input} is empty)`);
@@ -21876,7 +22438,7 @@ var parseResponseJson = async (response) => {
21876
22438
  }
21877
22439
  };
21878
22440
  var parseSigningKeys = (payload) => {
21879
- if (!isRecord8(payload) || !Array.isArray(payload.keys)) {
22441
+ if (!isRecord9(payload) || !Array.isArray(payload.keys)) {
21880
22442
  throw new VerifyCommandError(
21881
22443
  "verification keys unavailable (response payload is invalid)"
21882
22444
  );
@@ -21895,7 +22457,7 @@ var parseSigningKeys = (payload) => {
21895
22457
  };
21896
22458
  var parseRegistryKeysCache = (rawCache) => {
21897
22459
  const parsed = parseJson(rawCache);
21898
- if (!isRecord8(parsed)) {
22460
+ if (!isRecord9(parsed)) {
21899
22461
  return void 0;
21900
22462
  }
21901
22463
  const { registryUrl, fetchedAtMs, keys } = parsed;
@@ -21921,7 +22483,7 @@ var parseRegistryKeysCache = (rawCache) => {
21921
22483
  };
21922
22484
  var parseCrlCache = (rawCache) => {
21923
22485
  const parsed = parseJson(rawCache);
21924
- if (!isRecord8(parsed)) {
22486
+ if (!isRecord9(parsed)) {
21925
22487
  return void 0;
21926
22488
  }
21927
22489
  const { registryUrl, fetchedAtMs, claims } = parsed;
@@ -22019,7 +22581,7 @@ var fetchCrlClaims = async (input) => {
22019
22581
  );
22020
22582
  }
22021
22583
  const payload = await parseResponseJson(response);
22022
- if (!isRecord8(payload) || typeof payload.crl !== "string") {
22584
+ if (!isRecord9(payload) || typeof payload.crl !== "string") {
22023
22585
  throw new VerifyCommandError(
22024
22586
  "revocation check unavailable (response payload is invalid)"
22025
22587
  );
@@ -22064,7 +22626,7 @@ var loadCrlClaims = async (input) => {
22064
22626
  return claims;
22065
22627
  };
22066
22628
  var toInvalidTokenReason = (error48) => {
22067
- if (isRecord8(error48) && typeof error48.message === "string") {
22629
+ if (isRecord9(error48) && typeof error48.message === "string") {
22068
22630
  return `invalid token (${error48.message})`;
22069
22631
  }
22070
22632
  if (error48 instanceof Error && error48.message.length > 0) {
@@ -22132,7 +22694,7 @@ var runVerify = async (tokenOrFile) => {
22132
22694
  printResult(false, "revoked");
22133
22695
  return;
22134
22696
  }
22135
- logger9.info("cli.verify.success", {
22697
+ logger10.info("cli.verify.success", {
22136
22698
  did: claims.sub,
22137
22699
  jti: claims.jti,
22138
22700
  issuer: claims.iss
@@ -22140,7 +22702,7 @@ var runVerify = async (tokenOrFile) => {
22140
22702
  printResult(true, `token verified (${claims.sub})`);
22141
22703
  };
22142
22704
  var createVerifyCommand = () => {
22143
- return new Command8("verify").description("Verify an AIT using registry keys and CRL state").argument(
22705
+ return new Command9("verify").description("Verify an AIT using registry keys and CRL state").argument(
22144
22706
  "<tokenOrFile>",
22145
22707
  "Raw AIT token or file path containing the token"
22146
22708
  ).action(
@@ -22161,15 +22723,15 @@ var resolveCliVersion = () => {
22161
22723
  };
22162
22724
  var CLI_VERSION = resolveCliVersion();
22163
22725
  var createProgram = () => {
22164
- return new Command9("clawdentity").description("Clawdentity CLI - Agent identity management").version(CLI_VERSION).addCommand(createAdminCommand()).addCommand(createAgentCommand()).addCommand(createApiKeyCommand()).addCommand(createConnectorCommand()).addCommand(createConfigCommand()).addCommand(createInviteCommand()).addCommand(createOpenclawCommand()).addCommand(createVerifyCommand());
22726
+ return new Command10("clawdentity").description("Clawdentity CLI - Agent identity management").version(CLI_VERSION).addCommand(createAdminCommand()).addCommand(createAgentCommand()).addCommand(createApiKeyCommand()).addCommand(createConnectorCommand()).addCommand(createConfigCommand()).addCommand(createInviteCommand()).addCommand(createOpenclawCommand()).addCommand(createPairCommand()).addCommand(createVerifyCommand());
22165
22727
  };
22166
22728
 
22167
22729
  // src/bin.ts
22168
- var logger10 = createLogger({ service: "cli", module: "bin" });
22730
+ var logger11 = createLogger({ service: "cli", module: "bin" });
22169
22731
  createProgram().parseAsync(process.argv).catch((error48) => {
22170
22732
  process.exitCode = 1;
22171
22733
  const message2 = error48 instanceof Error ? error48.message : String(error48);
22172
- logger10.error("cli.execution_failed", { errorMessage: message2 });
22734
+ logger11.error("cli.execution_failed", { errorMessage: message2 });
22173
22735
  writeStderrLine(message2);
22174
22736
  });
22175
22737
  /*! Bundled license information: