create-principles-disciple 1.127.1 → 1.128.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 (114) hide show
  1. package/console/dist/server/config/pd-config-store.js +7 -2
  2. package/console/dist/server/models/HealthCheckModel.d.ts +4 -1
  3. package/console/dist/server/models/HealthCheckModel.js +6 -5
  4. package/console/dist/server/routes/update.js +8 -83
  5. package/console/dist/server/utils/installed-layout.d.ts +21 -0
  6. package/console/dist/server/utils/installed-layout.js +92 -0
  7. package/console/dist/server/utils/pd-backups.d.ts +9 -0
  8. package/console/dist/server/utils/pd-backups.js +5 -1
  9. package/console/dist/ui/pages/settings/UpdatePage.js +2 -1
  10. package/console/dist/web/assets/app.js +975 -824
  11. package/console/dist/web/index.html +4 -0
  12. package/core/dist/runtime-v2/activation/__tests__/production-gate-deps.test.js +69 -0
  13. package/core/dist/runtime-v2/activation/__tests__/production-gate-deps.test.js.map +1 -1
  14. package/core/dist/runtime-v2/activation/production-gate-deps.d.ts.map +1 -1
  15. package/core/dist/runtime-v2/activation/production-gate-deps.js +13 -46
  16. package/core/dist/runtime-v2/activation/production-gate-deps.js.map +1 -1
  17. package/core/dist/runtime-v2/config/__tests__/pd-config-sparse-bootstrap.test.d.ts +2 -0
  18. package/core/dist/runtime-v2/config/__tests__/pd-config-sparse-bootstrap.test.d.ts.map +1 -0
  19. package/core/dist/runtime-v2/config/__tests__/pd-config-sparse-bootstrap.test.js +143 -0
  20. package/core/dist/runtime-v2/config/__tests__/pd-config-sparse-bootstrap.test.js.map +1 -0
  21. package/core/dist/runtime-v2/feature-flags/__tests__/installer-config-parity.test.js +44 -27
  22. package/core/dist/runtime-v2/feature-flags/__tests__/installer-config-parity.test.js.map +1 -1
  23. package/core/dist/runtime-v2/internalization/__tests__/adversarial-feedback.test.js +9 -0
  24. package/core/dist/runtime-v2/internalization/__tests__/adversarial-feedback.test.js.map +1 -1
  25. package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js +15 -5
  26. package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js.map +1 -1
  27. package/core/dist/runtime-v2/internalization/__tests__/context-resolution.test.d.ts +2 -0
  28. package/core/dist/runtime-v2/internalization/__tests__/context-resolution.test.d.ts.map +1 -0
  29. package/core/dist/runtime-v2/internalization/__tests__/context-resolution.test.js +377 -0
  30. package/core/dist/runtime-v2/internalization/__tests__/context-resolution.test.js.map +1 -0
  31. package/core/dist/runtime-v2/internalization/__tests__/evaluator-artificer-repair-replay.test.d.ts +2 -0
  32. package/core/dist/runtime-v2/internalization/__tests__/evaluator-artificer-repair-replay.test.d.ts.map +1 -0
  33. package/core/dist/runtime-v2/internalization/__tests__/evaluator-artificer-repair-replay.test.js +413 -0
  34. package/core/dist/runtime-v2/internalization/__tests__/evaluator-artificer-repair-replay.test.js.map +1 -0
  35. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js +136 -0
  36. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js.map +1 -1
  37. package/core/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.js +8 -8
  38. package/core/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.js.map +1 -1
  39. package/core/dist/runtime-v2/internalization/__tests__/repair-replay-resolver.test.d.ts +2 -0
  40. package/core/dist/runtime-v2/internalization/__tests__/repair-replay-resolver.test.d.ts.map +1 -0
  41. package/core/dist/runtime-v2/internalization/__tests__/repair-replay-resolver.test.js +340 -0
  42. package/core/dist/runtime-v2/internalization/__tests__/repair-replay-resolver.test.js.map +1 -0
  43. package/core/dist/runtime-v2/internalization/__tests__/shared-information-plane-runner.test.d.ts +2 -0
  44. package/core/dist/runtime-v2/internalization/__tests__/shared-information-plane-runner.test.d.ts.map +1 -0
  45. package/core/dist/runtime-v2/internalization/__tests__/shared-information-plane-runner.test.js +628 -0
  46. package/core/dist/runtime-v2/internalization/__tests__/shared-information-plane-runner.test.js.map +1 -0
  47. package/core/dist/runtime-v2/internalization/adversarial-feedback.d.ts.map +1 -1
  48. package/core/dist/runtime-v2/internalization/adversarial-feedback.js +7 -1
  49. package/core/dist/runtime-v2/internalization/adversarial-feedback.js.map +1 -1
  50. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +9 -2
  51. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  52. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +19 -1
  53. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  54. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +23 -0
  55. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  56. package/core/dist/runtime-v2/internalization/artificer-runner.js +184 -16
  57. package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  58. package/core/dist/runtime-v2/internalization/context-manifests.d.ts +90 -17
  59. package/core/dist/runtime-v2/internalization/context-manifests.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/internalization/context-manifests.js +124 -27
  61. package/core/dist/runtime-v2/internalization/context-manifests.js.map +1 -1
  62. package/core/dist/runtime-v2/internalization/context-resolution.d.ts +189 -0
  63. package/core/dist/runtime-v2/internalization/context-resolution.d.ts.map +1 -0
  64. package/core/dist/runtime-v2/internalization/context-resolution.js +317 -0
  65. package/core/dist/runtime-v2/internalization/context-resolution.js.map +1 -0
  66. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +18 -1
  67. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/internalization/evaluator-output.js +12 -2
  69. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +32 -0
  71. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/internalization/evaluator-runner.js +171 -12
  73. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  74. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.d.ts +18 -1
  75. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js +20 -0
  77. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js.map +1 -1
  78. package/core/dist/runtime-v2/internalization/repair-replay-resolver.d.ts +112 -0
  79. package/core/dist/runtime-v2/internalization/repair-replay-resolver.d.ts.map +1 -0
  80. package/core/dist/runtime-v2/internalization/repair-replay-resolver.js +332 -0
  81. package/core/dist/runtime-v2/internalization/repair-replay-resolver.js.map +1 -0
  82. package/core/dist/runtime-v2/runner/base-peer-runner.d.ts +62 -17
  83. package/core/dist/runtime-v2/runner/base-peer-runner.d.ts.map +1 -1
  84. package/core/dist/runtime-v2/runner/base-peer-runner.js +116 -10
  85. package/core/dist/runtime-v2/runner/base-peer-runner.js.map +1 -1
  86. package/core/dist/telemetry-event.d.ts +2 -2
  87. package/core/dist/telemetry-event.d.ts.map +1 -1
  88. package/core/dist/telemetry-event.js +61 -0
  89. package/core/dist/telemetry-event.js.map +1 -1
  90. package/core/package.json +1 -1
  91. package/dist/installer.d.ts.map +1 -1
  92. package/dist/installer.js +16 -12
  93. package/dist/installer.js.map +1 -1
  94. package/dist/mvp-config.d.ts +22 -15
  95. package/dist/mvp-config.d.ts.map +1 -1
  96. package/dist/mvp-config.js +48 -106
  97. package/dist/mvp-config.js.map +1 -1
  98. package/host-runtime/package.json +1 -1
  99. package/install-layout/package.json +7 -3
  100. package/package.json +1 -1
  101. package/pd-cli/dist/commands/console.d.ts +13 -0
  102. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  103. package/pd-cli/dist/commands/console.js +104 -0
  104. package/pd-cli/dist/commands/console.js.map +1 -1
  105. package/pd-cli/dist/commands/runtime-init.d.ts.map +1 -1
  106. package/pd-cli/dist/commands/runtime-init.js +8 -12
  107. package/pd-cli/dist/commands/runtime-init.js.map +1 -1
  108. package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
  109. package/pd-cli/dist/commands/runtime-internalization-run-once.js +7 -1
  110. package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
  111. package/pd-cli/package.json +1 -1
  112. package/plugin/dist/bundle.js +449 -437
  113. package/plugin/dist/governance-audit.js +141 -141
  114. package/plugin/dist/rulehost-evidence.js +153 -153
