create-principles-disciple 1.121.9 → 1.121.10
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/console/dist/server/routes/update.js +82 -14
- package/console/dist/server/utils/update-links.d.ts +38 -0
- package/console/dist/server/utils/update-links.js +71 -0
- package/core/package.json +1 -1
- package/host-runtime/package.json +1 -1
- package/install-layout/package.json +3 -7
- package/package.json +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/openclaw.plugin.json +1 -1
|
@@ -9,6 +9,7 @@ import { checkOpenClawGateway, stopOpenClawGateway, restartOpenClawGateway, } fr
|
|
|
9
9
|
import { migrateLegacyExtensionBackups, reservePdBackupDestination, resolvePdBackupsRoot, } from '../utils/pd-backups.js';
|
|
10
10
|
import { ActivationCompatibilityReadModel } from '@principles/core/runtime-v2';
|
|
11
11
|
import { getInstallLayoutPaths, resolveInstallLayout } from '@principles/install-layout';
|
|
12
|
+
import { collectFileDepLinkSpecs } from '../utils/update-links.js';
|
|
12
13
|
/**
|
|
13
14
|
* Legacy rule contract preflight (2026-08-19): refuse to swap the runtime
|
|
14
15
|
* while an ACTIVE owner-approved rule still depends on a RuleHost contract
|
|
@@ -724,22 +725,35 @@ function depsMeaningfullyChanged(oldDeps, newDeps) {
|
|
|
724
725
|
return aKeys.some((k, i) => bKeys[i] !== k || a[k] !== b[k]);
|
|
725
726
|
}
|
|
726
727
|
/**
|
|
727
|
-
* Create the node_modules
|
|
728
|
-
*
|
|
728
|
+
* Create the node_modules resolution links for the installed components, if
|
|
729
|
+
* missing.
|
|
729
730
|
*
|
|
730
731
|
* Mirrors installer.ts syncPdCli: junction on Windows (no elevation needed),
|
|
731
732
|
* relative symlink elsewhere. Fresh installs get these links via npm install
|
|
732
|
-
* (the bundled package.json rewrites
|
|
733
|
-
* full update deliberately skips npm install, so it must create them
|
|
734
|
-
* Without
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
733
|
+
* (the bundled package.json rewrites internal deps to file:../<component>);
|
|
734
|
+
* the full update deliberately skips npm install, so it must create them
|
|
735
|
+
* itself. Without a link, updated dists crash at startup with
|
|
736
|
+
* ERR_MODULE_NOT_FOUND (host-runtime: PRI-561, 2026-08-21).
|
|
737
|
+
*
|
|
738
|
+
* Two sources, both fail-closed on creation errors:
|
|
739
|
+
* 1. the explicit link list below (known-critical links: a missing one
|
|
740
|
+
* means the updated console cannot start);
|
|
741
|
+
* 2. a data-driven pass that derives links from the STAGED manifests —
|
|
742
|
+
* the freshly extracted release trees under tempDir. Their `file:`
|
|
743
|
+
* declarations are the authoritative list of links the updated tree
|
|
744
|
+
* needs. Reading the deployed (pre-update) manifests instead would
|
|
745
|
+
* miss every dependency this release newly introduced: the running
|
|
746
|
+
* console executes update logic from its own dist (one generation
|
|
747
|
+
* behind the components it installs) — observed 2026-08-29 when the
|
|
748
|
+
* 1.221.2 console updated hosts to 1.222.5 but could not know about
|
|
749
|
+
* the newly introduced install-layout component, leaving
|
|
750
|
+
* host-runtime/node_modules/@principles/install-layout missing and
|
|
751
|
+
* every pd-cli runtime command failing with ERR_MODULE_NOT_FOUND.
|
|
738
752
|
*
|
|
739
753
|
* Returns undefined on success, or an error message (rc-9: observable, never
|
|
740
754
|
* silent — a missing link means the updated console cannot start).
|
|
741
755
|
*/
|
|
742
|
-
function ensureRuntimeResolutionLinks(layout) {
|
|
756
|
+
function ensureRuntimeResolutionLinks(layout, tempDir) {
|
|
743
757
|
const links = [
|
|
744
758
|
{ linkPath: path.join(layout.consoleDir, 'node_modules', '@principles', 'host-runtime'), target: layout.hostRuntimeDir },
|
|
745
759
|
{ linkPath: path.join(layout.pdCliDir, 'node_modules', '@principles', 'host-runtime'), target: layout.hostRuntimeDir },
|
|
@@ -750,11 +764,43 @@ function ensureRuntimeResolutionLinks(layout) {
|
|
|
750
764
|
{ linkPath: path.join(layout.hostRuntimeDir, 'node_modules', '@principles', 'install-layout'), target: layout.installLayoutDir },
|
|
751
765
|
{ linkPath: path.join(layout.consoleDir, 'node_modules', 'principles-disciple'), target: layout.pluginDir },
|
|
752
766
|
];
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
767
|
+
// Data-driven pass: derive links from the STAGED component manifests (see
|
|
768
|
+
// the comment above — staged, never the deployed pre-update manifests).
|
|
769
|
+
const stagedComponents = [
|
|
770
|
+
{ manifestDir: path.join(tempDir, 'console'), deployedDir: layout.consoleDir },
|
|
771
|
+
{ manifestDir: path.join(tempDir, 'pd-cli'), deployedDir: layout.pdCliDir },
|
|
772
|
+
{ manifestDir: path.join(tempDir, 'host-runtime'), deployedDir: layout.hostRuntimeDir },
|
|
773
|
+
{ manifestDir: path.join(tempDir, 'install-layout'), deployedDir: layout.installLayoutDir },
|
|
774
|
+
{ manifestDir: path.join(tempDir, 'core'), deployedDir: layout.coreDir },
|
|
775
|
+
{ manifestDir: path.join(tempDir, 'plugin'), deployedDir: layout.pluginDir },
|
|
776
|
+
];
|
|
777
|
+
const readStagedDependencies = (manifestDir) => {
|
|
778
|
+
try {
|
|
779
|
+
const pkgPath = path.join(manifestDir, 'package.json');
|
|
780
|
+
if (!fs.existsSync(pkgPath))
|
|
781
|
+
return {};
|
|
782
|
+
const parsed = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
783
|
+
if (!isRecord(parsed) || !isRecord(parsed.dependencies))
|
|
784
|
+
return {};
|
|
785
|
+
const out = {};
|
|
786
|
+
for (const [name, ref] of Object.entries(parsed.dependencies)) {
|
|
787
|
+
if (typeof ref === 'string')
|
|
788
|
+
out[name] = ref;
|
|
789
|
+
}
|
|
790
|
+
return out;
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
// rc-9: an unreadable staged manifest degrades to "no derived links" —
|
|
794
|
+
// the explicit list above still covers the known-critical links.
|
|
795
|
+
return {};
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const fileDepSpecs = collectFileDepLinkSpecs(stagedComponents, readStagedDependencies);
|
|
799
|
+
const createResolutionLink = (linkPath, target) => {
|
|
800
|
+
// Idempotent: never overwrite an existing link or directory (fresh
|
|
801
|
+
// installs have npm-created real dirs in these slots).
|
|
756
802
|
if (fs.existsSync(linkPath))
|
|
757
|
-
|
|
803
|
+
return undefined;
|
|
758
804
|
try {
|
|
759
805
|
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
760
806
|
if (process.platform === 'win32') {
|
|
@@ -763,10 +809,32 @@ function ensureRuntimeResolutionLinks(layout) {
|
|
|
763
809
|
else {
|
|
764
810
|
fs.symlinkSync(path.relative(path.dirname(linkPath), target), linkPath, 'dir');
|
|
765
811
|
}
|
|
812
|
+
return undefined;
|
|
766
813
|
}
|
|
767
814
|
catch (error) {
|
|
768
815
|
return `Failed to create runtime resolution link at ${linkPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
769
816
|
}
|
|
817
|
+
};
|
|
818
|
+
// Pass 1 — explicit known-critical links, fail-closed BEFORE any byte is
|
|
819
|
+
// swapped (a link-creation failure aborts with the installed packages
|
|
820
|
+
// untouched: the PRI-561 ordering contract).
|
|
821
|
+
for (const { linkPath, target } of links) {
|
|
822
|
+
if (!fs.existsSync(target))
|
|
823
|
+
continue;
|
|
824
|
+
const error = createResolutionLink(linkPath, target);
|
|
825
|
+
if (error)
|
|
826
|
+
return error;
|
|
827
|
+
}
|
|
828
|
+
// Pass 2 — data-driven derived links. Their deployed target dir may be
|
|
829
|
+
// created by the copy steps that follow (a brand-new component's dir does
|
|
830
|
+
// not exist yet); each staged target's existence was already proven by the
|
|
831
|
+
// extraction, and the copies below run unconditionally.
|
|
832
|
+
for (const spec of fileDepSpecs) {
|
|
833
|
+
if (fs.existsSync(spec.linkPath))
|
|
834
|
+
continue;
|
|
835
|
+
const error = createResolutionLink(spec.linkPath, spec.target);
|
|
836
|
+
if (error)
|
|
837
|
+
return error;
|
|
770
838
|
}
|
|
771
839
|
return undefined;
|
|
772
840
|
}
|
|
@@ -935,7 +1003,7 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
935
1003
|
if (fs.existsSync(path.join(installLayoutSrc, 'package.json')) && fs.existsSync(path.join(installLayoutSrc, 'dist'))) {
|
|
936
1004
|
copyDirRecursive(installLayoutSrc, layout.installLayoutDir, SKIP_DIRS);
|
|
937
1005
|
}
|
|
938
|
-
const linkError = ensureRuntimeResolutionLinks(layout);
|
|
1006
|
+
const linkError = ensureRuntimeResolutionLinks(layout, tempDir);
|
|
939
1007
|
if (linkError) {
|
|
940
1008
|
appendUpdateHistory(workspaceDir, {
|
|
941
1009
|
fromVersion,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type FileDepLinkSpec = {
|
|
2
|
+
linkPath: string;
|
|
3
|
+
/** Deployed layout dir the link points at; may be created by the copy
|
|
4
|
+
* steps that run after derivation (see stagedTargetExists). */
|
|
5
|
+
target: string;
|
|
6
|
+
/** The staged sibling dir proving this component ships in this release. */
|
|
7
|
+
stagedTarget: string;
|
|
8
|
+
};
|
|
9
|
+
export type StagedComponent = {
|
|
10
|
+
/** Freshly extracted component dir under the update temp dir. */
|
|
11
|
+
manifestDir: string;
|
|
12
|
+
/** Deployed layout dir this component is (or will be) installed into. */
|
|
13
|
+
deployedDir: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Derive the node_modules link specs implied by each staged component's
|
|
17
|
+
* declared `file:` dependencies.
|
|
18
|
+
*
|
|
19
|
+
* - `readDependencies(manifestDir)` returns the component's dependency map
|
|
20
|
+
* (name -> version-or-ref); return {} for unreadable/missing manifests.
|
|
21
|
+
* - A `file:` ref is resolved against its staged manifest dir to find the
|
|
22
|
+
* staged sibling target; the deployed link target is taken from the same
|
|
23
|
+
* staged->deployed pairing the caller provides — component identity is
|
|
24
|
+
* the staged directory itself (normalized absolute path), NOT the
|
|
25
|
+
* deployed directory's basename. In the legacy layout the deployed
|
|
26
|
+
* plugin dir is named `principles-disciple` while the staged one is
|
|
27
|
+
* `plugin`, so a basename lookup would silently drop the derived link
|
|
28
|
+
* (review P1, PR #1457).
|
|
29
|
+
* - Deps whose staged target is not one of the layout components (e.g.
|
|
30
|
+
* @principles/codex-adapter — a separate host install, not part of this
|
|
31
|
+
* layout) are skipped.
|
|
32
|
+
*
|
|
33
|
+
* Note: `target` (the deployed dir) may not exist yet at derivation time —
|
|
34
|
+
* for components whose copy step runs after this call, the caller creates
|
|
35
|
+
* the directory right after deriving and creating these links. Use
|
|
36
|
+
* `stagedTarget` to verify the component actually ships in this release.
|
|
37
|
+
*/
|
|
38
|
+
export declare function collectFileDepLinkSpecs(stagedComponents: readonly StagedComponent[], readDependencies: (manifestDir: string) => Record<string, string>): FileDepLinkSpec[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-driven resolution-link derivation for the full-update pipeline.
|
|
3
|
+
*
|
|
4
|
+
* The deployed console performs full updates with the update logic baked
|
|
5
|
+
* into its own dist — one generation behind the components it installs. A
|
|
6
|
+
* hardcoded link list therefore goes stale the moment a release introduces
|
|
7
|
+
* a new internal `file:` dependency (observed 2026-08-29: the 1.221.2
|
|
8
|
+
* console could not know about the newly introduced install-layout
|
|
9
|
+
* component, so the 1.222.5 update left
|
|
10
|
+
* host-runtime/node_modules/@principles/install-layout missing and every
|
|
11
|
+
* pd-cli runtime command failed with ERR_MODULE_NOT_FOUND).
|
|
12
|
+
*
|
|
13
|
+
* The authoritative source for "which links does the UPDATED tree need" is
|
|
14
|
+
* the STAGED manifests under the update's temp dir (the freshly extracted
|
|
15
|
+
* release trees) — NOT the deployed pre-update manifests, which by
|
|
16
|
+
* definition do not declare newly added dependencies.
|
|
17
|
+
*
|
|
18
|
+
* Each staged component's `file:` refs resolve to a sibling staged
|
|
19
|
+
* component directory; the deployed link target is that sibling's entry in
|
|
20
|
+
* the same staged->deployed pairing.
|
|
21
|
+
*/
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
/**
|
|
24
|
+
* Derive the node_modules link specs implied by each staged component's
|
|
25
|
+
* declared `file:` dependencies.
|
|
26
|
+
*
|
|
27
|
+
* - `readDependencies(manifestDir)` returns the component's dependency map
|
|
28
|
+
* (name -> version-or-ref); return {} for unreadable/missing manifests.
|
|
29
|
+
* - A `file:` ref is resolved against its staged manifest dir to find the
|
|
30
|
+
* staged sibling target; the deployed link target is taken from the same
|
|
31
|
+
* staged->deployed pairing the caller provides — component identity is
|
|
32
|
+
* the staged directory itself (normalized absolute path), NOT the
|
|
33
|
+
* deployed directory's basename. In the legacy layout the deployed
|
|
34
|
+
* plugin dir is named `principles-disciple` while the staged one is
|
|
35
|
+
* `plugin`, so a basename lookup would silently drop the derived link
|
|
36
|
+
* (review P1, PR #1457).
|
|
37
|
+
* - Deps whose staged target is not one of the layout components (e.g.
|
|
38
|
+
* @principles/codex-adapter — a separate host install, not part of this
|
|
39
|
+
* layout) are skipped.
|
|
40
|
+
*
|
|
41
|
+
* Note: `target` (the deployed dir) may not exist yet at derivation time —
|
|
42
|
+
* for components whose copy step runs after this call, the caller creates
|
|
43
|
+
* the directory right after deriving and creating these links. Use
|
|
44
|
+
* `stagedTarget` to verify the component actually ships in this release.
|
|
45
|
+
*/
|
|
46
|
+
export function collectFileDepLinkSpecs(stagedComponents, readDependencies) {
|
|
47
|
+
const deployedDirByStagedDir = new Map();
|
|
48
|
+
for (const component of stagedComponents) {
|
|
49
|
+
deployedDirByStagedDir.set(path.resolve(component.manifestDir), component.deployedDir);
|
|
50
|
+
}
|
|
51
|
+
const specs = [];
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
for (const component of stagedComponents) {
|
|
54
|
+
for (const [name, ref] of Object.entries(readDependencies(component.manifestDir))) {
|
|
55
|
+
if (typeof ref !== 'string' || !ref.startsWith('file:'))
|
|
56
|
+
continue;
|
|
57
|
+
const stagedTarget = path.resolve(component.manifestDir, ref.slice('file:'.length));
|
|
58
|
+
const deployedTarget = deployedDirByStagedDir.get(stagedTarget);
|
|
59
|
+
if (deployedTarget === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
// The link lives in the DEPLOYED component dir (what the updated dist
|
|
62
|
+
// resolves against) — never inside the throwaway temp dir.
|
|
63
|
+
const linkPath = path.join(component.deployedDir, 'node_modules', name);
|
|
64
|
+
if (seen.has(linkPath))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(linkPath);
|
|
67
|
+
specs.push({ linkPath, target: deployedTarget, stagedTarget });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return specs;
|
|
71
|
+
}
|
package/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.74.1",
|
|
4
4
|
"description": "Pure-logic core of Principles Disciple, an AI Agent Governance System - pain signal capture and principle proposal (no I/O)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/install-layout",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,17 +10,13 @@
|
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"files": [
|
|
14
|
-
"dist"
|
|
15
|
-
],
|
|
13
|
+
"files": ["dist"],
|
|
16
14
|
"license": "MIT",
|
|
17
15
|
"repository": {
|
|
18
16
|
"type": "git",
|
|
19
17
|
"url": "git+https://github.com/csuzngjh/principles.git"
|
|
20
18
|
},
|
|
21
|
-
"publishConfig": {
|
|
22
|
-
"access": "public"
|
|
23
|
-
},
|
|
19
|
+
"publishConfig": { "access": "public" },
|
|
24
20
|
"scripts": {
|
|
25
21
|
"build": "tsc",
|
|
26
22
|
"test": "npm run build && vitest run"
|
package/package.json
CHANGED
package/pd-cli/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "principles-disciple",
|
|
3
3
|
"name": "Principles Disciple",
|
|
4
4
|
"description": "Principles Disciple is an AI Agent Governance System. Stop correcting the same AI behavior across sessions. Turn repeated Agent corrections into Owner-approved, observable, reversible behavior principles.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.198.1",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onCapabilities": [
|
|
8
8
|
"hook"
|