clawdentity 0.0.12 → 0.0.14

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
@@ -17064,8 +17064,8 @@ function createLogger(baseFields = {}) {
17064
17064
  var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
17065
17065
 
17066
17066
  // src/index.ts
17067
- import { createRequire } from "module";
17068
- import { Command as Command10 } from "commander";
17067
+ import { createRequire as createRequire2 } from "module";
17068
+ import { Command as Command11 } from "commander";
17069
17069
 
17070
17070
  // src/commands/admin.ts
17071
17071
  import { Command } from "commander";
@@ -21167,13 +21167,7 @@ function resolveOpenclawConfigPath(openclawDir, homeDir) {
21167
21167
  return configCandidates[0];
21168
21168
  }
21169
21169
  function resolveDefaultTransformSource(openclawDir) {
21170
- return join6(
21171
- openclawDir,
21172
- "workspace",
21173
- "skills",
21174
- SKILL_DIR_NAME,
21175
- RELAY_MODULE_FILE_NAME
21176
- );
21170
+ return join6(openclawDir, "skills", SKILL_DIR_NAME, RELAY_MODULE_FILE_NAME);
21177
21171
  }
21178
21172
  function resolveTransformTargetPath(openclawDir) {
21179
21173
  return join6(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
@@ -21907,7 +21901,7 @@ async function runOpenclawDoctor(options = {}) {
21907
21901
  label: "Relay transform",
21908
21902
  status: "fail",
21909
21903
  message: "relay transform artifacts are missing or empty",
21910
- remediationHint: "Run: npm install clawdentity --skill",
21904
+ remediationHint: "Run: clawdentity skill install",
21911
21905
  details: {
21912
21906
  transformTargetPath,
21913
21907
  relayTransformRuntimePath,
@@ -21937,7 +21931,7 @@ async function runOpenclawDoctor(options = {}) {
21937
21931
  label: "Relay transform",
21938
21932
  status: "fail",
21939
21933
  message: "missing relay transform artifacts",
21940
- remediationHint: "Run: npm install clawdentity --skill",
21934
+ remediationHint: "Run: clawdentity skill install",
21941
21935
  details: {
21942
21936
  transformTargetPath,
21943
21937
  relayTransformRuntimePath,
@@ -22361,7 +22355,7 @@ var createOpenclawCommand = () => {
22361
22355
  "OpenClaw state directory (default ~/.openclaw)"
22362
22356
  ).option(
22363
22357
  "--transform-source <path>",
22364
- "Path to relay-to-peer.mjs (default <openclaw-dir>/workspace/skills/clawdentity-openclaw-relay/relay-to-peer.mjs)"
22358
+ "Path to relay-to-peer.mjs (default <openclaw-dir>/skills/clawdentity-openclaw-relay/relay-to-peer.mjs)"
22365
22359
  ).option(
22366
22360
  "--openclaw-base-url <url>",
22367
22361
  "Base URL for local OpenClaw hook API (default http://127.0.0.1:18789)"
@@ -22477,6 +22471,7 @@ var PAIRING_QR_DIR_NAME = "pairing";
22477
22471
  var PEERS_FILE_NAME2 = "peers.json";
22478
22472
  var PAIR_START_PATH = "/pair/start";
22479
22473
  var PAIR_CONFIRM_PATH = "/pair/confirm";
22474
+ var PAIR_STATUS_PATH = "/pair/status";
22480
22475
  var OWNER_PAT_HEADER = "x-claw-owner-pat";
22481
22476
  var NONCE_SIZE2 = 24;
22482
22477
  var PAIRING_TICKET_PREFIX = "clwpair1_";
@@ -22484,6 +22479,8 @@ var PAIRING_QR_MAX_AGE_SECONDS = 900;
22484
22479
  var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
22485
22480
  var FILE_MODE4 = 384;
22486
22481
  var PEER_ALIAS_PATTERN2 = /^[a-zA-Z0-9._-]+$/;
22482
+ var DEFAULT_STATUS_WAIT_SECONDS = 300;
22483
+ var DEFAULT_STATUS_POLL_INTERVAL_SECONDS = 3;
22487
22484
  var isRecord9 = (value) => {
22488
22485
  return typeof value === "object" && value !== null;
22489
22486
  };
@@ -22559,6 +22556,52 @@ function parsePairingTicketIssuerOrigin(ticket) {
22559
22556
  }
22560
22557
  return issuerUrl.origin;
22561
22558
  }
22559
+ function parseAitAgentDid(ait) {
22560
+ const parts = ait.split(".");
22561
+ if (parts.length < 2) {
22562
+ throw createCliError7(
22563
+ "CLI_PAIR_AGENT_NOT_FOUND",
22564
+ "Agent AIT is invalid. Recreate the agent before pairing."
22565
+ );
22566
+ }
22567
+ let payloadRaw;
22568
+ try {
22569
+ payloadRaw = new TextDecoder().decode(decodeBase64url(parts[1] ?? ""));
22570
+ } catch {
22571
+ throw createCliError7(
22572
+ "CLI_PAIR_AGENT_NOT_FOUND",
22573
+ "Agent AIT is invalid. Recreate the agent before pairing."
22574
+ );
22575
+ }
22576
+ let payload;
22577
+ try {
22578
+ payload = JSON.parse(payloadRaw);
22579
+ } catch {
22580
+ throw createCliError7(
22581
+ "CLI_PAIR_AGENT_NOT_FOUND",
22582
+ "Agent AIT is invalid. Recreate the agent before pairing."
22583
+ );
22584
+ }
22585
+ if (!isRecord9(payload) || typeof payload.sub !== "string") {
22586
+ throw createCliError7(
22587
+ "CLI_PAIR_AGENT_NOT_FOUND",
22588
+ "Agent AIT is invalid. Recreate the agent before pairing."
22589
+ );
22590
+ }
22591
+ const candidate = payload.sub.trim();
22592
+ try {
22593
+ const parsed = parseDid(candidate);
22594
+ if (parsed.kind !== "agent") {
22595
+ throw new Error("invalid kind");
22596
+ }
22597
+ } catch {
22598
+ throw createCliError7(
22599
+ "CLI_PAIR_AGENT_NOT_FOUND",
22600
+ "Agent AIT is invalid. Recreate the agent before pairing."
22601
+ );
22602
+ }
22603
+ return candidate;
22604
+ }
22562
22605
  function parsePeerAlias2(value) {
22563
22606
  if (value.length === 0 || value.length > 128) {
22564
22607
  throw createCliError7(
@@ -22690,6 +22733,20 @@ function parseTtlSeconds(value) {
22690
22733
  }
22691
22734
  return parsed;
22692
22735
  }
22736
+ function parsePositiveIntegerOption(input) {
22737
+ const raw = parseNonEmptyString9(input.value);
22738
+ if (raw.length === 0) {
22739
+ return input.defaultValue;
22740
+ }
22741
+ const parsed = Number.parseInt(raw, 10);
22742
+ if (!Number.isInteger(parsed) || parsed < 1) {
22743
+ throw createCliError7(
22744
+ "CLI_PAIR_STATUS_WAIT_INVALID",
22745
+ `${input.optionName} must be a positive integer`
22746
+ );
22747
+ }
22748
+ return parsed;
22749
+ }
22693
22750
  function parseProxyUrl2(candidate) {
22694
22751
  try {
22695
22752
  const parsed = new URL(candidate);
@@ -22810,6 +22867,29 @@ function mapConfirmPairError(status, payload) {
22810
22867
  }
22811
22868
  return `Pair confirm failed (${status})`;
22812
22869
  }
22870
+ function mapStatusPairError(status, payload) {
22871
+ const code = extractErrorCode(payload);
22872
+ const message2 = extractErrorMessage(payload);
22873
+ if (code === "PROXY_PAIR_TICKET_NOT_FOUND" || status === 404) {
22874
+ return "Pairing ticket not found";
22875
+ }
22876
+ if (code === "PROXY_PAIR_TICKET_EXPIRED" || status === 410) {
22877
+ return "Pairing ticket has expired";
22878
+ }
22879
+ if (code === "PROXY_PAIR_STATUS_FORBIDDEN" || status === 403) {
22880
+ return message2 ? `Pair status request is forbidden (403): ${message2}` : "Pair status request is forbidden (403).";
22881
+ }
22882
+ if (status === 400) {
22883
+ return message2 ? `Pair status request is invalid (400): ${message2}` : "Pair status request is invalid (400).";
22884
+ }
22885
+ if (status >= 500) {
22886
+ return `Proxy pairing service is unavailable (${status}).`;
22887
+ }
22888
+ if (message2) {
22889
+ return `Pair status failed (${status}): ${message2}`;
22890
+ }
22891
+ return `Pair status failed (${status})`;
22892
+ }
22813
22893
  function parsePairStartResponse(payload) {
22814
22894
  if (!isRecord9(payload)) {
22815
22895
  throw createCliError7(
@@ -22854,6 +22934,44 @@ function parsePairConfirmResponse(payload) {
22854
22934
  responderAgentDid
22855
22935
  };
22856
22936
  }
22937
+ function parsePairStatusResponse(payload) {
22938
+ if (!isRecord9(payload)) {
22939
+ throw createCliError7(
22940
+ "CLI_PAIR_STATUS_INVALID_RESPONSE",
22941
+ "Pair status response is invalid"
22942
+ );
22943
+ }
22944
+ const statusRaw = parseNonEmptyString9(payload.status);
22945
+ if (statusRaw !== "pending" && statusRaw !== "confirmed") {
22946
+ throw createCliError7(
22947
+ "CLI_PAIR_STATUS_INVALID_RESPONSE",
22948
+ "Pair status response is invalid"
22949
+ );
22950
+ }
22951
+ const initiatorAgentDid = parseNonEmptyString9(payload.initiatorAgentDid);
22952
+ const responderAgentDid = parseNonEmptyString9(payload.responderAgentDid);
22953
+ const expiresAt = parseNonEmptyString9(payload.expiresAt);
22954
+ const confirmedAt = parseNonEmptyString9(payload.confirmedAt);
22955
+ if (initiatorAgentDid.length === 0 || expiresAt.length === 0) {
22956
+ throw createCliError7(
22957
+ "CLI_PAIR_STATUS_INVALID_RESPONSE",
22958
+ "Pair status response is invalid"
22959
+ );
22960
+ }
22961
+ if (statusRaw === "confirmed" && responderAgentDid.length === 0) {
22962
+ throw createCliError7(
22963
+ "CLI_PAIR_STATUS_INVALID_RESPONSE",
22964
+ "Pair status response is invalid"
22965
+ );
22966
+ }
22967
+ return {
22968
+ status: statusRaw,
22969
+ initiatorAgentDid,
22970
+ responderAgentDid: responderAgentDid.length > 0 ? responderAgentDid : void 0,
22971
+ expiresAt,
22972
+ confirmedAt: confirmedAt.length > 0 ? confirmedAt : void 0
22973
+ };
22974
+ }
22857
22975
  async function readAgentProofMaterial(agentName, dependencies) {
22858
22976
  const readFileImpl = dependencies.readFileImpl ?? readFile5;
22859
22977
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
@@ -23244,6 +23362,147 @@ async function confirmPairing(agentName, options, dependencies = {}) {
23244
23362
  peerAlias
23245
23363
  };
23246
23364
  }
23365
+ async function getPairingStatusOnce(agentName, options, dependencies = {}) {
23366
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
23367
+ const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
23368
+ const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
23369
+ const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
23370
+ const config2 = await resolveConfigImpl();
23371
+ const proxyUrl = await resolveProxyUrl({
23372
+ config: config2,
23373
+ fetchImpl
23374
+ });
23375
+ const ticket = parsePairingTicket(options.ticket);
23376
+ const { ait, secretKey } = await readAgentProofMaterial(
23377
+ agentName,
23378
+ dependencies
23379
+ );
23380
+ const callerAgentDid = parseAitAgentDid(ait);
23381
+ const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_STATUS_PATH);
23382
+ const requestBody = JSON.stringify({ ticket });
23383
+ const bodyBytes = new TextEncoder().encode(requestBody);
23384
+ const timestampSeconds = nowSecondsImpl();
23385
+ const nonce = nonceFactoryImpl();
23386
+ const signedHeaders = await buildSignedHeaders({
23387
+ method: "POST",
23388
+ requestUrl,
23389
+ bodyBytes,
23390
+ secretKey,
23391
+ timestampSeconds,
23392
+ nonce
23393
+ });
23394
+ const response = await executePairRequest({
23395
+ fetchImpl,
23396
+ url: requestUrl,
23397
+ init: {
23398
+ method: "POST",
23399
+ headers: {
23400
+ authorization: `Claw ${ait}`,
23401
+ "content-type": "application/json",
23402
+ ...signedHeaders
23403
+ },
23404
+ body: requestBody
23405
+ }
23406
+ });
23407
+ const responseBody = await parseJsonResponse6(response);
23408
+ if (!response.ok) {
23409
+ throw createCliError7(
23410
+ "CLI_PAIR_STATUS_FAILED",
23411
+ mapStatusPairError(response.status, responseBody)
23412
+ );
23413
+ }
23414
+ const parsed = parsePairStatusResponse(responseBody);
23415
+ let peerAlias;
23416
+ if (parsed.status === "confirmed") {
23417
+ const responderAgentDid = parsed.responderAgentDid;
23418
+ if (!responderAgentDid) {
23419
+ throw createCliError7(
23420
+ "CLI_PAIR_STATUS_INVALID_RESPONSE",
23421
+ "Pair status response is invalid"
23422
+ );
23423
+ }
23424
+ const peerDid = callerAgentDid === parsed.initiatorAgentDid ? responderAgentDid : callerAgentDid === responderAgentDid ? parsed.initiatorAgentDid : void 0;
23425
+ if (!peerDid) {
23426
+ throw createCliError7(
23427
+ "CLI_PAIR_STATUS_FORBIDDEN",
23428
+ "Local agent is not a participant in the pairing ticket"
23429
+ );
23430
+ }
23431
+ peerAlias = await persistPairedPeer({
23432
+ ticket,
23433
+ peerDid,
23434
+ dependencies
23435
+ });
23436
+ }
23437
+ return {
23438
+ ...parsed,
23439
+ proxyUrl,
23440
+ peerAlias
23441
+ };
23442
+ }
23443
+ async function waitForPairingStatus(input) {
23444
+ const nowSecondsImpl = input.dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
23445
+ const sleepImpl = input.dependencies.sleepImpl ?? (async (ms) => {
23446
+ await new Promise((resolve2) => {
23447
+ setTimeout(resolve2, ms);
23448
+ });
23449
+ });
23450
+ const deadlineSeconds = nowSecondsImpl() + input.waitSeconds;
23451
+ while (true) {
23452
+ const status = await getPairingStatusOnce(
23453
+ input.agentName,
23454
+ { ticket: input.ticket },
23455
+ input.dependencies
23456
+ );
23457
+ if (status.status === "confirmed") {
23458
+ return status;
23459
+ }
23460
+ const nowSeconds = nowSecondsImpl();
23461
+ if (nowSeconds >= deadlineSeconds) {
23462
+ throw createCliError7(
23463
+ "CLI_PAIR_STATUS_WAIT_TIMEOUT",
23464
+ `Pairing is still pending after ${input.waitSeconds} seconds`
23465
+ );
23466
+ }
23467
+ const remainingSeconds = Math.max(0, deadlineSeconds - nowSeconds);
23468
+ const sleepSeconds = Math.min(input.pollIntervalSeconds, remainingSeconds);
23469
+ await sleepImpl(sleepSeconds * 1e3);
23470
+ }
23471
+ }
23472
+ async function getPairingStatus(agentName, options, dependencies = {}) {
23473
+ const ticketRaw = parseNonEmptyString9(options.ticket);
23474
+ if (ticketRaw.length === 0) {
23475
+ throw createCliError7(
23476
+ "CLI_PAIR_STATUS_TICKET_REQUIRED",
23477
+ "Pair status requires --ticket <clwpair1_...>"
23478
+ );
23479
+ }
23480
+ const ticket = parsePairingTicket(ticketRaw);
23481
+ if (options.wait !== true) {
23482
+ return getPairingStatusOnce(
23483
+ agentName,
23484
+ { ticket },
23485
+ dependencies
23486
+ );
23487
+ }
23488
+ const waitSeconds = parsePositiveIntegerOption({
23489
+ value: options.waitSeconds,
23490
+ optionName: "waitSeconds",
23491
+ defaultValue: DEFAULT_STATUS_WAIT_SECONDS
23492
+ });
23493
+ const pollIntervalSeconds = parsePositiveIntegerOption({
23494
+ value: options.pollIntervalSeconds,
23495
+ optionName: "pollIntervalSeconds",
23496
+ defaultValue: DEFAULT_STATUS_POLL_INTERVAL_SECONDS
23497
+ });
23498
+ return waitForPairingStatus({
23499
+ agentName,
23500
+ ticket,
23501
+ waitSeconds,
23502
+ pollIntervalSeconds,
23503
+ dependencies
23504
+ });
23505
+ }
23247
23506
  var createPairCommand = (dependencies = {}) => {
23248
23507
  const pairCommand = new Command8("pair").description(
23249
23508
  "Manage proxy trust pairing between agents"
@@ -23251,7 +23510,16 @@ var createPairCommand = (dependencies = {}) => {
23251
23510
  pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option(
23252
23511
  "--owner-pat <token>",
23253
23512
  "Owner PAT override (defaults to configured API key)"
23254
- ).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(
23513
+ ).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").option(
23514
+ "--wait",
23515
+ "Wait for responder confirmation and auto-save peer on initiator"
23516
+ ).option(
23517
+ "--wait-seconds <seconds>",
23518
+ "Max seconds to poll for confirmation (default: 300)"
23519
+ ).option(
23520
+ "--poll-interval-seconds <seconds>",
23521
+ "Polling interval in seconds while waiting (default: 3)"
23522
+ ).action(
23255
23523
  withErrorHandling(
23256
23524
  "pair start",
23257
23525
  async (agentName, options) => {
@@ -23269,6 +23537,44 @@ var createPairCommand = (dependencies = {}) => {
23269
23537
  if (result.qrPath) {
23270
23538
  writeStdoutLine(`QR File: ${result.qrPath}`);
23271
23539
  }
23540
+ if (options.wait === true) {
23541
+ const waitSeconds = parsePositiveIntegerOption({
23542
+ value: options.waitSeconds,
23543
+ optionName: "waitSeconds",
23544
+ defaultValue: DEFAULT_STATUS_WAIT_SECONDS
23545
+ });
23546
+ const pollIntervalSeconds = parsePositiveIntegerOption({
23547
+ value: options.pollIntervalSeconds,
23548
+ optionName: "pollIntervalSeconds",
23549
+ defaultValue: DEFAULT_STATUS_POLL_INTERVAL_SECONDS
23550
+ });
23551
+ writeStdoutLine(
23552
+ `Waiting for confirmation (timeout=${waitSeconds}s, interval=${pollIntervalSeconds}s) ...`
23553
+ );
23554
+ const status = await waitForPairingStatus({
23555
+ agentName,
23556
+ ticket: result.ticket,
23557
+ waitSeconds,
23558
+ pollIntervalSeconds,
23559
+ dependencies
23560
+ });
23561
+ logger9.info("cli.pair_status_confirmed_after_start", {
23562
+ initiatorAgentDid: status.initiatorAgentDid,
23563
+ responderAgentDid: status.responderAgentDid,
23564
+ peerAlias: status.peerAlias
23565
+ });
23566
+ writeStdoutLine("Pairing confirmed");
23567
+ writeStdoutLine(`Status: ${status.status}`);
23568
+ if (status.initiatorAgentDid) {
23569
+ writeStdoutLine(`Initiator Agent DID: ${status.initiatorAgentDid}`);
23570
+ }
23571
+ if (status.responderAgentDid) {
23572
+ writeStdoutLine(`Responder Agent DID: ${status.responderAgentDid}`);
23573
+ }
23574
+ if (status.peerAlias) {
23575
+ writeStdoutLine(`Peer alias saved: ${status.peerAlias}`);
23576
+ }
23577
+ }
23272
23578
  }
23273
23579
  )
23274
23580
  );
@@ -23293,12 +23599,377 @@ var createPairCommand = (dependencies = {}) => {
23293
23599
  }
23294
23600
  )
23295
23601
  );
23602
+ pairCommand.command("status <agentName>").description("Check pairing ticket status and sync local peer on confirm").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--wait", "Poll until ticket is confirmed or timeout is reached").option(
23603
+ "--wait-seconds <seconds>",
23604
+ "Max seconds to poll for confirmation (default: 300)"
23605
+ ).option(
23606
+ "--poll-interval-seconds <seconds>",
23607
+ "Polling interval in seconds while waiting (default: 3)"
23608
+ ).action(
23609
+ withErrorHandling(
23610
+ "pair status",
23611
+ async (agentName, options) => {
23612
+ const result = await getPairingStatus(agentName, options, dependencies);
23613
+ logger9.info("cli.pair_status", {
23614
+ initiatorAgentDid: result.initiatorAgentDid,
23615
+ responderAgentDid: result.responderAgentDid,
23616
+ status: result.status,
23617
+ proxyUrl: result.proxyUrl,
23618
+ peerAlias: result.peerAlias
23619
+ });
23620
+ writeStdoutLine(`Status: ${result.status}`);
23621
+ writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
23622
+ if (result.responderAgentDid) {
23623
+ writeStdoutLine(`Responder Agent DID: ${result.responderAgentDid}`);
23624
+ }
23625
+ writeStdoutLine(`Expires At: ${result.expiresAt}`);
23626
+ if (result.confirmedAt) {
23627
+ writeStdoutLine(`Confirmed At: ${result.confirmedAt}`);
23628
+ }
23629
+ if (result.peerAlias) {
23630
+ writeStdoutLine(`Peer alias saved: ${result.peerAlias}`);
23631
+ }
23632
+ }
23633
+ )
23634
+ );
23296
23635
  return pairCommand;
23297
23636
  };
23298
23637
 
23299
- // src/commands/verify.ts
23300
- import { readFile as readFile6 } from "fs/promises";
23638
+ // src/commands/skill.ts
23301
23639
  import { Command as Command9 } from "commander";
23640
+
23641
+ // src/install-skill-mode.ts
23642
+ import { constants, existsSync as existsSync2 } from "fs";
23643
+ import { access as access3, copyFile as copyFile2, mkdir as mkdir7, readdir as readdir2, readFile as readFile6 } from "fs/promises";
23644
+ import { createRequire } from "module";
23645
+ import { homedir as homedir4 } from "os";
23646
+ import { dirname as dirname6, join as join8, relative } from "path";
23647
+ import { fileURLToPath as fileURLToPath2 } from "url";
23648
+ var OPENCLAW_DIR_NAME2 = ".openclaw";
23649
+ var SKILL_PACKAGE_NAME = "@clawdentity/openclaw-skill";
23650
+ var SKILL_DIR_NAME2 = "clawdentity-openclaw-relay";
23651
+ var RELAY_MODULE_FILE_NAME2 = "relay-to-peer.mjs";
23652
+ function isRecord10(value) {
23653
+ return typeof value === "object" && value !== null;
23654
+ }
23655
+ var SkillInstallError = class extends Error {
23656
+ code;
23657
+ details;
23658
+ constructor(input) {
23659
+ super(input.message);
23660
+ this.name = "SkillInstallError";
23661
+ this.code = input.code;
23662
+ this.details = input.details ?? {};
23663
+ }
23664
+ };
23665
+ function getErrorCode3(error48) {
23666
+ if (!isRecord10(error48)) {
23667
+ return void 0;
23668
+ }
23669
+ return typeof error48.code === "string" ? error48.code : void 0;
23670
+ }
23671
+ function resolveHomeDir2(inputHomeDir) {
23672
+ if (typeof inputHomeDir === "string" && inputHomeDir.trim().length > 0) {
23673
+ return inputHomeDir.trim();
23674
+ }
23675
+ return homedir4();
23676
+ }
23677
+ function resolveOpenclawDir2(homeDir, inputOpenclawDir) {
23678
+ if (typeof inputOpenclawDir === "string" && inputOpenclawDir.trim().length > 0) {
23679
+ return inputOpenclawDir.trim();
23680
+ }
23681
+ return join8(homeDir, OPENCLAW_DIR_NAME2);
23682
+ }
23683
+ function resolveSkillPackageRoot(input) {
23684
+ if (typeof input.skillPackageRoot === "string" && input.skillPackageRoot.trim().length > 0) {
23685
+ return input.skillPackageRoot.trim();
23686
+ }
23687
+ const overriddenRoot = input.env.CLAWDENTITY_SKILL_PACKAGE_ROOT;
23688
+ if (typeof overriddenRoot === "string" && overriddenRoot.trim().length > 0) {
23689
+ return overriddenRoot.trim();
23690
+ }
23691
+ const bundledSkillRoot = join8(
23692
+ dirname6(fileURLToPath2(import.meta.url)),
23693
+ "..",
23694
+ "skill-bundle",
23695
+ "openclaw-skill"
23696
+ );
23697
+ if (existsSync2(bundledSkillRoot)) {
23698
+ return bundledSkillRoot;
23699
+ }
23700
+ const require3 = createRequire(import.meta.url);
23701
+ let packageJsonPath;
23702
+ try {
23703
+ packageJsonPath = require3.resolve(`${SKILL_PACKAGE_NAME}/package.json`);
23704
+ return dirname6(packageJsonPath);
23705
+ } catch {
23706
+ const workspaceFallbackRoot = join8(
23707
+ dirname6(fileURLToPath2(import.meta.url)),
23708
+ "..",
23709
+ "..",
23710
+ "openclaw-skill"
23711
+ );
23712
+ if (existsSync2(workspaceFallbackRoot)) {
23713
+ return workspaceFallbackRoot;
23714
+ }
23715
+ throw new SkillInstallError({
23716
+ code: "CLI_SKILL_PACKAGE_NOT_FOUND",
23717
+ message: "Skill artifacts are unavailable. Set CLAWDENTITY_SKILL_PACKAGE_ROOT or provide bundled skill assets before running skill install.",
23718
+ details: {
23719
+ packageName: SKILL_PACKAGE_NAME,
23720
+ bundledSkillRoot,
23721
+ workspaceFallbackRoot
23722
+ }
23723
+ });
23724
+ }
23725
+ }
23726
+ async function assertReadableFile(filePath, details) {
23727
+ try {
23728
+ await access3(filePath, constants.R_OK);
23729
+ } catch (error48) {
23730
+ if (getErrorCode3(error48) === "ENOENT") {
23731
+ throw new SkillInstallError({
23732
+ code: "CLI_SKILL_ARTIFACT_MISSING",
23733
+ message: "Required skill artifact is missing",
23734
+ details: {
23735
+ ...details,
23736
+ sourcePath: filePath
23737
+ }
23738
+ });
23739
+ }
23740
+ throw error48;
23741
+ }
23742
+ }
23743
+ async function listFilesRecursively(directoryPath) {
23744
+ const entries = await readdir2(directoryPath, { withFileTypes: true });
23745
+ const files = [];
23746
+ for (const entry of entries.sort(
23747
+ (left, right) => left.name.localeCompare(right.name)
23748
+ )) {
23749
+ const entryPath = join8(directoryPath, entry.name);
23750
+ if (entry.isDirectory()) {
23751
+ files.push(...await listFilesRecursively(entryPath));
23752
+ continue;
23753
+ }
23754
+ if (entry.isFile()) {
23755
+ files.push(entryPath);
23756
+ }
23757
+ }
23758
+ return files;
23759
+ }
23760
+ async function resolveArtifacts(input) {
23761
+ const skillRoot = join8(input.skillPackageRoot, "skill");
23762
+ const skillDocSource = join8(skillRoot, "SKILL.md");
23763
+ const referencesRoot = join8(skillRoot, "references");
23764
+ const relaySource = join8(
23765
+ input.skillPackageRoot,
23766
+ "dist",
23767
+ RELAY_MODULE_FILE_NAME2
23768
+ );
23769
+ await assertReadableFile(skillDocSource, {
23770
+ artifact: "SKILL.md"
23771
+ });
23772
+ await assertReadableFile(relaySource, {
23773
+ artifact: RELAY_MODULE_FILE_NAME2
23774
+ });
23775
+ let referenceFiles;
23776
+ try {
23777
+ referenceFiles = await listFilesRecursively(referencesRoot);
23778
+ } catch (error48) {
23779
+ if (getErrorCode3(error48) === "ENOENT") {
23780
+ throw new SkillInstallError({
23781
+ code: "CLI_SKILL_ARTIFACT_MISSING",
23782
+ message: "Required skill references directory is missing",
23783
+ details: {
23784
+ sourcePath: referencesRoot,
23785
+ artifact: "references"
23786
+ }
23787
+ });
23788
+ }
23789
+ throw error48;
23790
+ }
23791
+ if (referenceFiles.length === 0) {
23792
+ throw new SkillInstallError({
23793
+ code: "CLI_SKILL_REFERENCE_DIR_EMPTY",
23794
+ message: "Required skill references directory is empty",
23795
+ details: {
23796
+ sourcePath: referencesRoot
23797
+ }
23798
+ });
23799
+ }
23800
+ const targetSkillRoot = join8(
23801
+ input.openclawDir,
23802
+ "skills",
23803
+ SKILL_DIR_NAME2
23804
+ );
23805
+ const artifacts = [
23806
+ {
23807
+ sourcePath: skillDocSource,
23808
+ targetPath: join8(targetSkillRoot, "SKILL.md")
23809
+ },
23810
+ {
23811
+ sourcePath: relaySource,
23812
+ targetPath: join8(targetSkillRoot, RELAY_MODULE_FILE_NAME2)
23813
+ },
23814
+ {
23815
+ sourcePath: relaySource,
23816
+ targetPath: join8(
23817
+ input.openclawDir,
23818
+ "hooks",
23819
+ "transforms",
23820
+ RELAY_MODULE_FILE_NAME2
23821
+ )
23822
+ }
23823
+ ];
23824
+ for (const referenceFile of referenceFiles) {
23825
+ const relativePath = relative(referencesRoot, referenceFile);
23826
+ artifacts.push({
23827
+ sourcePath: referenceFile,
23828
+ targetPath: join8(targetSkillRoot, "references", relativePath)
23829
+ });
23830
+ }
23831
+ return artifacts.sort(
23832
+ (left, right) => left.targetPath.localeCompare(right.targetPath)
23833
+ );
23834
+ }
23835
+ async function copyArtifact(input) {
23836
+ const sourceContent = await readFile6(input.sourcePath);
23837
+ let existingContent;
23838
+ try {
23839
+ existingContent = await readFile6(input.targetPath);
23840
+ } catch (error48) {
23841
+ if (getErrorCode3(error48) !== "ENOENT") {
23842
+ throw error48;
23843
+ }
23844
+ }
23845
+ if (existingContent !== void 0 && sourceContent.equals(existingContent)) {
23846
+ return "unchanged";
23847
+ }
23848
+ await mkdir7(dirname6(input.targetPath), { recursive: true });
23849
+ await copyFile2(input.sourcePath, input.targetPath);
23850
+ if (existingContent !== void 0) {
23851
+ return "updated";
23852
+ }
23853
+ return "installed";
23854
+ }
23855
+ async function installOpenclawSkillArtifacts(options = {}) {
23856
+ const env = options.env ?? process.env;
23857
+ const homeDir = resolveHomeDir2(options.homeDir);
23858
+ const openclawDir = resolveOpenclawDir2(homeDir, options.openclawDir);
23859
+ const skillPackageRoot = resolveSkillPackageRoot({
23860
+ skillPackageRoot: options.skillPackageRoot,
23861
+ env
23862
+ });
23863
+ const artifacts = await resolveArtifacts({
23864
+ skillPackageRoot,
23865
+ openclawDir
23866
+ });
23867
+ const records = [];
23868
+ for (const artifact of artifacts) {
23869
+ const action = await copyArtifact(artifact);
23870
+ records.push({
23871
+ action,
23872
+ sourcePath: artifact.sourcePath,
23873
+ targetPath: artifact.targetPath
23874
+ });
23875
+ }
23876
+ return {
23877
+ homeDir,
23878
+ openclawDir,
23879
+ skillPackageRoot,
23880
+ targetSkillDirectory: join8(openclawDir, "skills", SKILL_DIR_NAME2),
23881
+ records
23882
+ };
23883
+ }
23884
+ function formatSkillInstallError(error48) {
23885
+ if (error48 instanceof SkillInstallError) {
23886
+ const details = Object.entries(error48.details).map(([key, value]) => `${key}=${value}`).join(" ");
23887
+ if (details.length === 0) {
23888
+ return `${error48.code}: ${error48.message}`;
23889
+ }
23890
+ return `${error48.code}: ${error48.message} (${details})`;
23891
+ }
23892
+ if (error48 instanceof Error) {
23893
+ return error48.message;
23894
+ }
23895
+ return String(error48);
23896
+ }
23897
+
23898
+ // src/commands/skill.ts
23899
+ function collectStringOption(value, previous) {
23900
+ const trimmed = value.trim();
23901
+ if (trimmed.length === 0) {
23902
+ return previous;
23903
+ }
23904
+ return [...previous, trimmed];
23905
+ }
23906
+ function toInstallSummary(records) {
23907
+ const installed = records.filter((record2) => record2.action === "installed");
23908
+ const updated = records.filter((record2) => record2.action === "updated");
23909
+ const unchanged = records.filter((record2) => record2.action === "unchanged");
23910
+ return `installed=${installed.length} updated=${updated.length} unchanged=${unchanged.length}`;
23911
+ }
23912
+ async function runSkillInstall(options) {
23913
+ const requestedDirs = (options.openclawDir ?? []).filter(
23914
+ (dir) => dir.trim().length > 0
23915
+ );
23916
+ const dirs = requestedDirs.length > 0 ? requestedDirs : [void 0];
23917
+ const results = [];
23918
+ for (const openclawDir of dirs) {
23919
+ const result = await installOpenclawSkillArtifacts({
23920
+ openclawDir,
23921
+ skillPackageRoot: options.skillPackageRoot
23922
+ });
23923
+ results.push(result);
23924
+ }
23925
+ return results;
23926
+ }
23927
+ var createSkillCommand = () => {
23928
+ const skillCommand = new Command9("skill").description(
23929
+ "Install and manage Clawdentity skill artifacts"
23930
+ );
23931
+ skillCommand.command("install").description("Install Clawdentity OpenClaw skill artifacts").option(
23932
+ "--openclaw-dir <path>",
23933
+ "OpenClaw state directory target (repeat for multiple profiles)",
23934
+ collectStringOption,
23935
+ []
23936
+ ).option(
23937
+ "--skill-package-root <path>",
23938
+ "Override skill package root (defaults to bundled assets)"
23939
+ ).option("--json", "Print machine-readable JSON output").action(
23940
+ withErrorHandling(
23941
+ "skill install",
23942
+ async (options) => {
23943
+ let results;
23944
+ try {
23945
+ results = await runSkillInstall(options);
23946
+ } catch (error48) {
23947
+ throw new Error(formatSkillInstallError(error48));
23948
+ }
23949
+ if (options.json) {
23950
+ writeStdoutLine(JSON.stringify({ installs: results }, null, 2));
23951
+ return;
23952
+ }
23953
+ for (const result of results) {
23954
+ writeStdoutLine(`OpenClaw dir: ${result.openclawDir}`);
23955
+ writeStdoutLine(`Skill source: ${result.skillPackageRoot}`);
23956
+ writeStdoutLine(`Target skill dir: ${result.targetSkillDirectory}`);
23957
+ for (const record2 of result.records) {
23958
+ writeStdoutLine(
23959
+ `${record2.action}: ${record2.targetPath} (source: ${record2.sourcePath})`
23960
+ );
23961
+ }
23962
+ writeStdoutLine(toInstallSummary(result.records));
23963
+ }
23964
+ }
23965
+ )
23966
+ );
23967
+ return skillCommand;
23968
+ };
23969
+
23970
+ // src/commands/verify.ts
23971
+ import { readFile as readFile7 } from "fs/promises";
23972
+ import { Command as Command10 } from "commander";
23302
23973
  var logger10 = createLogger({ service: "cli", module: "verify" });
23303
23974
  var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
23304
23975
  var CRL_CLAIMS_CACHE_FILE = "crl-claims.json";
@@ -23310,7 +23981,7 @@ var VerifyCommandError = class extends Error {
23310
23981
  this.name = "VerifyCommandError";
23311
23982
  }
23312
23983
  };
23313
- var isRecord10 = (value) => {
23984
+ var isRecord11 = (value) => {
23314
23985
  return typeof value === "object" && value !== null;
23315
23986
  };
23316
23987
  var normalizeRegistryUrl2 = (registryUrl) => {
@@ -23346,7 +24017,7 @@ var resolveToken = async (tokenOrFile) => {
23346
24017
  throw new VerifyCommandError("invalid token (value is empty)");
23347
24018
  }
23348
24019
  try {
23349
- const fileContents = await readFile6(input, "utf-8");
24020
+ const fileContents = await readFile7(input, "utf-8");
23350
24021
  const token = fileContents.trim();
23351
24022
  if (token.length === 0) {
23352
24023
  throw new VerifyCommandError(`invalid token (${input} is empty)`);
@@ -23378,7 +24049,7 @@ var parseResponseJson = async (response) => {
23378
24049
  }
23379
24050
  };
23380
24051
  var parseSigningKeys = (payload) => {
23381
- if (!isRecord10(payload) || !Array.isArray(payload.keys)) {
24052
+ if (!isRecord11(payload) || !Array.isArray(payload.keys)) {
23382
24053
  throw new VerifyCommandError(
23383
24054
  "verification keys unavailable (response payload is invalid)"
23384
24055
  );
@@ -23397,7 +24068,7 @@ var parseSigningKeys = (payload) => {
23397
24068
  };
23398
24069
  var parseRegistryKeysCache = (rawCache) => {
23399
24070
  const parsed = parseJson(rawCache);
23400
- if (!isRecord10(parsed)) {
24071
+ if (!isRecord11(parsed)) {
23401
24072
  return void 0;
23402
24073
  }
23403
24074
  const { registryUrl, fetchedAtMs, keys } = parsed;
@@ -23423,7 +24094,7 @@ var parseRegistryKeysCache = (rawCache) => {
23423
24094
  };
23424
24095
  var parseCrlCache = (rawCache) => {
23425
24096
  const parsed = parseJson(rawCache);
23426
- if (!isRecord10(parsed)) {
24097
+ if (!isRecord11(parsed)) {
23427
24098
  return void 0;
23428
24099
  }
23429
24100
  const { registryUrl, fetchedAtMs, claims } = parsed;
@@ -23521,7 +24192,7 @@ var fetchCrlClaims = async (input) => {
23521
24192
  );
23522
24193
  }
23523
24194
  const payload = await parseResponseJson(response);
23524
- if (!isRecord10(payload) || typeof payload.crl !== "string") {
24195
+ if (!isRecord11(payload) || typeof payload.crl !== "string") {
23525
24196
  throw new VerifyCommandError(
23526
24197
  "revocation check unavailable (response payload is invalid)"
23527
24198
  );
@@ -23566,7 +24237,7 @@ var loadCrlClaims = async (input) => {
23566
24237
  return claims;
23567
24238
  };
23568
24239
  var toInvalidTokenReason = (error48) => {
23569
- if (isRecord10(error48) && typeof error48.message === "string") {
24240
+ if (isRecord11(error48) && typeof error48.message === "string") {
23570
24241
  return `invalid token (${error48.message})`;
23571
24242
  }
23572
24243
  if (error48 instanceof Error && error48.message.length > 0) {
@@ -23642,7 +24313,7 @@ var runVerify = async (tokenOrFile) => {
23642
24313
  printResult(true, `token verified (${claims.sub})`);
23643
24314
  };
23644
24315
  var createVerifyCommand = () => {
23645
- return new Command9("verify").description("Verify an AIT using registry keys and CRL state").argument(
24316
+ return new Command10("verify").description("Verify an AIT using registry keys and CRL state").argument(
23646
24317
  "<tokenOrFile>",
23647
24318
  "Raw AIT token or file path containing the token"
23648
24319
  ).action(
@@ -23653,7 +24324,7 @@ var createVerifyCommand = () => {
23653
24324
  };
23654
24325
 
23655
24326
  // src/index.ts
23656
- var require2 = createRequire(import.meta.url);
24327
+ var require2 = createRequire2(import.meta.url);
23657
24328
  var resolveCliVersion = () => {
23658
24329
  const packageJson = require2("../package.json");
23659
24330
  if (typeof packageJson.version === "string" && packageJson.version.length > 0) {
@@ -23663,7 +24334,7 @@ var resolveCliVersion = () => {
23663
24334
  };
23664
24335
  var CLI_VERSION = resolveCliVersion();
23665
24336
  var createProgram = () => {
23666
- 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());
24337
+ return new Command11("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(createSkillCommand()).addCommand(createVerifyCommand());
23667
24338
  };
23668
24339
 
23669
24340
  // src/bin.ts