chainlesschain 0.166.13 → 0.166.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.
Files changed (173) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BN25cNOD.js → AIOps-C8SyFhOo.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Da9IP-m5.js → ActionButton-Dup5gFK0.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-D7hkEvQm.js → Analytics-8FyOWA7V.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-CCV4Xnbc.js → AppLayout-DBzdU9dg.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CVSnVJeA.js → Artifacts-C_0Z8gNy.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CuYdw5Ag.js → Audit-Cf2a-58m.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DZCnK-Tf.js → BackgroundAgents-M7w-2zgB.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-cV9FdpTy.js → Backup-CCTHQXoL.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BRnv6YSp.js → BaseInput-DjXpeFwJ.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-Bw5w87P4.js → Chat-cAvchsJU.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-C0GjtUAw.js → ChatBubbleRenderer-DvEvxlZv.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DaoTqcdt.js → Checkbox-DKs6qvOn.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-B4dYcEIi.js → Codegen-DlkA7b9X.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-mAhuOeuD.js → Col-CkfdKhyJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BK6KEdBk.js → Community-iNXMHzj6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CCglmNu5.js → Compact-DQxPEd09.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Cm8S4of7.js → Compliance-CSKvMCjs.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Dhs1QLMe.js → Cowork-CkWqazpD.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CLlhmXWk.js → Cron-k6W8IzhB.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BnHVbaNs.js → Crosschain-C8MS1OuY.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-UzQP9-0O.js → DID-DJuCcgiQ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CBdlPClz.js → Dashboard-LeThKzp3.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-axIRPdq1.js → Dropdown-BvxwgpE4.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DUalW5rx.js → EmailListRenderer-n3e_a1M3.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bl4W9luY.js → FamilyGuardDashboard-509W6W6v.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-C1yP-uEu.js → Federation-xgZReQm0.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Xh6Ig5q8.js → FormItemContext-jZOhKcQx.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-C2cz3bt2.js → GenericCardRenderer-Jo0ULzHv.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C_z63ixg.js → Git-BsRcyWQP.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BYqB33_3.js → Governance-JlpMYTAk.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CEULuArm.js → Inference-CUEz872-.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-fgMsu-0H.js → KnowledgeGraph-DgrvvEDU.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DO9Hdcec.js → Logs-Df-KY4U3.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-BEPDcSwz.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-B8zyLiFe.js → Marketplace-kPZDTOeI.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BLAN2mWW.js → McpTools-CRRzPqNX.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CrQcFM7u.js → Memory-D6pNSkg9.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DVnyiiU-.js → MobileBridge-g-pvtwRC.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-qjCME5OD.js → MobileProjects-Bzayfrfe.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-4JE3C3It.js → Mtc-B7S3W-XL.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-CjBuak9I.js → MtcAudit-B8OMEWVb.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-BtPcYQcC.js → Multisig-CowRp7GH.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Cx653_Ba.js → NLProgramming-BkQNyi4c.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CkuD3YzS.js → Notes-Cm7gtkKz.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BgNgU7HH.js → NotificationSettings-CbPTVh9m.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-BTC8W0a-.js → OrderTableRenderer-Dswi26mJ.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dmd4Hjxz.js → Organization-BkjQ9yy7.js} +2 -2
  49. package/src/assets/web-panel/assets/{Overflow-C_5DA-G0.js → Overflow-DmRFXP7Y.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-uwslV4XC.js → P2P-DQE1toAu.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CuNgwVnX.js → PdhVaultBrowser-BujyoDRG.js} +4 -4
  52. package/src/assets/web-panel/assets/{Permissions-CJayVX9H.js → Permissions-xSjfOaiU.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-Cb05H5go.js → PersonalDataHub-B9ICLqVP.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-BZlGKapZ.js → Pipeline-CUNwyN9E.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BIzZWYzd.js → Privacy-SOoCricX.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-vEYYyQXQ.js → ProjectInit-CSaFul0F.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-MZqTy5CC.js → ProjectSettings-CueiB_oe.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-CRARUOl8.js → Projects-BOeXvU7y.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-80VAK8PX.js → Providers-CTqf_31h.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CzRSoIwK.js → QrScannerModal-C8ZP31E9.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-BpwQ2n4C.js → QuickAsk-CPgn4713.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BC03TZ79.js → Recommend-8_e0zw0i.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-DE59-jaa.js → RemoteSession-iT7lbGqw.js} +3 -3
  64. package/src/assets/web-panel/assets/{Reputation-DOHmeWyA.js → Reputation-B4q9SUOz.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-hc5RZl1D.js → Row-CA_nASyL.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-Cv5uMP7E.js → RssFeed-Cwb0E3yv.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-qohu7Ug0.js → Search-D9iVsOxJ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-drCPFWV5.js → Security-DPLbYchG.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BAEgUeRy.js → Services-DGMVL_LF.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CkJkKBlo.js → Skeleton-B_6kym0D.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-bMuKEpez.js → Skills-Ds6r0dXB.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DxWQVHRC.js → Sla-BLHAbBhb.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BpS8idmV.js → SpeechSettings-CZuddd14.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-BPBLlW5v.js → SyncSettings-CdBq7oSl.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-C4SLEeUh.js → Tasks-DfT60Nyl.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-r-wfU39U.js → Templates-RLfsKy2V.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-C7vjRmLN.js → Tenant-Cw8xCNLE.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-DdzeFCV2.js → Terminal-BOUkNu4f.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-CYyWN2KD.js → TimelineRenderer-CgGZWaRm.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-D8A9osUg.js → Tokens-CpwjVyir.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DBvrNlyS.js → Trigger-DL21H-eb.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DBVAGc7o.js → Trust-B3ffFclW.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-C1vQUCRJ.js → UkeySign-CkBeb5aI.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BKs4dah4.js → VideoEditing-U2iVaE3r.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-ByIuj3lD.js → Wallet-CQk2wzYd.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DYlAFCm9.js → WebAuthn-DYIuIYzT.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-Dwyadd0c.js → WorkflowEditor-tg1c2mZQ.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-Bm2f3s08.js → chat-CtL61spq.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-C8ORSh7V.js → colors-DKpbeo8s.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-1iVlnsM-.js → compact-item-gPZsrswt.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CmqNyUSL.js → createContext-CfHlwzVr.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DdqnMhzi.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-rUMaX3Ul.js → hasIn-MV47g_6X.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CCEyz58d.js → index-6G0ETFTW.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BVtnizyC.js → index-BBw52sEl.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BIvM-uAX.js → index-BN-wxSQ8.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DvedhJeM.js → index-BOyGUuBE.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BCOhTILd.js → index-BQp5M0s1.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BcK8o4ke.js +1 -0
  100. package/src/assets/web-panel/assets/{index-DVJ1uKf4.js → index-BjlPD7GO.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BQL9r73Y.js → index-C4lF-f_z.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-C5RfRciU.js → index-C6nC_DyY.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DgUE51Pz.js → index-C9RF4ieu.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CkFvuAex.js → index-C9gtY4nG.js} +3 -3
  105. package/src/assets/web-panel/assets/{index-C-J5-pGH.js → index-CB1HnbNK.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Bf1PlIHp.js → index-CRv_uMLV.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-tqTx5wXo.js → index-C_B9LiKs.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bgw4Y2rU.js → index-Cjv0kKCU.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DJxtPFOh.js → index-CmM9jqwe.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-pHWA7bmM.js → index-CmPJpLoD.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Dsve5Gkv.js → index-CzfxTSPW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B59OWAs7.js → index-D1XB5-t5.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BK-RY0-7.js → index-DGtEBodr.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BC32_0xT.js → index-DIeJmlAA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-OcbzdRpT.js → index-DMMvlLp9.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-geQinnns.js → index-DU4rZpiK.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D98FXb0w.js → index-D_Pl1GnW.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BC8Wb6cy.js → index-Dd19vRwy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-fPK6GFEg.js → index-Ddcpj1El.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CfiHyVyl.js → index-Df9sWKpl.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C-ZIHi42.js → index-DfLyHCWi.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DDHk911Q.js → index-DnZ_zS1M.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BFniLMn9.js → index-DsH-i_3d.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DvbAjyYI.js +1 -0
  125. package/src/assets/web-panel/assets/{index-Dcck8QfW.js → index-ExAGPAnX.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DwMdWKiF.js → index-Rb7CRMw9.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-reUHhuz8.js → index-Zvkt9xqi.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BgwLNxmr.js → index-bSFrAszw.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C3B1S-mW.js → index-fYcS7NS0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CTfEmKK0.js → index-srBc9U_s.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B53c3diB.js → index-xUlgG0Xe.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B8BrB0Ls.js → index-yxxXlqBA.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-CuA-cpkL.js → initDefaultProps-BeOZoSZ4.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CimmE_9b.js → motion-BXE8VMKx.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DlGlrltz.js → move-I9sdvPGX.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CK6BJQ07.js → mtc-parser-d25zrJd0.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BKjBfZfB.js → omit-D71V_T4e.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-B2chVYw9.js → pickAttrs-BNZKpIAR.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BW8aLJJ3.js → placementArrow-jOcgxTTz.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-dXlPl66S.js → responsiveObserve-pta_O8AU.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-hSa89S4G.js → slide-Dlq6om7z.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DM6KLt-s.js → statusUtils-BnkO_yRv.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-C9MQAK15.js → styleChecker-p8NfWmcG.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-BpQDpP0z.js → useFlexGapSupport-KcjZZ5U7.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-C7rtRViA.js → useFs-gqonDMWB.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-Dm15xafq.js → usePersonalDataHub-jIIfE4go.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-mEiEytw4.js → vnode-Dyo7fBrM.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DzPxflhY.js → zoom-6l8BMg9W.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +2 -2
  151. package/src/commands/agent.js +49 -27
  152. package/src/commands/record-replay.js +772 -0
  153. package/src/commands/skill.js +3 -0
  154. package/src/data/changelog.json +13 -0
  155. package/src/lib/agent-sandbox.js +4 -8
  156. package/src/lib/agent-team/team-message-tools.js +23 -5
  157. package/src/lib/process-execution-broker/credential-transport.js +31 -0
  158. package/src/lib/process-execution-broker/index.js +74 -0
  159. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  160. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  161. package/src/lib/process-execution-broker/windows-sandbox.cs +7 -3
  162. package/src/lib/record-replay/browser-target-policy.js +406 -0
  163. package/src/lib/record-replay/index.js +4 -0
  164. package/src/lib/record-replay/playwright-ui-driver.js +83 -20
  165. package/src/lib/record-replay/playwright-ui-recorder.js +611 -0
  166. package/src/lib/record-replay/recorded-skill-package.js +486 -0
  167. package/src/lib/record-replay/recorded-skill-store.js +1166 -0
  168. package/src/lib/record-replay/skill-recorder.js +634 -64
  169. package/src/runtime/headless-runner.js +72 -51
  170. package/src/assets/web-panel/assets/MarkdownRenderer-BkrK04xW.js +0 -1
  171. package/src/assets/web-panel/assets/devWarning-B69RCDDH.js +0 -1
  172. package/src/assets/web-panel/assets/index-9biMQ-tw.js +0 -1
  173. package/src/assets/web-panel/assets/index-COie87kl.js +0 -1
@@ -0,0 +1,772 @@
1
+ import {
2
+ closeSync,
3
+ lstatSync,
4
+ mkdirSync,
5
+ openSync,
6
+ readFileSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { dirname, resolve } from "node:path";
10
+ import { createInterface } from "node:readline/promises";
11
+ import chalk from "chalk";
12
+ import { logger } from "../lib/logger.js";
13
+ import {
14
+ DEFAULT_RECORDED_SKILL_POLICY,
15
+ RecordedSkillStore,
16
+ assertRecordedSkillBrowserBinding,
17
+ installRecordedSkillPackage,
18
+ launchPlaywrightRecordedSkillDriver,
19
+ launchPlaywrightRecordedSkillRecorder,
20
+ replayRecordedSkill,
21
+ reviewRecordedSkillDraft,
22
+ stageRecordedSkillPackageRevocation,
23
+ } from "../lib/record-replay/index.js";
24
+
25
+ const MAX_INPUT_FILE_BYTES = 2 * 1024 * 1024;
26
+ const MAX_FIXTURE_BYTES = 4 * 1024 * 1024;
27
+
28
+ function collect(value, previous) {
29
+ return [...previous, value];
30
+ }
31
+
32
+ function commandError(code, message) {
33
+ const error = new Error(message);
34
+ error.name = "RecordReplayCommandError";
35
+ error.code = code;
36
+ return error;
37
+ }
38
+
39
+ function readRegularFile(filePath, maxBytes, label) {
40
+ const target = resolve(filePath);
41
+ const entry = lstatSync(target);
42
+ if (!entry.isFile() || entry.isSymbolicLink() || entry.size > maxBytes) {
43
+ throw commandError(
44
+ "CC_RECORD_FILE_UNSAFE",
45
+ `${label} must be a bounded regular file and may not be a symbolic link`,
46
+ );
47
+ }
48
+ return readFileSync(target, "utf8");
49
+ }
50
+
51
+ function readJsonFile(filePath, label) {
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(readRegularFile(filePath, MAX_INPUT_FILE_BYTES, label));
55
+ } catch (error) {
56
+ if (error?.code) throw error;
57
+ throw commandError("CC_RECORD_JSON_INVALID", `${label} is not valid JSON`);
58
+ }
59
+ return parsed;
60
+ }
61
+
62
+ function writeJsonExclusive(filePath, value) {
63
+ const target = resolve(filePath);
64
+ mkdirSync(dirname(target), { recursive: true });
65
+ const handle = openSync(target, "wx", 0o600);
66
+ try {
67
+ writeFileSync(handle, `${JSON.stringify(value, null, 2)}\n`, "utf8");
68
+ } finally {
69
+ closeSync(handle);
70
+ }
71
+ return target;
72
+ }
73
+
74
+ function parseAssignments(values = [], inputFile) {
75
+ const inputs = {};
76
+ if (inputFile) {
77
+ const fromFile = readJsonFile(inputFile, "input file");
78
+ if (!fromFile || typeof fromFile !== "object" || Array.isArray(fromFile)) {
79
+ throw commandError(
80
+ "CC_RECORD_INPUT_INVALID",
81
+ "input file must contain a JSON object",
82
+ );
83
+ }
84
+ Object.assign(inputs, fromFile);
85
+ }
86
+ for (const assignment of values) {
87
+ const separator = assignment.indexOf("=");
88
+ const name = assignment.slice(0, separator);
89
+ if (
90
+ separator < 1 ||
91
+ !/^[A-Za-z][A-Za-z0-9_]*$/u.test(name) ||
92
+ Object.prototype.hasOwnProperty.call(inputs, name)
93
+ ) {
94
+ throw commandError(
95
+ "CC_RECORD_INPUT_INVALID",
96
+ "each --input must be a unique name=value assignment",
97
+ );
98
+ }
99
+ inputs[name] = assignment.slice(separator + 1);
100
+ }
101
+ return inputs;
102
+ }
103
+
104
+ function reviewProjection(entry) {
105
+ return Object.freeze({
106
+ schema: "chainlesschain.recorded-skill-review-projection/v1",
107
+ name: entry.name,
108
+ revision: entry.revision,
109
+ state: entry.state,
110
+ description: entry.skill.description,
111
+ actions: entry.skill.actions.map((action) => ({ ...action })),
112
+ parameters: entry.skill.parameters.map((parameter) => ({ ...parameter })),
113
+ capabilityManifest: [...entry.skill.capabilityManifest],
114
+ environment: entry.skill.environment,
115
+ failureConditions: [...entry.skill.failureConditions],
116
+ draftDigest: entry.skill.draftDigest,
117
+ approvalDigest: entry.skill.approvalDigest || null,
118
+ lastReplayDigest: entry.lastReplay?.report?.replayDigest || null,
119
+ installation: entry.installation,
120
+ entryDigest: entry.entryDigest,
121
+ });
122
+ }
123
+
124
+ function printReview(entry) {
125
+ const projection = reviewProjection(entry);
126
+ logger.log(chalk.bold(`\nRecorded Skill: ${projection.name}`));
127
+ logger.log(` state: ${projection.state} revision: ${projection.revision}`);
128
+ logger.log(` draft: ${projection.draftDigest}`);
129
+ logger.log(` capabilities: ${projection.capabilityManifest.join(", ")}`);
130
+ logger.log(` environment: ${projection.environment.digest}`);
131
+ logger.log(chalk.bold("\nActions:"));
132
+ for (const action of projection.actions) {
133
+ const value =
134
+ "value" in action ? ` value=${JSON.stringify(action.value)}` : "";
135
+ logger.log(
136
+ ` ${action.id} ${action.kind} target=${JSON.stringify(action.target)}${value}`,
137
+ );
138
+ }
139
+ logger.log(chalk.bold("\nParameters:"));
140
+ if (projection.parameters.length === 0) logger.log(" (none)");
141
+ for (const parameter of projection.parameters) {
142
+ logger.log(
143
+ ` ${parameter.name} required=${parameter.required} sensitive=${parameter.sensitive}`,
144
+ );
145
+ }
146
+ logger.log(chalk.bold("\nFailure conditions:"));
147
+ if (projection.failureConditions.length === 0)
148
+ logger.log(" (none declared)");
149
+ for (const condition of projection.failureConditions)
150
+ logger.log(` - ${condition}`);
151
+ logger.log("");
152
+ }
153
+
154
+ function output(value, options, message) {
155
+ if (options.json) console.log(JSON.stringify(value, null, 2));
156
+ else if (message) logger.success(message);
157
+ }
158
+
159
+ async function guarded(action, options = {}) {
160
+ try {
161
+ await action();
162
+ } catch (error) {
163
+ const code = String(error?.code || "CC_RECORD_COMMAND_FAILED");
164
+ if (options.json) {
165
+ console.log(
166
+ JSON.stringify({
167
+ success: false,
168
+ code,
169
+ error: String(error?.message || "Record & Replay command failed"),
170
+ }),
171
+ );
172
+ } else {
173
+ logger.error(
174
+ `[${code}] ${error?.message || "Record & Replay command failed"}`,
175
+ );
176
+ }
177
+ process.exitCode = 1;
178
+ }
179
+ }
180
+
181
+ async function waitForRecordingStop() {
182
+ if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
183
+ throw commandError(
184
+ "CC_RECORD_INTERACTIVE_REQUIRED",
185
+ "interactive recording requires a TTY; use --automation for headless capture",
186
+ );
187
+ }
188
+ const prompt = createInterface({
189
+ input: process.stdin,
190
+ output: process.stdout,
191
+ });
192
+ try {
193
+ await prompt.question(
194
+ "Interact with the Chromium window, then press Enter here to finish recording. ",
195
+ );
196
+ } finally {
197
+ prompt.close();
198
+ }
199
+ }
200
+
201
+ function parseAssertions(options) {
202
+ if (!options.assertions) return [];
203
+ const assertions = readJsonFile(options.assertions, "assertions file");
204
+ if (!Array.isArray(assertions)) {
205
+ throw commandError(
206
+ "CC_RECORD_ASSERTION_INVALID",
207
+ "assertions file must contain a JSON array",
208
+ );
209
+ }
210
+ return assertions;
211
+ }
212
+
213
+ function parseEnvironment(options) {
214
+ if (!options.environment) return {};
215
+ const environment = readJsonFile(options.environment, "environment file");
216
+ if (
217
+ !environment ||
218
+ typeof environment !== "object" ||
219
+ Array.isArray(environment)
220
+ ) {
221
+ throw commandError(
222
+ "CC_RECORD_ENVIRONMENT_INVALID",
223
+ "environment file must contain a JSON object",
224
+ );
225
+ }
226
+ return environment;
227
+ }
228
+
229
+ function parseBrowserTarget(options, { reviewedEnvironment } = {}) {
230
+ const hasFixture =
231
+ typeof options.fixture === "string" && options.fixture.length > 0;
232
+ const hasUrl = typeof options.url === "string" && options.url.length > 0;
233
+ if (hasFixture === hasUrl) {
234
+ throw commandError(
235
+ "CC_RECORD_TARGET_INVALID",
236
+ "provide exactly one --fixture or --url browser target",
237
+ );
238
+ }
239
+ const expectedAdapter = reviewedEnvironment?.adapter;
240
+ if (
241
+ (expectedAdapter === "self-contained-html" && !hasFixture) ||
242
+ (expectedAdapter === "url-origin" && !hasUrl)
243
+ ) {
244
+ throw commandError(
245
+ "CC_RECORD_TARGET_DRIFT",
246
+ "replay target type does not match the reviewed recording",
247
+ );
248
+ }
249
+ const storageState = options.storageState
250
+ ? readJsonFile(options.storageState, "browser storage state")
251
+ : undefined;
252
+ return {
253
+ html: hasFixture
254
+ ? readRegularFile(options.fixture, MAX_FIXTURE_BYTES, "fixture")
255
+ : undefined,
256
+ url: hasUrl ? options.url : undefined,
257
+ allowedOrigins:
258
+ reviewedEnvironment?.networkPolicy?.allowedOrigins ||
259
+ options.allowedOrigin ||
260
+ [],
261
+ identity: reviewedEnvironment?.identity || options.identity || "anonymous",
262
+ storageState,
263
+ };
264
+ }
265
+
266
+ export function registerRecordReplayCommands(skill) {
267
+ const recording = skill
268
+ .command("recording")
269
+ .alias("record-replay")
270
+ .description(
271
+ "Record, review, replay, enable, and govern deterministic UI skills",
272
+ );
273
+
274
+ recording
275
+ .command("record <name>")
276
+ .description(
277
+ "Capture a real Chromium interaction into a parameterized draft",
278
+ )
279
+ .option("--fixture <path>", "Self-contained HTML fixture")
280
+ .option(
281
+ "--url <url>",
282
+ "HTTPS target URL (loopback HTTP is allowed for testing)",
283
+ )
284
+ .option(
285
+ "--allowed-origin <origin>",
286
+ "Exact additional network origin; repeat as needed",
287
+ collect,
288
+ [],
289
+ )
290
+ .option("--identity <id>", "Non-secret browser identity label", "anonymous")
291
+ .option(
292
+ "--storage-state <path>",
293
+ "Playwright storage-state JSON (never persisted)",
294
+ )
295
+ .option(
296
+ "--sensitive <parameter>",
297
+ "Mark a captured parameter as sensitive; repeat as needed",
298
+ collect,
299
+ [],
300
+ )
301
+ .option("--description <text>", "Recorded Skill description", "")
302
+ .option("--failure <condition>", "Reviewed failure condition", collect, [])
303
+ .option("--observe <selector>", "Append an observe action", collect, [])
304
+ .option("--assertions <path>", "JSON array of explicit assert actions")
305
+ .option("--environment <path>", "Additional environment requirements JSON")
306
+ .option(
307
+ "--automation <path>",
308
+ "Bounded click/type/select JSON used for headless capture",
309
+ )
310
+ .option("--headless", "Launch Chromium headless")
311
+ .option("--timeout-ms <ms>", "Recorder action timeout", "5000")
312
+ .option("--max-actions <count>", "Maximum captured actions", "256")
313
+ .option("--json", "Machine-readable result")
314
+ .action(async (name, options) =>
315
+ guarded(async () => {
316
+ const target = parseBrowserTarget(options);
317
+ const automation = options.automation
318
+ ? readJsonFile(options.automation, "automation file")
319
+ : null;
320
+ const recorder = await launchPlaywrightRecordedSkillRecorder({
321
+ ...target,
322
+ headless: options.headless === true || Boolean(automation),
323
+ timeoutMs: Number(options.timeoutMs),
324
+ maxActions: Number(options.maxActions),
325
+ });
326
+ try {
327
+ if (automation) await recorder.runAutomation(automation);
328
+ else await waitForRecordingStop();
329
+ const draft = await recorder.finish({
330
+ name,
331
+ description: options.description,
332
+ environment: parseEnvironment(options),
333
+ failureConditions: options.failure,
334
+ observations: options.observe,
335
+ assertions: parseAssertions(options),
336
+ sensitiveParameters: options.sensitive,
337
+ });
338
+ const store = new RecordedSkillStore();
339
+ const entry = store.create({
340
+ draft,
341
+ source: {
342
+ adapter: recorder.adapter,
343
+ targetDigest: recorder.targetDigest,
344
+ browserVersion: recorder.browserVersion,
345
+ },
346
+ actor: "cli-recorder",
347
+ });
348
+ const projection = reviewProjection(entry);
349
+ if (!options.json) printReview(entry);
350
+ output(
351
+ projection,
352
+ options,
353
+ `Recorded draft ${name} at revision ${entry.revision}`,
354
+ );
355
+ } finally {
356
+ await recorder.close();
357
+ }
358
+ }, options),
359
+ );
360
+
361
+ recording
362
+ .command("list", { isDefault: true })
363
+ .description("List retained recorded Skills")
364
+ .option("--json", "Machine-readable result")
365
+ .action((options) =>
366
+ guarded(async () => {
367
+ const records = new RecordedSkillStore().list();
368
+ if (options.json) console.log(JSON.stringify({ records }, null, 2));
369
+ else if (records.length === 0) logger.info("No recorded Skills found.");
370
+ else {
371
+ logger.log(chalk.bold(`\nRecorded Skills (${records.length}):\n`));
372
+ for (const record of records) {
373
+ logger.log(
374
+ ` ${chalk.cyan(record.name.padEnd(28))} ${record.state.padEnd(10)} rev ${record.revision} ${record.actionCount} actions`,
375
+ );
376
+ }
377
+ logger.log("");
378
+ }
379
+ }, options),
380
+ );
381
+
382
+ recording
383
+ .command("show <name>")
384
+ .description("Show the exact review projection for one recorded Skill")
385
+ .option("--json", "Machine-readable result")
386
+ .action((name, options) =>
387
+ guarded(async () => {
388
+ const entry = new RecordedSkillStore().get(name);
389
+ if (options.json)
390
+ console.log(JSON.stringify(reviewProjection(entry), null, 2));
391
+ else printReview(entry);
392
+ }, options),
393
+ );
394
+
395
+ recording
396
+ .command("review <name>")
397
+ .description("Review and explicitly approve the current draft revision")
398
+ .requiredOption("--reviewer <id>", "Stable reviewer identity")
399
+ .option(
400
+ "--approve",
401
+ "Approve the displayed exact revision and failure conditions",
402
+ )
403
+ .option("--json", "Machine-readable result")
404
+ .action((name, options) =>
405
+ guarded(async () => {
406
+ const store = new RecordedSkillStore();
407
+ const current = store.get(name);
408
+ if (!options.json) printReview(current);
409
+ if (options.approve !== true) {
410
+ throw commandError(
411
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
412
+ "review is read-only without --approve",
413
+ );
414
+ }
415
+ const approved = reviewRecordedSkillDraft(current.skill, {
416
+ reviewerId: options.reviewer,
417
+ approvedCapabilities: current.skill.capabilityManifest,
418
+ acceptedFailureConditions: true,
419
+ });
420
+ const entry = store.approve({
421
+ name,
422
+ expectedRevision: current.revision,
423
+ skill: approved,
424
+ actor: options.reviewer,
425
+ });
426
+ output(
427
+ reviewProjection(entry),
428
+ options,
429
+ `Approved ${name} at revision ${entry.revision}`,
430
+ );
431
+ }, options),
432
+ );
433
+
434
+ recording
435
+ .command("replay <name>")
436
+ .description("Replay an approved Skill in ephemeral policy-bound Chromium")
437
+ .option("--fixture <path>", "Original self-contained HTML fixture")
438
+ .option("--url <url>", "Original reviewed HTTPS target URL")
439
+ .option("--storage-state <path>", "Matching Playwright storage-state JSON")
440
+ .option(
441
+ "--input <name=value>",
442
+ "Runtime parameter; repeat as needed",
443
+ collect,
444
+ [],
445
+ )
446
+ .option("--input-file <path>", "JSON object of runtime parameters")
447
+ .option("--timeout-ms <ms>", "Per-action timeout", "5000")
448
+ .option("--settle-ms <ms>", "Post-action settle delay", "25")
449
+ .option("--json", "Machine-readable result")
450
+ .action((name, options) =>
451
+ guarded(async () => {
452
+ const store = new RecordedSkillStore();
453
+ const current = store.get(name);
454
+ if (
455
+ !["approved", "validated", "enabled", "revoked"].includes(
456
+ current.state,
457
+ )
458
+ ) {
459
+ throw commandError(
460
+ "CC_RECORD_NOT_APPROVED",
461
+ "recorded Skill must be approved before replay",
462
+ );
463
+ }
464
+ const reviewedEnvironment = current.skill.environment.requirements;
465
+ const target = parseBrowserTarget(options, { reviewedEnvironment });
466
+ const driver = await launchPlaywrightRecordedSkillDriver({
467
+ ...target,
468
+ timeoutMs: Number(options.timeoutMs),
469
+ settleMs: Number(options.settleMs),
470
+ });
471
+ try {
472
+ assertRecordedSkillBrowserBinding(driver, {
473
+ source: current.source,
474
+ environment: reviewedEnvironment,
475
+ });
476
+ const startedAt = Date.now();
477
+ const report = await replayRecordedSkill(current.skill, {
478
+ inputs: parseAssignments(options.input, options.inputFile),
479
+ environment: reviewedEnvironment,
480
+ isolation: {
481
+ sandboxed: true,
482
+ network: driver.networkPolicy.mode,
483
+ allowedOrigins: driver.networkPolicy.allowedOrigins,
484
+ },
485
+ executor: driver.executor,
486
+ });
487
+ const entry = store.recordReplay({
488
+ name,
489
+ expectedRevision: current.revision,
490
+ report,
491
+ targetDigest: driver.targetDigest,
492
+ browserVersion: driver.browserVersion,
493
+ durationMs: Date.now() - startedAt,
494
+ actor: "cli-replay",
495
+ });
496
+ output(
497
+ {
498
+ success: true,
499
+ name,
500
+ state: entry.state,
501
+ revision: entry.revision,
502
+ replayDigest: report.replayDigest,
503
+ actionCount: report.receipts.length,
504
+ driver: driver.summary(),
505
+ },
506
+ options,
507
+ `Replay succeeded for ${name} (${report.receipts.length} actions)`,
508
+ );
509
+ } finally {
510
+ await driver.close();
511
+ }
512
+ }, options),
513
+ );
514
+
515
+ recording
516
+ .command("enable <name>")
517
+ .description(
518
+ "Install a replay-validated workflow into the existing Skill loader",
519
+ )
520
+ .option("--global", "Install in the global managed Skill layer")
521
+ .option("--approve", "Confirm installation of the exact validated revision")
522
+ .option("--json", "Machine-readable result")
523
+ .action((name, options) =>
524
+ guarded(async () => {
525
+ if (options.approve !== true) {
526
+ throw commandError(
527
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
528
+ "enablement requires --approve",
529
+ );
530
+ }
531
+ const store = new RecordedSkillStore();
532
+ const current = store.get(name);
533
+ const scope = options.global ? "global" : "project";
534
+ const installed = installRecordedSkillPackage(current, { scope });
535
+ let entry;
536
+ try {
537
+ entry = store.enable({
538
+ name,
539
+ expectedRevision: current.revision,
540
+ scope,
541
+ packageDigest: installed.packageDigest,
542
+ actor: "cli-enable",
543
+ });
544
+ } catch (error) {
545
+ if (installed.created) {
546
+ const rollback = stageRecordedSkillPackageRevocation({
547
+ name,
548
+ scope,
549
+ expectedPackageDigest: installed.packageDigest,
550
+ });
551
+ rollback.commit();
552
+ }
553
+ throw error;
554
+ }
555
+ output(
556
+ {
557
+ success: true,
558
+ name,
559
+ state: entry.state,
560
+ revision: entry.revision,
561
+ scope,
562
+ packageDigest: installed.packageDigest,
563
+ },
564
+ options,
565
+ `Enabled recorded Skill ${name} in the ${scope} layer`,
566
+ );
567
+ }, options),
568
+ );
569
+
570
+ recording
571
+ .command("revoke <name>")
572
+ .description("Atomically revoke an enabled generated Skill package")
573
+ .option("--approve", "Confirm revocation of the exact installed package")
574
+ .option("--json", "Machine-readable result")
575
+ .action((name, options) =>
576
+ guarded(async () => {
577
+ if (options.approve !== true) {
578
+ throw commandError(
579
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
580
+ "revocation requires --approve",
581
+ );
582
+ }
583
+ const store = new RecordedSkillStore();
584
+ const current = store.get(name);
585
+ if (current.state !== "enabled" || !current.installation) {
586
+ throw commandError(
587
+ "CC_RECORD_NOT_ENABLED",
588
+ "recorded Skill is not enabled",
589
+ );
590
+ }
591
+ const staged = stageRecordedSkillPackageRevocation({
592
+ name,
593
+ scope: current.installation.scope,
594
+ expectedPackageDigest: current.installation.packageDigest,
595
+ });
596
+ let entry;
597
+ try {
598
+ entry = store.revoke({
599
+ name,
600
+ expectedRevision: current.revision,
601
+ actor: "cli-revoke",
602
+ });
603
+ staged.commit();
604
+ } catch (error) {
605
+ staged.rollback();
606
+ throw error;
607
+ }
608
+ output(
609
+ { success: true, name, state: entry.state, revision: entry.revision },
610
+ options,
611
+ `Revoked recorded Skill ${name}`,
612
+ );
613
+ }, options),
614
+ );
615
+
616
+ recording
617
+ .command("export <name>")
618
+ .description("Export a digest-bound portable record without runtime inputs")
619
+ .requiredOption("--output <path>", "New export JSON path")
620
+ .option("--json", "Machine-readable result")
621
+ .action((name, options) =>
622
+ guarded(async () => {
623
+ const exported = new RecordedSkillStore().export(name, {
624
+ actor: "cli-export",
625
+ });
626
+ const target = writeJsonExclusive(options.output, exported);
627
+ output(
628
+ {
629
+ success: true,
630
+ name,
631
+ output: target,
632
+ exportDigest: exported.exportDigest,
633
+ },
634
+ options,
635
+ `Exported ${name} to ${target}`,
636
+ );
637
+ }, options),
638
+ );
639
+
640
+ recording
641
+ .command("import <path>")
642
+ .description("Import and fully revalidate a portable recorded Skill")
643
+ .option("--json", "Machine-readable result")
644
+ .action((filePath, options) =>
645
+ guarded(async () => {
646
+ const entry = new RecordedSkillStore().import(
647
+ readJsonFile(filePath, "recorded Skill export"),
648
+ { actor: "cli-import" },
649
+ );
650
+ output(
651
+ reviewProjection(entry),
652
+ options,
653
+ `Imported recorded Skill ${entry.name} at revision ${entry.revision}`,
654
+ );
655
+ }, options),
656
+ );
657
+
658
+ recording
659
+ .command("delete <name>")
660
+ .description("Delete a retained, non-enabled recorded Skill")
661
+ .option("--approve", "Confirm deletion of the exact current revision")
662
+ .option("--json", "Machine-readable result")
663
+ .action((name, options) =>
664
+ guarded(async () => {
665
+ if (options.approve !== true) {
666
+ throw commandError(
667
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
668
+ "deletion requires --approve",
669
+ );
670
+ }
671
+ const store = new RecordedSkillStore();
672
+ const current = store.get(name);
673
+ const removed = store.delete({
674
+ name,
675
+ expectedRevision: current.revision,
676
+ actor: "cli-delete",
677
+ });
678
+ output(
679
+ { success: true, removed },
680
+ options,
681
+ `Deleted retained recorded Skill ${name}`,
682
+ );
683
+ }, options),
684
+ );
685
+
686
+ recording
687
+ .command("audit")
688
+ .description(
689
+ "Verify and display the content-free Record & Replay audit chain",
690
+ )
691
+ .option("--name <name>", "Filter one recorded Skill")
692
+ .option("--json", "Machine-readable result")
693
+ .action((options) =>
694
+ guarded(async () => {
695
+ const events = new RecordedSkillStore().audit({ name: options.name });
696
+ if (options.json) console.log(JSON.stringify({ events }, null, 2));
697
+ else {
698
+ logger.log(
699
+ chalk.bold(`\nRecord & Replay audit events (${events.length}):\n`),
700
+ );
701
+ for (const event of events) {
702
+ logger.log(
703
+ ` #${event.sequence} ${event.action.padEnd(18)} ${event.name.padEnd(24)} rev ${event.revision} ${event.state}`,
704
+ );
705
+ }
706
+ logger.log("");
707
+ }
708
+ }, options),
709
+ );
710
+
711
+ recording
712
+ .command("prune")
713
+ .description(
714
+ "Delete expired non-enabled records under the retention policy",
715
+ )
716
+ .option("--approve", "Confirm retention deletion")
717
+ .option("--json", "Machine-readable result")
718
+ .action((options) =>
719
+ guarded(async () => {
720
+ if (options.approve !== true) {
721
+ throw commandError(
722
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
723
+ "retention pruning requires --approve",
724
+ );
725
+ }
726
+ const removed = new RecordedSkillStore().pruneExpired({
727
+ actor: "cli-prune",
728
+ });
729
+ output(
730
+ { success: true, removed },
731
+ options,
732
+ `Pruned ${removed.length} expired recorded Skill record(s)`,
733
+ );
734
+ }, options),
735
+ );
736
+
737
+ recording
738
+ .command("policy")
739
+ .description("Show or replace owner policy for retention and capabilities")
740
+ .option("--set <path>", "Validated policy JSON to apply")
741
+ .option("--approve", "Confirm replacement of the current governance policy")
742
+ .option("--json", "Machine-readable result")
743
+ .action((options) =>
744
+ guarded(async () => {
745
+ const store = new RecordedSkillStore();
746
+ if (options.set && options.approve !== true) {
747
+ throw commandError(
748
+ "CC_RECORD_EXPLICIT_APPROVAL_REQUIRED",
749
+ "policy replacement requires --approve",
750
+ );
751
+ }
752
+ const policy = options.set
753
+ ? store.setPolicy(readJsonFile(options.set, "policy file"), {
754
+ actor: "cli-policy",
755
+ })
756
+ : store.policy();
757
+ if (options.json) console.log(JSON.stringify(policy, null, 2));
758
+ else {
759
+ logger.log(chalk.bold("\nRecord & Replay policy:\n"));
760
+ logger.log(JSON.stringify(policy, null, 2));
761
+ logger.log("");
762
+ }
763
+ }, options),
764
+ );
765
+
766
+ recording
767
+ .command("policy-template")
768
+ .description("Print the default owner policy JSON")
769
+ .action(() =>
770
+ console.log(JSON.stringify(DEFAULT_RECORDED_SKILL_POLICY, null, 2)),
771
+ );
772
+ }