@lesliechan721/aw 0.1.0-beta.11 → 0.1.0-beta.13
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/dist/authoring/scripts/validate-plugin.mjs +33 -42
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/src/commands/operation-planner.js +11 -1
- package/dist/src/commands/operation-planner.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +12 -0
- package/dist/src/commands/rebuild-command.js +72 -17
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +1 -0
- package/dist/src/commands/rebuild-integration.js +323 -105
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/status-v6.js +8 -8
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/index.js +52 -14
- package/dist/src/index.js.map +1 -1
- package/dist/src/portable/materializer.js +5 -3
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/console-app-v4.js +1 -21
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +165 -48
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +4 -4
- package/dist/src/tui/controller.js +19 -7
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostics.js +6 -6
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/model.js +15 -10
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +66 -19
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/utils/artifact-plan.d.ts +2 -2
- package/dist/src/utils/artifact-plan.js +8 -6
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.js +96 -77
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +5 -0
- package/dist/src/utils/artifact-request.js +40 -0
- package/dist/src/utils/artifact-request.js.map +1 -1
- package/dist/src/utils/codex-shared-native.d.ts +4 -0
- package/dist/src/utils/codex-shared-native.js +13 -0
- package/dist/src/utils/codex-shared-native.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +14 -1
- package/dist/src/utils/local-marketplace.js +242 -14
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +33 -0
- package/dist/src/utils/plugin-config-impact.js +298 -0
- package/dist/src/utils/plugin-config-impact.js.map +1 -0
- package/dist/src/utils/plugin-config-write.d.ts +22 -2
- package/dist/src/utils/plugin-config-write.js +368 -104
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +10 -0
- package/dist/src/utils/plugin-config.js +39 -22
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.js +47 -39
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/project-boundary.js +22 -9
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/sensitive-content.d.ts +3 -0
- package/dist/src/utils/sensitive-content.js +28 -7
- package/dist/src/utils/sensitive-content.js.map +1 -1
- package/package.json +3 -3
|
@@ -6,23 +6,23 @@ import { generateInstructionArtifacts, renderCodexManagedInstructionBlock, } fro
|
|
|
6
6
|
import { generateCodexPermissionArtifacts, mergeClaudePermissions, } from '../generators/permissions.js';
|
|
7
7
|
import { buildGeneratedName } from '../generators/shared.js';
|
|
8
8
|
import { applyConditionalCodexConfigTransactionFile, applyCodexConfigTransactionFile, composeManagedDeveloperInstructionsBlock, readCodexConfigFile, } from '../utils/codex-config.js';
|
|
9
|
-
import { canonicalizePathThroughExistingAncestor } from '../utils/state-paths.js';
|
|
9
|
+
import { canonicalizePathThroughExistingAncestor, resolveClaudeProjectKey, resolveCodexProjectKey, } from '../utils/state-paths.js';
|
|
10
10
|
import { createCodexSkillExpectedTree, createCodexSkillStagingTreeContentDigest, } from '../utils/codex-skill-staging.js';
|
|
11
|
-
import { createDynamicSkillExpectedTree, createDynamicSkillTreeContentDigest, readDynamicSkillTree, validateSkillDirectory, writeDynamicSkillTree, } from '../utils/dynamic-skill-tree.js';
|
|
11
|
+
import { AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH, createDynamicSkillExpectedTree, createDynamicSkillTreeContentDigest, readDynamicSkillTree, validateSkillDirectory, writeDynamicSkillTree, } from '../utils/dynamic-skill-tree.js';
|
|
12
12
|
import { parseCodexSharedHooksFile, serializeTrackedHookGroupIdentity, } from '../utils/hooks-contract.js';
|
|
13
13
|
import { buildCodexMarketplaceAddInvocation, buildCodexMarketplaceRemoveInvocation, buildCodexPluginAddInvocation, buildCodexPluginRemoveInvocation, CodexProjectConfigNotEffectiveError, runNativeCliChecked, SYSTEM_CODEX_MANAGER, } from '../utils/native-cli.js';
|
|
14
14
|
import { OWNED_TEXT_HEADER_PREFIX, createSharedOwnerToken, createSkillOwnerMarkerContent, parseSharedOwnerToken, parseOwnedTextHeader, readSkillOwnerMarker, renderOwnedTextFile, inspectOwnedComponents, writeOwnedTextFile, } from '../utils/ownership.js';
|
|
15
15
|
import { resolveArtifactRenderConfig, resolveArtifactRendererTarget, runArtifactRenderer, } from '../utils/artifact-renderer.js';
|
|
16
16
|
import { createArtifactConfigDigest, validateArtifactPlan, } from '../utils/artifact-plan.js';
|
|
17
|
-
import {
|
|
18
|
-
import { createAwPluginRequirementsReceipt, } from '../utils/manifest.js';
|
|
17
|
+
import { resolveAwDeliveredArtifactRequestBatches } from '../utils/artifact-request.js';
|
|
18
|
+
import { createAwPluginRequirementsReceipt, parseAwPluginRequirementsReceipt, tryParseStrictSemVer, } from '../utils/manifest.js';
|
|
19
19
|
import { resolvePlatformEntries } from '../utils/platform-entries.js';
|
|
20
20
|
import { resolveAwHome, resolveClaudePaths, resolveCodexPaths, resolveRepoRoot, getCodexHome, } from '../utils/paths.js';
|
|
21
21
|
import { assertNoSensitiveConfigInFiles, collectSensitivePluginConfigValues, runPreApplyScript, } from '../utils/plugin-scripts.js';
|
|
22
22
|
import { findSensitiveTokenPath } from '../utils/sensitive-content.js';
|
|
23
23
|
import { atomicRemoveConfigFile, atomicReplaceConfigFile, withConfigFileMutation, } from '../utils/plugin-config.js';
|
|
24
24
|
import { readCatalogSourcesConfigSnapshot } from '../utils/catalog-sources.js';
|
|
25
|
-
import { getNamespacedMarketplaceName, projectMarketplaceManifestForPlatform, readStrictNamespacedMarketplaceRoot, replayNamespacedCodexSkillOverlays, } from '../utils/local-marketplace.js';
|
|
25
|
+
import { getNamespacedMarketplaceName, MarketplaceRootValidationError, projectMarketplaceManifestForPlatform, readStrictNamespacedMarketplaceRoot, replayNamespacedCodexNativeRenderProjections, replayNamespacedCodexSkillOverlays, } from '../utils/local-marketplace.js';
|
|
26
26
|
import { commitMarketplaceGenerationBackup } from '../utils/orphan-marketplace-cleanup.js';
|
|
27
27
|
import { readClaudeSettings, writeClaudeSettings } from '../adapters/helpers.js';
|
|
28
28
|
import { analyzeCodexSurface } from '../utils/codex-surface-analysis.js';
|
|
@@ -39,6 +39,26 @@ function requirementsReceipt(plugin) {
|
|
|
39
39
|
const receipt = createAwPluginRequirementsReceipt({ name: plugin.manifest.name, version: plugin.manifest.version }, plugin.manifest.requires?.aw.capabilities ?? []);
|
|
40
40
|
return Buffer.from(`${JSON.stringify(receipt, null, 2)}\n`, 'utf8');
|
|
41
41
|
}
|
|
42
|
+
function withoutVerifiedRequirementsReceipt(plugin, files) {
|
|
43
|
+
const receipt = files[AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH];
|
|
44
|
+
if (!receipt)
|
|
45
|
+
return { ...files };
|
|
46
|
+
let actual;
|
|
47
|
+
let expected;
|
|
48
|
+
try {
|
|
49
|
+
actual = parseAwPluginRequirementsReceipt(JSON.parse(receipt.toString('utf8')));
|
|
50
|
+
expected = parseAwPluginRequirementsReceipt(JSON.parse(requirementsReceipt(plugin).toString('utf8')));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { ...files };
|
|
54
|
+
}
|
|
55
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
56
|
+
return { ...files };
|
|
57
|
+
}
|
|
58
|
+
const output = { ...files };
|
|
59
|
+
delete output[AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH];
|
|
60
|
+
return output;
|
|
61
|
+
}
|
|
42
62
|
export class CodexActivationPostcheckError extends Error {
|
|
43
63
|
code = 'codexActivationPostcheckFailed';
|
|
44
64
|
codexPluginIds;
|
|
@@ -924,25 +944,29 @@ function renderCodexRole(options) {
|
|
|
924
944
|
].join('\n');
|
|
925
945
|
}
|
|
926
946
|
async function resolveDynamicArtifacts(options) {
|
|
927
|
-
const
|
|
947
|
+
const requestBatches = resolveAwDeliveredArtifactRequestBatches({
|
|
928
948
|
manifest: options.plugin.manifest,
|
|
929
949
|
platform: options.platform,
|
|
930
950
|
scope: options.scope,
|
|
931
951
|
});
|
|
932
|
-
if (
|
|
952
|
+
if (requestBatches.length === 0) {
|
|
933
953
|
return { artifacts: [], sourceDirs: () => [], sensitiveValues: [] };
|
|
934
954
|
}
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
955
|
+
const previousArtifacts = [...options.previousArtifacts];
|
|
956
|
+
const renders = [];
|
|
957
|
+
for (const requestedArtifactIds of requestBatches) {
|
|
958
|
+
renders.push(await runArtifactRenderer({
|
|
959
|
+
event: options.event,
|
|
960
|
+
plugin: options.plugin,
|
|
961
|
+
platform: options.platform,
|
|
962
|
+
scope: options.scope,
|
|
963
|
+
cwd: options.cwd,
|
|
964
|
+
commandEnv: options.env,
|
|
965
|
+
previousArtifacts,
|
|
966
|
+
requestedArtifactIds,
|
|
967
|
+
scriptTimeoutMs: options.scriptTimeoutMs,
|
|
968
|
+
}));
|
|
969
|
+
}
|
|
946
970
|
const config = await resolveArtifactRenderConfig({
|
|
947
971
|
sourceId: options.plugin.sourceId,
|
|
948
972
|
sourceIdentity: options.plugin.sourceIdentity,
|
|
@@ -953,19 +977,26 @@ async function resolveDynamicArtifacts(options) {
|
|
|
953
977
|
cwd: options.cwd,
|
|
954
978
|
env: options.env,
|
|
955
979
|
});
|
|
956
|
-
const artifacts =
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
980
|
+
const artifacts = [];
|
|
981
|
+
const sourceDirsByArtifact = new Map();
|
|
982
|
+
for (const render of renders) {
|
|
983
|
+
artifacts.push(...validateArtifactPlan({
|
|
984
|
+
manifest: options.plugin.manifest,
|
|
985
|
+
plan: render.plan,
|
|
986
|
+
platform: options.platform,
|
|
987
|
+
scope: options.scope,
|
|
988
|
+
requestedArtifactIds: render.context.requestedArtifactIds,
|
|
989
|
+
renderConfig: config,
|
|
990
|
+
rendererDigest: render.target.rendererDigest,
|
|
991
|
+
manifestDigest: render.target.manifestDigest,
|
|
992
|
+
}).artifacts);
|
|
993
|
+
for (const source of render.context.artifactSources) {
|
|
994
|
+
sourceDirsByArtifact.set(`${source.id}\0${source.target}`, source.sourceDirs);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
966
997
|
return {
|
|
967
998
|
artifacts,
|
|
968
|
-
sourceDirs: (artifact) =>
|
|
999
|
+
sourceDirs: (artifact) => sourceDirsByArtifact.get(`${artifact.id}\0${artifact.target}`) ?? [],
|
|
969
1000
|
sensitiveValues: collectSensitivePluginConfigValues(options.plugin.manifest, config.values),
|
|
970
1001
|
};
|
|
971
1002
|
}
|
|
@@ -1140,7 +1171,7 @@ async function buildClaudeWrites(options) {
|
|
|
1140
1171
|
requirementsReceipt: requirementsReceipt(options.plugin),
|
|
1141
1172
|
sourceRequirementsReceiptPolicy: 'allow',
|
|
1142
1173
|
});
|
|
1143
|
-
assertNoSensitiveConfigInFiles(tree.files, dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
1174
|
+
assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
1144
1175
|
writes.push(dynamicSkillWrite({
|
|
1145
1176
|
...options,
|
|
1146
1177
|
platform: 'claude',
|
|
@@ -1217,7 +1248,7 @@ async function buildCodexWrites(options) {
|
|
|
1217
1248
|
requirementsReceipt: requirementsReceipt(options.plugin),
|
|
1218
1249
|
sourceRequirementsReceiptPolicy: 'allow',
|
|
1219
1250
|
});
|
|
1220
|
-
assertNoSensitiveConfigInFiles(tree.files, dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
1251
|
+
assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
1221
1252
|
writes.push(dynamicSkillWrite({
|
|
1222
1253
|
...options,
|
|
1223
1254
|
platform: 'codex',
|
|
@@ -1782,6 +1813,8 @@ async function codexPayloadPathSafe(payloadRoot, allowedRoot = payloadRoot) {
|
|
|
1782
1813
|
}
|
|
1783
1814
|
return true;
|
|
1784
1815
|
}
|
|
1816
|
+
class CodexNativeProjectionUnsafeError extends Error {
|
|
1817
|
+
}
|
|
1785
1818
|
async function resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot) {
|
|
1786
1819
|
const normalizedSeparators = rawSkillsRoot.replace(/\\/gu, '/');
|
|
1787
1820
|
const relativeRoot = path.posix.normalize(normalizedSeparators.replace(/^\.\//u, ''));
|
|
@@ -1791,11 +1824,11 @@ async function resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot) {
|
|
|
1791
1824
|
|| relativeRoot.startsWith('../')
|
|
1792
1825
|
|| path.posix.isAbsolute(normalizedSeparators)
|
|
1793
1826
|
|| path.win32.isAbsolute(rawSkillsRoot)) {
|
|
1794
|
-
throw new
|
|
1827
|
+
throw new CodexNativeProjectionUnsafeError('codexNativeSkillsRootInvalid: Codex native Skills root is invalid.');
|
|
1795
1828
|
}
|
|
1796
1829
|
const root = path.join(payloadRoot, ...relativeRoot.split('/'));
|
|
1797
1830
|
if (!await codexReadPathSafe(payloadRoot, root)) {
|
|
1798
|
-
throw new
|
|
1831
|
+
throw new CodexNativeProjectionUnsafeError('codexNativeSkillsRootInvalid: Codex native Skills root is unsafe.');
|
|
1799
1832
|
}
|
|
1800
1833
|
return { relativeRoot, root };
|
|
1801
1834
|
}
|
|
@@ -1810,16 +1843,45 @@ async function readCodexNativeRenderReceiptEvidence(payloadRoot) {
|
|
|
1810
1843
|
if (!stats)
|
|
1811
1844
|
return { state: 'absent' };
|
|
1812
1845
|
if (!stats.isFile() || stats.isSymbolicLink())
|
|
1846
|
+
return { state: 'unsafe' };
|
|
1847
|
+
let content;
|
|
1848
|
+
try {
|
|
1849
|
+
content = await fs.readFile(receiptPath, 'utf8');
|
|
1850
|
+
}
|
|
1851
|
+
catch {
|
|
1813
1852
|
return { state: 'unreadable' };
|
|
1853
|
+
}
|
|
1814
1854
|
try {
|
|
1815
|
-
return {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
};
|
|
1855
|
+
return { state: 'present', receipt: parseCodexNativeRenderReceipt(content) };
|
|
1856
|
+
}
|
|
1857
|
+
catch {
|
|
1858
|
+
return { state: 'invalid' };
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
function codexNativeRenderEvidenceIssue(reason, diagnostic) {
|
|
1862
|
+
return { state: 'issue', reason, diagnostic };
|
|
1863
|
+
}
|
|
1864
|
+
async function readCodexNativeProjectionManifest(manifestPath) {
|
|
1865
|
+
let stats;
|
|
1866
|
+
try {
|
|
1867
|
+
stats = await lstatIfPresent(manifestPath);
|
|
1819
1868
|
}
|
|
1820
1869
|
catch {
|
|
1821
1870
|
return { state: 'unreadable' };
|
|
1822
1871
|
}
|
|
1872
|
+
if (!stats)
|
|
1873
|
+
return { state: 'missing' };
|
|
1874
|
+
if (!stats.isFile() || stats.isSymbolicLink())
|
|
1875
|
+
return { state: 'unsafe' };
|
|
1876
|
+
try {
|
|
1877
|
+
const value = await fs.readJson(manifestPath);
|
|
1878
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
1879
|
+
? { state: 'present', value: value }
|
|
1880
|
+
: { state: 'invalid' };
|
|
1881
|
+
}
|
|
1882
|
+
catch (error) {
|
|
1883
|
+
return { state: error instanceof SyntaxError ? 'invalid' : 'unreadable' };
|
|
1884
|
+
}
|
|
1823
1885
|
}
|
|
1824
1886
|
async function resolveCodexNativeRenderExpectedEvidence(options) {
|
|
1825
1887
|
const logicalPluginId = options.plugin.pluginId
|
|
@@ -1830,54 +1892,101 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
|
|
|
1830
1892
|
const marketplace = await readStrictNamespacedMarketplaceRoot({
|
|
1831
1893
|
trustedMarketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces'),
|
|
1832
1894
|
rootDir: options.marketplaceRoot,
|
|
1833
|
-
sourceId: options.plugin.sourceId,
|
|
1834
|
-
marketplaceName: options.marketplaceName,
|
|
1835
1895
|
});
|
|
1836
1896
|
if (!marketplace) {
|
|
1837
|
-
|
|
1897
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
|
|
1838
1898
|
}
|
|
1839
|
-
if (marketplace.config.
|
|
1840
|
-
|
|
1899
|
+
if (marketplace.config.sourceId !== options.plugin.sourceId
|
|
1900
|
+
|| marketplace.config.sourceIdentity !== options.plugin.sourceIdentity
|
|
1901
|
+
|| marketplace.config.marketplaceName !== options.marketplaceName) {
|
|
1902
|
+
return codexNativeRenderEvidenceIssue('foreign', 'projectionReceiptIdentityMismatch');
|
|
1841
1903
|
}
|
|
1842
1904
|
const payloadRoot = path.join(options.marketplaceRoot, 'plugins', options.plugin.manifest.name);
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
|
|
1847
|
-
if (receiptEvidence.state !== 'present') {
|
|
1848
|
-
throw new Error('Codex native render canonical marketplace receipt is missing or unreadable.');
|
|
1905
|
+
const payloadStats = await lstatIfPresent(payloadRoot);
|
|
1906
|
+
if (!payloadStats) {
|
|
1907
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
|
|
1849
1908
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|| receipt.strategy !== 'shared-user'
|
|
1853
|
-
|| receipt.sourceIdentity !== marketplace.config.sourceIdentity) {
|
|
1854
|
-
throw new Error('Codex native render canonical marketplace receipt identity is invalid.');
|
|
1909
|
+
if (!payloadStats.isDirectory() || payloadStats.isSymbolicLink()) {
|
|
1910
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1855
1911
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
throw new Error('Codex native render canonical marketplace payload does not match its receipt.');
|
|
1859
|
-
}
|
|
1860
|
-
const sourceManifestPath = path.join(payloadRoot, '.agents-plugin', 'plugin.json');
|
|
1861
|
-
const sourceManifestStats = await lstatIfPresent(sourceManifestPath);
|
|
1862
|
-
const sourceManifest = sourceManifestStats?.isFile() && !sourceManifestStats.isSymbolicLink()
|
|
1863
|
-
? await fs.readJson(sourceManifestPath).catch(() => null)
|
|
1864
|
-
: null;
|
|
1865
|
-
if (sourceManifest?.name !== options.plugin.manifest.name
|
|
1866
|
-
|| sourceManifest.version !== receipt.pluginVersion) {
|
|
1867
|
-
throw new Error('Codex native render canonical marketplace plugin identity is invalid.');
|
|
1912
|
+
if (!await codexPayloadPathSafe(payloadRoot, options.marketplaceRoot)) {
|
|
1913
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1868
1914
|
}
|
|
1869
1915
|
const codexManifestPath = path.join(payloadRoot, '.codex-plugin', 'plugin.json');
|
|
1870
|
-
const
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
if (
|
|
1875
|
-
|
|
1916
|
+
const [sourceManifestEvidence, codexManifestEvidence] = await Promise.all([
|
|
1917
|
+
readCodexNativeProjectionManifest(path.join(payloadRoot, '.agents-plugin', 'plugin.json')),
|
|
1918
|
+
readCodexNativeProjectionManifest(codexManifestPath),
|
|
1919
|
+
]);
|
|
1920
|
+
if (sourceManifestEvidence.state === 'unsafe'
|
|
1921
|
+
|| codexManifestEvidence.state === 'unsafe') {
|
|
1922
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1923
|
+
}
|
|
1924
|
+
if ((sourceManifestEvidence.state === 'present'
|
|
1925
|
+
&& sourceManifestEvidence.value.name !== options.plugin.manifest.name)
|
|
1926
|
+
|| (codexManifestEvidence.state === 'present'
|
|
1927
|
+
&& codexManifestEvidence.value.name !== options.plugin.manifest.name)
|
|
1928
|
+
|| (sourceManifestEvidence.state === 'present'
|
|
1929
|
+
&& codexManifestEvidence.state === 'present'
|
|
1930
|
+
&& sourceManifestEvidence.value.version !== codexManifestEvidence.value.version)) {
|
|
1931
|
+
return codexNativeRenderEvidenceIssue('foreign', 'manifestIdentityMismatch');
|
|
1932
|
+
}
|
|
1933
|
+
if (sourceManifestEvidence.state === 'unreadable'
|
|
1934
|
+
|| codexManifestEvidence.state === 'unreadable') {
|
|
1935
|
+
return codexNativeRenderEvidenceIssue('unreadable', 'manifestUnreadable');
|
|
1936
|
+
}
|
|
1937
|
+
if (sourceManifestEvidence.state === 'missing'
|
|
1938
|
+
|| codexManifestEvidence.state === 'missing') {
|
|
1939
|
+
return codexNativeRenderEvidenceIssue('damaged', 'manifestMissing');
|
|
1940
|
+
}
|
|
1941
|
+
if (sourceManifestEvidence.state === 'invalid'
|
|
1942
|
+
|| codexManifestEvidence.state === 'invalid') {
|
|
1943
|
+
return codexNativeRenderEvidenceIssue('damaged', 'manifestUnreadable');
|
|
1944
|
+
}
|
|
1945
|
+
if (sourceManifestEvidence.state !== 'present'
|
|
1946
|
+
|| codexManifestEvidence.state !== 'present') {
|
|
1947
|
+
return codexNativeRenderEvidenceIssue('damaged', 'manifestUnreadable');
|
|
1948
|
+
}
|
|
1949
|
+
const sourceManifest = sourceManifestEvidence.value;
|
|
1950
|
+
const codexManifest = codexManifestEvidence.value;
|
|
1951
|
+
if (typeof sourceManifest.version !== 'string'
|
|
1952
|
+
|| !tryParseStrictSemVer(sourceManifest.version)) {
|
|
1953
|
+
return codexNativeRenderEvidenceIssue('damaged', 'manifestVersionInvalid');
|
|
1876
1954
|
}
|
|
1877
1955
|
const rawSkillsRoot = typeof codexManifest.skills === 'string' && codexManifest.skills.trim() !== ''
|
|
1878
1956
|
? codexManifest.skills
|
|
1879
1957
|
: './codex/skills';
|
|
1880
1958
|
const { relativeRoot: skillsRelativeRoot } = await resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot);
|
|
1959
|
+
const skillsRoot = path.join(payloadRoot, ...skillsRelativeRoot.split('/'));
|
|
1960
|
+
const skillsRootStats = await lstatIfPresent(skillsRoot);
|
|
1961
|
+
if (skillsRootStats && (!skillsRootStats.isDirectory() || skillsRootStats.isSymbolicLink())) {
|
|
1962
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1963
|
+
}
|
|
1964
|
+
const targets = codexNativeRenderTargets(options.plugin);
|
|
1965
|
+
for (const target of targets) {
|
|
1966
|
+
if (await lstatIfPresent(path.join(skillsRoot, target.skillKey, '.agent-workflows-owner.json'))) {
|
|
1967
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
|
|
1971
|
+
if (receiptEvidence.state === 'absent') {
|
|
1972
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
|
|
1973
|
+
}
|
|
1974
|
+
if (receiptEvidence.state === 'unsafe') {
|
|
1975
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1976
|
+
}
|
|
1977
|
+
if (receiptEvidence.state === 'invalid') {
|
|
1978
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptUnreadable');
|
|
1979
|
+
}
|
|
1980
|
+
if (receiptEvidence.state === 'unreadable') {
|
|
1981
|
+
return codexNativeRenderEvidenceIssue('unreadable', 'projectionReceiptUnreadable');
|
|
1982
|
+
}
|
|
1983
|
+
const receipt = receiptEvidence.receipt;
|
|
1984
|
+
if (receipt.logicalPluginId !== logicalPluginId
|
|
1985
|
+
|| receipt.strategy !== 'shared-user'
|
|
1986
|
+
|| receipt.sourceIdentity !== marketplace.config.sourceIdentity
|
|
1987
|
+
|| receipt.pluginVersion !== sourceManifest.version) {
|
|
1988
|
+
return codexNativeRenderEvidenceIssue('foreign', 'projectionReceiptIdentityMismatch');
|
|
1989
|
+
}
|
|
1881
1990
|
const emptyArtifactDigest = createCodexSkillStagingTreeContentDigest({
|
|
1882
1991
|
skillKey: 'empty',
|
|
1883
1992
|
files: {},
|
|
@@ -1885,22 +1994,37 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
|
|
|
1885
1994
|
for (const receiptTarget of receipt.targets) {
|
|
1886
1995
|
const skillRoot = path.join(payloadRoot, ...skillsRelativeRoot.split('/'), receiptTarget.skillKey);
|
|
1887
1996
|
if (await lstatIfPresent(path.join(skillRoot, '.agent-workflows-owner.json'))) {
|
|
1888
|
-
|
|
1997
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
1998
|
+
}
|
|
1999
|
+
const skillStats = await lstatIfPresent(skillRoot);
|
|
2000
|
+
if (skillStats && (!skillStats.isDirectory() || skillStats.isSymbolicLink())) {
|
|
2001
|
+
return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
|
|
2002
|
+
}
|
|
2003
|
+
let tree;
|
|
2004
|
+
try {
|
|
2005
|
+
tree = skillStats ? await readDynamicSkillTree(skillRoot, 'codex.skill') : null;
|
|
2006
|
+
}
|
|
2007
|
+
catch (error) {
|
|
2008
|
+
return codexNativeRenderEvidenceIssue(error instanceof Error && typeof error.code !== 'string'
|
|
2009
|
+
? 'damaged'
|
|
2010
|
+
: 'unreadable', 'projectionPayloadDigestMismatch');
|
|
1889
2011
|
}
|
|
1890
|
-
const tree = await readDynamicSkillTree(skillRoot, 'codex.skill');
|
|
1891
2012
|
const artifactDigest = tree
|
|
1892
2013
|
? createCodexSkillStagingTreeContentDigest(tree)
|
|
1893
2014
|
: emptyArtifactDigest;
|
|
1894
2015
|
if (artifactDigest !== receiptTarget.artifactDigest) {
|
|
1895
|
-
|
|
2016
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionPayloadDigestMismatch');
|
|
1896
2017
|
}
|
|
1897
2018
|
}
|
|
1898
|
-
const
|
|
2019
|
+
const payloadDigest = await calculateCodexNativeRenderPayloadDigest(payloadRoot);
|
|
2020
|
+
if (payloadDigest !== receipt.payloadDigest) {
|
|
2021
|
+
return codexNativeRenderEvidenceIssue('damaged', 'projectionPayloadDigestMismatch');
|
|
2022
|
+
}
|
|
1899
2023
|
const receiptTargets = new Map(receipt.targets.map((target) => [target.id, target]));
|
|
1900
2024
|
if (targets.length === 0
|
|
1901
2025
|
|| targets.length !== receipt.targets.length
|
|
1902
2026
|
|| targets.some((target) => receiptTargets.get(target.id)?.skillKey !== target.skillKey)) {
|
|
1903
|
-
return { state: 'present', payloadDigest, sourceProjection: 'drifted' };
|
|
2027
|
+
return { state: 'present', payloadDigest, receipt, sourceProjection: 'drifted' };
|
|
1904
2028
|
}
|
|
1905
2029
|
const renderer = await resolveArtifactRendererTarget(options.plugin);
|
|
1906
2030
|
if (renderer.noOp || !renderer.rendererDigest) {
|
|
@@ -1934,7 +2058,7 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
|
|
|
1934
2058
|
target: 'codex.skill',
|
|
1935
2059
|
refreshKeys: target.refreshKeys,
|
|
1936
2060
|
},
|
|
1937
|
-
|
|
2061
|
+
renderConfig: config,
|
|
1938
2062
|
digestSalt: renderer.rendererDigest,
|
|
1939
2063
|
}),
|
|
1940
2064
|
artifactDigest: receiptTargets.get(target.id).artifactDigest,
|
|
@@ -1942,6 +2066,7 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
|
|
|
1942
2066
|
});
|
|
1943
2067
|
return {
|
|
1944
2068
|
state: 'present',
|
|
2069
|
+
receipt,
|
|
1945
2070
|
projection,
|
|
1946
2071
|
payloadDigest,
|
|
1947
2072
|
sourceProjection: codexNativeRenderProjectionsEqual(receipt, projection)
|
|
@@ -1966,11 +2091,31 @@ async function assertNoSensitiveValuesInCanonicalCodexNativePayload(options) {
|
|
|
1966
2091
|
throw new Error('Codex native sensitive scan canonical marketplace path is unsafe.');
|
|
1967
2092
|
}
|
|
1968
2093
|
const payloadRoot = path.join(options.marketplaceRoot, 'plugins', options.plugin.manifest.name);
|
|
2094
|
+
if (!await codexPayloadPathSafe(payloadRoot, options.marketplaceRoot)) {
|
|
2095
|
+
throw new Error('Codex native sensitive scan payload path is unsafe.');
|
|
2096
|
+
}
|
|
2097
|
+
const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
|
|
2098
|
+
if (receiptEvidence.state === 'unsafe') {
|
|
2099
|
+
throw new Error('codexNativeRenderReceiptInvalid: Codex native render receipt path is unsafe.');
|
|
2100
|
+
}
|
|
2101
|
+
let verifiedRenderReceipt;
|
|
2102
|
+
if (receiptEvidence.state === 'present') {
|
|
2103
|
+
const expected = await resolveCodexNativeRenderExpectedEvidence({
|
|
2104
|
+
plugin: options.plugin,
|
|
2105
|
+
marketplaceRoot: options.marketplaceRoot,
|
|
2106
|
+
marketplaceName: options.marketplaceName,
|
|
2107
|
+
env: options.env,
|
|
2108
|
+
}).catch(() => undefined);
|
|
2109
|
+
if (expected?.state === 'present' && expected.sourceProjection === 'current') {
|
|
2110
|
+
verifiedRenderReceipt = expected.receipt;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
1969
2113
|
await assertNoSensitiveValuesInCodexNativePayload({
|
|
1970
2114
|
plugin: options.plugin,
|
|
1971
2115
|
payloadRoot,
|
|
1972
2116
|
allowedRoot: options.marketplaceRoot,
|
|
1973
2117
|
sensitiveValues: options.sensitiveValues,
|
|
2118
|
+
verifiedRenderReceipt,
|
|
1974
2119
|
});
|
|
1975
2120
|
}
|
|
1976
2121
|
function observedCodexNativeState(observation, operation) {
|
|
@@ -2053,7 +2198,7 @@ export async function inspectCodexSharedNativeLive(options) {
|
|
|
2053
2198
|
});
|
|
2054
2199
|
let expectedPayloadDigest = dynamicProjectionExpected ? undefined : options.expectedPayloadDigest;
|
|
2055
2200
|
let sourceProjection;
|
|
2056
|
-
let
|
|
2201
|
+
let sourceProjectionIssue;
|
|
2057
2202
|
const installed = pluginList.observation.state === 'observed'
|
|
2058
2203
|
&& pluginList.observation.value === 'installed';
|
|
2059
2204
|
if (installed && dynamicProjectionExpected && options.nativeRenderPlugin) {
|
|
@@ -2064,32 +2209,48 @@ export async function inspectCodexSharedNativeLive(options) {
|
|
|
2064
2209
|
marketplaceName,
|
|
2065
2210
|
env: options.env,
|
|
2066
2211
|
});
|
|
2067
|
-
if (expected.state === '
|
|
2068
|
-
|
|
2212
|
+
if (expected.state === 'issue') {
|
|
2213
|
+
sourceProjectionIssue = expected;
|
|
2069
2214
|
}
|
|
2070
2215
|
else {
|
|
2071
2216
|
expectedPayloadDigest = expected.payloadDigest;
|
|
2072
2217
|
sourceProjection = expected.sourceProjection;
|
|
2073
2218
|
}
|
|
2074
2219
|
}
|
|
2075
|
-
catch {
|
|
2076
|
-
|
|
2220
|
+
catch (error) {
|
|
2221
|
+
if (error instanceof MarketplaceRootValidationError) {
|
|
2222
|
+
sourceProjectionIssue = {
|
|
2223
|
+
reason: error.reason === 'unsafe'
|
|
2224
|
+
? 'foreign'
|
|
2225
|
+
: error.reason === 'invalid' ? 'damaged' : 'unreadable',
|
|
2226
|
+
diagnostic: error.reason === 'unsafe'
|
|
2227
|
+
? 'payloadPathUnsafe'
|
|
2228
|
+
: 'projectionReceiptUnreadable',
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
sourceProjectionIssue = {
|
|
2233
|
+
reason: error instanceof CodexNativeProjectionUnsafeError ? 'foreign' : 'unreadable',
|
|
2234
|
+
diagnostic: error instanceof CodexNativeProjectionUnsafeError
|
|
2235
|
+
? 'payloadPathUnsafe'
|
|
2236
|
+
: 'projectionReceiptUnreadable',
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2077
2239
|
}
|
|
2078
2240
|
}
|
|
2079
|
-
const sharedNative =
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
});
|
|
2241
|
+
const sharedNative = inspectCodexSharedNative({
|
|
2242
|
+
pluginName: options.pluginName,
|
|
2243
|
+
pluginId,
|
|
2244
|
+
marketplaceName,
|
|
2245
|
+
canonicalMarketplaceRoot: marketplaceRoot,
|
|
2246
|
+
desiredVersion: options.desiredVersion,
|
|
2247
|
+
expectedPayloadDigest,
|
|
2248
|
+
marketplaceRegistration: config.snapshot.marketplaces[marketplaceName],
|
|
2249
|
+
marketplacePathSafe,
|
|
2250
|
+
observation: pluginList.observation,
|
|
2251
|
+
...(sourceProjection ? { sourceProjection } : {}),
|
|
2252
|
+
...(sourceProjectionIssue ? { sourceProjectionIssue } : {}),
|
|
2253
|
+
});
|
|
2093
2254
|
return {
|
|
2094
2255
|
sharedNative,
|
|
2095
2256
|
...(expectedPayloadDigest ? { expectedPayloadDigest } : {}),
|
|
@@ -2522,7 +2683,7 @@ async function assertNoSensitiveValuesInCodexNativePayload(options) {
|
|
|
2522
2683
|
const tree = await readDynamicSkillTree(skillRoot, 'codex.skill');
|
|
2523
2684
|
if (!tree)
|
|
2524
2685
|
continue;
|
|
2525
|
-
const leakedPath = findSensitiveTokenPath(tree.files, options.sensitiveValues);
|
|
2686
|
+
const leakedPath = findSensitiveTokenPath(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), options.sensitiveValues);
|
|
2526
2687
|
if (leakedPath) {
|
|
2527
2688
|
throw new Error(`codexNativeRenderSensitiveValue: Codex native Skill "${entry.name}" exposes ` +
|
|
2528
2689
|
`sensitive config in "${leakedPath}".`);
|
|
@@ -2535,10 +2696,22 @@ async function assertNoSensitiveValuesInCodexNativePayload(options) {
|
|
|
2535
2696
|
if (!receiptStats.isFile()) {
|
|
2536
2697
|
throw new Error('Codex native sensitive scan render receipt path is unsafe.');
|
|
2537
2698
|
}
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2699
|
+
const receiptContent = await fs.readFile(receiptPath);
|
|
2700
|
+
let actualReceipt;
|
|
2701
|
+
try {
|
|
2702
|
+
actualReceipt = parseCodexNativeRenderReceipt(receiptContent.toString('utf8'));
|
|
2703
|
+
}
|
|
2704
|
+
catch {
|
|
2705
|
+
actualReceipt = undefined;
|
|
2706
|
+
}
|
|
2707
|
+
const verified = options.verifiedRenderReceipt
|
|
2708
|
+
&& JSON.stringify(actualReceipt) === JSON.stringify(options.verifiedRenderReceipt);
|
|
2709
|
+
if (!verified) {
|
|
2710
|
+
const receiptLeak = findSensitiveTokenPath({ [CODEX_NATIVE_RENDER_RECEIPT_PATH]: receiptContent }, options.sensitiveValues);
|
|
2711
|
+
if (receiptLeak) {
|
|
2712
|
+
throw new Error(`codexNativeRenderSensitiveValue: Codex native render receipt exposes sensitive config in ` +
|
|
2713
|
+
`"${receiptLeak}".`);
|
|
2714
|
+
}
|
|
2542
2715
|
}
|
|
2543
2716
|
}
|
|
2544
2717
|
}
|
|
@@ -2596,7 +2769,7 @@ async function prepareCodexNativeRender(options) {
|
|
|
2596
2769
|
requirementsReceipt: requirementsReceipt(options.plugin),
|
|
2597
2770
|
sourceRequirementsReceiptPolicy: 'allow',
|
|
2598
2771
|
});
|
|
2599
|
-
assertNoSensitiveConfigInFiles(tree.files, collectSensitivePluginConfigValues(options.plugin.manifest, rendered.context.config.values), 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
2772
|
+
assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), collectSensitivePluginConfigValues(options.plugin.manifest, rendered.context.config.values), 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
|
|
2600
2773
|
const skillKey = artifact.skillKey;
|
|
2601
2774
|
renderedArtifacts.push({
|
|
2602
2775
|
artifact,
|
|
@@ -2693,11 +2866,12 @@ async function materializeCodexNativeRender(options) {
|
|
|
2693
2866
|
return {
|
|
2694
2867
|
payloadDigest,
|
|
2695
2868
|
projection: options.prepared.projection,
|
|
2869
|
+
receipt,
|
|
2696
2870
|
presentSkillKeys: options.prepared.presentSkillKeys,
|
|
2697
2871
|
targetSkillKeys: options.prepared.targetSkillKeys,
|
|
2698
2872
|
};
|
|
2699
2873
|
}
|
|
2700
|
-
async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStaging) {
|
|
2874
|
+
async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStaging, rematerializedCodexNativePluginIds = new Set()) {
|
|
2701
2875
|
if (!target?.sourceId || !target.marketplaceRoot) {
|
|
2702
2876
|
const rootDir = resolveAwHome(env);
|
|
2703
2877
|
await fs.ensureDir(rootDir);
|
|
@@ -2761,6 +2935,14 @@ async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStagi
|
|
|
2761
2935
|
sourceIdentity: target.sourceIdentity,
|
|
2762
2936
|
marketplaceName,
|
|
2763
2937
|
});
|
|
2938
|
+
await replayNamespacedCodexNativeRenderProjections({
|
|
2939
|
+
sourceRoot: destination,
|
|
2940
|
+
stagingRoot: staging,
|
|
2941
|
+
sourceId: target.sourceId,
|
|
2942
|
+
sourceIdentity: target.sourceIdentity,
|
|
2943
|
+
marketplaceName,
|
|
2944
|
+
rematerializedPluginIds: rematerializedCodexNativePluginIds,
|
|
2945
|
+
});
|
|
2764
2946
|
}
|
|
2765
2947
|
await prepareStaging?.(staging);
|
|
2766
2948
|
const backup = `${destination}.backup-${crypto.randomUUID()}`;
|
|
@@ -3085,10 +3267,15 @@ function createNativeDriver(options) {
|
|
|
3085
3267
|
payloadRoot: path.join(stagingRoot, 'plugins', candidate.identity.pluginName),
|
|
3086
3268
|
allowedRoot: stagingRoot,
|
|
3087
3269
|
sensitiveValues: await resolveSensitiveUserConfigValues(candidate.plugin, options.context.env),
|
|
3270
|
+
...(nativeRender ? { verifiedRenderReceipt: nativeRender.receipt } : {}),
|
|
3088
3271
|
});
|
|
3089
3272
|
}
|
|
3090
3273
|
}
|
|
3091
|
-
} : undefined
|
|
3274
|
+
} : undefined, new Set(first.identity.platform === 'codex'
|
|
3275
|
+
? materializationTargets.flatMap((candidate) => pluginHasCodexNativeSkillSurface(candidate.plugin)
|
|
3276
|
+
? [candidate.identity.pluginId ?? `${candidate.identity.sourceId}/${candidate.identity.pluginName}`]
|
|
3277
|
+
: [])
|
|
3278
|
+
: []));
|
|
3092
3279
|
try {
|
|
3093
3280
|
await transaction.commit();
|
|
3094
3281
|
}
|
|
@@ -3478,6 +3665,7 @@ function createNativeDriver(options) {
|
|
|
3478
3665
|
await assertNoSensitiveValuesInCanonicalCodexNativePayload({
|
|
3479
3666
|
plugin: target.plugin,
|
|
3480
3667
|
marketplaceRoot: target.identity.marketplaceRoot ?? resolveAwHome(options.context.env),
|
|
3668
|
+
marketplaceName: target.identity.marketplaceName ?? MARKETPLACE_NAME,
|
|
3481
3669
|
env: options.context.env,
|
|
3482
3670
|
sensitiveValues,
|
|
3483
3671
|
});
|
|
@@ -3803,7 +3991,7 @@ function createNativeDriver(options) {
|
|
|
3803
3991
|
return target.artifacts;
|
|
3804
3992
|
},
|
|
3805
3993
|
runPreApply: async (invocation) => {
|
|
3806
|
-
|
|
3994
|
+
const run = () => runPreApplyScript({
|
|
3807
3995
|
plugin: invocation.plugin,
|
|
3808
3996
|
platform: invocation.target.identity.platform,
|
|
3809
3997
|
scope: invocation.target.identity.scope,
|
|
@@ -3811,6 +3999,36 @@ function createNativeDriver(options) {
|
|
|
3811
3999
|
context: options.context,
|
|
3812
4000
|
scriptTimeoutMs: options.scriptTimeoutMs,
|
|
3813
4001
|
});
|
|
4002
|
+
const cache = options.context.preApplyExecutionCache;
|
|
4003
|
+
if (!cache) {
|
|
4004
|
+
await run();
|
|
4005
|
+
return;
|
|
4006
|
+
}
|
|
4007
|
+
const { identity } = invocation.target;
|
|
4008
|
+
const sourceIdentity = invocation.plugin.sourceIdentity ?? identity.sourceIdentity;
|
|
4009
|
+
const pluginId = invocation.plugin.pluginId ?? identity.pluginId;
|
|
4010
|
+
if (!sourceIdentity || !pluginId) {
|
|
4011
|
+
await run();
|
|
4012
|
+
return;
|
|
4013
|
+
}
|
|
4014
|
+
const projectKey = identity.scope === 'project'
|
|
4015
|
+
? identity.platform === 'codex'
|
|
4016
|
+
? await resolveCodexProjectKey(identity.cwd)
|
|
4017
|
+
: await resolveClaudeProjectKey(identity.cwd)
|
|
4018
|
+
: '';
|
|
4019
|
+
const key = [
|
|
4020
|
+
sourceIdentity,
|
|
4021
|
+
pluginId,
|
|
4022
|
+
identity.platform,
|
|
4023
|
+
identity.scope,
|
|
4024
|
+
projectKey,
|
|
4025
|
+
].join('\0');
|
|
4026
|
+
let execution = cache.get(key);
|
|
4027
|
+
if (!execution) {
|
|
4028
|
+
execution = run();
|
|
4029
|
+
cache.set(key, execution);
|
|
4030
|
+
}
|
|
4031
|
+
await execution;
|
|
3814
4032
|
},
|
|
3815
4033
|
};
|
|
3816
4034
|
}
|