create-principles-disciple 1.132.5 → 1.133.0

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 (95) hide show
  1. package/console/dist/server/routes/update.js +105 -1
  2. package/core/dist/runtime-v2/__tests__/evidence-sanitizer.test.js +33 -2
  3. package/core/dist/runtime-v2/__tests__/evidence-sanitizer.test.js.map +1 -1
  4. package/core/dist/runtime-v2/evidence-sanitizer.d.ts.map +1 -1
  5. package/core/dist/runtime-v2/evidence-sanitizer.js +73 -4
  6. package/core/dist/runtime-v2/evidence-sanitizer.js.map +1 -1
  7. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +7 -0
  8. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  9. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  10. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +17 -6
  11. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  12. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
  13. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +14 -6
  14. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
  15. package/core/package.json +1 -1
  16. package/dist/i18n.d.ts.map +1 -1
  17. package/dist/i18n.js +4 -0
  18. package/dist/i18n.js.map +1 -1
  19. package/dist/installer.d.ts +18 -3
  20. package/dist/installer.d.ts.map +1 -1
  21. package/dist/installer.js +99 -29
  22. package/dist/installer.js.map +1 -1
  23. package/dist/mvp-config.d.ts +7 -0
  24. package/dist/mvp-config.d.ts.map +1 -1
  25. package/dist/mvp-config.js +21 -7
  26. package/dist/mvp-config.js.map +1 -1
  27. package/dist/uninstaller.d.ts +15 -0
  28. package/dist/uninstaller.d.ts.map +1 -1
  29. package/dist/uninstaller.js +85 -0
  30. package/dist/uninstaller.js.map +1 -1
  31. package/dist/update/apply-payload.d.ts +88 -0
  32. package/dist/update/apply-payload.d.ts.map +1 -0
  33. package/dist/update/apply-payload.js +166 -0
  34. package/dist/update/apply-payload.js.map +1 -0
  35. package/dist/update/bootstrap-protocol.d.ts.map +1 -1
  36. package/dist/update/bootstrap-protocol.js +14 -2
  37. package/dist/update/bootstrap-protocol.js.map +1 -1
  38. package/dist/update/release-manager-authority.d.ts +15 -0
  39. package/dist/update/release-manager-authority.d.ts.map +1 -1
  40. package/dist/update/release-manager-authority.js +35 -7
  41. package/dist/update/release-manager-authority.js.map +1 -1
  42. package/dist/update/release-manager.d.ts +74 -6
  43. package/dist/update/release-manager.d.ts.map +1 -1
  44. package/dist/update/release-manager.js +272 -36
  45. package/dist/update/release-manager.js.map +1 -1
  46. package/dist/update/transaction-journal.d.ts +3 -1
  47. package/dist/update/transaction-journal.d.ts.map +1 -1
  48. package/dist/update/transaction-journal.js +3 -1
  49. package/dist/update/transaction-journal.js.map +1 -1
  50. package/package.json +1 -1
  51. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  52. package/pd-cli/dist/commands/console.js +12 -2
  53. package/pd-cli/dist/commands/console.js.map +1 -1
  54. package/pd-cli/dist/services/console-launcher.d.ts.map +1 -1
  55. package/pd-cli/dist/services/console-launcher.js +8 -1
  56. package/pd-cli/dist/services/console-launcher.js.map +1 -1
  57. package/pd-cli/package.json +1 -1
  58. package/plugin/dist/bundle.js +327 -327
  59. package/plugin/dist/governance-audit.js +1 -1
  60. package/plugin/dist/rulehost-evidence.js +1 -1
  61. package/release-manager/dist/i18n.d.ts.map +1 -1
  62. package/release-manager/dist/i18n.js +4 -0
  63. package/release-manager/dist/i18n.js.map +1 -1
  64. package/release-manager/dist/installer.d.ts +18 -3
  65. package/release-manager/dist/installer.d.ts.map +1 -1
  66. package/release-manager/dist/installer.js +99 -29
  67. package/release-manager/dist/installer.js.map +1 -1
  68. package/release-manager/dist/mvp-config.d.ts +7 -0
  69. package/release-manager/dist/mvp-config.d.ts.map +1 -1
  70. package/release-manager/dist/mvp-config.js +21 -7
  71. package/release-manager/dist/mvp-config.js.map +1 -1
  72. package/release-manager/dist/uninstaller.d.ts +15 -0
  73. package/release-manager/dist/uninstaller.d.ts.map +1 -1
  74. package/release-manager/dist/uninstaller.js +85 -0
  75. package/release-manager/dist/uninstaller.js.map +1 -1
  76. package/release-manager/dist/update/apply-payload.d.ts +88 -0
  77. package/release-manager/dist/update/apply-payload.d.ts.map +1 -0
  78. package/release-manager/dist/update/apply-payload.js +166 -0
  79. package/release-manager/dist/update/apply-payload.js.map +1 -0
  80. package/release-manager/dist/update/bootstrap-protocol.d.ts.map +1 -1
  81. package/release-manager/dist/update/bootstrap-protocol.js +14 -2
  82. package/release-manager/dist/update/bootstrap-protocol.js.map +1 -1
  83. package/release-manager/dist/update/release-manager-authority.d.ts +15 -0
  84. package/release-manager/dist/update/release-manager-authority.d.ts.map +1 -1
  85. package/release-manager/dist/update/release-manager-authority.js +35 -7
  86. package/release-manager/dist/update/release-manager-authority.js.map +1 -1
  87. package/release-manager/dist/update/release-manager.d.ts +74 -6
  88. package/release-manager/dist/update/release-manager.d.ts.map +1 -1
  89. package/release-manager/dist/update/release-manager.js +272 -36
  90. package/release-manager/dist/update/release-manager.js.map +1 -1
  91. package/release-manager/dist/update/transaction-journal.d.ts +3 -1
  92. package/release-manager/dist/update/transaction-journal.d.ts.map +1 -1
  93. package/release-manager/dist/update/transaction-journal.js +3 -1
  94. package/release-manager/dist/update/transaction-journal.js.map +1 -1
  95. package/release-manager/package.json +1 -1
