chainlesschain 0.166.141 → 0.166.143
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/README.md +249 -14
- package/package.json +2 -2
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BefmcZw2.js → AIOps-DNySV2Kf.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BBlS9rmj.js → ActionButton-ByFuxJsa.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DfNjXT9e.js → Analytics-ETbdywk5.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-CIBZPPMS.js → AppLayout-CuHekZaP.js} +2 -2
- package/src/assets/web-panel/assets/{Artifacts-CE66rTTZ.js → Artifacts-BJJq2BDq.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DOC8Lt8U.js → Audit-DXrietv8.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-9TMG041Q.js → BackgroundAgents-CFB1fxsr.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-us3KWb9F.js → Backup-VLtflRmp.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D6Q95Wr5.js → BaseInput-BbqBaS2r.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BPrM5ARp.js → Chat-t0g7BXL8.js} +1 -1
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-Drs9vSc4.js → ChatBubbleRenderer-JNFsjV0j.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DqZGMV0o.js → Checkbox-BCSclHQb.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CmolL6wr.js → Codegen-BcyINEHT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BwHSmoYQ.js → Col-C-0WNlwS.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DGWqobpd.js → Community-DxZeoORX.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-t_yy09-r.js → Compact-BNnX4jpz.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-D0S8-EsN.js → Compliance-KnjHtn9C.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Y1LiuRwP.js → Cowork-CDyCrhbX.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-FTqEw7QV.js → Cron-DxpS3D1G.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-C3rPZ4Qu.js → Crosschain-BSzTZYyc.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Ba2zySM-.js → DID-Bfj9Vdne.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-D6xyN2ND.js → Dashboard-CcERVdgo.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BF4QuXJw.js → Dropdown-BtYXAz6E.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-s9dUxq14.js → EmailListRenderer-fdCMeGXU.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-CoAKOfqZ.js → EvolutionSettings-LHxhcuF2.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CEznwH1c.js → FamilyGuardDashboard-MIgtLsos.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-rIzOt8-a.js → Federation-9-TOztwa.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BARFiExr.js → FormItemContext-BKcwEE6w.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DI0A4swS.js → GenericCardRenderer-DJBPVUK2.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DdmysND4.js → Git-C_XYISjG.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-DrdhwUz2.js → Governance-DWsbPrwq.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Scm8tumd.js → Inference-D0_bYket.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-_6bgPs4W.js → KnowledgeGraph-BoA3VQlw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DW_v7bSo.js → Logs-LEGItChf.js} +1 -1
- package/src/assets/web-panel/assets/{MarkdownRenderer-7JABuv1j.js → MarkdownRenderer-DOHVc-Uw.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-0h3YSvME.js → Marketplace-BKovl9NH.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Byrg0Y3e.js → McpTools-D61ktoGb.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-Cyoq-o29.js → Memory-CllorS0l.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-ISIlploU.js → MobileBridge-D-dJ78bk.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-04EncaRx.js → MobileProjects-CaxMKkd3.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BMzkpyOF.js → Mtc-qEc62Qsx.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-Cx7BR5sz.js → MtcAudit-BY68jOZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-D4pmkSX4.js → Multisig-eCCR8ELt.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-CCfaaWNu.js → NLProgramming-VtGv8pky.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Mq-1ZyaH.js → Notes-CPkYQySe.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-rzf5V1TR.js → NotificationSettings-BuAvlHUu.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-OREUs5ue.js → OrderTableRenderer-CLf_HAGv.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BQfOATgw.js → Organization-DjujhMbD.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-D7Pa6aaA.js → Overflow-RznznTS4.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CaJ79ZsC.js → P2P-DSa5ot0t.js} +1 -1
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CtimQQN3.js → PdhVaultBrowser-oSEh6vkf.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-AzmmkLZ0.js → Permissions-CdOZguX9.js} +1 -1
- package/src/assets/web-panel/assets/{PersonalDataHub-BRU8ME7z.js → PersonalDataHub-B4--6iEF.js} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-BnNqutXh.js → Pipeline-BnI57jE8.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-gMyZcxsz.js → Privacy-C8MId3ad.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CkK-Fvr0.js → ProjectInit-DPGyYO8a.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-CGY_uiIX.js → ProjectSettings-qBMqaJaO.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-DBNC7xXG.js → Projects-Bq87Syce.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BVgWawtE.js → Providers-CVuG9Ugf.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DZILESCa.js → QrScannerModal-C94c8m3q.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DwRBzFWW.js → QuickAsk-BvfsXZ8s.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B3VxvUih.js → Recommend-CoezBYH9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DM74lufU.js → RemoteSession-CcYC4v3u.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DOOEuGHh.js → Reputation-diDbL4cO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BZJYAo0S.js → Row-5qp3_fNS.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CYw44jcD.js → RssFeed-BCsMvpV4.js} +1 -1
- package/src/assets/web-panel/assets/{Search-CbypjDNG.js → Search-Br9nVxOp.js} +1 -1
- package/src/assets/web-panel/assets/{Security-0rSBDrXh.js → Security-BdOUzRfA.js} +1 -1
- package/src/assets/web-panel/assets/{Services-kqP79CBD.js → Services-Cp9TqzyN.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-BjxvGVfI.js → Skeleton-DSdWsUMR.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-SOoWyPCO.js → Skills-DRczAMTe.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-kdRVAT19.js → Sla-B6G_Vd8N.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DILtcMaJ.js → SpeechSettings-BCcLx5x4.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C98Jauc4.js → SyncSettings-BFhwtxCp.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-Bw-KaQAs.js → Tasks-DGRE6VHO.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CDJ027Hd.js → Templates-CJkJRFzd.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BqNV62UA.js → Tenant-CAOv6p00.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B8aQ3GLO.js → Terminal-CDrwwrKj.js} +1 -1
- package/src/assets/web-panel/assets/{TimelineRenderer-BSpccq11.js → TimelineRenderer-DI0YE2SN.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BbGMoFO6.js → Tokens-DmqWTZSG.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D75Mawa_.js → Trigger-CkXkhmjk.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BiK_Pk05.js → Trust-U8YJLcoJ.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Dv2xX_b9.js → UkeySign-BLTozMmr.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CdBWfeEE.js → VideoEditing-DHKyFlZa.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Cvrp4agg.js → Wallet-DapBxIQt.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-D0Htlf3v.js → WebAuthn-CkvG3yOr.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-nuUBOHIF.js → WorkflowEditor-cNLsinXP.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C6MLShIo.js → chat-BzgkgfMC.js} +1 -1
- package/src/assets/web-panel/assets/{colors-9G5FqFOC.js → colors-BPpVKQsb.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-0M49EurU.js → compact-item-zepznuTv.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CiDDLED_.js → createContext-BRto-SQH.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-wZvUczmn.js → hasIn-CqsmU_n6.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0JZ_ccM.js → index-B-bNzvdU.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_XOMujL.js → index-B0Z6rfV-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwX9cOL6.js → index-B9_jRD2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwUOiN8M.js → index-BEXKES7V.js} +1 -1
- package/src/assets/web-panel/assets/{index-8jz-X3xg.js → index-BGHd4D8F.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVV2mBbo.js → index-BLwY9y0X.js} +1 -1
- package/src/assets/web-panel/assets/{index-qJcOtd8J.js → index-B_l6gfDh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6TAOS4G.js → index-BeBxP1M6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C63qiwQz.js → index-BeSzdB7x.js} +1 -1
- package/src/assets/web-panel/assets/{index-k0BVfcwQ.js → index-BuBnM6Kz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXIY2BIN.js → index-Bz7pG8z3.js} +1 -1
- package/src/assets/web-panel/assets/{index-D51V5vmz.js → index-CBI-7Q3W.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWTcG128.js → index-CTk9gY88.js} +1 -1
- package/src/assets/web-panel/assets/{index-sBsJaBmM.js → index-CdHXVSZ4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dclwd_lj.js → index-CmPnDXx4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlcOkfzz.js → index-CnezE05k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOxTmc3k.js → index-CwkhrUmw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs66y4oC.js → index-CzoKAiyT.js} +3 -3
- package/src/assets/web-panel/assets/{index-BzeA3eRQ.js → index-DDPmr5Yr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdaLWIz-.js → index-DEbAW3Ec.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvWRS-s7.js → index-DJSn3owP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DN9t1qjW.js → index-DKa_jSfj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTCIIdoS.js → index-DUclk35X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmS2P_eX.js → index-DXkw-nt5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoxzvhoN.js → index-D_-w4KkD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz1SAgnb.js → index-D_asIcU7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfwJm0DU.js → index-Df_CY0We.js} +1 -1
- package/src/assets/web-panel/assets/{index-JhfNEldK.js → index-Dnvj_XU-.js} +1 -1
- package/src/assets/web-panel/assets/index-DruOFzuF.js +1 -0
- package/src/assets/web-panel/assets/{index-D9J_tLGk.js → index-DvGttIXQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcZxOXd8.js → index-JrVhUwyh.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdUmgLxP.js → index-LVvzNdPa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTy9gD3m.js → index-Q5sAV7wp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtbFv3Et.js → index-S8fkzbQ_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCfxxScC.js → index-Z6DBe2RU.js} +1 -1
- package/src/assets/web-panel/assets/{index-dJ_V_Fqy.js → index-faHHtBKj.js} +1 -1
- package/src/assets/web-panel/assets/index-hhOrZ6HO.js +1 -0
- package/src/assets/web-panel/assets/{index-DeHSwWVG.js → index-w36TkwyO.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2wN6VIq.js → index-wbvZayBb.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DyIyjurJ.js → initDefaultProps-BlhbrsQk.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B2D0PuTM.js → motion-UZx12NPM.js} +1 -1
- package/src/assets/web-panel/assets/{move-B22T5MnN.js → move-BTby5W4q.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-i4b7HNzF.js → mtc-parser-BOOaGiCg.js} +1 -1
- package/src/assets/web-panel/assets/{omit-ByNIr_gz.js → omit-BVylGzEr.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-_xP2RvxA.js → pickAttrs-Cp3Nflbr.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CVDvzK0z.js → placementArrow-BVXYvwH2.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CI1toXJ_.js → responsiveObserve-C67SbpBN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-2_VosSRQ.js → slide-CnBvM22R.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CMiWWrZC.js → statusUtils-T8fy53tf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DY8wTVCe.js → styleChecker-Bf7i4nlm.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DkdcgIyl.js → useFlexGapSupport-BUKk7jqw.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-gpl11PEF.js → useFs-T5lg6SpD.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DqnQ0H9y.js → usePersonalDataHub-Brvye8Tg.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CgY9xUvr.js → vnode-CK992ojP.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Ch0jan5_.js → zoom-CcABB_97.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/goal.js +340 -145
- package/src/commands/setup.js +17 -0
- package/src/data/changelog.json +26 -0
- package/src/lib/llm-onboarding-catalog.json +4 -3
- package/src/lib/plugin-runtime/cli-goal-capabilities.js +29 -0
- package/src/lib/plugin-runtime/cli-goal-draft-request.js +25 -15
- package/src/lib/plugin-runtime/cli-goal-drafts.js +78 -35
- package/src/lib/plugin-runtime/cli-goal-event-history.js +284 -0
- package/src/lib/plugin-runtime/cli-goal-events.js +175 -0
- package/src/lib/plugin-runtime/cli-goal-extension-host.js +21 -13
- package/src/lib/plugin-runtime/cli-project-goal-events.js +212 -0
- package/src/lib/plugin-runtime/examples/goal-project-observer/plugin.json +20 -0
- package/src/lib/plugin-runtime/examples/goal-status-observer/plugin.json +17 -0
- package/src/lib/plugin-runtime/examples/goal-task-creator/README.md +60 -0
- package/src/lib/plugin-runtime/examples/goal-task-creator/plugin.json +13 -0
- package/src/lib/plugin-runtime/examples/project-delivery-assistant/README.md +125 -0
- package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +8 -8
- package/src/lib/plugin-runtime/goal-extensions.js +17 -0
- package/src/lib/project-goal-connection.js +126 -30
- package/src/lib/project-goal-draft-connection.js +18 -11
- package/src/assets/web-panel/assets/index-C101Sec6.js +0 -1
- package/src/assets/web-panel/assets/index-D23TaGOp.js +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Implemented CLI surfaces; source leases and per-call activation still gate use. */
|
|
2
|
+
import { VERSION } from "../../constants.js";
|
|
3
|
+
|
|
4
|
+
export const CLI_GOAL_CAPABILITIES = Object.freeze([
|
|
5
|
+
"event:goal.status.changed",
|
|
6
|
+
"event:risk.review.completed",
|
|
7
|
+
"event:action.receipt.updated",
|
|
8
|
+
"panel:goal.summary",
|
|
9
|
+
"panel:goal.evidence",
|
|
10
|
+
"proposal:task.description.update",
|
|
11
|
+
"proposal:task.create",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export function cliGoalExtensionHost(activeCapabilities) {
|
|
15
|
+
if (
|
|
16
|
+
!Array.isArray(activeCapabilities) ||
|
|
17
|
+
activeCapabilities.some(
|
|
18
|
+
(value) => !CLI_GOAL_CAPABILITIES.includes(value),
|
|
19
|
+
) ||
|
|
20
|
+
new Set(activeCapabilities).size !== activeCapabilities.length
|
|
21
|
+
)
|
|
22
|
+
throw new Error("CLI_GOAL_HOST_CAPABILITIES_INVALID");
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
id: "cli",
|
|
25
|
+
version: VERSION,
|
|
26
|
+
capabilities: CLI_GOAL_CAPABILITIES,
|
|
27
|
+
activeCapabilities: Object.freeze([...activeCapabilities]),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
|
|
4
4
|
import { createProjectGoalConnection } from "../project-goal-connection.js";
|
|
5
5
|
import { collectGoalExtensions } from "./goal-extensions.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createGoalDraftRequest,
|
|
8
|
+
createGoalTaskCreateRequest,
|
|
9
|
+
} from "@chainlesschain/session-core/goal-draft-request-contract";
|
|
7
10
|
import { digestBusinessObjectContent as digest } from "@chainlesschain/session-core/business-object-contract";
|
|
8
|
-
const HOST = Object.freeze({
|
|
9
|
-
id: "cli",
|
|
10
|
-
version: VERSION,
|
|
11
|
-
capabilities: Object.freeze(["proposal:task.description.update"]),
|
|
12
|
-
});
|
|
13
11
|
function fail(code) {
|
|
14
12
|
throw new Error(code);
|
|
15
13
|
}
|
|
@@ -53,12 +51,19 @@ export function readGoalDraftDescription(file) {
|
|
|
53
51
|
}
|
|
54
52
|
}
|
|
55
53
|
export async function exportCliGoalDraftRequest(id, options = {}) {
|
|
54
|
+
return exportRequest(id, options, false);
|
|
55
|
+
}
|
|
56
|
+
export async function exportCliGoalTaskCreateRequest(id, options = {}) {
|
|
57
|
+
return exportRequest(id, options, true);
|
|
58
|
+
}
|
|
59
|
+
async function exportRequest(id, options, creation) {
|
|
56
60
|
options = Object.freeze({
|
|
57
61
|
...options,
|
|
58
62
|
env: Object.freeze({ ...(options.env || process.env) }),
|
|
59
63
|
});
|
|
60
64
|
const cwd = path.resolve(options.cwd || process.cwd()),
|
|
61
65
|
env = Object.freeze({ ...(options.env || process.env) });
|
|
66
|
+
const type = creation ? "task.create" : "task.description.update";
|
|
62
67
|
if (
|
|
63
68
|
!options.connection ||
|
|
64
69
|
!options.plugin ||
|
|
@@ -77,7 +82,7 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
|
|
|
77
82
|
const hostOptions = {
|
|
78
83
|
cwd,
|
|
79
84
|
env,
|
|
80
|
-
host:
|
|
85
|
+
host: cliGoalExtensionHost([`proposal:${type}`]),
|
|
81
86
|
managedSettingsFile: options.managedSettingsFile,
|
|
82
87
|
};
|
|
83
88
|
const admitted = () =>
|
|
@@ -85,12 +90,11 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
|
|
|
85
90
|
(entry) => entry.name === options.plugin && entry.scope === options.scope,
|
|
86
91
|
);
|
|
87
92
|
const plugin = admitted();
|
|
88
|
-
if (
|
|
89
|
-
!plugin ||
|
|
90
|
-
!plugin.declaration.proposals.includes("task.description.update")
|
|
91
|
-
)
|
|
93
|
+
if (!plugin || !plugin.declaration.proposals.includes(type))
|
|
92
94
|
fail("GOAL_DRAFT_PLUGIN_NOT_ADMITTED");
|
|
93
|
-
const request =
|
|
95
|
+
const request = (
|
|
96
|
+
creation ? createGoalTaskCreateRequest : createGoalDraftRequest
|
|
97
|
+
)({
|
|
94
98
|
declaredPlugin: {
|
|
95
99
|
name: plugin.name,
|
|
96
100
|
scope: plugin.scope,
|
|
@@ -98,7 +102,8 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
|
|
|
98
102
|
},
|
|
99
103
|
declarationDigest: digest(plugin.declaration),
|
|
100
104
|
proposal: {
|
|
101
|
-
type
|
|
105
|
+
type,
|
|
106
|
+
...(creation ? { taskType: options.taskType } : {}),
|
|
102
107
|
goalRef: snapshot.goalRef,
|
|
103
108
|
sourceRef: options.source,
|
|
104
109
|
requestId: options.requestId,
|
|
@@ -127,12 +132,17 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
|
|
|
127
132
|
fs.closeSync(fd);
|
|
128
133
|
}
|
|
129
134
|
return {
|
|
130
|
-
schema:
|
|
135
|
+
schema: creation
|
|
136
|
+
? "chainlesschain.goal-task-create-export/v1"
|
|
137
|
+
: "chainlesschain.goal-draft-export/v1",
|
|
131
138
|
state: "request-exported",
|
|
132
139
|
goalRef: request.proposal.goalRef,
|
|
133
140
|
declaredPlugin: request.declaredPlugin,
|
|
134
141
|
requestId: request.proposal.requestId,
|
|
135
142
|
contentDigest: request.contentDigest,
|
|
136
143
|
file,
|
|
144
|
+
...(creation
|
|
145
|
+
? { type: "task.create", taskType: request.proposal.taskType }
|
|
146
|
+
: {}),
|
|
137
147
|
};
|
|
138
148
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
|
|
4
4
|
import { managedSettingsPath } from "../settings-loader.cjs";
|
|
5
5
|
import { createProjectGoalConnection } from "../project-goal-connection.js";
|
|
6
6
|
import {
|
|
@@ -11,24 +11,26 @@ import { readGoalDraftDescription } from "./cli-goal-draft-request.js";
|
|
|
11
11
|
import { digestBusinessObjectContent as digest } from "@chainlesschain/session-core/business-object-contract";
|
|
12
12
|
import {
|
|
13
13
|
ACCESS_SCHEMA,
|
|
14
|
+
CREATE_ACCESS_SCHEMA,
|
|
14
15
|
MAX_SUBMISSION_REFERENCE_BYTES,
|
|
15
16
|
validateGoalDraftAccess,
|
|
17
|
+
validateGoalCreateDraftAccess,
|
|
16
18
|
validateGoalDraftProposal,
|
|
19
|
+
validateGoalCreateDraftProposal,
|
|
17
20
|
validateGoalDraftReceipt,
|
|
18
21
|
validateGoalDraftSources,
|
|
19
22
|
createGoalDraftSubmissionReference,
|
|
20
23
|
validateGoalDraftSubmissionReference,
|
|
21
24
|
} from "@chainlesschain/session-core/goal-draft-connection-contract";
|
|
22
25
|
|
|
23
|
-
const HOST = Object.freeze({
|
|
24
|
-
id: "cli",
|
|
25
|
-
version: VERSION,
|
|
26
|
-
capabilities: Object.freeze(["proposal:task.description.update"]),
|
|
27
|
-
});
|
|
28
26
|
export const CLI_GOAL_DRAFT_SUBMISSION_SCHEMA =
|
|
29
27
|
"chainlesschain.goal-draft-submission/v1";
|
|
30
28
|
export const CLI_GOAL_DRAFT_SOURCES_SCHEMA =
|
|
31
29
|
"chainlesschain.goal-draft-source-page/v1";
|
|
30
|
+
export const CLI_GOAL_CREATE_DRAFT_SUBMISSION_SCHEMA =
|
|
31
|
+
"chainlesschain.goal-create-draft-submission/v1";
|
|
32
|
+
export const CLI_GOAL_CREATE_DRAFT_SOURCES_SCHEMA =
|
|
33
|
+
"chainlesschain.goal-create-draft-source-page/v1";
|
|
32
34
|
export const _deps = {
|
|
33
35
|
createProjectGoalConnection,
|
|
34
36
|
collectGoalExtensions,
|
|
@@ -48,7 +50,7 @@ function required(value) {
|
|
|
48
50
|
fail("CLI_GOAL_DRAFT_ARGUMENTS_REQUIRED");
|
|
49
51
|
return value;
|
|
50
52
|
}
|
|
51
|
-
function fixedOptions(raw) {
|
|
53
|
+
function fixedOptions(raw, creation = false) {
|
|
52
54
|
const options = { ...raw },
|
|
53
55
|
env = Object.freeze({ ...(options.env || process.env) });
|
|
54
56
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
@@ -72,6 +74,8 @@ function fixedOptions(raw) {
|
|
|
72
74
|
);
|
|
73
75
|
return Object.freeze({
|
|
74
76
|
...options,
|
|
77
|
+
creation,
|
|
78
|
+
draftType: creation ? "task.create" : "task.description.update",
|
|
75
79
|
cwd,
|
|
76
80
|
env,
|
|
77
81
|
managedSettingsFile: managedSettingsPath({
|
|
@@ -85,7 +89,7 @@ function hostOptions(options) {
|
|
|
85
89
|
cwd: options.cwd,
|
|
86
90
|
env: options.env,
|
|
87
91
|
managedSettingsFile: options.managedSettingsFile,
|
|
88
|
-
host:
|
|
92
|
+
host: cliGoalExtensionHost([`proposal:${options.draftType}`]),
|
|
89
93
|
});
|
|
90
94
|
}
|
|
91
95
|
function admitted(options, expected) {
|
|
@@ -94,13 +98,12 @@ function admitted(options, expected) {
|
|
|
94
98
|
.extensions.find(
|
|
95
99
|
(item) => item.name === options.plugin && item.scope === options.scope,
|
|
96
100
|
);
|
|
97
|
-
if (
|
|
98
|
-
!entry ||
|
|
99
|
-
!entry.declaration.proposals.includes("task.description.update")
|
|
100
|
-
)
|
|
101
|
+
if (!entry || !entry.declaration.proposals.includes(options.draftType))
|
|
101
102
|
fail("CLI_GOAL_DRAFT_NOT_ADMITTED");
|
|
102
|
-
const access =
|
|
103
|
-
|
|
103
|
+
const access = (
|
|
104
|
+
options.creation ? validateGoalCreateDraftAccess : validateGoalDraftAccess
|
|
105
|
+
)({
|
|
106
|
+
schema: options.creation ? CREATE_ACCESS_SCHEMA : ACCESS_SCHEMA,
|
|
104
107
|
plugin: { name: entry.name, scope: entry.scope, version: entry.version },
|
|
105
108
|
declarationDigest: digest(entry.declaration),
|
|
106
109
|
});
|
|
@@ -113,7 +116,9 @@ function connected(id, options) {
|
|
|
113
116
|
required(options.connection);
|
|
114
117
|
const connection = _deps.createProjectGoalConnection(options.connection);
|
|
115
118
|
if (
|
|
116
|
-
|
|
119
|
+
(options.creation
|
|
120
|
+
? connection.createDrafts !== true || connection.drafts === true
|
|
121
|
+
: connection.drafts !== true || connection.createDrafts === true) ||
|
|
117
122
|
!["read", "sources", "receipt", "enqueue"].every(
|
|
118
123
|
(key) => typeof connection[key] === "function",
|
|
119
124
|
)
|
|
@@ -229,9 +234,12 @@ function referenceFor(connection, access, requestId, inputDigest) {
|
|
|
229
234
|
inputDigest,
|
|
230
235
|
});
|
|
231
236
|
}
|
|
232
|
-
function outcome(reference, file, receipt) {
|
|
237
|
+
function outcome(reference, file, receipt, creation = false) {
|
|
233
238
|
return {
|
|
234
|
-
schema:
|
|
239
|
+
schema: creation
|
|
240
|
+
? CLI_GOAL_CREATE_DRAFT_SUBMISSION_SCHEMA
|
|
241
|
+
: CLI_GOAL_DRAFT_SUBMISSION_SCHEMA,
|
|
242
|
+
...(creation ? { type: "task.create" } : {}),
|
|
235
243
|
state: receipt?.state ?? "unknown",
|
|
236
244
|
goalRef: reference.goalRef,
|
|
237
245
|
plugin: reference.plugin,
|
|
@@ -257,28 +265,40 @@ async function queryReceipt(connection, saved, file) {
|
|
|
257
265
|
receipt.inputDigest !== saved.reference.inputDigest
|
|
258
266
|
)
|
|
259
267
|
fail("CLI_GOAL_DRAFT_RECEIPT_MISMATCH");
|
|
260
|
-
return outcome(
|
|
268
|
+
return outcome(
|
|
269
|
+
saved.reference,
|
|
270
|
+
file,
|
|
271
|
+
receipt,
|
|
272
|
+
connection.createDrafts === true,
|
|
273
|
+
);
|
|
261
274
|
} catch {
|
|
262
|
-
return outcome(
|
|
275
|
+
return outcome(
|
|
276
|
+
saved.reference,
|
|
277
|
+
file,
|
|
278
|
+
undefined,
|
|
279
|
+
connection.createDrafts === true,
|
|
280
|
+
);
|
|
263
281
|
}
|
|
264
282
|
}
|
|
265
283
|
|
|
266
284
|
/** Exports an identity label and declaration digest, never native authority. */
|
|
267
|
-
|
|
268
|
-
const options = fixedOptions(raw);
|
|
285
|
+
async function exportAccess(raw = {}, creation = false) {
|
|
286
|
+
const options = fixedOptions(raw, creation);
|
|
269
287
|
required(options.out);
|
|
270
288
|
const access = admitted(options);
|
|
271
289
|
writeNew(options.out, access);
|
|
272
290
|
return {
|
|
273
|
-
schema:
|
|
291
|
+
schema: options.creation
|
|
292
|
+
? "chainlesschain.goal-create-draft-access-export/v1"
|
|
293
|
+
: "chainlesschain.goal-draft-access-export/v1",
|
|
274
294
|
state: "access-exported",
|
|
275
295
|
plugin: access.plugin,
|
|
276
296
|
declarationDigest: access.declarationDigest,
|
|
277
297
|
file: options.out,
|
|
278
298
|
};
|
|
279
299
|
}
|
|
280
|
-
|
|
281
|
-
const options = fixedOptions(raw),
|
|
300
|
+
async function readSources(id, raw = {}, creation = false) {
|
|
301
|
+
const options = fixedOptions(raw, creation),
|
|
282
302
|
{ connection, access } = connected(id, options);
|
|
283
303
|
if (
|
|
284
304
|
options.after !== undefined &&
|
|
@@ -291,7 +311,9 @@ export async function readCliGoalDraftSources(id, raw = {}) {
|
|
|
291
311
|
);
|
|
292
312
|
admitted(options, access);
|
|
293
313
|
return {
|
|
294
|
-
schema:
|
|
314
|
+
schema: options.creation
|
|
315
|
+
? CLI_GOAL_CREATE_DRAFT_SOURCES_SCHEMA
|
|
316
|
+
: CLI_GOAL_DRAFT_SOURCES_SCHEMA,
|
|
295
317
|
goalRef: connection.goalRef,
|
|
296
318
|
plugin: access.plugin,
|
|
297
319
|
declarationDigest: access.declarationDigest,
|
|
@@ -299,13 +321,17 @@ export async function readCliGoalDraftSources(id, raw = {}) {
|
|
|
299
321
|
sources,
|
|
300
322
|
};
|
|
301
323
|
}
|
|
302
|
-
|
|
303
|
-
const options = fixedOptions(raw);
|
|
324
|
+
async function sendDraft(id, raw = {}, creation = false) {
|
|
325
|
+
const options = fixedOptions(raw, creation);
|
|
304
326
|
required(options.record);
|
|
305
327
|
required(options.descriptionFile);
|
|
306
328
|
const { connection, access } = connected(id, options);
|
|
307
|
-
const
|
|
308
|
-
|
|
329
|
+
const validateProposal = options.creation
|
|
330
|
+
? validateGoalCreateDraftProposal
|
|
331
|
+
: validateGoalDraftProposal;
|
|
332
|
+
const proposal = validateProposal({
|
|
333
|
+
type: options.draftType,
|
|
334
|
+
...(options.creation ? { taskType: options.taskType } : {}),
|
|
309
335
|
goalRef: connection.goalRef,
|
|
310
336
|
sourceRef: options.source,
|
|
311
337
|
requestId: options.requestId,
|
|
@@ -349,7 +375,7 @@ export async function sendCliGoalDraft(id, raw = {}) {
|
|
|
349
375
|
return true;
|
|
350
376
|
},
|
|
351
377
|
async enqueueProposal(context) {
|
|
352
|
-
const projected =
|
|
378
|
+
const projected = validateProposal(
|
|
353
379
|
Object.fromEntries(
|
|
354
380
|
[
|
|
355
381
|
"type",
|
|
@@ -358,6 +384,7 @@ export async function sendCliGoalDraft(id, raw = {}) {
|
|
|
358
384
|
"requestId",
|
|
359
385
|
"title",
|
|
360
386
|
"description",
|
|
387
|
+
...(options.creation ? ["taskType"] : []),
|
|
361
388
|
].map((key) => [key, context[key]]),
|
|
362
389
|
),
|
|
363
390
|
);
|
|
@@ -389,16 +416,22 @@ export async function sendCliGoalDraft(id, raw = {}) {
|
|
|
389
416
|
});
|
|
390
417
|
if (!nativeReceipt) fail("CLI_GOAL_DRAFT_RECEIPT_MISMATCH");
|
|
391
418
|
// A verified saved receipt is not undone by a later local admission change.
|
|
392
|
-
return outcome(reference, options.record, nativeReceipt);
|
|
419
|
+
return outcome(reference, options.record, nativeReceipt, options.creation);
|
|
393
420
|
} catch (error) {
|
|
394
|
-
if (nativeReceipt)
|
|
421
|
+
if (nativeReceipt)
|
|
422
|
+
return outcome(
|
|
423
|
+
reference,
|
|
424
|
+
options.record,
|
|
425
|
+
nativeReceipt,
|
|
426
|
+
options.creation,
|
|
427
|
+
);
|
|
395
428
|
if (dispatched || error.outcomeUnknown)
|
|
396
|
-
return outcome(reference, options.record);
|
|
429
|
+
return outcome(reference, options.record, undefined, options.creation);
|
|
397
430
|
throw error;
|
|
398
431
|
}
|
|
399
432
|
}
|
|
400
|
-
|
|
401
|
-
const options = fixedOptions(raw);
|
|
433
|
+
async function readReceipt(id, raw = {}, creation = false) {
|
|
434
|
+
const options = fixedOptions(raw, creation);
|
|
402
435
|
required(options.record);
|
|
403
436
|
const { connection, access } = connected(id, options),
|
|
404
437
|
saved = readReference(options.record);
|
|
@@ -411,3 +444,13 @@ export async function readCliGoalDraftReceipt(id, raw = {}) {
|
|
|
411
444
|
if (!same(expected, saved.reference)) fail("CLI_GOAL_DRAFT_RECORD_CONFLICT");
|
|
412
445
|
return queryReceipt(connection, saved, options.record);
|
|
413
446
|
}
|
|
447
|
+
export const exportCliGoalDraftAccess = (raw) => exportAccess(raw, false);
|
|
448
|
+
export const exportCliGoalCreateDraftAccess = (raw) => exportAccess(raw, true);
|
|
449
|
+
export const readCliGoalDraftSources = (id, raw) => readSources(id, raw, false);
|
|
450
|
+
export const readCliGoalCreateDraftSources = (id, raw) =>
|
|
451
|
+
readSources(id, raw, true);
|
|
452
|
+
export const sendCliGoalDraft = (id, raw) => sendDraft(id, raw, false);
|
|
453
|
+
export const sendCliGoalCreateDraft = (id, raw) => sendDraft(id, raw, true);
|
|
454
|
+
export const readCliGoalDraftReceipt = (id, raw) => readReceipt(id, raw, false);
|
|
455
|
+
export const readCliGoalCreateDraftReceipt = (id, raw) =>
|
|
456
|
+
readReceipt(id, raw, true);
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/** One acknowledged page at a time; checkpoints contain no credentials or bodies. */
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
|
|
6
|
+
import { managedSettingsPath } from "../settings-loader.cjs";
|
|
7
|
+
import { withFileLockAsync } from "../with-file-lock.js";
|
|
8
|
+
import { createProjectGoalConnection } from "../project-goal-connection.js";
|
|
9
|
+
import {
|
|
10
|
+
collectGoalExtensions,
|
|
11
|
+
projectGoalExtensionEvent,
|
|
12
|
+
} from "./goal-extensions.js";
|
|
13
|
+
import { parseGoalEventCursor } from "@chainlesschain/session-core/goal-evidence-contract";
|
|
14
|
+
const TYPES = ["risk.review.completed", "action.receipt.updated"];
|
|
15
|
+
const hash = (value) =>
|
|
16
|
+
createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
17
|
+
const fail = (code) => {
|
|
18
|
+
throw Object.assign(new Error(code), { code });
|
|
19
|
+
};
|
|
20
|
+
export const _deps = { createProjectGoalConnection, projectGoalExtensionEvent };
|
|
21
|
+
const sameFile = (a, b) =>
|
|
22
|
+
a.dev === b.dev &&
|
|
23
|
+
a.ino === b.ino &&
|
|
24
|
+
a.size === b.size &&
|
|
25
|
+
a.mtimeMs === b.mtimeMs &&
|
|
26
|
+
a.ctimeMs === b.ctimeMs;
|
|
27
|
+
function readFile(file) {
|
|
28
|
+
const entry = fs.lstatSync(file);
|
|
29
|
+
if (
|
|
30
|
+
!entry.isFile() ||
|
|
31
|
+
entry.isSymbolicLink() ||
|
|
32
|
+
entry.nlink !== 1 ||
|
|
33
|
+
entry.size > 8192
|
|
34
|
+
)
|
|
35
|
+
fail("GOAL_EVENT_CHECKPOINT_INVALID");
|
|
36
|
+
const fd = fs.openSync(
|
|
37
|
+
file,
|
|
38
|
+
fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0),
|
|
39
|
+
);
|
|
40
|
+
try {
|
|
41
|
+
if (!sameFile(entry, fs.fstatSync(fd)))
|
|
42
|
+
fail("GOAL_EVENT_CHECKPOINT_CHANGED");
|
|
43
|
+
const bytes = Buffer.alloc(entry.size + 1),
|
|
44
|
+
n = fs.readSync(fd, bytes, 0, bytes.length, 0);
|
|
45
|
+
if (
|
|
46
|
+
n !== entry.size ||
|
|
47
|
+
!sameFile(entry, fs.fstatSync(fd)) ||
|
|
48
|
+
!sameFile(entry, fs.lstatSync(file))
|
|
49
|
+
)
|
|
50
|
+
fail("GOAL_EVENT_CHECKPOINT_CHANGED");
|
|
51
|
+
const body = bytes.subarray(0, n).toString("utf8");
|
|
52
|
+
return { record: JSON.parse(body), digest: hash(body) };
|
|
53
|
+
} finally {
|
|
54
|
+
fs.closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Publish must acknowledge flushed output; a crash before checkpoint commit replays. */
|
|
58
|
+
export function replayCliGoalEvents(id, raw = {}, publish) {
|
|
59
|
+
const cwd = path.resolve(raw.cwd || process.cwd()),
|
|
60
|
+
env = Object.freeze({ ...(raw.env || process.env) });
|
|
61
|
+
const required = (value) => {
|
|
62
|
+
if (
|
|
63
|
+
typeof value !== "string" ||
|
|
64
|
+
!value ||
|
|
65
|
+
value.length > 4096 ||
|
|
66
|
+
/[\u0000-\u001f]/u.test(value)
|
|
67
|
+
)
|
|
68
|
+
fail("GOAL_EVENT_ARGUMENT_REQUIRED");
|
|
69
|
+
return value;
|
|
70
|
+
};
|
|
71
|
+
const connection = path.resolve(cwd, required(raw.connection)),
|
|
72
|
+
checkpoint = path.resolve(cwd, required(raw.checkpoint));
|
|
73
|
+
const name = required(raw.plugin),
|
|
74
|
+
scope = raw.scope,
|
|
75
|
+
expectedStore = raw.expectedStore;
|
|
76
|
+
const signal = raw.signal;
|
|
77
|
+
const checkpointMode = raw.checkpointMode ?? "auto";
|
|
78
|
+
if (!["auto", "new", "resume"].includes(checkpointMode))
|
|
79
|
+
fail("GOAL_EVENT_ARGUMENT_REQUIRED");
|
|
80
|
+
if (
|
|
81
|
+
!["user", "project", "local"].includes(scope) ||
|
|
82
|
+
typeof publish !== "function"
|
|
83
|
+
)
|
|
84
|
+
fail("GOAL_EVENT_ARGUMENT_REQUIRED");
|
|
85
|
+
const policyEnv = { ...env };
|
|
86
|
+
if (policyEnv.CC_MANAGED_SETTINGS)
|
|
87
|
+
policyEnv.CC_MANAGED_SETTINGS = path.resolve(
|
|
88
|
+
cwd,
|
|
89
|
+
required(policyEnv.CC_MANAGED_SETTINGS),
|
|
90
|
+
);
|
|
91
|
+
const policyFile = managedSettingsPath({
|
|
92
|
+
env: policyEnv,
|
|
93
|
+
managedSettingsFile:
|
|
94
|
+
raw.managedSettingsFile === undefined
|
|
95
|
+
? undefined
|
|
96
|
+
: path.resolve(cwd, required(raw.managedSettingsFile)),
|
|
97
|
+
});
|
|
98
|
+
const options = Object.freeze({
|
|
99
|
+
cwd,
|
|
100
|
+
env,
|
|
101
|
+
managedSettingsFile: policyFile,
|
|
102
|
+
host: cliGoalExtensionHost(TYPES.map((type) => `event:${type}`)),
|
|
103
|
+
});
|
|
104
|
+
return (async () => {
|
|
105
|
+
const source = _deps.createProjectGoalConnection(connection);
|
|
106
|
+
if (!source.eventHistory || typeof source.readEventPage !== "function")
|
|
107
|
+
fail("GOAL_EVENT_HISTORY_CONNECTION_REQUIRED");
|
|
108
|
+
const goalRef = source.goalRef;
|
|
109
|
+
if (
|
|
110
|
+
goalRef.id !== id ||
|
|
111
|
+
(expectedStore !== undefined && expectedStore !== goalRef.storeId)
|
|
112
|
+
)
|
|
113
|
+
fail("CLI_GOAL_STORE_MISMATCH");
|
|
114
|
+
const selected = collectGoalExtensions(options).extensions.find(
|
|
115
|
+
(entry) =>
|
|
116
|
+
entry.name === name &&
|
|
117
|
+
entry.scope === scope &&
|
|
118
|
+
entry.declaration.events.some((type) => TYPES.includes(type)),
|
|
119
|
+
);
|
|
120
|
+
if (!selected) fail("CLI_GOAL_EVENT_PLUGIN_UNAVAILABLE");
|
|
121
|
+
const plugin = Object.freeze({
|
|
122
|
+
name: selected.name,
|
|
123
|
+
scope: selected.scope,
|
|
124
|
+
version: selected.version,
|
|
125
|
+
}),
|
|
126
|
+
declarationDigest = hash(selected.declaration);
|
|
127
|
+
function admission() {
|
|
128
|
+
if (signal?.aborted) fail("GOAL_EVENT_REPLAY_ABORTED");
|
|
129
|
+
const current = collectGoalExtensions(options).extensions.find(
|
|
130
|
+
(entry) =>
|
|
131
|
+
entry.name === name &&
|
|
132
|
+
entry.scope === scope &&
|
|
133
|
+
entry.version === plugin.version,
|
|
134
|
+
);
|
|
135
|
+
if (!current || hash(current.declaration) !== declarationDigest)
|
|
136
|
+
fail("CLI_GOAL_EVENT_ADMISSION_CHANGED");
|
|
137
|
+
}
|
|
138
|
+
admission();
|
|
139
|
+
const root = path.dirname(checkpoint),
|
|
140
|
+
realRoot = fs.realpathSync(root),
|
|
141
|
+
rootStat = fs.statSync(root);
|
|
142
|
+
const checkRoot = () => {
|
|
143
|
+
const stat = fs.statSync(root);
|
|
144
|
+
if (
|
|
145
|
+
fs.realpathSync(root) !== realRoot ||
|
|
146
|
+
!stat.isDirectory() ||
|
|
147
|
+
stat.dev !== rootStat.dev ||
|
|
148
|
+
stat.ino !== rootStat.ino
|
|
149
|
+
)
|
|
150
|
+
fail("GOAL_EVENT_CHECKPOINT_CHANGED");
|
|
151
|
+
};
|
|
152
|
+
const binding = {
|
|
153
|
+
schema: "chainlesschain.goal-event-checkpoint/v1",
|
|
154
|
+
goalRef,
|
|
155
|
+
plugin,
|
|
156
|
+
declarationDigest,
|
|
157
|
+
};
|
|
158
|
+
return withFileLockAsync(
|
|
159
|
+
checkpoint,
|
|
160
|
+
async (lock) => {
|
|
161
|
+
checkRoot();
|
|
162
|
+
admission();
|
|
163
|
+
const exists = fs.existsSync(checkpoint);
|
|
164
|
+
if (checkpointMode === "new" && exists)
|
|
165
|
+
fail("GOAL_EVENT_CHECKPOINT_EXISTS");
|
|
166
|
+
if (checkpointMode === "resume" && !exists)
|
|
167
|
+
fail("GOAL_EVENT_CHECKPOINT_MISSING");
|
|
168
|
+
if (!fs.existsSync(checkpoint)) {
|
|
169
|
+
const fd = fs.openSync(checkpoint, "wx", 0o600);
|
|
170
|
+
try {
|
|
171
|
+
fs.writeFileSync(fd, JSON.stringify({ ...binding, after: null }));
|
|
172
|
+
fs.fsyncSync(fd);
|
|
173
|
+
} finally {
|
|
174
|
+
fs.closeSync(fd);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const initial = readFile(checkpoint),
|
|
178
|
+
record = initial.record;
|
|
179
|
+
if (
|
|
180
|
+
!record ||
|
|
181
|
+
Object.keys(record).sort().join() !==
|
|
182
|
+
"after,declarationDigest,goalRef,plugin,schema" ||
|
|
183
|
+
hash({ ...record, after: null }) !== hash({ ...binding, after: null })
|
|
184
|
+
)
|
|
185
|
+
fail("GOAL_EVENT_CHECKPOINT_BINDING_CONFLICT");
|
|
186
|
+
if (record.after !== null) parseGoalEventCursor(record.after);
|
|
187
|
+
const after = record.after ?? undefined;
|
|
188
|
+
const page = await source.readEventPage(after, { signal });
|
|
189
|
+
admission();
|
|
190
|
+
const events = [];
|
|
191
|
+
for (const entry of page.entries) {
|
|
192
|
+
if (!selected.declaration.events.includes(entry.type)) continue;
|
|
193
|
+
const summary =
|
|
194
|
+
entry.type === "risk.review.completed"
|
|
195
|
+
? `Historical risk check ${entry.sourceId}: ${entry.state}; risk task count ${entry.riskTaskCount ?? "unknown"}. This does not prove current risk or goal completion.`
|
|
196
|
+
: `Historical action receipt ${entry.sourceId}: ${entry.state} (${entry.actionType}). This does not prove goal completion.`;
|
|
197
|
+
const event = await _deps.projectGoalExtensionEvent(
|
|
198
|
+
plugin,
|
|
199
|
+
{ type: entry.type, goalRef },
|
|
200
|
+
{
|
|
201
|
+
...options,
|
|
202
|
+
authorize(context) {
|
|
203
|
+
admission();
|
|
204
|
+
return (
|
|
205
|
+
context.operation === "read-event" &&
|
|
206
|
+
context.type === entry.type &&
|
|
207
|
+
hash(context.goalRef) === hash(goalRef)
|
|
208
|
+
);
|
|
209
|
+
},
|
|
210
|
+
readEvent() {
|
|
211
|
+
return { summary, occurredAt: entry.recordedAt };
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
events.push({
|
|
216
|
+
id: entry.id,
|
|
217
|
+
sequence: entry.sequence,
|
|
218
|
+
sourceRef: entry.sourceRef,
|
|
219
|
+
recordedAt: entry.recordedAt,
|
|
220
|
+
event,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async function revalidate() {
|
|
224
|
+
admission();
|
|
225
|
+
checkRoot();
|
|
226
|
+
if (readFile(checkpoint).digest !== initial.digest)
|
|
227
|
+
fail("GOAL_EVENT_CHECKPOINT_CHANGED");
|
|
228
|
+
const current = await source.readEventPage(after, { signal });
|
|
229
|
+
if (
|
|
230
|
+
hash(current.entries.slice(0, page.entries.length)) !==
|
|
231
|
+
hash(page.entries) ||
|
|
232
|
+
(page.nextCursor === null && current.nextCursor !== null) ||
|
|
233
|
+
(page.nextCursor &&
|
|
234
|
+
parseGoalEventCursor(page.nextCursor).epoch !==
|
|
235
|
+
parseGoalEventCursor(current.nextCursor).epoch)
|
|
236
|
+
)
|
|
237
|
+
fail("GOAL_EVENT_HISTORY_CHANGED");
|
|
238
|
+
admission();
|
|
239
|
+
}
|
|
240
|
+
await revalidate();
|
|
241
|
+
const result = {
|
|
242
|
+
schema: "chainlesschain.goal-extension-history/v1",
|
|
243
|
+
goalRef,
|
|
244
|
+
plugin,
|
|
245
|
+
observedAt: new Date().toISOString(),
|
|
246
|
+
delivery: "at-least-once",
|
|
247
|
+
entries: events,
|
|
248
|
+
scannedCount: page.entries.length,
|
|
249
|
+
nextCursor: page.nextCursor,
|
|
250
|
+
hasMore: page.hasMore,
|
|
251
|
+
truncated: page.truncated,
|
|
252
|
+
retainedFrom: page.retainedFrom,
|
|
253
|
+
highWatermark: page.highWatermark,
|
|
254
|
+
};
|
|
255
|
+
if ((await publish(Object.freeze(result))) !== true)
|
|
256
|
+
fail("GOAL_EVENT_OUTPUT_NOT_ACKNOWLEDGED");
|
|
257
|
+
await revalidate();
|
|
258
|
+
lock.assertOwnership();
|
|
259
|
+
const temporary = `${checkpoint}.${randomUUID()}.tmp`;
|
|
260
|
+
try {
|
|
261
|
+
const fd = fs.openSync(temporary, "wx", 0o600);
|
|
262
|
+
try {
|
|
263
|
+
fs.writeFileSync(
|
|
264
|
+
fd,
|
|
265
|
+
JSON.stringify({ ...binding, after: page.nextCursor }),
|
|
266
|
+
);
|
|
267
|
+
fs.fsyncSync(fd);
|
|
268
|
+
} finally {
|
|
269
|
+
fs.closeSync(fd);
|
|
270
|
+
}
|
|
271
|
+
checkRoot();
|
|
272
|
+
lock.assertOwnership();
|
|
273
|
+
if (readFile(checkpoint).digest !== initial.digest)
|
|
274
|
+
fail("GOAL_EVENT_CHECKPOINT_CHANGED");
|
|
275
|
+
fs.renameSync(temporary, checkpoint);
|
|
276
|
+
} finally {
|
|
277
|
+
if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
|
|
278
|
+
}
|
|
279
|
+
return { checkpoint, hasMore: page.hasMore };
|
|
280
|
+
},
|
|
281
|
+
{ failIfUnavailable: true, timeoutMs: 2000 },
|
|
282
|
+
);
|
|
283
|
+
})();
|
|
284
|
+
}
|