@@ -328,10 +328,13 @@ export function updateAgentBinding(workspaceDir, agentName, payload) {
328
328
  // the merged effective map. Defaults belong in the registry, not in config —
329
329
  // snapshotting `effective.config.features` here would materialize every
330
330
  // missing default flag into an immortal override on any agent-binding change.
331
+ // PRI-645: the only reachable fallback is "config file absent" (a malformed
332
+ // features section was already rejected by loadPdConfig above) — an empty
333
+ // map keeps the newly created config sparse; the resolver fills defaults.
331
334
  const updatedConfig = {
332
335
  ...rawConfig,
333
336
  version: effective.config.version,
334
- features: isRecord(rawConfig.features) ? rawConfig.features : { ...effective.config.features },
337
+ features: isRecord(rawConfig.features) ? rawConfig.features : {},
335
338
  runtimeProfiles: { ...effective.config.runtimeProfiles },
336
339
  internalAgents: {
337
340
  defaultRuntime: effective.config.internalAgents.defaultRuntime,
@@ -474,10 +477,12 @@ export function updateDefaultRuntime(workspaceDir, payload) {
474
477
  // Merge: update only defaultRuntime, preserve agent overrides.
475
478
  // PRI-637: preserve the file's own `features:` section (same rationale as
476
479
  // updateAgentBinding — never re-snapshot merged defaults into config).
480
+ // PRI-645: config-absent fallback writes an empty map, not a default
481
+ // snapshot — the resolver owns defaults.
477
482
  const updatedConfig = {
478
483
  ...rawConfig,
479
484
  version: effective.config.version,
480
- features: isRecord(rawConfig.features) ? rawConfig.features : { ...effective.config.features },
485
+ features: isRecord(rawConfig.features) ? rawConfig.features : {},
481
486
  runtimeProfiles: { ...effective.config.runtimeProfiles },
482
487
  internalAgents: {
483
488
  defaultRuntime: defaultRuntimeRaw,
@@ -38,7 +38,10 @@ export declare class HealthCheckModel {
38
38
  constructor(workspaceDir: string);
39
39
  checkSystemHealth(): Promise<SystemHealthStatus>;
40
40
  /**
41
- * P0-2: Read the PD (monorepo root) version from package.json.
41
+ * Read the installed PD plugin version the same authority the update
42
+ * page shows as "当前版本" (utils/installed-layout.ts resolves the install
43
+ * layout; a fixed relative path cannot work because the dev tree and the
44
+ * installed legacy layout have different depths).
42
45
  * Returns 'unknown' on any resolution/parse failure (rc-9: no silent throw).
43
46
  */
44
47
  private static readPdVersion;
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { OperatorHealthReadModel, PainChainReadModel, PruningReadModel, RuntimeStateManager, } from '@principles/core/runtime-v2';
3
+ import { readInstalledPdVersion } from '../utils/installed-layout.js';
3
4
  // P0-2: createRequire lets us read package.json via Node's module resolution
4
5
  // in ESM context. Resolves relative to this module's URL in both tsx (dev)
5
6
  // and compiled JS (prod) since both mirror the same directory depth.
@@ -45,15 +46,15 @@ export class HealthCheckModel {
45
46
  };
46
47
  }
47
48
  /**
48
- * P0-2: Read the PD (monorepo root) version from package.json.
49
+ * Read the installed PD plugin version the same authority the update
50
+ * page shows as "当前版本" (utils/installed-layout.ts resolves the install
51
+ * layout; a fixed relative path cannot work because the dev tree and the
52
+ * installed legacy layout have different depths).
49
53
  * Returns 'unknown' on any resolution/parse failure (rc-9: no silent throw).
50
54
  */
51
55
  static readPdVersion() {
52
56
  try {
53
- // From src/server/models/ (or dist/server/models/), five levels up
54
- // reaches the monorepo root package.json.
55
- const pkg = moduleRequire('../../../../../package.json');
56
- return typeof pkg.version === 'string' ? pkg.version : 'unknown';
57
+ return readInstalledPdVersion() ?? 'unknown';
57
58
  }
58
59
  catch {
59
60
  return 'unknown';
@@ -7,8 +7,8 @@ import { sendSuccess, sendError, sendMethodNotAllowed, sendBadRequest, sendNotFo
7
7
  import { appendUpdateHistory } from './update-history.js';
8
8
  import { checkOpenClawGateway, stopOpenClawGateway, restartOpenClawGateway, } from '../utils/gateway.js';
9
9
  import { migrateLegacyExtensionBackups, reservePdBackupDestination, resolvePdBackupsRoot, } from '../utils/pd-backups.js';
10
+ import { resolveExtensionsDir, resolveUpdateLayout, resolvePluginDir, readCurrentVersion, } from '../utils/installed-layout.js';
10
11
  import { ActivationCompatibilityReadModel } from '@principles/core/runtime-v2';
11
- import { getInstallLayoutPaths, resolveInstallLayout } from '@principles/install-layout';
12
12
  import { collectFileDepLinkSpecs } from '../utils/update-links.js';
13
13
  /**
14
14
  * Legacy rule contract preflight (2026-08-19): refuse to swap the runtime
@@ -49,88 +49,13 @@ const SKIP_DIRS = new Set(['node_modules']);
49
49
  // ---------------------------------------------------------------------------
50
50
  // Helpers
51
51
  // ---------------------------------------------------------------------------
52
- /**
53
- * Resolve the OpenClaw installation home.
54
- *
55
- * Priority:
56
- * 1. `OPENCLAW_HOME` env var (explicit override)
57
- * 2. `~/.openclaw` (default install location)
58
- *
59
- * Note: the workspace (where PD state lives) and the OpenClaw install home
60
- * (where extensions live) are NOT necessarily the same directory or even
61
- * siblings — the workspace can be on a different drive. Do not derive the
62
- * extensions dir from `path.dirname(workspaceDir)`.
63
- */
64
- function resolveOpenclawHome() {
65
- const envHome = process.env.OPENCLAW_HOME;
66
- if (envHome && envHome.trim().length > 0)
67
- return path.resolve(envHome);
68
- return path.join(os.homedir(), '.openclaw');
69
- }
70
- function resolveExtensionsDir() {
71
- return path.join(resolveOpenclawHome(), 'extensions');
72
- }
73
- function resolveUpdateLayout() {
74
- const homeDir = os.homedir();
75
- const paths = getInstallLayoutPaths(homeDir);
76
- const legacyPluginDir = path.join(resolveExtensionsDir(), 'principles-disciple');
77
- let manifest;
78
- try {
79
- manifest = JSON.parse(fs.readFileSync(paths.manifest, 'utf8'));
80
- }
81
- catch {
82
- manifest = undefined;
83
- }
84
- const resolution = resolveInstallLayout({
85
- homeDir,
86
- manifest,
87
- canonicalRuntimeExists: fs.existsSync(paths.runtimeDir),
88
- legacyExtensionExists: fs.existsSync(legacyPluginDir),
89
- });
90
- if (resolution.mode === 'missing')
91
- return undefined;
92
- if (resolution.mode === 'canonical') {
93
- return {
94
- pluginDir: paths.pluginDir,
95
- consoleDir: paths.consoleDir,
96
- coreDir: paths.coreDir,
97
- hostRuntimeDir: paths.hostRuntimeDir,
98
- pdCliDir: paths.pdCliDir,
99
- installLayoutDir: paths.installLayoutDir,
100
- hosts: resolution.manifest?.hosts ?? [],
101
- };
102
- }
103
- return {
104
- pluginDir: legacyPluginDir,
105
- consoleDir: path.join(legacyPluginDir, 'console'),
106
- coreDir: path.join(legacyPluginDir, 'core'),
107
- hostRuntimeDir: path.join(legacyPluginDir, 'host-runtime'),
108
- pdCliDir: path.join(legacyPluginDir, 'pd-cli'),
109
- installLayoutDir: path.join(legacyPluginDir, 'install-layout'),
110
- hosts: ['openclaw'],
111
- };
112
- }
113
- function resolvePluginDir(_workspaceDir) {
114
- return resolveUpdateLayout()?.pluginDir ?? path.join(resolveExtensionsDir(), 'principles-disciple');
115
- }
116
- function readCurrentVersion(pluginDir) {
117
- const pkgPath = path.join(pluginDir, 'package.json');
118
- try {
119
- if (!fs.existsSync(pkgPath))
120
- return undefined;
121
- const raw = fs.readFileSync(pkgPath, 'utf-8');
122
- const parsed = JSON.parse(raw);
123
- if (typeof parsed === 'object' && parsed !== null && Object.hasOwn(parsed, 'version')) {
124
- const { version } = parsed;
125
- if (typeof version === 'string')
126
- return version;
127
- }
128
- return undefined;
129
- }
130
- catch {
131
- return undefined;
132
- }
133
- }
52
+ // Installed-layout resolution (OpenClaw home, extensions dir, plugin dir,
53
+ // installed version) moved verbatim to utils/installed-layout.ts so the
54
+ // health diagnostics read the SAME authority as the update page (P4).
55
+ // The workspace (where PD state lives) and the OpenClaw install home (where
56
+ // extensions live) are NOT necessarily the same directory or even siblings —
57
+ // the workspace can be on a different drive — so the extensions dir is never
58
+ // derived from `path.dirname(workspaceDir)`.
134
59
  async function readJsonBody(req) {
135
60
  let body = '';
136
61
  req.on('data', (chunk) => { body += chunk.toString(); });
@@ -0,0 +1,21 @@
1
+ import { type InstallHost } from '@principles/install-layout';
2
+ export declare function resolveExtensionsDir(): string;
3
+ export interface UpdateLayout {
4
+ pluginDir: string;
5
+ consoleDir: string;
6
+ coreDir: string;
7
+ hostRuntimeDir: string;
8
+ pdCliDir: string;
9
+ installLayoutDir: string;
10
+ hosts: InstallHost[];
11
+ }
12
+ export declare function resolveUpdateLayout(): UpdateLayout | undefined;
13
+ export declare function resolvePluginDir(_workspaceDir: string): string;
14
+ export declare function readCurrentVersion(pluginDir: string): string | undefined;
15
+ /**
16
+ * The PD version this console governs — the installed plugin's package.json
17
+ * version, i.e. the same "当前版本" the update page shows. Returns undefined
18
+ * when no installation can be resolved (dev tree without a local install);
19
+ * callers decide their own fallback display (rc-9).
20
+ */
21
+ export declare function readInstalledPdVersion(): string | undefined;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Installed PD layout resolution — where the PD plugin (and its bundled
3
+ * components) live on this machine, and which version they report.
4
+ *
5
+ * Single authority (P4) for the "installed PD version": both the update page
6
+ * (`routes/update.ts`) and the health diagnostics
7
+ * (`models/HealthCheckModel.ts`) read the same plugin package.json through
8
+ * `readInstalledPdVersion()`. The previous health implementation resolved a
9
+ * monorepo-root package.json via a fixed five-level relative path, which only
10
+ * worked in the dev tree — in the installed legacy layout it resolved into
11
+ * `<openclawHome>/extensions/` and returned 'unknown' forever.
12
+ */
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import * as os from 'os';
16
+ import { getInstallLayoutPaths, resolveInstallLayout } from '@principles/install-layout';
17
+ import { resolveOpenClawHome } from './pd-backups.js';
18
+ export function resolveExtensionsDir() {
19
+ return path.join(resolveOpenClawHome(), 'extensions');
20
+ }
21
+ export function resolveUpdateLayout() {
22
+ const homeDir = os.homedir();
23
+ const paths = getInstallLayoutPaths(homeDir);
24
+ const legacyPluginDir = path.join(resolveExtensionsDir(), 'principles-disciple');
25
+ let manifest;
26
+ try {
27
+ manifest = JSON.parse(fs.readFileSync(paths.manifest, 'utf8'));
28
+ }
29
+ catch {
30
+ manifest = undefined;
31
+ }
32
+ const resolution = resolveInstallLayout({
33
+ homeDir,
34
+ manifest,
35
+ canonicalRuntimeExists: fs.existsSync(paths.runtimeDir),
36
+ legacyExtensionExists: fs.existsSync(legacyPluginDir),
37
+ });
38
+ if (resolution.mode === 'missing')
39
+ return undefined;
40
+ if (resolution.mode === 'canonical') {
41
+ return {
42
+ pluginDir: paths.pluginDir,
43
+ consoleDir: paths.consoleDir,
44
+ coreDir: paths.coreDir,
45
+ hostRuntimeDir: paths.hostRuntimeDir,
46
+ pdCliDir: paths.pdCliDir,
47
+ installLayoutDir: paths.installLayoutDir,
48
+ hosts: resolution.manifest?.hosts ?? [],
49
+ };
50
+ }
51
+ return {
52
+ pluginDir: legacyPluginDir,
53
+ consoleDir: path.join(legacyPluginDir, 'console'),
54
+ coreDir: path.join(legacyPluginDir, 'core'),
55
+ hostRuntimeDir: path.join(legacyPluginDir, 'host-runtime'),
56
+ pdCliDir: path.join(legacyPluginDir, 'pd-cli'),
57
+ installLayoutDir: path.join(legacyPluginDir, 'install-layout'),
58
+ hosts: ['openclaw'],
59
+ };
60
+ }
61
+ export function resolvePluginDir(_workspaceDir) {
62
+ return resolveUpdateLayout()?.pluginDir ?? path.join(resolveExtensionsDir(), 'principles-disciple');
63
+ }
64
+ // rc-2: type-guard the parsed JSON instead of an `as` cast (ERR-001).
65
+ function isRecord(value) {
66
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
67
+ }
68
+ export function readCurrentVersion(pluginDir) {
69
+ const pkgPath = path.join(pluginDir, 'package.json');
70
+ try {
71
+ if (!fs.existsSync(pkgPath))
72
+ return undefined;
73
+ const raw = fs.readFileSync(pkgPath, 'utf-8');
74
+ const parsed = JSON.parse(raw);
75
+ if (!isRecord(parsed) || !Object.hasOwn(parsed, 'version'))
76
+ return undefined;
77
+ const { version } = parsed;
78
+ return typeof version === 'string' ? version : undefined;
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
84
+ /**
85
+ * The PD version this console governs — the installed plugin's package.json
86
+ * version, i.e. the same "当前版本" the update page shows. Returns undefined
87
+ * when no installation can be resolved (dev tree without a local install);
88
+ * callers decide their own fallback display (rc-9).
89
+ */
90
+ export function readInstalledPdVersion() {
91
+ return readCurrentVersion(resolvePluginDir(''));
92
+ }
@@ -5,6 +5,15 @@ export interface LegacyBackupMigrationResult {
5
5
  reason: string;
6
6
  }[];
7
7
  }
8
+ /**
9
+ * Resolve the OpenClaw install home.
10
+ * Priority: OPENCLAW_HOME env var (explicit override) > ~/.openclaw
11
+ *
12
+ * Single authority for home resolution — backup placement
13
+ * (`resolvePdBackupsRoot`) and installed-layout resolution
14
+ * (`utils/installed-layout.ts`) both derive from it.
15
+ */
16
+ export declare function resolveOpenClawHome(): string;
8
17
  /** Backups root — outside the extensions dir OpenClaw scans for plugins. */
9
18
  export declare function resolvePdBackupsRoot(): string;
10
19
  /**
@@ -20,8 +20,12 @@ import * as os from 'os';
20
20
  /**
21
21
  * Resolve the OpenClaw install home.
22
22
  * Priority: OPENCLAW_HOME env var (explicit override) > ~/.openclaw
23
+ *
24
+ * Single authority for home resolution — backup placement
25
+ * (`resolvePdBackupsRoot`) and installed-layout resolution
26
+ * (`utils/installed-layout.ts`) both derive from it.
23
27
  */
24
- function resolveOpenClawHome() {
28
+ export function resolveOpenClawHome() {
25
29
  const envHome = process.env.OPENCLAW_HOME;
26
30
  if (envHome && envHome.trim().length > 0)
27
31
  return path.resolve(envHome);
@@ -19,6 +19,7 @@ import { validateUpdateStatus, validateUpdateHistory } from "../../utils/validat
19
19
  import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, } from "../../components/ui/alert-dialog.js";
20
20
  import { Loader2, RotateCcw, CheckCircle2, XCircle, ArrowRight } from "lucide-react";
21
21
  import { formatDate } from "../../utils/format-date.js";
22
+ import { MarkdownRenderer } from "../../components/ui/markdown.js";
22
23
  export function UpdatePage() {
23
24
  const { t, i18n } = useTranslation();
24
25
  const [statusData, setStatusData] = useState(null);
@@ -179,7 +180,7 @@ export function UpdatePage() {
179
180
  }
180
181
  // ── Loaded state ─────────────────────────────────────────────────────────
181
182
  const isUpToDate = statusData ? !statusData.hasUpdate : true;
182
- return (_jsxs(PageShell, { children: [_jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.update.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.update.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.update.subtitle") }), _jsxs("section", { className: "mb-8", "aria-labelledby": "section-update-status", children: [_jsx(SectionTitle, { id: "section-update-status", children: t("pages.update.currentVersion") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.currentVersion") }), _jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.currentVersion ?? "—" })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.latestVersion") }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.latestVersion ?? "—" }), isUpToDate ? (_jsx("span", { className: "inline-flex items-center border border-green/35 text-green rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.upToDate") })) : (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.updateAvailable") }))] })] })] }), statusData?.error && (_jsxs("div", { className: "mt-4 pt-3 border-t border-line text-[12px] text-ink-4 font-mono", children: [t("pages.update.checkError"), ": ", statusData.error] })), statusData?.syncPending && (_jsx("div", { className: "mt-4 p-3 rounded-[4px] border border-amber/30 bg-amber/5", children: _jsx("p", { className: "text-[12px] text-amber leading-relaxed", children: t("pages.update.syncPending", { pluginVersion: statusData.pluginLatestVersion ?? "" }) }) })), statusData?.codexInstalled && (_jsx("div", { className: "mt-4 p-3 rounded-[4px] border border-amber/30 bg-amber/5", children: _jsx("p", { className: "text-[12px] text-amber leading-relaxed", children: t("pages.update.codexWarning") }) })), statusData?.changelog && statusData.hasUpdate && (_jsxs("div", { className: "mt-4 p-4 rounded-[4px] border border-line bg-surface/50", children: [_jsx("p", { className: "text-[12px] font-mono text-ink-3 uppercase tracking-wide mb-2", children: t("pages.update.whatsNew") }), _jsx("div", { className: "text-[13px] text-ink-2 leading-relaxed max-h-[300px] overflow-y-auto whitespace-pre-wrap", children: statusData.changelog })] })), _jsxs("div", { className: "mt-5 pt-4 border-t border-line flex items-center gap-3 flex-wrap", children: [_jsx("button", { type: "button", onClick: handleCheckForUpdates, disabled: checking || fullUpdating, className: "border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: checking ? t("pages.update.checking") : t("pages.update.checkForUpdates") }), !isUpToDate && (_jsx("button", { type: "button", onClick: () => setShowFullUpdateDialog(true), disabled: checking || fullUpdating, className: "bg-gov text-white rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: fullUpdating ? (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }), t("pages.update.updating")] })) : (t("pages.update.applyUpdate")) }))] }), updateResult && (_jsx("div", { className: `mt-4 p-4 rounded-[6px] border animate-[pdFadeIn_400ms_ease-out] ${updateResult.success ? 'bg-green/5 border-green/20' : 'bg-red/5 border-red/20'}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [updateResult.success ? (_jsx(CheckCircle2, { className: "h-6 w-6 text-green shrink-0 mt-0.5 animate-[pdFadeIn_600ms_ease-out]", style: { transform: 'scale(1)', animationFillMode: 'both' } })) : (_jsx(XCircle, { className: "h-6 w-6 text-red shrink-0 mt-0.5" })), _jsxs("div", { className: "flex-1 min-w-0", children: [updateResult.success && updateResult.fromVersion && updateResult.newVersion && (_jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [_jsx("span", { className: "font-mono text-[13px] text-ink-3 line-through", children: updateResult.fromVersion }), _jsx(ArrowRight, { className: "h-3.5 w-3.5 text-green" }), _jsx("span", { className: "font-mono text-[13px] text-green font-medium", children: updateResult.newVersion })] })), _jsx("p", { className: `text-[13px] ${updateResult.success ? 'text-green' : 'text-red'}`, children: updateResult.message }), updateResult.success && updateResult.updatedFiles && updateResult.updatedFiles.length > 0 && (_jsx("p", { className: "mt-1 text-[12px] text-ink-4 font-mono", children: t("pages.update.filesUpdated", { count: updateResult.updatedFiles.length }) })), updateResult.success && !updateResult.requiresRestart && (_jsx("p", { className: "mt-2 text-[12px] font-mono text-ink-4", children: t("pages.update.restartHint") })), updateResult.success && updateResult.requiresRestart && (_jsx("p", { className: "mt-2 p-2 rounded-[3px] bg-amber/5 border border-amber/20 text-[12px] text-amber leading-relaxed", children: t("pages.update.fullUpdateRestartPrompt") })), updateResult.success && updateResult.partialUpdate && (_jsx("p", { className: "mt-1 text-[12px] text-amber leading-relaxed", children: t("pages.update.partialUpdateHint") })), !updateResult.success && updateResult.nextAction && (_jsx("p", { className: "mt-1 text-[12px] text-ink-4 font-mono leading-relaxed", children: updateResult.nextAction })), !updateResult.success && (_jsxs("div", { className: "mt-2 flex items-center gap-2", children: [/network|fetch|timeout|ECONNREFUSED|ENOTFOUND/i.test(updateResult.message) && (_jsx("p", { className: "text-[12px] text-ink-4 font-mono", children: t("pages.update.networkErrorHint") })), _jsx("button", { type: "button", onClick: handleApplyUpdate, disabled: updating, className: "text-[12px] text-red underline hover:text-red/80 disabled:opacity-50", children: t("pages.update.retry") })] }))] })] }) }))] })] }), _jsxs("section", { "aria-labelledby": "section-update-history", children: [_jsx(SectionTitle, { id: "section-update-history", children: t("pages.update.history") }), historyErrorReason ? (_jsxs("div", { className: "text-ink-3 text-[13px] leading-relaxed py-3", children: [t("pages.update.loadError"), " (", historyErrorReason, ")"] })) : historyData && historyData.updates.length > 0 ? (_jsx("div", { className: "space-y-[10px]", children: historyData.updates.map((entry) => (_jsxs("article", { className: "bg-panel border border-line rounded-[6px] px-5 py-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t("pages.update.version") }), _jsxs("span", { className: "font-mono text-[13px] text-ink", children: [entry.fromVersion, " \u2192 ", entry.toVersion] }), _jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t(`pages.update.historyKind.${entry.kind}`) }), !entry.success && entry.kind !== 'refusal' && (_jsx("span", { className: "inline-flex items-center border border-red/35 text-red rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.failed") }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: formatDate(entry.timestamp, i18n.language, {
183
+ return (_jsxs(PageShell, { children: [_jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.update.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.update.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.update.subtitle") }), _jsxs("section", { className: "mb-8", "aria-labelledby": "section-update-status", children: [_jsx(SectionTitle, { id: "section-update-status", children: t("pages.update.currentVersion") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.currentVersion") }), _jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.currentVersion ?? "—" })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.latestVersion") }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.latestVersion ?? "—" }), isUpToDate ? (_jsx("span", { className: "inline-flex items-center border border-green/35 text-green rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.upToDate") })) : (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.updateAvailable") }))] })] })] }), statusData?.error && (_jsxs("div", { className: "mt-4 pt-3 border-t border-line text-[12px] text-ink-4 font-mono", children: [t("pages.update.checkError"), ": ", statusData.error] })), statusData?.syncPending && (_jsx("div", { className: "mt-4 p-3 rounded-[4px] border border-amber/30 bg-amber/5", children: _jsx("p", { className: "text-[12px] text-amber leading-relaxed", children: t("pages.update.syncPending", { pluginVersion: statusData.pluginLatestVersion ?? "" }) }) })), statusData?.codexInstalled && (_jsx("div", { className: "mt-4 p-3 rounded-[4px] border border-amber/30 bg-amber/5", children: _jsx("p", { className: "text-[12px] text-amber leading-relaxed", children: t("pages.update.codexWarning") }) })), statusData?.changelog && statusData.hasUpdate && (_jsxs("div", { className: "mt-4 p-4 rounded-[4px] border border-line bg-surface/50", children: [_jsx("p", { className: "text-[12px] font-mono text-ink-3 uppercase tracking-wide mb-2", children: t("pages.update.whatsNew") }), _jsx("div", { className: "text-[13px] text-ink-2 leading-relaxed max-h-[300px] overflow-y-auto", children: _jsx(MarkdownRenderer, { content: statusData.changelog }) })] })), _jsxs("div", { className: "mt-5 pt-4 border-t border-line flex items-center gap-3 flex-wrap", children: [_jsx("button", { type: "button", onClick: handleCheckForUpdates, disabled: checking || fullUpdating, className: "border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: checking ? t("pages.update.checking") : t("pages.update.checkForUpdates") }), !isUpToDate && (_jsx("button", { type: "button", onClick: () => setShowFullUpdateDialog(true), disabled: checking || fullUpdating, className: "bg-gov text-white rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: fullUpdating ? (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }), t("pages.update.updating")] })) : (t("pages.update.applyUpdate")) }))] }), updateResult && (_jsx("div", { className: `mt-4 p-4 rounded-[6px] border animate-[pdFadeIn_400ms_ease-out] ${updateResult.success ? 'bg-green/5 border-green/20' : 'bg-red/5 border-red/20'}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [updateResult.success ? (_jsx(CheckCircle2, { className: "h-6 w-6 text-green shrink-0 mt-0.5 animate-[pdFadeIn_600ms_ease-out]", style: { transform: 'scale(1)', animationFillMode: 'both' } })) : (_jsx(XCircle, { className: "h-6 w-6 text-red shrink-0 mt-0.5" })), _jsxs("div", { className: "flex-1 min-w-0", children: [updateResult.success && updateResult.fromVersion && updateResult.newVersion && (_jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [_jsx("span", { className: "font-mono text-[13px] text-ink-3 line-through", children: updateResult.fromVersion }), _jsx(ArrowRight, { className: "h-3.5 w-3.5 text-green" }), _jsx("span", { className: "font-mono text-[13px] text-green font-medium", children: updateResult.newVersion })] })), _jsx("p", { className: `text-[13px] ${updateResult.success ? 'text-green' : 'text-red'}`, children: updateResult.message }), updateResult.success && updateResult.updatedFiles && updateResult.updatedFiles.length > 0 && (_jsx("p", { className: "mt-1 text-[12px] text-ink-4 font-mono", children: t("pages.update.filesUpdated", { count: updateResult.updatedFiles.length }) })), updateResult.success && !updateResult.requiresRestart && (_jsx("p", { className: "mt-2 text-[12px] font-mono text-ink-4", children: t("pages.update.restartHint") })), updateResult.success && updateResult.requiresRestart && (_jsx("p", { className: "mt-2 p-2 rounded-[3px] bg-amber/5 border border-amber/20 text-[12px] text-amber leading-relaxed", children: t("pages.update.fullUpdateRestartPrompt") })), updateResult.success && updateResult.partialUpdate && (_jsx("p", { className: "mt-1 text-[12px] text-amber leading-relaxed", children: t("pages.update.partialUpdateHint") })), !updateResult.success && updateResult.nextAction && (_jsx("p", { className: "mt-1 text-[12px] text-ink-4 font-mono leading-relaxed", children: updateResult.nextAction })), !updateResult.success && (_jsxs("div", { className: "mt-2 flex items-center gap-2", children: [/network|fetch|timeout|ECONNREFUSED|ENOTFOUND/i.test(updateResult.message) && (_jsx("p", { className: "text-[12px] text-ink-4 font-mono", children: t("pages.update.networkErrorHint") })), _jsx("button", { type: "button", onClick: handleApplyUpdate, disabled: updating, className: "text-[12px] text-red underline hover:text-red/80 disabled:opacity-50", children: t("pages.update.retry") })] }))] })] }) }))] })] }), _jsxs("section", { "aria-labelledby": "section-update-history", children: [_jsx(SectionTitle, { id: "section-update-history", children: t("pages.update.history") }), historyErrorReason ? (_jsxs("div", { className: "text-ink-3 text-[13px] leading-relaxed py-3", children: [t("pages.update.loadError"), " (", historyErrorReason, ")"] })) : historyData && historyData.updates.length > 0 ? (_jsx("div", { className: "space-y-[10px]", children: historyData.updates.map((entry) => (_jsxs("article", { className: "bg-panel border border-line rounded-[6px] px-5 py-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t("pages.update.version") }), _jsxs("span", { className: "font-mono text-[13px] text-ink", children: [entry.fromVersion, " \u2192 ", entry.toVersion === "failed" ? "—" : entry.toVersion] }), entry.kind !== "failure" && (_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t(`pages.update.historyKind.${entry.kind}`) })), !entry.success && entry.kind !== 'refusal' && (_jsx("span", { className: "inline-flex items-center border border-red/35 text-red rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.failed") }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: formatDate(entry.timestamp, i18n.language, {
183
184
  year: "numeric",
184
185
  month: "short",
185
186
  day: "numeric",