@@ -31,14 +31,28 @@
31
31
  * activation rollout opens the `shadow_mode_read_only` gate (its own go/no-go);
32
32
  * until then these kinds explicitly fall back to the legacy console updater —
33
33
  * TEMPORARY migration debt per ADR-0024 D-1, not a supported long-term state.
34
+ *
35
+ * PRI-698 Phase 1: `apply-full` (full-runtime update) is served by the real
36
+ * ReleaseManager.apply() orchestration (installer + journal) and reports the
37
+ * same base readiness as `check`. The CONSOLE additionally gates the routing
38
+ * behind the `release_manager_write_authority` flag (default off) — flag-off
39
+ * dispatches fall back explicitly with `release_manager_write_disabled`.
40
+ * `apply` (the plugin-diff mechanism, which the ReleaseManager does not
41
+ * implement) and `rollback` (Phase 2 — must prove same-version restore first)
42
+ * stay structurally not-ready with explicit reasons.
34
43
  */
35
44
  import * as fs from 'node:fs';
36
45
  import { ReleaseManager, ReleaseManagerError, } from './release-manager.js';
37
46
  import { InstallLayoutError, resolvePdHomePaths } from './install-layout.js';
38
47
  /** Mutation kinds the console MutationController routes (PRI-659 contract). */
39
48
  export const RELEASE_MANAGER_AUTHORITY_KINDS = ['check', 'apply', 'apply-full', 'rollback'];
40
- /** Structural gate: activation/rollback arrive with the Phase 4 transaction rollout. */
41
- const ACTIVATION_AVAILABLE = false;
49
+ /**
50
+ * Structural gate for the kinds whose write path does not exist yet:
51
+ * `rollback` arrives with the Phase 2 restore migration; `apply` (plugin
52
+ * diff) is not a ReleaseManager mechanism at all. `apply-full` left this
53
+ * gate in PRI-698 Phase 1.
54
+ */
55
+ const ROLLBACK_AVAILABLE = false;
42
56
  /**
43
57
  * Probe the transactions directory WITHOUT writing: an existing directory must
44
58
  * be writable (the journal is a mandatory mutation contract, ADR-0024 D-2), a
@@ -95,15 +109,18 @@ export function createReleaseManagerAuthority(options) {
95
109
  },
96
110
  apply: {
97
111
  ready: false,
98
- reasons: ACTIVATION_AVAILABLE ? baseReasons : [...baseReasons, 'rollback_not_available'],
112
+ reasons: ROLLBACK_AVAILABLE ? baseReasons : [...baseReasons, 'rollback_not_available'],
99
113
  },
114
+ // PRI-698 Phase 1: the full-runtime write path exists (ReleaseManager.apply
115
+ // → installer → journal). Console routing is additionally flag-gated
116
+ // (`release_manager_write_authority`, default off).
100
117
  'apply-full': {
101
- ready: false,
102
- reasons: ACTIVATION_AVAILABLE ? baseReasons : [...baseReasons, 'rollback_not_available'],
118
+ ready: baseReady,
119
+ reasons: baseReasons,
103
120
  },
104
121
  rollback: {
105
122
  ready: false,
106
- reasons: ACTIVATION_AVAILABLE ? baseReasons : [...baseReasons, 'rollback_not_available'],
123
+ reasons: ROLLBACK_AVAILABLE ? baseReasons : [...baseReasons, 'rollback_not_available'],
107
124
  },
108
125
  };
109
126
  return { manager, installStatus, kinds };
@@ -111,15 +128,26 @@ export function createReleaseManagerAuthority(options) {
111
128
  /**
112
129
  * Map a ReleaseManager refusal onto the console's explicit-fallback reason
113
130
  * vocabulary (rc-9: degradation must be observable with a stable reason).
131
+ * `transactionOpened` passes through the PRI-698 default-on safety net
132
+ * semantics: false ⇒ the refusal happened before any side effect, so the
133
+ * console may fall back to the legacy updater with an explicit reason; true
134
+ * ⇒ runtime-side effects may exist and the refusal must surface as a
135
+ * failure, never as a fallback.
114
136
  */
