create-principles-disciple 1.134.0 → 1.134.2
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/codex-adapter/package.json +3 -2
- package/console/dist/server/routes/update.js +143 -27
- package/console/dist/server/utils/cli-smoke.d.ts +19 -0
- package/console/dist/server/utils/cli-smoke.js +60 -0
- package/console/dist/server/utils/installed-layout.d.ts +8 -0
- package/console/dist/server/utils/installed-layout.js +4 -0
- package/console/dist/ui/i18n/en.json +2 -2
- package/console/dist/ui/i18n/zh-CN.json +2 -2
- package/console/dist/web/assets/app.js +5 -5
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +157 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js +9 -0
- package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts +10 -0
- package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/output-repair-contract.js.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +53 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
- package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts +9 -1
- package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/structured-output-repair.js +8 -0
- package/core/dist/runtime-v2/adapter/structured-output-repair.js.map +1 -1
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-failure-details.test.js +36 -0
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-failure-details.test.js.map +1 -1
- package/core/package.json +1 -1
- package/dist/installer.d.ts +21 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +85 -1
- package/dist/installer.js.map +1 -1
- package/dist/uninstaller.d.ts +8 -11
- package/dist/uninstaller.d.ts.map +1 -1
- package/dist/uninstaller.js +97 -19
- package/dist/uninstaller.js.map +1 -1
- package/host-runtime/package.json +1 -1
- package/install-layout/dist/index.d.ts +8 -0
- package/install-layout/dist/index.js +1 -0
- package/install-layout/package.json +1 -1
- package/package.json +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.js +16 -2
- package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +186 -186
- package/plugin/dist/rulehost-evidence.js +77 -77
- package/release-manager/dist/installer.d.ts +21 -0
- package/release-manager/dist/installer.d.ts.map +1 -1
- package/release-manager/dist/installer.js +85 -1
- package/release-manager/dist/installer.js.map +1 -1
- package/release-manager/dist/uninstaller.d.ts +8 -11
- package/release-manager/dist/uninstaller.d.ts.map +1 -1
- package/release-manager/dist/uninstaller.js +97 -19
- package/release-manager/dist/uninstaller.js.map +1 -1
- package/release-manager/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/codex-adapter",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@principles/core": "file:../core",
|
|
35
|
-
"@principles/host-runtime": "file:../host-runtime"
|
|
35
|
+
"@principles/host-runtime": "file:../host-runtime",
|
|
36
|
+
"@principles/install-layout": "file:../install-layout"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@types/node": "^26.4.1",
|
|
@@ -10,6 +10,7 @@ import { migrateLegacyExtensionBackups, reservePdBackupDestination, resolvePdBac
|
|
|
10
10
|
import { resolveExtensionsDir, resolveUpdateLayout, resolvePluginDir, resolveCanonicalRuntimeRoot, readCurrentVersion, } from '../utils/installed-layout.js';
|
|
11
11
|
import { ActivationCompatibilityReadModel, isFeatureEnabled } from '@principles/core/runtime-v2';
|
|
12
12
|
import { collectFileDepLinkSpecs } from '../utils/update-links.js';
|
|
13
|
+
import { runPostUpdateCliSmoke } from '../utils/cli-smoke.js';
|
|
13
14
|
import { updateMutationController, LEGACY_MUTATION_AUTHORITY, RELEASE_MANAGER_AUTHORITY, MUTATION_KINDS, } from '../update/mutation-controller.js';
|
|
14
15
|
import { runLegacyJournaledMutation } from '../update/legacy-mutation-journal.js';
|
|
15
16
|
import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
|
|
@@ -49,6 +50,10 @@ const WORKSPACE_FILES = ['AGENTS.md', 'SOUL.md', 'USER.md', 'CLAUDE.md'];
|
|
|
49
50
|
// .node addons locked by the gateway/console processes (EPERM on copyfile),
|
|
50
51
|
// npm symlinks/junctions, and thousands of regenerable files.
|
|
51
52
|
const SKIP_DIRS = new Set(['node_modules']);
|
|
53
|
+
// Full-update backups extend the existing plugin backup with sibling code
|
|
54
|
+
// trees. Dependency directories stay in place (native modules may be loaded).
|
|
55
|
+
const RUNTIME_BACKUP_DIR = '.runtime-components';
|
|
56
|
+
const BACKUP_SKIP_DIRS = new Set(['node_modules', RUNTIME_BACKUP_DIR]);
|
|
52
57
|
// ---------------------------------------------------------------------------
|
|
53
58
|
// Helpers
|
|
54
59
|
// ---------------------------------------------------------------------------
|
|
@@ -223,6 +228,58 @@ function copyFileTo(src, dest) {
|
|
|
223
228
|
fs.mkdirSync(dir, { recursive: true });
|
|
224
229
|
fs.copyFileSync(src, dest);
|
|
225
230
|
}
|
|
231
|
+
function runtimeComponents(layout) {
|
|
232
|
+
return {
|
|
233
|
+
console: layout.consoleDir,
|
|
234
|
+
'pd-cli': layout.pdCliDir,
|
|
235
|
+
'host-runtime': layout.hostRuntimeDir,
|
|
236
|
+
'install-layout': layout.installLayoutDir,
|
|
237
|
+
core: layout.coreDir,
|
|
238
|
+
plugin: layout.pluginDir,
|
|
239
|
+
'release-manager': layout.releaseManagerDir,
|
|
240
|
+
...(layout.codexAdapterDir ? { 'codex-adapter': layout.codexAdapterDir } : {}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/** Resolve a backup entry under the backup root, refusing escapes — the
|
|
244
|
+
* component names recorded in components.json are untrusted runtime data
|
|
245
|
+
* (rc-1/rc-2), so every join into the backup tree is boundary-checked. */
|
|
246
|
+
function backupEntryPath(root, name) {
|
|
247
|
+
const target = path.resolve(root, name);
|
|
248
|
+
if (target === root || !target.startsWith(root + path.sep)) {
|
|
249
|
+
throw new Error(`Runtime backup entry "${name}" escapes the backup root`);
|
|
250
|
+
}
|
|
251
|
+
return target;
|
|
252
|
+
}
|
|
253
|
+
function backupRuntimeComponents(layout, backupDir) {
|
|
254
|
+
const root = path.join(backupDir, RUNTIME_BACKUP_DIR);
|
|
255
|
+
fs.mkdirSync(root, { recursive: true });
|
|
256
|
+
const present = [];
|
|
257
|
+
for (const [name, dir] of Object.entries(runtimeComponents(layout))) {
|
|
258
|
+
if (name === 'plugin' || !fs.existsSync(dir))
|
|
259
|
+
continue;
|
|
260
|
+
copyDirRecursive(dir, backupEntryPath(root, name), BACKUP_SKIP_DIRS);
|
|
261
|
+
present.push(name);
|
|
262
|
+
}
|
|
263
|
+
// Written last: a partial backup must never be accepted as recoverable.
|
|
264
|
+
fs.writeFileSync(path.join(root, 'components.json'), JSON.stringify(present));
|
|
265
|
+
}
|
|
266
|
+
function restoreRuntimeComponents(layout, backupDir) {
|
|
267
|
+
const root = path.join(backupDir, RUNTIME_BACKUP_DIR);
|
|
268
|
+
if (!fs.existsSync(root))
|
|
269
|
+
return false; // Historical plugin-only backup.
|
|
270
|
+
const present = JSON.parse(fs.readFileSync(path.join(root, 'components.json'), 'utf8'));
|
|
271
|
+
const components = runtimeComponents(layout);
|
|
272
|
+
if (!Array.isArray(present) || present.some((name) => typeof name !== 'string' || name === 'plugin' || !Object.hasOwn(components, name)
|
|
273
|
+
|| !fs.existsSync(backupEntryPath(root, name)))) {
|
|
274
|
+
throw new Error('Runtime backup is incomplete or incompatible with this installation');
|
|
275
|
+
}
|
|
276
|
+
for (const [name, dir] of Object.entries(components)) {
|
|
277
|
+
if (name === 'plugin' || !present.includes(name))
|
|
278
|
+
continue;
|
|
279
|
+
copyDirRecursive(backupEntryPath(root, name), dir, BACKUP_SKIP_DIRS);
|
|
280
|
+
}
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
226
283
|
// --- Plugin skill-language preservation (PR #1332 companion) -----------------
|
|
227
284
|
// OpenClaw publishes plugin skills by directory name (first declaration
|
|
228
285
|
// wins, same-name roots only warn — no locale mechanism), so a manifest must
|
|
@@ -749,7 +806,11 @@ async function doRollbackUpdate(options, workspaceDir) {
|
|
|
749
806
|
// dependencies. Instead, overwrite from backup: modified files (dist/,
|
|
750
807
|
// package.json, etc.) get the backup version, while node_modules/console/
|
|
751
808
|
// core/ (not in the backup) are left untouched.
|
|
752
|
-
|
|
809
|
+
const layout = resolveUpdateLayout();
|
|
810
|
+
const fullBackup = layout && path.resolve(layout.pluginDir) === path.resolve(targetDir)
|
|
811
|
+
? restoreRuntimeComponents(layout, backupDir)
|
|
812
|
+
: false;
|
|
813
|
+
copyDirRecursive(backupDir, targetDir, BACKUP_SKIP_DIRS);
|
|
753
814
|
// Keep the OpenClaw extension copy in sync with the restored canonical
|
|
754
815
|
// plugin (CP-5 invariant from the 2026-09-05 investigation): OpenClaw
|
|
755
816
|
// loads ~/.openclaw/extensions/principles-disciple, so a rollback that
|
|
@@ -759,9 +820,29 @@ async function doRollbackUpdate(options, workspaceDir) {
|
|
|
759
820
|
const openClawPluginCopy = path.join(resolveExtensionsDir(), 'principles-disciple');
|
|
760
821
|
let extCopySynced = false;
|
|
761
822
|
if (path.resolve(openClawPluginCopy) !== path.resolve(targetDir) && fs.existsSync(openClawPluginCopy)) {
|
|
762
|
-
copyDirRecursive(backupDir, openClawPluginCopy);
|
|
823
|
+
copyDirRecursive(backupDir, openClawPluginCopy, BACKUP_SKIP_DIRS);
|
|
763
824
|
extCopySynced = true;
|
|
764
825
|
}
|
|
826
|
+
if (fullBackup && layout) {
|
|
827
|
+
const smoke = runPostUpdateCliSmoke(layout.pdCliDir);
|
|
828
|
+
if (!smoke.ok) {
|
|
829
|
+
// rc-9: a failed rollback must be observable in history, like every
|
|
830
|
+
// other failure path — not just in the HTTP response.
|
|
831
|
+
appendUpdateHistory(workspaceDir, {
|
|
832
|
+
fromVersion: 'rolled-back',
|
|
833
|
+
toVersion: readCurrentVersion(targetDir) ?? 'unknown',
|
|
834
|
+
success: false,
|
|
835
|
+
kind: 'failure',
|
|
836
|
+
backupPath: backupDir,
|
|
837
|
+
});
|
|
838
|
+
return {
|
|
839
|
+
success: false,
|
|
840
|
+
message: `Runtime backup restored but pd CLI still fails to start: ${smoke.error}`,
|
|
841
|
+
reason: 'rollback_cli_smoke_failed',
|
|
842
|
+
nextAction: 'Keep the backup and use the official installer pinned to the previous installer release to repair dependencies before restarting Console.',
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
}
|
|
765
846
|
// Record rollback history
|
|
766
847
|
appendUpdateHistory(workspaceDir, {
|
|
767
848
|
fromVersion: 'rolled-back',
|
|
@@ -772,9 +853,11 @@ async function doRollbackUpdate(options, workspaceDir) {
|
|
|
772
853
|
});
|
|
773
854
|
return {
|
|
774
855
|
success: true,
|
|
775
|
-
message:
|
|
776
|
-
? '
|
|
777
|
-
:
|
|
856
|
+
message: fullBackup
|
|
857
|
+
? 'Runtime components restored and pd CLI startup verified. Restart Console to load the restored code.'
|
|
858
|
+
: extCopySynced
|
|
859
|
+
? 'Rollback completed successfully (OpenClaw extension copy restored too). Note: only the plugin was rolled back; console/core/host-runtime/pd-cli keep the upgraded version. For a full step-back, re-run the official installer pinned to the older release.'
|
|
860
|
+
: 'Rollback completed successfully. Note: only the plugin was rolled back; console/core/host-runtime/pd-cli keep the upgraded version. For a full step-back, re-run the official installer pinned to the older release.',
|
|
778
861
|
};
|
|
779
862
|
}
|
|
780
863
|
catch (error) {
|
|
@@ -998,19 +1081,9 @@ function ensureRuntimeResolutionLinks(layout, tempDir) {
|
|
|
998
1081
|
];
|
|
999
1082
|
// Data-driven pass: derive links from the STAGED component manifests (see
|
|
1000
1083
|
// the comment above — staged, never the deployed pre-update manifests).
|
|
1001
|
-
const stagedComponents = [
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
{ manifestDir: path.join(tempDir, 'host-runtime'), deployedDir: layout.hostRuntimeDir },
|
|
1005
|
-
{ manifestDir: path.join(tempDir, 'install-layout'), deployedDir: layout.installLayoutDir },
|
|
1006
|
-
{ manifestDir: path.join(tempDir, 'core'), deployedDir: layout.coreDir },
|
|
1007
|
-
{ manifestDir: path.join(tempDir, 'plugin'), deployedDir: layout.pluginDir },
|
|
1008
|
-
// PRI-672: staged release-manager component (npm name
|
|
1009
|
-
// create-principles-disciple). Its staged manifest carries the
|
|
1010
|
-
// file:../install-layout ref, and the staged console's manifest carries
|
|
1011
|
-
// file:../release-manager — both derive their resolution links here.
|
|
1012
|
-
{ manifestDir: path.join(tempDir, 'release-manager'), deployedDir: layout.releaseManagerDir },
|
|
1013
|
-
];
|
|
1084
|
+
const stagedComponents = Object.entries(runtimeComponents(layout)).map(([name, deployedDir]) => ({
|
|
1085
|
+
manifestDir: path.join(tempDir, name), deployedDir,
|
|
1086
|
+
}));
|
|
1014
1087
|
const readStagedDependencies = (manifestDir) => {
|
|
1015
1088
|
try {
|
|
1016
1089
|
const pkgPath = path.join(manifestDir, 'package.json');
|
|
@@ -1263,9 +1336,8 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1263
1336
|
// 2026-09-05 install/upgrade investigation). The backup excludes
|
|
1264
1337
|
// node_modules (same SKIP_DIRS contract as /apply), and the backup dir
|
|
1265
1338
|
// is recorded in update-history (success AND failure) so the rollback
|
|
1266
|
-
// handler can find it.
|
|
1267
|
-
//
|
|
1268
|
-
// next update, so only the version authority (plugin) is backed up.
|
|
1339
|
+
// handler can find it. Include every sibling code tree: a plugin-only
|
|
1340
|
+
// rollback cannot repair a CLI/adapter import failure after an update.
|
|
1269
1341
|
// R5 (review): reservePdBackupDestination creates the directory BEFORE
|
|
1270
1342
|
// the copy — a mid-copy failure must not leave backupPath pointing at a
|
|
1271
1343
|
// partial backup (a later /rollback would happily restore the fragment).
|
|
@@ -1276,6 +1348,7 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1276
1348
|
const backupDest = reservePdBackupDestination(path.basename(extDir));
|
|
1277
1349
|
try {
|
|
1278
1350
|
copyDirRecursive(extDir, backupDest, SKIP_DIRS);
|
|
1351
|
+
backupRuntimeComponents(layout, backupDest);
|
|
1279
1352
|
}
|
|
1280
1353
|
catch (backupError) {
|
|
1281
1354
|
try {
|
|
@@ -1307,6 +1380,23 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1307
1380
|
fs.existsSync(path.join(releaseManagerSrc, 'dist'))) {
|
|
1308
1381
|
copyDirRecursive(releaseManagerSrc, layout.releaseManagerDir, SKIP_DIRS);
|
|
1309
1382
|
}
|
|
1383
|
+
// PRI-711: same PRI-561 ordering for codex-adapter — pd-cli's eager
|
|
1384
|
+
// import graph statically resolves it, so an updated pd-cli paired with
|
|
1385
|
+
// a stale deployed adapter is exactly the generation drift that bricked
|
|
1386
|
+
// the 1.231.2 update (health-codex imported symbols the deployed
|
|
1387
|
+
// adapter happened to carry; the next API addition would not). The
|
|
1388
|
+
// copy keeps the deployed node_modules (SKIP_DIRS), so its resolution
|
|
1389
|
+
// links survive; the data-driven pass below re-derives them anyway.
|
|
1390
|
+
// Skipped when the running console resolves a one-generation-old
|
|
1391
|
+
// install-layout without codexAdapterDir — that generation also lacks
|
|
1392
|
+
// the derivation entry, mirroring the old no-op behavior (rc-9).
|
|
1393
|
+
const codexAdapterDest = layout.codexAdapterDir;
|
|
1394
|
+
if (codexAdapterDest !== undefined &&
|
|
1395
|
+
fs.existsSync(path.join(tempDir, 'codex-adapter')) &&
|
|
1396
|
+
fs.existsSync(path.join(tempDir, 'codex-adapter', 'package.json')) &&
|
|
1397
|
+
fs.existsSync(path.join(tempDir, 'codex-adapter', 'dist'))) {
|
|
1398
|
+
copyDirRecursive(path.join(tempDir, 'codex-adapter'), codexAdapterDest, SKIP_DIRS);
|
|
1399
|
+
}
|
|
1310
1400
|
const { error: linkError, quarantined } = ensureRuntimeResolutionLinks(layout, tempDir);
|
|
1311
1401
|
reconciledQuarantine = quarantined;
|
|
1312
1402
|
if (linkError) {
|
|
@@ -1383,12 +1473,32 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1383
1473
|
if (tempDir && fs.existsSync(tempDir)) {
|
|
1384
1474
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
1385
1475
|
}
|
|
1386
|
-
// 6.
|
|
1387
|
-
//
|
|
1388
|
-
//
|
|
1389
|
-
//
|
|
1390
|
-
|
|
1391
|
-
|
|
1476
|
+
// 6.8 PRI-711: post-apply CLI smoke. The swap and link steps above are
|
|
1477
|
+
// data-driven but not self-proving — run the updated CLI once and refuse
|
|
1478
|
+
// to record a success if the eager import graph is broken (see
|
|
1479
|
+
// runPostUpdateCliSmoke for the three precedents this catches). The
|
|
1480
|
+
// backup recorded below keeps the failure recoverable via /rollback.
|
|
1481
|
+
const smoke = runPostUpdateCliSmoke(layout.pdCliDir);
|
|
1482
|
+
if (!smoke.ok) {
|
|
1483
|
+
restoreQuarantined(reconciledQuarantine);
|
|
1484
|
+
reconciledQuarantine = [];
|
|
1485
|
+
const failedVersion = readCurrentVersion(extDir) ?? toVersion ?? 'unknown';
|
|
1486
|
+
appendUpdateHistory(workspaceDir, {
|
|
1487
|
+
fromVersion,
|
|
1488
|
+
toVersion: failedVersion,
|
|
1489
|
+
success: false,
|
|
1490
|
+
kind: 'failure',
|
|
1491
|
+
...(pluginBackupDir ? { backupPath: pluginBackupDir } : {}),
|
|
1492
|
+
});
|
|
1493
|
+
return {
|
|
1494
|
+
success: false,
|
|
1495
|
+
message: `Update applied but the updated pd CLI fails to start: ${smoke.error}`,
|
|
1496
|
+
reason: 'post_update_cli_smoke_failed',
|
|
1497
|
+
nextAction: pluginBackupDir
|
|
1498
|
+
? 'Restore the backup recorded in update history (backupPath), then report the stderr above. The console must be restarted before further use.'
|
|
1499
|
+
: 'Report the stderr above; no backup was reserved for this update. The console must be restarted before further use.',
|
|
1500
|
+
requiresRestart: false,
|
|
1501
|
+
};
|
|
1392
1502
|
}
|
|
1393
1503
|
// 7. Version-advance check (drift guard). The full update installs the
|
|
1394
1504
|
// plugin bundled inside the installer. If the installer is stale (its
|
|
@@ -1400,6 +1510,8 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1400
1510
|
const newVersion = readCurrentVersion(extDir) ?? toVersion ?? 'unknown';
|
|
1401
1511
|
const installedExpectedRelease = newVersion === stagedVersion;
|
|
1402
1512
|
if (!installedExpectedRelease) {
|
|
1513
|
+
restoreQuarantined(reconciledQuarantine);
|
|
1514
|
+
reconciledQuarantine = [];
|
|
1403
1515
|
// Files were already rewritten to the same (or lower) version. Record a
|
|
1404
1516
|
// FAILED history entry so the operator sees why, and return a structured
|
|
1405
1517
|
// error with nextAction.
|
|
@@ -1426,6 +1538,10 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
1426
1538
|
kind: 'update',
|
|
1427
1539
|
...(pluginBackupDir ? { backupPath: pluginBackupDir } : {}),
|
|
1428
1540
|
});
|
|
1541
|
+
// The quarantined pre-update copies are discarded on success; no reset of
|
|
1542
|
+
// the variable is needed here — the return below cannot re-enter the
|
|
1543
|
+
// catch (CodeQL js/useless-assignment-to-local, PR #1580 review).
|
|
1544
|
+
cleanupQuarantined(reconciledQuarantine);
|
|
1429
1545
|
return {
|
|
1430
1546
|
success: true,
|
|
1431
1547
|
message: depsChanged
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type CliSmokeResult = {
|
|
2
|
+
ok: true;
|
|
3
|
+
version: string;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Spawn the pd CLI entry and require exit 0.
|
|
10
|
+
*
|
|
11
|
+
* Resolves `node` from PATH — the same resolution the installed bin/pd.cmd
|
|
12
|
+
* shim already depends on, so any environment that can run `pd` at all can
|
|
13
|
+
* run this probe. The entry path is boundary-checked to stay inside the
|
|
14
|
+
* pd-cli component dir (same guard shape as the legacy-rule preflight's
|
|
15
|
+
* subprocess target), and the subprocess is invoked with an argv array —
|
|
16
|
+
* never a shell string (ERR-045). Bounded stderr extract on failure (rc-8);
|
|
17
|
+
* never throws.
|
|
18
|
+
*/
|
|
19
|
+
export declare function runPostUpdateCliSmoke(pdCliDir: string): CliSmokeResult;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRI-711: post-apply CLI smoke for the full-update pipeline.
|
|
3
|
+
*
|
|
4
|
+
* The full update swaps component dists without running npm install, and
|
|
5
|
+
* resolution gaps of one class have now shipped three times — each discovered
|
|
6
|
+
* only as a bricked CLI at the next console launch (PRI-561 host-runtime
|
|
7
|
+
* links, the 2026-08-29 install-layout component gap, PRI-711 codex-adapter).
|
|
8
|
+
* Spawning the pd CLI once with `--version` loads its entire eager import
|
|
9
|
+
* graph (health-codex → @principles/codex-adapter →
|
|
10
|
+
* @principles/host-runtime → @principles/core) and exits without touching
|
|
11
|
+
* workspace state, so a gap anywhere in that graph surfaces before the update
|
|
12
|
+
* is recorded as a success.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync } from 'child_process';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
/**
|
|
18
|
+
* Spawn the pd CLI entry and require exit 0.
|
|
19
|
+
*
|
|
20
|
+
* Resolves `node` from PATH — the same resolution the installed bin/pd.cmd
|
|
21
|
+
* shim already depends on, so any environment that can run `pd` at all can
|
|
22
|
+
* run this probe. The entry path is boundary-checked to stay inside the
|
|
23
|
+
* pd-cli component dir (same guard shape as the legacy-rule preflight's
|
|
24
|
+
* subprocess target), and the subprocess is invoked with an argv array —
|
|
25
|
+
* never a shell string (ERR-045). Bounded stderr extract on failure (rc-8);
|
|
26
|
+
* never throws.
|
|
27
|
+
*/
|
|
28
|
+
export function runPostUpdateCliSmoke(pdCliDir) {
|
|
29
|
+
const resolvedRoot = path.resolve(pdCliDir);
|
|
30
|
+
const pdCliEntry = path.join(resolvedRoot, 'dist', 'index.js');
|
|
31
|
+
if (!pdCliEntry.startsWith(resolvedRoot + path.sep)) {
|
|
32
|
+
return { ok: false, error: `pd CLI entry ${pdCliEntry} is outside the pd-cli component dir ${resolvedRoot}` };
|
|
33
|
+
}
|
|
34
|
+
if (!fs.existsSync(pdCliEntry)) {
|
|
35
|
+
return { ok: false, error: `pd CLI entry not found at ${pdCliEntry}` };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const stdout = execFileSync('node', [pdCliEntry, '--version'], {
|
|
39
|
+
timeout: 60_000,
|
|
40
|
+
encoding: 'utf8',
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
43
|
+
});
|
|
44
|
+
const version = (stdout.trim().split(/\r?\n/, 1)[0] ?? '').trim();
|
|
45
|
+
return { ok: true, version };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// rc-1/rc-2: the child failure shape is unknown — guard every read.
|
|
49
|
+
// Node prints the error MESSAGE first and stack frames after, so the
|
|
50
|
+
// operator-facing extract is the bounded HEAD of stderr (a tail would
|
|
51
|
+
// carry only stack noise — observed in the PRI-711 test fixture).
|
|
52
|
+
let reason = error instanceof Error ? error.message : String(error);
|
|
53
|
+
if (typeof error === 'object' && error !== null && Object.hasOwn(error, 'stderr')) {
|
|
54
|
+
const { stderr } = error;
|
|
55
|
+
if (typeof stderr === 'string' && stderr.trim().length > 0)
|
|
56
|
+
reason = stderr;
|
|
57
|
+
}
|
|
58
|
+
return { ok: false, error: reason.trim().slice(0, 400) };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -8,6 +8,14 @@ export interface UpdateLayout {
|
|
|
8
8
|
pdCliDir: string;
|
|
9
9
|
installLayoutDir: string;
|
|
10
10
|
releaseManagerDir: string;
|
|
11
|
+
/**
|
|
12
|
+
* PRI-711: codex-adapter is a runtime-layout component pd-cli resolves
|
|
13
|
+
* eagerly. Undefined when the DEPLOYED @principles/install-layout is one
|
|
14
|
+
* generation old (this update runs inside the currently-running console,
|
|
15
|
+
* which resolves the layout helper installed by the PREVIOUS update) —
|
|
16
|
+
* consumers must degrade to skipping the adapter, never assume it.
|
|
17
|
+
*/
|
|
18
|
+
codexAdapterDir: string | undefined;
|
|
11
19
|
hosts: InstallHost[];
|
|
12
20
|
}
|
|
13
21
|
export declare function resolveUpdateLayout(): UpdateLayout | undefined;
|
|
@@ -46,6 +46,9 @@ export function resolveUpdateLayout() {
|
|
|
46
46
|
pdCliDir: paths.pdCliDir,
|
|
47
47
|
installLayoutDir: paths.installLayoutDir,
|
|
48
48
|
releaseManagerDir: paths.releaseManagerDir,
|
|
49
|
+
// rc-1: the deployed install-layout may predate the codexAdapterDir
|
|
50
|
+
// field — guard instead of trusting the shape.
|
|
51
|
+
codexAdapterDir: typeof paths.codexAdapterDir === 'string' ? paths.codexAdapterDir : undefined,
|
|
49
52
|
hosts: resolution.manifest?.hosts ?? [],
|
|
50
53
|
};
|
|
51
54
|
}
|
|
@@ -57,6 +60,7 @@ export function resolveUpdateLayout() {
|
|
|
57
60
|
pdCliDir: path.join(legacyPluginDir, 'pd-cli'),
|
|
58
61
|
installLayoutDir: path.join(legacyPluginDir, 'install-layout'),
|
|
59
62
|
releaseManagerDir: path.join(legacyPluginDir, 'release-manager'),
|
|
63
|
+
codexAdapterDir: path.join(legacyPluginDir, 'codex-adapter'),
|
|
60
64
|
hosts: ['openclaw'],
|
|
61
65
|
};
|
|
62
66
|
}
|
|
@@ -1355,8 +1355,8 @@
|
|
|
1355
1355
|
"filesUpdated": "Updated {{count}} files",
|
|
1356
1356
|
"networkErrorHint": "Please check your network connection and try again.",
|
|
1357
1357
|
"retry": "Retry",
|
|
1358
|
-
"codexWarning": "Codex Host detected.
|
|
1359
|
-
"partialUpdateHint": "Plugin code updated.
|
|
1358
|
+
"codexWarning": "Codex Host detected. The full update also refreshes the Codex adapter (@principles/codex-adapter) in the runtime layout. To re-register Codex hooks or repair the Codex host install, run the installer.",
|
|
1359
|
+
"partialUpdateHint": "Plugin code updated. If components ever look out of sync, refresh the full runtime with: npx create-principles-disciple",
|
|
1360
1360
|
"applyFullUpdate": "Full Update",
|
|
1361
1361
|
"fullUpdating": "Full update in progress…",
|
|
1362
1362
|
"fullUpdateSuccess": "Full update completed.",
|
|
@@ -1355,8 +1355,8 @@
|
|
|
1355
1355
|
"filesUpdated": "更新了 {{count}} 个文件",
|
|
1356
1356
|
"networkErrorHint": "请检查网络连接后重试。",
|
|
1357
1357
|
"retry": "重试",
|
|
1358
|
-
"codexWarning": "检测到 Codex Host
|
|
1359
|
-
"partialUpdateHint": "
|
|
1358
|
+
"codexWarning": "检测到 Codex Host 已安装。全量更新现在会同步刷新运行时布局中的 Codex 适配器(@principles/codex-adapter)。如需重新注册 Codex hooks 或修复 Codex 宿主安装,请运行安装器。",
|
|
1359
|
+
"partialUpdateHint": "插件代码已更新。如发现组件版本不同步,请运行以下命令刷新完整运行时:npx create-principles-disciple",
|
|
1360
1360
|
"applyFullUpdate": "全量更新",
|
|
1361
1361
|
"fullUpdating": "全量更新中…",
|
|
1362
1362
|
"fullUpdateSuccess": "全量更新完成。",
|