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,486 @@
1
+ import {
2
+ closeSync,
3
+ existsSync,
4
+ lstatSync,
5
+ mkdirSync,
6
+ openSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ renameSync,
10
+ rmSync,
11
+ writeFileSync,
12
+ } from "node:fs";
13
+ import { createHash, randomUUID } from "node:crypto";
14
+ import { isAbsolute, join, relative, resolve } from "node:path";
15
+ import { getElectronUserDataDir } from "../paths.js";
16
+ import { findProjectRoot } from "../project-detector.js";
17
+ import { validateReviewedRecordedSkill } from "./skill-recorder.js";
18
+
19
+ const PACKAGE_SCHEMA = "chainlesschain.recorded-skill-package/v1";
20
+ const PACKAGE_DIGEST_DOMAIN = "cc.record-replay.skill-package/v1";
21
+ const PACKAGE_FILES = Object.freeze([
22
+ "SKILL.md",
23
+ "handler.js",
24
+ "recorded-skill.json",
25
+ ]);
26
+ const MAX_PACKAGE_FILE_BYTES = 2 * 1024 * 1024;
27
+ const SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/u;
28
+
29
+ function packageError(code, message, details = {}) {
30
+ const error = new Error(message);
31
+ error.name = "RecordedSkillPackageError";
32
+ error.code = code;
33
+ Object.assign(error, details);
34
+ return error;
35
+ }
36
+
37
+ function canonicalValue(value) {
38
+ if (Array.isArray(value)) return value.map(canonicalValue);
39
+ if (!value || typeof value !== "object") return value;
40
+ return Object.fromEntries(
41
+ Object.keys(value)
42
+ .sort()
43
+ .map((key) => [key, canonicalValue(value[key])]),
44
+ );
45
+ }
46
+
47
+ function digest(value, domain = PACKAGE_DIGEST_DOMAIN) {
48
+ return `sha256:${createHash("sha256")
49
+ .update(domain)
50
+ .update("\0")
51
+ .update(JSON.stringify(canonicalValue(value)))
52
+ .digest("hex")}`;
53
+ }
54
+
55
+ function assertName(value) {
56
+ const name = String(value || "").trim();
57
+ if (!/^[A-Za-z][A-Za-z0-9._-]{0,127}$/u.test(name)) {
58
+ throw packageError(
59
+ "CC_RECORD_PACKAGE_ARGUMENT_INVALID",
60
+ "recorded skill name is invalid",
61
+ );
62
+ }
63
+ return name;
64
+ }
65
+
66
+ function pathContains(parent, candidate) {
67
+ const relation = relative(resolve(parent), resolve(candidate));
68
+ return (
69
+ relation === "" || (!relation.startsWith("..") && !isAbsolute(relation))
70
+ );
71
+ }
72
+
73
+ function assertNoSymlinkTraversal(root, target) {
74
+ const safeRoot = resolve(root);
75
+ const safeTarget = resolve(target);
76
+ if (!pathContains(safeRoot, safeTarget) || safeRoot === safeTarget) {
77
+ throw packageError(
78
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
79
+ "recorded skill package path escaped its skill root",
80
+ );
81
+ }
82
+ if (existsSync(safeRoot) && lstatSync(safeRoot).isSymbolicLink()) {
83
+ throw packageError(
84
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
85
+ "recorded skill root may not be a symbolic link",
86
+ );
87
+ }
88
+ const segments = relative(safeRoot, safeTarget)
89
+ .split(/[\\/]+/u)
90
+ .filter(Boolean);
91
+ let current = safeRoot;
92
+ for (const segment of segments) {
93
+ current = join(current, segment);
94
+ if (!existsSync(current)) break;
95
+ const entry = lstatSync(current);
96
+ if (entry.isSymbolicLink()) {
97
+ throw packageError(
98
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
99
+ "recorded skill package path contains a symbolic link",
100
+ );
101
+ }
102
+ }
103
+ return safeTarget;
104
+ }
105
+
106
+ function assertNoExistingSymlinkBetween(root, target) {
107
+ const safeRoot = resolve(root);
108
+ const safeTarget = resolve(target);
109
+ if (!pathContains(safeRoot, safeTarget)) {
110
+ throw packageError(
111
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
112
+ "recorded skill path escaped its authority root",
113
+ );
114
+ }
115
+ let current = safeRoot;
116
+ if (existsSync(current) && lstatSync(current).isSymbolicLink()) {
117
+ throw packageError(
118
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
119
+ "recorded skill authority root may not be a symbolic link",
120
+ );
121
+ }
122
+ for (const segment of relative(safeRoot, safeTarget)
123
+ .split(/[\\/]+/u)
124
+ .filter(Boolean)) {
125
+ current = join(current, segment);
126
+ if (!existsSync(current)) break;
127
+ if (lstatSync(current).isSymbolicLink()) {
128
+ throw packageError(
129
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
130
+ "recorded skill path contains a symbolic link",
131
+ );
132
+ }
133
+ }
134
+ }
135
+
136
+ function yamlString(value) {
137
+ return JSON.stringify(
138
+ String(value)
139
+ .replace(/[\r\n]+/gu, " ")
140
+ .slice(0, 512),
141
+ );
142
+ }
143
+
144
+ function packagePayload(entry) {
145
+ const skill = validateReviewedRecordedSkill(entry.skill);
146
+ if (!entry.lastReplay?.report?.replayDigest || entry.state !== "validated") {
147
+ throw packageError(
148
+ "CC_RECORD_PACKAGE_NOT_VALIDATED",
149
+ "recorded skill must pass its current replay before it can be enabled",
150
+ );
151
+ }
152
+ return Object.freeze({
153
+ schema: PACKAGE_SCHEMA,
154
+ name: skill.name,
155
+ entryDigest: entry.entryDigest,
156
+ draftDigest: skill.draftDigest,
157
+ approvalDigest: skill.approvalDigest,
158
+ replayDigest: entry.lastReplay.report.replayDigest,
159
+ targetDigest: entry.source.targetDigest,
160
+ capabilities: [...skill.capabilityManifest],
161
+ parameters: skill.parameters.map((parameter) => ({ ...parameter })),
162
+ });
163
+ }
164
+
165
+ function renderSkillMd(entry) {
166
+ const skill = validateReviewedRecordedSkill(entry.skill);
167
+ const parameterNames = skill.parameters.map((parameter) => parameter.name);
168
+ const targetOption =
169
+ entry.source.adapter === "url-origin"
170
+ ? "--url <reviewed-url> [--storage-state <playwright-storage-state.json>]"
171
+ : "--fixture <self-contained-html-file>";
172
+ const usage = [
173
+ `cc skill recording replay ${skill.name} ${targetOption}`,
174
+ ...parameterNames.map((name) => ` --input ${name}=<value>`),
175
+ ].join(" \\\n");
176
+ return `---
177
+ name: ${skill.name}
178
+ display-name: ${yamlString(`Recorded: ${skill.name}`)}
179
+ description: ${yamlString(skill.description || "Reviewed and replay-validated UI workflow")}
180
+ version: 1.0.0
181
+ category: automation
182
+ tags: [recorded, ui, replay]
183
+ user-invocable: true
184
+ handler: handler.js
185
+ isolation: true
186
+ capabilities: [${skill.capabilityManifest.join(", ")}]
187
+ ---
188
+
189
+ # ${skill.name}
190
+
191
+ This Skill is generated from a reviewed Record & Replay workflow. Its actions,
192
+ capabilities, environment binding, and successful replay digest are immutable.
193
+
194
+ ## Execution contract
195
+
196
+ 1. Ask the user for the original reviewed browser target and, when required,
197
+ an ephemeral matching Playwright storage-state file.
198
+ 2. Ask for every declared parameter; never persist sensitive parameter values.
199
+ 3. Run the exact governed replay command below. Do not reproduce the actions as
200
+ arbitrary browser JavaScript or relax the reviewed network policy.
201
+
202
+ \`\`\`bash
203
+ ${usage}
204
+ \`\`\`
205
+
206
+ The command revalidates the stored draft, approval, target digest, environment,
207
+ capabilities, and terminal evidence before reporting success.
208
+ `;
209
+ }
210
+
211
+ function renderHandler(payload) {
212
+ return `/**
213
+ * Execution identity marker for recorded Skill ${payload.name}.
214
+ *
215
+ * The CLI never imports this module. The authoritative workflow remains in the
216
+ * owner-private Record & Replay store and executes only through the governed
217
+ * \`cc skill recording replay\` command.
218
+ */
219
+ export default Object.freeze({
220
+ skill: ${JSON.stringify(payload.name)},
221
+ executionMode: "record-replay-cli",
222
+ entryDigest: ${JSON.stringify(payload.entryDigest)},
223
+ replayDigest: ${JSON.stringify(payload.replayDigest)},
224
+ });
225
+ `;
226
+ }
227
+
228
+ function packageContents(entry) {
229
+ const payload = packagePayload(entry);
230
+ return Object.freeze({
231
+ "SKILL.md": renderSkillMd(entry),
232
+ "handler.js": renderHandler(payload),
233
+ "recorded-skill.json": `${JSON.stringify(payload, null, 2)}\n`,
234
+ });
235
+ }
236
+
237
+ function packageDigest(contents) {
238
+ return digest(
239
+ Object.fromEntries(
240
+ PACKAGE_FILES.map((file) => [
241
+ file,
242
+ `sha256:${createHash("sha256").update(contents[file]).digest("hex")}`,
243
+ ]),
244
+ ),
245
+ );
246
+ }
247
+
248
+ function readInstalledPackage(target) {
249
+ const entry = lstatSync(target);
250
+ if (!entry.isDirectory() || entry.isSymbolicLink()) {
251
+ throw packageError(
252
+ "CC_RECORD_PACKAGE_PATH_UNSAFE",
253
+ "recorded skill package target is unsafe",
254
+ );
255
+ }
256
+ const names = readdirSync(target).sort();
257
+ if (JSON.stringify(names) !== JSON.stringify([...PACKAGE_FILES].sort())) {
258
+ throw packageError(
259
+ "CC_RECORD_INSTALL_MODIFIED",
260
+ "installed recorded skill contains modified files",
261
+ );
262
+ }
263
+ const contents = {};
264
+ for (const name of PACKAGE_FILES) {
265
+ const file = join(target, name);
266
+ const fileEntry = lstatSync(file);
267
+ if (
268
+ !fileEntry.isFile() ||
269
+ fileEntry.isSymbolicLink() ||
270
+ fileEntry.size > MAX_PACKAGE_FILE_BYTES
271
+ ) {
272
+ throw packageError(
273
+ "CC_RECORD_INSTALL_MODIFIED",
274
+ "installed recorded skill file is unsafe",
275
+ );
276
+ }
277
+ contents[name] = readFileSync(file, "utf8");
278
+ }
279
+ let payload;
280
+ try {
281
+ payload = JSON.parse(contents["recorded-skill.json"]);
282
+ } catch {
283
+ throw packageError(
284
+ "CC_RECORD_INSTALL_MODIFIED",
285
+ "installed recorded skill metadata is invalid",
286
+ );
287
+ }
288
+ if (
289
+ payload.schema !== PACKAGE_SCHEMA ||
290
+ payload.name !== assertName(payload.name) ||
291
+ !SHA256_PATTERN.test(String(payload.entryDigest || "")) ||
292
+ !SHA256_PATTERN.test(String(payload.replayDigest || ""))
293
+ ) {
294
+ throw packageError(
295
+ "CC_RECORD_INSTALL_MODIFIED",
296
+ "installed recorded skill metadata is invalid",
297
+ );
298
+ }
299
+ return Object.freeze({
300
+ payload,
301
+ contents,
302
+ packageDigest: packageDigest(contents),
303
+ });
304
+ }
305
+
306
+ export function resolveRecordedSkillInstallTarget({
307
+ name,
308
+ scope = "project",
309
+ projectRoot,
310
+ userDataDir,
311
+ } = {}) {
312
+ const safeName = assertName(name);
313
+ let skillsRoot;
314
+ let authorityRoot;
315
+ if (scope === "global") {
316
+ authorityRoot = resolve(userDataDir || getElectronUserDataDir());
317
+ skillsRoot = resolve(authorityRoot, "skills");
318
+ } else if (scope === "project") {
319
+ const root = projectRoot || findProjectRoot();
320
+ if (!root) {
321
+ throw packageError(
322
+ "CC_RECORD_PROJECT_REQUIRED",
323
+ "project installation requires a ChainlessChain project",
324
+ );
325
+ }
326
+ authorityRoot = resolve(root);
327
+ skillsRoot = resolve(authorityRoot, ".chainlesschain", "skills");
328
+ } else {
329
+ throw packageError(
330
+ "CC_RECORD_PACKAGE_ARGUMENT_INVALID",
331
+ "installation scope is invalid",
332
+ );
333
+ }
334
+ assertNoExistingSymlinkBetween(authorityRoot, skillsRoot);
335
+ mkdirSync(skillsRoot, { recursive: true, mode: 0o700 });
336
+ const target = assertNoSymlinkTraversal(
337
+ skillsRoot,
338
+ join(skillsRoot, safeName),
339
+ );
340
+ return Object.freeze({ scope, skillsRoot, target });
341
+ }
342
+
343
+ export function installRecordedSkillPackage(entry, options = {}) {
344
+ const location = resolveRecordedSkillInstallTarget({
345
+ name: entry?.name,
346
+ ...options,
347
+ });
348
+ const contents = packageContents(entry);
349
+ const expectedDigest = packageDigest(contents);
350
+ if (existsSync(location.target)) {
351
+ const installed = readInstalledPackage(location.target);
352
+ if (
353
+ installed.payload.name === entry.name &&
354
+ installed.payload.entryDigest === entry.entryDigest &&
355
+ installed.packageDigest === expectedDigest
356
+ ) {
357
+ return Object.freeze({
358
+ ...location,
359
+ packageDigest: expectedDigest,
360
+ created: false,
361
+ });
362
+ }
363
+ throw packageError(
364
+ "CC_RECORD_INSTALL_CONFLICT",
365
+ "a different skill already occupies the install target",
366
+ );
367
+ }
368
+ const staging = join(
369
+ location.skillsRoot,
370
+ `.${entry.name}.record-replay-${process.pid}-${randomUUID()}`,
371
+ );
372
+ assertNoSymlinkTraversal(location.skillsRoot, staging);
373
+ mkdirSync(staging, { mode: 0o700 });
374
+ try {
375
+ for (const name of PACKAGE_FILES) {
376
+ const target = join(staging, name);
377
+ const handle = openSync(target, "wx", 0o600);
378
+ try {
379
+ writeFileSync(handle, contents[name], "utf8");
380
+ } finally {
381
+ closeSync(handle);
382
+ }
383
+ }
384
+ renameSync(staging, location.target);
385
+ } catch (error) {
386
+ rmSync(staging, { recursive: true, force: true });
387
+ throw error;
388
+ }
389
+ return Object.freeze({
390
+ ...location,
391
+ packageDigest: expectedDigest,
392
+ created: true,
393
+ });
394
+ }
395
+
396
+ export function revokeRecordedSkillPackage({
397
+ name,
398
+ scope,
399
+ expectedPackageDigest,
400
+ ...options
401
+ } = {}) {
402
+ const staged = stageRecordedSkillPackageRevocation({
403
+ name,
404
+ scope,
405
+ expectedPackageDigest,
406
+ ...options,
407
+ });
408
+ staged.commit();
409
+ return Object.freeze({
410
+ scope: staged.scope,
411
+ skillsRoot: staged.skillsRoot,
412
+ target: staged.target,
413
+ removed: staged.staged,
414
+ });
415
+ }
416
+
417
+ export function stageRecordedSkillPackageRevocation({
418
+ name,
419
+ scope,
420
+ expectedPackageDigest,
421
+ ...options
422
+ } = {}) {
423
+ const location = resolveRecordedSkillInstallTarget({
424
+ name,
425
+ scope,
426
+ ...options,
427
+ });
428
+ if (!existsSync(location.target)) {
429
+ return Object.freeze({
430
+ ...location,
431
+ staged: false,
432
+ commit() {},
433
+ rollback() {},
434
+ });
435
+ }
436
+ const installed = readInstalledPackage(location.target);
437
+ if (
438
+ installed.payload.name !== assertName(name) ||
439
+ installed.packageDigest !== expectedPackageDigest
440
+ ) {
441
+ throw packageError(
442
+ "CC_RECORD_INSTALL_MODIFIED",
443
+ "installed recorded skill changed after enablement; refusing automatic removal",
444
+ );
445
+ }
446
+ const quarantine = join(
447
+ location.skillsRoot,
448
+ `.${name}.record-replay-revoked-${randomUUID()}`,
449
+ );
450
+ assertNoSymlinkTraversal(location.skillsRoot, quarantine);
451
+ renameSync(location.target, quarantine);
452
+ let settled = false;
453
+ return Object.freeze({
454
+ ...location,
455
+ staged: true,
456
+ commit() {
457
+ if (settled) return;
458
+ rmSync(quarantine, { recursive: true, force: true });
459
+ settled = true;
460
+ },
461
+ rollback() {
462
+ if (settled) return;
463
+ if (existsSync(location.target)) {
464
+ throw packageError(
465
+ "CC_RECORD_INSTALL_CONFLICT",
466
+ "recorded skill target was replaced during revocation rollback",
467
+ );
468
+ }
469
+ renameSync(quarantine, location.target);
470
+ settled = true;
471
+ },
472
+ });
473
+ }
474
+
475
+ export function inspectRecordedSkillPackage(options = {}) {
476
+ const location = resolveRecordedSkillInstallTarget(options);
477
+ if (!existsSync(location.target)) return null;
478
+ const installed = readInstalledPackage(location.target);
479
+ return Object.freeze({
480
+ ...location,
481
+ name: installed.payload.name,
482
+ entryDigest: installed.payload.entryDigest,
483
+ replayDigest: installed.payload.replayDigest,
484
+ packageDigest: installed.packageDigest,
485
+ });
486
+ }