115
137
  export function mapReleaseManagerErrorToFallback(error) {
116
138
  if (error instanceof ReleaseManagerError) {
117
- return { reason: error.reason, message: error.message, nextAction: error.nextAction };
139
+ return {
140
+ reason: error.reason,
141
+ message: error.message,
142
+ nextAction: error.nextAction,
143
+ transactionOpened: error.transactionOpened,
144
+ };
118
145
  }
119
146
  return {
120
147
  reason: 'release_manager_check_failed',
121
148
  message: error instanceof Error ? error.message : String(error),
122
149
  nextAction: null,
150
+ transactionOpened: false,
123
151
  };
124
152
  }
125
153
  //# sourceMappingURL=release-manager-authority.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"release-manager-authority.js","sourceRoot":"","sources":["../../src/update/release-manager-authority.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,cAAc,EACd,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE7E,+EAA+E;AAC/E,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,CAAU,CAAC;AAerG,wFAAwF;AACxF,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAmBnC;;;;;GAKG;AACH,SAAS,0BAA0B,CAAC,MAAc;IAChD,MAAM,EAAE,eAAe,EAAE,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,eAAe,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,OAAuC;IAEvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEzD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClF,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC;QACjC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,EAAE;QAC9C,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC,CAAC;IAEH,IAAI,aAAa,GAAyB,IAAI,CAAC;IAC/C,IAAI,CAAC;QACH,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,wEAAwE;QACxE,IAAI,KAAK,YAAY,kBAAkB,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;YAChF,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,aAAa,KAAK,IAAI,IAAI,aAAa,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAU,CAAC;IAC1C,MAAM,KAAK,GAAG;QACZ,KAAK,EAAE;YACL,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,WAAW;SACrB;QACD,KAAK,EAAE;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,GAAG,WAAW,EAAE,wBAAwB,CAAW;SACpG;QACD,YAAY,EAAE;YACZ,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,GAAG,WAAW,EAAE,wBAAwB,CAAW;SACpG;QACD,QAAQ,EAAE;YACR,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,GAAG,WAAW,EAAE,wBAAwB,CAAW;SACpG;KACwF,CAAC;IAE5F,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC,CAAC,KAAc;IAK7D,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;QACzC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;IACxF,CAAC;IACD,OAAO;QACL,MAAM,EAAE,8BAA8B;QACtC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/D,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"release-manager-authority.js","sourceRoot":"","sources":["../../src/update/release-manager-authority.ts"],"names":[],"mappings":"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"}
@@ -3,9 +3,18 @@
3
3
  *
4
4
  * External surface is intentionally small: inspect / check / apply /
5
5
  * rollback. The module hides metadata validation, download, extraction,
6
- * staging, probes, journaling, host control, rollback, and cleanup. In this
7
- * phase (shadow mode) apply/rollback refuse read-only: activation lands with
8
- * the transaction state machine, never by loosening this gate.
6
+ * staging, probes, journaling, host control, rollback, and cleanup.
7
+ *
8
+ * PRI-698 Phase 1: `apply()` is the update ORCHESTRATOR. It acquires the
9
+ * signed release payload into the staging area (apply-payload.ts) and hands
10
+ * deployment to the installer — the only direct artifact deployment authority
11
+ * (ADR-0024 §2.1) — as ONE journaled transaction (planned → downloaded →
12
+ * verified by this module; staged → probed → activated → confirmed by the
13
+ * installer's existing cycle, same journal file). This module performs ZERO
14
+ * deployment-side filesystem mutation: no writes under `~/.pd/runtime` or the
15
+ * host extension directories ever originate here. `rollback()` still refuses:
16
+ * rollback migration is Phase 2 and must prove same-version restore before it
17
+ * replaces the legacy rollback.
9
18
  *
10
19
  * Every refusal carries a stable reason and an Owner-visible next action
11
20
  * (rc-9) and is computed BEFORE any installation state is mutated.
@@ -13,12 +22,23 @@
13
22
  import { resolveTrustedReleaseTarget, type TrustedReleaseTarget } from './trust-metadata.js';
14
23
  import { buildReleaseMetadata } from './release-metadata.js';
15
24
  import { type ReleasePolicyDecision } from './release-policy.js';
25
+ import type { Language } from '../i18n.js';
26
+ import type { HostTarget } from '../installers/index.js';
16
27
  import type { ReleaseChannelName } from './product-identity.js';
17
- export type ReleaseManagerReason = 'shadow_mode_read_only' | 'bootstrap_not_installed' | 'metadata_refresh_failed' | 'release_metadata_unavailable' | 'release_metadata_invalid' | 'active_record_corrupt';
28
+ export type ReleaseManagerReason = 'shadow_mode_read_only' | 'bootstrap_not_installed' | 'metadata_refresh_failed' | 'release_metadata_unavailable' | 'release_metadata_invalid' | 'active_record_corrupt' | 'legacy_layout_not_supported' | 'journal_unavailable' | 'apply_failed';
18
29
  export declare class ReleaseManagerError extends Error {
19
30
  readonly reason: ReleaseManagerReason;
20
31
  readonly nextAction: string;
21
- constructor(reason: ReleaseManagerReason, message: string, nextAction: string);
32
+ /**
33
+ * PRI-698 Phase 1 default-on safety net: true when the refusal happened
34
+ * AFTER the apply transaction was opened (planned journaled) — runtime
35
+ * state may include staging writes and a terminal journal tail. False for
36
+ * pre-transaction refusals (layout, metadata, journal-unavailable): the
37
+ * caller can safely fall back to the legacy updater with an explicit
38
+ * reason, because zero side effects exist.
39
+ */
40
+ readonly transactionOpened: boolean;
41
+ constructor(reason: ReleaseManagerReason, message: string, nextAction: string, transactionOpened?: boolean);
22
42
  }
23
43
  export type InstallationLayout = 'dual-slot' | 'legacy-overlay' | 'none';
24
44
  export interface InstallStatus {
@@ -55,6 +75,25 @@ export interface UpdateCheck {
55
75
  readonly note: string | null;
56
76
  };
57
77
  }
78
+ /** Caller-supplied deployment context for apply() (PRI-698 Phase 1). */
79
+ export interface ApplyOptions {
80
+ /** Workspace the installer re-run operates on (console passes its own). */
81
+ readonly workspaceDir: string;
82
+ /** Installer language; defaults to 'zh' (the installer's default locale). */
83
+ readonly language?: Language;
84
+ /** Host installers to run; defaults to 'openclaw' (matches the legacy full-update sync). */
85
+ readonly host?: HostTarget;
86
+ }
87
+ export type ApplyOutcome = {
88
+ readonly kind: 'applied';
89
+ readonly productVersion: string;
90
+ readonly transactionId: string;
91
+ readonly journalPath: string;
92
+ } | {
93
+ readonly kind: 'no_update';
94
+ /** Why nothing was applied (policy refusal reason or already-current note). */
95
+ readonly note: string;
96
+ };
58
97
  export interface ReleaseManagerOptions {
59
98
  readonly pdHome: string;
60
99
  readonly metadataBaseUrl: string;
@@ -83,8 +122,37 @@ export declare class ReleaseManager {
83
122
  constructor(options: ReleaseManagerOptions);
84
123
  inspect(): InstallStatus;
85
124
  check(channel: ReleaseChannelName): Promise<UpdateCheck>;
86
- apply(): Promise<never>;
125
+ /**
126
+ * PRI-698 Phase 1: the update orchestrator. Readiness → signed candidate
127
+ * resolution → payload acquisition into staging (journaled) → deployment
128
+ * through the installer (the only direct artifact deployment authority,
129
+ * ADR-0024 §2.1) as ONE transaction. Every failure leaves the runtime on
130
+ * the previous release and the journal at a terminal state.
131
+ */
132
+ apply(options: ApplyOptions): Promise<ApplyOutcome>;
87
133
  rollback(): Promise<never>;
134
+ /**
135
+ * Resolve + digest-verify + cache the signed channel document (shared by
136
+ * check() and apply(); writes stay inside the RM-owned channels cache).
137
+ */
138
+ private refreshSignedChannel;
139
+ /**
140
+ * evaluateReleaseAdvancement with the ACTIVE release's own metadata as the
141
+ * current state when a dual-slot installation records one (shared by
142
+ * check() and apply()).
143
+ */
144
+ private evaluateCandidateDecision;
145
+ /**
146
+ * The release metadata document names the bytes to deploy. check() only
147
+ * evaluates already-cached documents; apply() is the "transactional updater"
148
+ * that finally downloads it: TUF target `releases/<releaseId>/metadata.json`
149
+ * (Phase 1 convention, custom identity {releaseId, channel, platform:
150
+ * 'metadata'}) fetched digest-verified into the releases cache, then read
151
+ * back through the SAME strict validation path (identity + expiry + channel
152
+ * digest binding). A pre-seeded local document is still accepted unchanged,
153
+ * so check()-time behavior is untouched.
154
+ */
155
+ private ensureReleaseMetadataDocument;
88
156
  private legacyOverlayMarker;
89
157
  private readChannelMetadataPayload;
90
158
  private readActiveReleaseMetadata;
@@ -1 +1 @@
1
- {"version":3,"file":"release-manager.d.ts","sourceRoot":"","sources":["../../src/update/release-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"release-manager.d.ts","sourceRoot":"","sources":["../../src/update/release-manager.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,23 @@
3
3
  *
4
4
  * External surface is intentionally small: inspect / check / apply /
5
5
  * rollback. The module hides metadata validation, download, extraction,
6
- * staging, probes, journaling, host control, rollback, and cleanup. In this
7
- * phase (shadow mode) apply/rollback refuse read-only: activation lands with
8
- * the transaction state machine, never by loosening this gate.
6
+ * staging, probes, journaling, host control, rollback, and cleanup.
7
+ *
8
+ * PRI-698 Phase 1: `apply()` is the update ORCHESTRATOR. It acquires the
9
+ * signed release payload into the staging area (apply-payload.ts) and hands
10
+ * deployment to the installer — the only direct artifact deployment authority
11
+ * (ADR-0024 §2.1) — as ONE journaled transaction (planned → downloaded →
12
+ * verified by this module; staged → probed → activated → confirmed by the
13
+ * installer's existing cycle, same journal file). This module performs ZERO
14
+ * deployment-side filesystem mutation: no writes under `~/.pd/runtime` or the
15
+ * host extension directories ever originate here. `rollback()` still refuses:
16
+ * rollback migration is Phase 2 and must prove same-version restore before it
17
+ * replaces the legacy rollback.
9
18
  *
10
19
  * Every refusal carries a stable reason and an Owner-visible next action
11
20
  * (rc-9) and is computed BEFORE any installation state is mutated.
12
21
  */
22
+ import { randomUUID } from 'node:crypto';
13
23
  import * as fs from 'node:fs';
14
24
  import * as os from 'node:os';
15
25
  import * as path from 'node:path';
@@ -18,16 +28,89 @@ import { parseChannelMetadata } from './channel-metadata.js';
18
28
  import { buildReleaseMetadata, isReleaseMetadataExpired, parseReleaseMetadata, verifyReleaseMetadataIdentity, } from './release-metadata.js';
19
29
  import { evaluateReleaseAdvancement, } from './release-policy.js';
20
30
  import { readBootstrapManifest, readInstallConfig, resolvePdHomePaths, } from './install-layout.js';
21
- import { readActiveRecord, TransactionJournalError } from './transaction-journal.js';
31
+ import { appendJournalTransition, readActiveRecord, TransactionJournalError, } from './transaction-journal.js';
32
+ import { downloadReleaseAsset, extractAndVerifyReleaseAsset, ApplyPayloadError } from './apply-payload.js';
22
33
  export class ReleaseManagerError extends Error {
23
34
  reason;
24
35
  nextAction;
25
- constructor(reason, message, nextAction) {
36
+ /**
37
+ * PRI-698 Phase 1 default-on safety net: true when the refusal happened
38
+ * AFTER the apply transaction was opened (planned journaled) — runtime
39
+ * state may include staging writes and a terminal journal tail. False for
40
+ * pre-transaction refusals (layout, metadata, journal-unavailable): the
41
+ * caller can safely fall back to the legacy updater with an explicit
42
+ * reason, because zero side effects exist.
43
+ */
44
+ transactionOpened;
45
+ // eslint-disable-next-line @typescript-eslint/max-params -- (reason, message, nextAction, transactionOpened) mirrors the four fields of the refusal contract; the 4th is an optional PRI-698 default-on safety-net marker
46
+ constructor(reason, message, nextAction, transactionOpened = false) {
26
47
  super(message);
27
48
  this.name = 'ReleaseManagerError';
28
49
  this.reason = reason;
29
50
  this.nextAction = nextAction;
51
+ this.transactionOpened = transactionOpened;
52
+ }
53
+ }
54
+ /** Terminal journal states (mirrors transaction-journal.ts TERMINAL_STATES semantics). */
55
+ const APPLY_TERMINAL_STATES = new Set([
56
+ 'confirmed', 'rolled_back', 'refused', 'failed',
57
+ ]);
58
+ /**
59
+ * Append one transition to the apply transaction's journal file (journal-first
60
+ * append+fsync, same discipline and format as the installer's writer — one
61
+ * JSONL file per transaction under ~/.pd/transactions/, ADR-0024 D-2/D-6).
62
+ * The installer continues THIS file when it takes over deployment.
63
+ */
64
+ function appendApplyTransition(journal, to, detail) {
65
+ appendJournalTransition(journal.journalPath, {
66
+ at: new Date().toISOString(),
67
+ from: journal.lastState,
68
+ to,
69
+ transactionId: journal.transactionId,
70
+ releaseId: journal.releaseId,
71
+ productVersion: journal.productVersion,
72
+ releaseMetadataDigest: journal.releaseMetadataDigest,
73
+ releaseMetadataDigestSource: journal.releaseMetadataDigestSource,
74
+ generation: journal.generation,
75
+ detail,
76
+ });
77
+ journal.lastState = to;
78
+ }
79
+ /**
80
+ * Close the apply transaction with a terminal `failed` state when the flow
81
+ * dies before the installer reached one. The installer's own catch path
82
+ * appends rolled_back/failed for failures inside deployment; this covers
83
+ * acquisition-phase failures and unexpected throws so a journal never ends
84
+ * mid-chain (the strict reader rejects transitions after a terminal state,
85
+ * and a non-terminal tail means "unfinished" for Phase 3 recovery).
86
+ * The append itself is best-effort: if even the failure record cannot be
87
+ * written, the on-disk journal stays where it stopped and the thrown error
88
+ * still reports the failure (observable degradation, rc-9).
89
+ */
90
+ function ensureTerminalFailed(journal, detail) {
91
+ if (journal.lastState !== null && APPLY_TERMINAL_STATES.has(journal.lastState))
92
+ return;
93
+ try {
94
+ appendApplyTransition(journal, 'failed', detail);
95
+ }
96
+ catch (appendError) {
97
+ // Tier-2 degradation (same policy as installer.ts): the transaction stays
98
+ // partially journaled; the backup/restore safety net inside the installer
99
+ // does not depend on the journal.
100
+ void appendError;
101
+ }
102
+ }
103
+ function toReleaseManagerError(error) {
104
+ if (error instanceof ReleaseManagerError)
105
+ return error;
106
+ if (error instanceof ApplyPayloadError) {
107
+ return new ReleaseManagerError(error.reason, error.message, error.nextAction);
30
108
  }
109
+ if (error instanceof ReleaseTrustError) {
110
+ return new ReleaseManagerError('metadata_refresh_failed', error.message, error.nextAction);
111
+ }
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ return new ReleaseManagerError('apply_failed', `Release application failed: ${message}`, 'The runtime was left on the previous release (installer backup/restore). Retry the update; if it fails again, run the official installer to repair.');
31
114
  }
32
115
  /**
33
116
  * Single authority for active.json: the transaction journal's strict reader
@@ -97,6 +180,150 @@ export class ReleaseManager {
97
180
  async check(channel) {
98
181
  const now = this.options.now ?? (() => new Date());
99
182
  const status = this.inspect();
183
+ const { channelMetadata, trustedTarget } = await this.refreshSignedChannel(channel);
184
+ const releaseMetadata = this.readReleaseMetadataDocument(channelMetadata, now);
185
+ const decision = this.evaluateCandidateDecision({ channelMetadata, releaseMetadata, status, now: now() });
186
+ const candidate = {
187
+ productVersion: releaseMetadata.productVersion,
188
+ releaseId: releaseMetadata.releaseId,
189
+ publicationSequence: releaseMetadata.publicationSequence,
190
+ assets: releaseMetadata.assets.map((asset) => ({
191
+ platform: asset.platform,
192
+ arch: asset.arch,
193
+ nodeAbi: asset.nodeAbi,
194
+ })),
195
+ };
196
+ const shadowComparison = await this.compareWithLegacyUpdater(status, decision, candidate.productVersion);
197
+ return {
198
+ channel,
199
+ candidate,
200
+ decision,
201
+ trustedTarget,
202
+ shadowComparison,
203
+ };
204
+ }
205
+ /**
206
+ * PRI-698 Phase 1: the update orchestrator. Readiness → signed candidate
207
+ * resolution → payload acquisition into staging (journaled) → deployment
208
+ * through the installer (the only direct artifact deployment authority,
209
+ * ADR-0024 §2.1) as ONE transaction. Every failure leaves the runtime on
210
+ * the previous release and the journal at a terminal state.
211
+ */
212
+ async apply(options) {
213
+ const status = this.inspect();
214
+ if (status.layout === 'none') {
215
+ throw new ReleaseManagerError('bootstrap_not_installed', 'No PD installation was found under this PD home; there is nothing to update.', 'Run the official installer (npx create-principles-disciple) first.');
216
+ }
217
+ if (status.layout === 'legacy-overlay') {
218
+ throw new ReleaseManagerError('legacy_layout_not_supported', 'This installation uses the legacy overlay layout, which the transactional updater does not serve.', 'Run the official installer once to migrate into the dual-slot layout; the current updater continues to serve this installation until then.');
219
+ }
220
+ // Pre-transaction resolution phase: no side effects have happened, so any
221
+ // refusal here is a clean, unjournaled refusal — still mapped onto the
222
+ // typed ReleaseManagerError contract (rc-9), never a raw transport error.
223
+ let channelMetadata;
224
+ let releaseMetadata;
225
+ try {
226
+ const { channelMetadata: refreshedChannel } = await this.refreshSignedChannel(status.channel);
227
+ channelMetadata = refreshedChannel;
228
+ releaseMetadata = await this.ensureReleaseMetadataDocument(channelMetadata);
229
+ }
230
+ catch (error) {
231
+ throw toReleaseManagerError(error);
232
+ }
233
+ const now = this.options.now ?? (() => new Date());
234
+ const decision = this.evaluateCandidateDecision({ channelMetadata, releaseMetadata, status, now: now() });
235
+ if (!decision.allowed) {
236
+ return { kind: 'no_update', note: `${decision.reason}: ${decision.message}` };
237
+ }
238
+ const transactionId = `update-${Date.now()}-${randomUUID().slice(0, 8)}`;
239
+ const journalPath = path.join(this.paths.transactionsDir, `${transactionId}.jsonl`);
240
+ const journal = {
241
+ transactionId,
242
+ journalPath,
243
+ releaseId: releaseMetadata.releaseId,
244
+ productVersion: releaseMetadata.productVersion,
245
+ releaseMetadataDigest: releaseMetadata.metadataDigest,
246
+ releaseMetadataDigestSource: 'signed_channel',
247
+ generation: (status.generation ?? 0) + 1,
248
+ degraded: false,
249
+ lastState: null,
250
+ };
251
+ // Journal-first (ADR-0024 D-2, installer Tier-1 policy): record the
252
+ // transaction BEFORE the first side effect. If even 'planned' cannot be
253
+ // written, refuse with zero side effects.
254
+ try {
255
+ appendApplyTransition(journal, 'planned', `host=${options.host ?? 'openclaw'} mode=smart source=release-manager`);
256
+ }
257
+ catch (error) {
258
+ throw new ReleaseManagerError('journal_unavailable', `The transaction journal could not be written — refusing to update unjournaled (ADR-0024 D-2): ${error instanceof Error ? error.message : String(error)}`, 'Resolve write access to ~/.pd/transactions (disk space / permissions), then retry. No changes were made.');
259
+ }
260
+ try {
261
+ const { install } = await import('../installer.js');
262
+ const downloaded = await downloadReleaseAsset({
263
+ paths: this.paths,
264
+ metadataBaseUrl: this.options.metadataBaseUrl,
265
+ fetcher: this.options.fetcher,
266
+ releaseMetadata,
267
+ channel: status.channel,
268
+ transactionId,
269
+ });
270
+ appendApplyTransition(journal, 'downloaded', `artifact sha256=${downloaded.trustedTarget.artifactSha256} target=${downloaded.trustedTarget.targetPath}`);
271
+ const { payloadDir } = await extractAndVerifyReleaseAsset({
272
+ transactionDir: downloaded.transactionDir,
273
+ archivePath: downloaded.archivePath,
274
+ });
275
+ appendApplyTransition(journal, 'verified', 'release-asset preflight passed (identity + whole-payload digest)');
276
+ // Deployment is the installer's job, byte for byte the same cycle a
277
+ // manual run performs: digest preflight → backup rename-swap → component
278
+ // deploy → console probe → host installers → commit (backup cleanup).
279
+ // The transaction handle keeps ONE journal file for the whole update.
280
+ const installResult = await install({
281
+ language: options.language ?? 'zh',
282
+ mode: 'smart',
283
+ workspaceDir: options.workspaceDir,
284
+ channels: [],
285
+ overwriteConfig: false,
286
+ host: options.host ?? 'openclaw',
287
+ stopGateway: true,
288
+ }, payloadDir, { quiet: true, nonInteractive: true }, journal);
289
+ if (!installResult.success) {
290
+ ensureTerminalFailed(journal, `installer reported failure before deploying (no runtime mutation): ${installResult.error ?? installResult.reason ?? 'unknown'}`);
291
+ throw new ReleaseManagerError('apply_failed', `The installer refused or failed the update: ${installResult.error ?? installResult.reason ?? 'unknown'}`, installResult.nextAction ?? 'The runtime is unchanged. Resolve the reported cause and retry.');
292
+ }
293
+ if (journal.lastState !== 'confirmed') {
294
+ // rc-7: never claim success from a non-terminal journal — the state
295
+ // the installer left behind is what recovery will reason about.
296
+ ensureTerminalFailed(journal, `installer reported success but the transaction ended at '${journal.lastState ?? 'nothing'}'`);
297
+ throw new ReleaseManagerError('apply_failed', `The installer reported success but the transaction journal ended at '${journal.lastState ?? 'nothing'}' instead of 'confirmed'.`, 'Treat the update as NOT applied. Inspect the transaction journal and re-run the installer to reach a consistent state.');
298
+ }
299
+ return {
300
+ kind: 'applied',
301
+ productVersion: releaseMetadata.productVersion,
302
+ transactionId,
303
+ journalPath,
304
+ };
305
+ }
306
+ catch (error) {
307
+ ensureTerminalFailed(journal, `update failed at '${journal.lastState ?? 'planned'}': ${error instanceof Error ? error.message : String(error)}`);
308
+ // The transaction was opened (planned journaled): mark the refusal so
309
+ // the caller knows runtime-side effects may exist (staging writes +
310
+ // terminal journal tail) and must NOT auto-fallback to the legacy
311
+ // updater over it.
312
+ const mapped = toReleaseManagerError(error);
313
+ throw mapped.transactionOpened
314
+ ? mapped
315
+ : new ReleaseManagerError(mapped.reason, mapped.message, mapped.nextAction, true);
316
+ }
317
+ }
318
+ async rollback() {
319
+ void this.paths; // reserved: Phase 2 rollback drives this.paths / this.options
320
+ throw new ReleaseManagerError('shadow_mode_read_only', 'Rollback is not enabled yet: the ReleaseManager runs in read-only shadow mode while the transactional activation system is brought up.', 'Continue using the current update path. Rollback arrives with the dual-slot transaction rollout.');
321
+ }
322
+ /**
323
+ * Resolve + digest-verify + cache the signed channel document (shared by
324
+ * check() and apply(); writes stay inside the RM-owned channels cache).
325
+ */
326
+ async refreshSignedChannel(channel) {
100
327
  const channelTargetPath = `channels/${channel}.json`;
101
328
  let trustedTarget;
102
329
  try {
@@ -124,16 +351,21 @@ export class ReleaseManager {
124
351
  destinationPath: path.join(this.paths.channelsDir, `${channel}.json`),
125
352
  fetcher: this.options.fetcher,
126
353
  });
127
- const channelMetadata = this.readChannelMetadataPayload(channel);
128
- const releaseMetadata = this.readReleaseMetadataDocument(channelMetadata, now);
129
- // Current state for the advancement policy comes from the ACTIVE release's
130
- // own metadata when a dual-slot installation exists.
354
+ return { channelMetadata: this.readChannelMetadataPayload(channel), trustedTarget };
355
+ }
356
+ /**
357
+ * evaluateReleaseAdvancement with the ACTIVE release's own metadata as the
358
+ * current state when a dual-slot installation records one (shared by
359
+ * check() and apply()).
360
+ */
361
+ evaluateCandidateDecision(input) {
362
+ const { channelMetadata, releaseMetadata, status, now } = input;
131
363
  let decision = evaluateReleaseAdvancement({
132
364
  channel: channelMetadata,
133
365
  candidate: releaseMetadata,
134
366
  current: null,
135
367
  bootstrapVersion: status.bootstrapVersion ?? '0.0.0',
136
- now: now(),
368
+ now,
137
369
  });
138
370
  if (status.releaseId !== null && status.productVersion !== null) {
139
371
  const activeMetadata = this.readActiveReleaseMetadata(status.releaseId);
@@ -149,36 +381,40 @@ export class ReleaseManager {
149
381
  previouslyConfirmedReleaseIds: [status.releaseId],
150
382
  },
151
383
  bootstrapVersion: status.bootstrapVersion ?? '0.0.0',
152
- now: now(),
384
+ now,
153
385
  });
154
386
  }
155
387
  }
156
- const candidate = {
157
- productVersion: releaseMetadata.productVersion,
158
- releaseId: releaseMetadata.releaseId,
159
- publicationSequence: releaseMetadata.publicationSequence,
160
- assets: releaseMetadata.assets.map((asset) => ({
161
- platform: asset.platform,
162
- arch: asset.arch,
163
- nodeAbi: asset.nodeAbi,
164
- })),
165
- };
166
- const shadowComparison = await this.compareWithLegacyUpdater(status, decision, candidate.productVersion);
167
- return {
168
- channel,
169
- candidate,
170
- decision,
171
- trustedTarget,
172
- shadowComparison,
173
- };
174
- }
175
- async apply() {
176
- void this.paths; // reserved: Phase 4 activation drives this.paths / this.options
177
- throw new ReleaseManagerError('shadow_mode_read_only', 'Release application is not enabled yet: the ReleaseManager runs in read-only shadow mode while the transactional activation system is brought up.', 'Continue using the current update path. Activation arrives with the dual-slot transaction rollout; this refusal protects against hybrid installations.');
388
+ return decision;
178
389
  }
179
- async rollback() {
180
- void this.paths; // reserved: Phase 4 rollback drives this.paths / this.options
181
- throw new ReleaseManagerError('shadow_mode_read_only', 'Rollback is not enabled yet: the ReleaseManager runs in read-only shadow mode while the transactional activation system is brought up.', 'Continue using the current update path. Rollback arrives with the dual-slot transaction rollout.');
390
+ /**
391
+ * The release metadata document names the bytes to deploy. check() only
392
+ * evaluates already-cached documents; apply() is the "transactional updater"
393
+ * that finally downloads it: TUF target `releases/<releaseId>/metadata.json`
394
+ * (Phase 1 convention, custom identity {releaseId, channel, platform:
395
+ * 'metadata'}) fetched digest-verified into the releases cache, then read
396
+ * back through the SAME strict validation path (identity + expiry + channel
397
+ * digest binding). A pre-seeded local document is still accepted unchanged,
398
+ * so check()-time behavior is untouched.
399
+ */
400
+ async ensureReleaseMetadataDocument(channel) {
401
+ const now = this.options.now ?? (() => new Date());
402
+ try {
403
+ return this.readReleaseMetadataDocument(channel, now);
404
+ }
405
+ catch (error) {
406
+ if (!(error instanceof ReleaseManagerError) || error.reason !== 'release_metadata_unavailable') {
407
+ throw error;
408
+ }
409
+ await downloadTrustedReleasePayload({
410
+ metadataDir: this.paths.trustDir,
411
+ metadataBaseUrl: this.options.metadataBaseUrl,
412
+ targetPath: `releases/${channel.releaseId}/metadata.json`,
413
+ destinationPath: path.join(this.paths.releasesDir, channel.releaseId, 'metadata.json'),
414
+ fetcher: this.options.fetcher,
415
+ });
416
+ return this.readReleaseMetadataDocument(channel, now);
417
+ }
182
418
  }
183
419
  legacyOverlayMarker() {
184
420
  const openclawRoot = this.options.openclawHome