@open-agent-toolkit/cli 0.2.51 → 0.2.53
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/bootstrap.md +6 -2
- package/assets/docs/cli-utilities/tool-packs.md +87 -33
- package/assets/docs/provider-sync/commands.md +30 -2
- package/assets/docs/provider-sync/config.md +19 -0
- package/assets/docs/provider-sync/manifest-and-drift.md +37 -0
- package/assets/docs/provider-sync/providers.md +69 -2
- package/assets/docs/provider-sync/scope-and-surface.md +44 -0
- package/assets/docs/reference/cli-reference.md +1 -0
- package/assets/docs/reference/oat-directory-structure.md +11 -4
- package/assets/docs/workflows/projects/evidence-layers.md +110 -1
- package/assets/docs/workflows/projects/implementation-execution.md +13 -0
- package/assets/docs/workflows/projects/orchestration-model.md +18 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +57 -1
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +77 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
- package/assets/skills/oat-project-dispatch-subagents/SKILL.md +24 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -0
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-plan-writing/SKILL.md +12 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-review-provide/SKILL.md +12 -1
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +12 -1
- package/dist/agents/canonical/index.d.ts +2 -0
- package/dist/agents/canonical/index.d.ts.map +1 -1
- package/dist/agents/canonical/index.js +1 -0
- package/dist/agents/canonical/resolve.d.ts +45 -0
- package/dist/agents/canonical/resolve.d.ts.map +1 -0
- package/dist/agents/canonical/resolve.js +175 -0
- package/dist/commands/decision/agents-guidance.d.ts +2 -2
- package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
- package/dist/commands/decision/agents-guidance.js +1 -4
- package/dist/commands/decision/index.d.ts.map +1 -1
- package/dist/commands/decision/index.js +25 -6
- package/dist/commands/docs/init/index.d.ts +7 -1
- package/dist/commands/docs/init/index.d.ts.map +1 -1
- package/dist/commands/docs/init/index.js +53 -20
- package/dist/commands/doctor/index.d.ts +8 -2
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +157 -46
- package/dist/commands/init/index.d.ts +8 -3
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +66 -28
- package/dist/commands/init/tools/index.d.ts +12 -12
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +430 -99
- package/dist/commands/init/tools/install-state.d.ts +2 -0
- package/dist/commands/init/tools/install-state.d.ts.map +1 -1
- package/dist/commands/init/tools/install-state.js +11 -0
- package/dist/commands/init/tools/project-guidance.d.ts +49 -0
- package/dist/commands/init/tools/project-guidance.d.ts.map +1 -0
- package/dist/commands/init/tools/project-guidance.js +144 -0
- package/dist/commands/init/tools/workflows/index.d.ts +7 -0
- package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/index.js +160 -7
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +28 -3
- package/dist/commands/pjm/init.d.ts +5 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +19 -3
- package/dist/commands/project/dispatch/index.d.ts +10 -0
- package/dist/commands/project/dispatch/index.d.ts.map +1 -0
- package/dist/commands/project/dispatch/index.js +137 -0
- package/dist/commands/project/dispatch/record.d.ts +91 -0
- package/dist/commands/project/dispatch/record.d.ts.map +1 -0
- package/dist/commands/project/dispatch/record.js +402 -0
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +2 -0
- package/dist/commands/providers/inspect/inspect.d.ts +8 -1
- package/dist/commands/providers/inspect/inspect.d.ts.map +1 -1
- package/dist/commands/providers/inspect/inspect.js +146 -26
- package/dist/commands/providers/list/list.d.ts +8 -1
- package/dist/commands/providers/list/list.d.ts.map +1 -1
- package/dist/commands/providers/list/list.js +139 -23
- package/dist/commands/providers/providers.types.d.ts +25 -1
- package/dist/commands/providers/providers.types.d.ts.map +1 -1
- package/dist/commands/providers/set/index.d.ts +9 -1
- package/dist/commands/providers/set/index.d.ts.map +1 -1
- package/dist/commands/providers/set/index.js +43 -33
- package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
- package/dist/commands/remove/skill/remove-skill.js +11 -0
- package/dist/commands/shared/agents-md.d.ts +42 -14
- package/dist/commands/shared/agents-md.d.ts.map +1 -1
- package/dist/commands/shared/agents-md.js +364 -70
- package/dist/commands/status/index.d.ts +8 -3
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +149 -37
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +220 -29
- package/dist/commands/sync/dry-run.d.ts.map +1 -1
- package/dist/commands/sync/dry-run.js +30 -6
- package/dist/commands/sync/index.d.ts +17 -2
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +108 -43
- package/dist/commands/sync/sync.types.d.ts +39 -2
- package/dist/commands/sync/sync.types.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.d.ts +5 -1
- package/dist/commands/sync/sync.utils.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.js +73 -0
- package/dist/commands/tools/info/info-tool.d.ts +2 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +34 -12
- package/dist/commands/tools/install/index.d.ts.map +1 -1
- package/dist/commands/tools/install/index.js +19 -3
- package/dist/commands/tools/list/list-tools.d.ts +2 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +36 -7
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +150 -21
- package/dist/commands/tools/remove/remove-tools.d.ts +20 -0
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +122 -1
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +19 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/format-pack-inventory.js +109 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts +66 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-evidence.js +132 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts +1 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +44 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.js +91 -0
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +74 -4
- package/dist/commands/tools/update/update-tools.d.ts +5 -0
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +64 -2
- package/dist/config/oat-config.d.ts +5 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +17 -5
- package/dist/config/user-sync-config.d.ts +1 -0
- package/dist/config/user-sync-config.d.ts.map +1 -1
- package/dist/config/user-sync-config.js +8 -0
- package/dist/drift/detector.d.ts +2 -2
- package/dist/drift/detector.d.ts.map +1 -1
- package/dist/drift/detector.js +18 -0
- package/dist/drift/strays.d.ts.map +1 -1
- package/dist/drift/strays.js +5 -0
- package/dist/engine/collection-sync.d.ts +14 -0
- package/dist/engine/collection-sync.d.ts.map +1 -0
- package/dist/engine/collection-sync.js +282 -0
- package/dist/engine/compute-plan.d.ts +8 -1
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +303 -12
- package/dist/engine/engine.types.d.ts +61 -0
- package/dist/engine/engine.types.d.ts.map +1 -1
- package/dist/engine/execute-plan.d.ts +22 -2
- package/dist/engine/execute-plan.d.ts.map +1 -1
- package/dist/engine/execute-plan.js +459 -27
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +1 -1
- package/dist/engine/provider-path-safety.d.ts +1 -0
- package/dist/engine/provider-path-safety.d.ts.map +1 -1
- package/dist/engine/provider-path-safety.js +15 -0
- package/dist/engine/scanner.d.ts +9 -2
- package/dist/engine/scanner.d.ts.map +1 -1
- package/dist/engine/scanner.js +58 -4
- package/dist/fs/io.d.ts +67 -0
- package/dist/fs/io.d.ts.map +1 -1
- package/dist/fs/io.js +536 -3
- package/dist/manifest/index.d.ts +2 -2
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +1 -1
- package/dist/manifest/manager.d.ts +2 -2
- package/dist/manifest/manager.d.ts.map +1 -1
- package/dist/manifest/manager.js +20 -3
- package/dist/manifest/manifest.types.d.ts +704 -13
- package/dist/manifest/manifest.types.d.ts.map +1 -1
- package/dist/manifest/manifest.types.js +131 -15
- package/dist/providers/codex/codec/sync-extension.d.ts +1 -1
- package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/codex/codec/sync-extension.js +48 -17
- package/dist/providers/cursor/codec/sync-extension.d.ts +1 -1
- package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/cursor/codec/sync-extension.js +52 -23
- package/dist/providers/identity/absolute-paths.d.ts +68 -0
- package/dist/providers/identity/absolute-paths.d.ts.map +1 -0
- package/dist/providers/identity/absolute-paths.js +144 -0
- package/dist/providers/identity/claude-runtime-observation.d.ts +105 -0
- package/dist/providers/identity/claude-runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts +40 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts.map +1 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.js +146 -0
- package/dist/providers/identity/claude-runtime-observation.js +229 -0
- package/dist/providers/identity/codex-runtime-observation.d.ts +75 -0
- package/dist/providers/identity/codex-runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts +37 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts.map +1 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.js +539 -0
- package/dist/providers/identity/codex-runtime-observation.js +255 -0
- package/dist/providers/identity/generic-dispatch-record.d.ts +313 -0
- package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -0
- package/dist/providers/identity/generic-dispatch-record.js +539 -0
- package/dist/providers/identity/oat-dispatch-record.d.ts +1964 -0
- package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -0
- package/dist/providers/identity/oat-dispatch-record.js +691 -0
- package/dist/providers/identity/runtime-observation.d.ts +100 -0
- package/dist/providers/identity/runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/runtime-observation.js +169 -0
- package/dist/providers/shared/index.d.ts +2 -0
- package/dist/providers/shared/index.d.ts.map +1 -1
- package/dist/providers/shared/index.js +1 -0
- package/dist/providers/shared/materialization-extension.d.ts +12 -0
- package/dist/providers/shared/materialization-extension.d.ts.map +1 -1
- package/dist/providers/shared/registry.d.ts +69 -0
- package/dist/providers/shared/registry.d.ts.map +1 -0
- package/dist/providers/shared/registry.js +252 -0
- package/dist/providers/shared/restart-adviser.d.ts +26 -0
- package/dist/providers/shared/restart-adviser.d.ts.map +1 -0
- package/dist/providers/shared/restart-adviser.js +96 -0
- package/dist/shared/types.d.ts +4 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,14 +1,60 @@
|
|
|
1
1
|
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
|
-
import { copyDirectory, copySingleFile, createSymlink } from '../fs/io.js';
|
|
3
|
+
import { copyDirectory, copySingleFile, copySingleFileNoClobber, createCollectionSymlinkNoClobber, createSymlink, createSymlinkNoClobber, removeCollectionSymlinkIfUnchanged, writeFileNoClobber, } from '../fs/io.js';
|
|
4
4
|
import { computeContentHash, computeStringHash } from '../manifest/hash.js';
|
|
5
|
-
import {
|
|
5
|
+
import { saveManifest as persistManifest } from '../manifest/manager.js';
|
|
6
|
+
import { collectionProofMatches, projectCollectionOwnership, proveCollectionIdentity, } from './collection-sync.js';
|
|
6
7
|
import { insertMarker, writeDirectorySentinel } from './markers.js';
|
|
7
8
|
import { assertSafeProviderMutationPath } from './provider-path-safety.js';
|
|
8
9
|
const DEFAULT_EXECUTE_SYNC_PLAN_DEPENDENCIES = {};
|
|
9
10
|
function mutatesProviderPath(entry) {
|
|
10
11
|
return entry.operation !== 'skip' && entry.operation !== 'detach';
|
|
11
12
|
}
|
|
13
|
+
function operationEvidence(scope, entry, status, failure) {
|
|
14
|
+
return {
|
|
15
|
+
scope,
|
|
16
|
+
provider: entry.provider,
|
|
17
|
+
contentKind: entry.canonical.type,
|
|
18
|
+
asset: entry.canonical.name,
|
|
19
|
+
action: entry.operation,
|
|
20
|
+
status,
|
|
21
|
+
...(failure ? { failure } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function classifyFailure(error, destinationPolicy) {
|
|
25
|
+
if (hasErrorCode(error, 'E_DEFERRED_DIRECTORY_TRANSITION_UNSAFE')) {
|
|
26
|
+
return {
|
|
27
|
+
status: 'failed',
|
|
28
|
+
failure: 'Manual recovery required: deferred collection-directory transitions are unavailable on this runtime. Disable the provider and run sync to detach OAT ownership, then verify and remove the preserved alias before managing the provider directory externally. Automatic publication requires an identity-bound, non-following primitive.',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (destinationPolicy === 'preserve-existing' &&
|
|
32
|
+
hasErrorCode(error, 'EEXIST')) {
|
|
33
|
+
return {
|
|
34
|
+
status: 'failed',
|
|
35
|
+
failure: 'Destination appeared after collection clearance; preserved unmanaged content. Resolve the destination and retry sync.',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (typeof error === 'object' &&
|
|
39
|
+
error !== null &&
|
|
40
|
+
'code' in error &&
|
|
41
|
+
error.code === 'ENOENT') {
|
|
42
|
+
return {
|
|
43
|
+
status: 'missing',
|
|
44
|
+
failure: 'Canonical or provider input was missing; restore it and retry sync.',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
status: 'failed',
|
|
49
|
+
failure: 'Operation failed; inspect local verbose diagnostics and retry sync.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function hasErrorCode(error, code) {
|
|
53
|
+
return (typeof error === 'object' &&
|
|
54
|
+
error !== null &&
|
|
55
|
+
'code' in error &&
|
|
56
|
+
error.code === code);
|
|
57
|
+
}
|
|
12
58
|
async function assertSafeEntryProviderPath(entry) {
|
|
13
59
|
const scopeRoot = inferScopeRoot(resolve(entry.canonical.canonicalPath));
|
|
14
60
|
await assertSafeProviderMutationPath(scopeRoot, entry.providerPath);
|
|
@@ -71,19 +117,41 @@ async function applyCopyMarker(entry) {
|
|
|
71
117
|
throw error;
|
|
72
118
|
}
|
|
73
119
|
}
|
|
74
|
-
async function applyEntry(planEntry, manifest) {
|
|
120
|
+
async function applyEntry(planEntry, manifest, destinationPolicy, copyDirectoryImpl, createSymlinkNoClobberImpl) {
|
|
75
121
|
switch (planEntry.operation) {
|
|
76
122
|
case 'create_symlink':
|
|
77
123
|
case 'update_symlink': {
|
|
124
|
+
if (destinationPolicy === 'preserve-existing') {
|
|
125
|
+
if (!planEntry.canonical.isFile) {
|
|
126
|
+
throw Object.assign(new Error('Deferred collection-directory publication is unavailable on this runtime.'), { code: 'E_DEFERRED_DIRECTORY_TRANSITION_UNSAFE' });
|
|
127
|
+
}
|
|
128
|
+
await createSymlinkNoClobberImpl(planEntry.canonical.canonicalPath, planEntry.providerPath, planEntry.canonical.isFile);
|
|
129
|
+
const manifestEntry = await toManifestEntry(planEntry, 'symlink');
|
|
130
|
+
return addManifestEntry(manifest, manifestEntry);
|
|
131
|
+
}
|
|
78
132
|
if (planEntry.operation === 'update_symlink') {
|
|
79
133
|
await rm(planEntry.providerPath, { recursive: true, force: true });
|
|
80
134
|
}
|
|
81
135
|
const strategyUsed = await createSymlink(planEntry.canonical.canonicalPath, planEntry.providerPath, undefined, planEntry.canonical.isFile);
|
|
82
136
|
const manifestEntry = await toManifestEntry(planEntry, strategyUsed);
|
|
83
|
-
return
|
|
137
|
+
return addManifestEntry(manifest, manifestEntry);
|
|
84
138
|
}
|
|
85
139
|
case 'create_copy':
|
|
86
140
|
case 'update_copy': {
|
|
141
|
+
if (destinationPolicy === 'preserve-existing') {
|
|
142
|
+
if (planEntry.canonical.isFile &&
|
|
143
|
+
planEntry.renderedContent !== undefined) {
|
|
144
|
+
await writeFileNoClobber(planEntry.providerPath, planEntry.renderedContent);
|
|
145
|
+
}
|
|
146
|
+
else if (planEntry.canonical.isFile) {
|
|
147
|
+
await copySingleFileNoClobber(planEntry.canonical.canonicalPath, planEntry.providerPath);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
throw Object.assign(new Error('Deferred collection-directory publication is unavailable on this runtime.'), { code: 'E_DEFERRED_DIRECTORY_TRANSITION_UNSAFE' });
|
|
151
|
+
}
|
|
152
|
+
const manifestEntry = await toManifestEntry(planEntry, 'copy');
|
|
153
|
+
return addManifestEntry(manifest, manifestEntry);
|
|
154
|
+
}
|
|
87
155
|
if (planEntry.operation === 'update_copy') {
|
|
88
156
|
await rm(planEntry.providerPath, { recursive: true, force: true });
|
|
89
157
|
}
|
|
@@ -96,20 +164,20 @@ async function applyEntry(planEntry, manifest) {
|
|
|
96
164
|
await copySingleFile(planEntry.canonical.canonicalPath, planEntry.providerPath);
|
|
97
165
|
}
|
|
98
166
|
else {
|
|
99
|
-
await
|
|
167
|
+
await copyDirectoryImpl(planEntry.canonical.canonicalPath, planEntry.providerPath);
|
|
100
168
|
await applyCopyMarker(planEntry);
|
|
101
169
|
}
|
|
102
170
|
const manifestEntry = await toManifestEntry(planEntry, 'copy');
|
|
103
|
-
return
|
|
171
|
+
return addManifestEntry(manifest, manifestEntry);
|
|
104
172
|
}
|
|
105
173
|
case 'remove': {
|
|
106
174
|
await rm(planEntry.providerPath, { recursive: true, force: true });
|
|
107
175
|
const { canonicalPath } = resolveManifestPaths(planEntry);
|
|
108
|
-
return
|
|
176
|
+
return removeManifestEntry(manifest, canonicalPath, planEntry.provider);
|
|
109
177
|
}
|
|
110
178
|
case 'detach': {
|
|
111
179
|
const { canonicalPath } = resolveManifestPaths(planEntry);
|
|
112
|
-
return
|
|
180
|
+
return removeManifestEntry(manifest, canonicalPath, planEntry.provider);
|
|
113
181
|
}
|
|
114
182
|
case 'skip': {
|
|
115
183
|
return manifest;
|
|
@@ -118,50 +186,414 @@ async function applyEntry(planEntry, manifest) {
|
|
|
118
186
|
return manifest;
|
|
119
187
|
}
|
|
120
188
|
}
|
|
189
|
+
function addManifestEntry(manifest, entry) {
|
|
190
|
+
return {
|
|
191
|
+
...manifest,
|
|
192
|
+
entries: [
|
|
193
|
+
...manifest.entries.filter((candidate) => !(candidate.canonicalPath === entry.canonicalPath &&
|
|
194
|
+
candidate.provider === entry.provider)),
|
|
195
|
+
entry,
|
|
196
|
+
],
|
|
197
|
+
lastUpdated: new Date().toISOString(),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function removeManifestEntry(manifest, canonicalPath, provider) {
|
|
201
|
+
const entries = manifest.entries.filter((entry) => !(entry.canonicalPath === canonicalPath && entry.provider === provider));
|
|
202
|
+
return entries.length === manifest.entries.length
|
|
203
|
+
? manifest
|
|
204
|
+
: { ...manifest, entries, lastUpdated: new Date().toISOString() };
|
|
205
|
+
}
|
|
121
206
|
async function ensureSkipEntryManaged(planEntry, manifest) {
|
|
122
207
|
const { canonicalPath } = resolveManifestPaths(planEntry);
|
|
123
|
-
const existing =
|
|
208
|
+
const existing = manifest.entries.find((entry) => entry.canonicalPath === canonicalPath &&
|
|
209
|
+
entry.provider === planEntry.provider);
|
|
124
210
|
if (existing) {
|
|
125
211
|
return manifest;
|
|
126
212
|
}
|
|
127
213
|
const manifestEntry = await toManifestEntry(planEntry, planEntry.strategy);
|
|
128
|
-
return
|
|
214
|
+
return addManifestEntry(manifest, manifestEntry);
|
|
215
|
+
}
|
|
216
|
+
function detachCollectionOwnership(manifest, plan) {
|
|
217
|
+
const scopeRoot = inferScopeRoot(plan.canonicalDir);
|
|
218
|
+
const canonicalDir = relative(scopeRoot, plan.canonicalDir).replaceAll('\\', '/');
|
|
219
|
+
const providerDir = relative(scopeRoot, plan.providerDir).replaceAll('\\', '/');
|
|
220
|
+
const detachedIds = new Set(manifest.collections
|
|
221
|
+
.filter((collection) => collection.provider === plan.provider &&
|
|
222
|
+
collection.contentType === plan.contentType &&
|
|
223
|
+
collection.canonicalDir === canonicalDir &&
|
|
224
|
+
collection.providerDir === providerDir)
|
|
225
|
+
.map(({ id }) => id));
|
|
226
|
+
if (detachedIds.size === 0) {
|
|
227
|
+
return manifest;
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
...manifest,
|
|
231
|
+
collections: manifest.collections.filter(({ id }) => !detachedIds.has(id)),
|
|
232
|
+
entries: manifest.entries.filter((entry) => entry.strategy !== 'collection' ||
|
|
233
|
+
entry.collectionId === undefined ||
|
|
234
|
+
!detachedIds.has(entry.collectionId)),
|
|
235
|
+
lastUpdated: new Date().toISOString(),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function executeCollectionTransaction(plans, manifest, blockedDeferredDirectoryTransitions, dependencies, manifestPath, beforeMutation) {
|
|
239
|
+
const passiveResults = plans
|
|
240
|
+
.filter(({ action }) => action === 'fallback-per-entry' || action === 'reject-collection')
|
|
241
|
+
.map((plan) => ({
|
|
242
|
+
provider: plan.provider,
|
|
243
|
+
contentType: plan.contentType,
|
|
244
|
+
action: plan.action,
|
|
245
|
+
ownership: plan.ownership,
|
|
246
|
+
status: plan.action === 'fallback-per-entry' ? 'fallback' : 'rejected',
|
|
247
|
+
reason: plan.reason,
|
|
248
|
+
}));
|
|
249
|
+
const actionable = plans.filter(({ action }) => action === 'create-collection-link' ||
|
|
250
|
+
action === 'adopt-collection-link' ||
|
|
251
|
+
action === 'inherit-collection');
|
|
252
|
+
const detachments = plans.filter(({ action }) => action === 'detach-collection');
|
|
253
|
+
if (actionable.length === 0 && detachments.length === 0) {
|
|
254
|
+
return {
|
|
255
|
+
manifest,
|
|
256
|
+
results: passiveResults,
|
|
257
|
+
attempted: false,
|
|
258
|
+
persisted: false,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const verified = new Map();
|
|
262
|
+
const createdCollections = [];
|
|
263
|
+
const removedTransitionPlans = new Set();
|
|
264
|
+
const absentTransitionPlans = new Set();
|
|
265
|
+
const blockedTransitionResults = new Map();
|
|
266
|
+
let nextManifest = manifest;
|
|
267
|
+
try {
|
|
268
|
+
await beforeMutation();
|
|
269
|
+
for (const plan of actionable) {
|
|
270
|
+
const current = await dependencies.proveCollectionIdentity({
|
|
271
|
+
root: inferScopeRoot(plan.canonicalDir),
|
|
272
|
+
canonicalDir: plan.canonicalDir,
|
|
273
|
+
providerDir: plan.providerDir,
|
|
274
|
+
});
|
|
275
|
+
if (!collectionProofMatches(plan.proof, current)) {
|
|
276
|
+
throw new Error('collection identity changed after planning; preserved destination');
|
|
277
|
+
}
|
|
278
|
+
verified.set(plan, current);
|
|
279
|
+
if (plan.action === 'create-collection-link') {
|
|
280
|
+
if (current.status !== 'absent') {
|
|
281
|
+
throw new Error('collection destination is no longer absent');
|
|
282
|
+
}
|
|
283
|
+
const created = await dependencies.createCollectionSymlinkNoClobber(plan.canonicalDir, plan.providerDir, {
|
|
284
|
+
scopeRoot: inferScopeRoot(plan.canonicalDir),
|
|
285
|
+
expectedParent: current.providerParent,
|
|
286
|
+
});
|
|
287
|
+
createdCollections.push({ plan, created });
|
|
288
|
+
const rescanned = await dependencies.proveCollectionIdentity({
|
|
289
|
+
root: inferScopeRoot(plan.canonicalDir),
|
|
290
|
+
canonicalDir: plan.canonicalDir,
|
|
291
|
+
providerDir: plan.providerDir,
|
|
292
|
+
});
|
|
293
|
+
if (rescanned.status !== 'exact-link') {
|
|
294
|
+
throw new Error('created collection alias did not verify as exact');
|
|
295
|
+
}
|
|
296
|
+
verified.set(plan, rescanned);
|
|
297
|
+
}
|
|
298
|
+
const proof = verified.get(plan);
|
|
299
|
+
if (proof?.status !== 'exact-link') {
|
|
300
|
+
throw new Error('collection alias exact identity is unavailable');
|
|
301
|
+
}
|
|
302
|
+
nextManifest = await projectCollectionOwnership(nextManifest, plan, proof);
|
|
303
|
+
}
|
|
304
|
+
for (const plan of detachments) {
|
|
305
|
+
if (!plan.transitionToPerEntry) {
|
|
306
|
+
nextManifest = detachCollectionOwnership(nextManifest, plan);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const blockTransition = (status, reason) => {
|
|
310
|
+
blockedTransitionResults.set(plan, {
|
|
311
|
+
provider: plan.provider,
|
|
312
|
+
contentType: plan.contentType,
|
|
313
|
+
action: plan.action,
|
|
314
|
+
ownership: plan.ownership,
|
|
315
|
+
status,
|
|
316
|
+
reason,
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
if (blockedDeferredDirectoryTransitions.has(`${plan.provider}::${plan.contentType}`)) {
|
|
320
|
+
blockTransition('rejected', `Manual recovery required: deferred collection-directory transitions are unavailable on this runtime, so collection ownership was preserved. Run "oat providers set --scope ${plan.scope} --disabled ${plan.provider}" and then "oat sync --scope ${plan.scope}" to detach OAT ownership. Then verify and remove the preserved alias before managing the provider directory externally. Automatic publication requires an identity-bound, non-following primitive.`);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (plan.proof.status === 'absent') {
|
|
324
|
+
try {
|
|
325
|
+
const current = await dependencies.proveCollectionIdentity({
|
|
326
|
+
root: inferScopeRoot(plan.canonicalDir),
|
|
327
|
+
canonicalDir: plan.canonicalDir,
|
|
328
|
+
providerDir: plan.providerDir,
|
|
329
|
+
});
|
|
330
|
+
if (current.status !== 'absent' ||
|
|
331
|
+
!collectionProofMatches(plan.proof, current)) {
|
|
332
|
+
blockTransition('rejected', 'collection transition blocked; the manually cleared destination changed after planning, so ownership and child operations were preserved');
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
absentTransitionPlans.add(plan);
|
|
336
|
+
nextManifest = detachCollectionOwnership(nextManifest, plan);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
blockTransition('rejected', 'collection transition blocked; destination absence could not be safely re-proven, so ownership and child operations were preserved');
|
|
340
|
+
}
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (plan.ownership !== 'oat-created' ||
|
|
344
|
+
plan.proof.status !== 'exact-link' ||
|
|
345
|
+
plan.createdLink === undefined) {
|
|
346
|
+
blockTransition('rejected', 'collection transition blocked; exact durable creation identity is unavailable, so ownership and the alias were preserved');
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const current = await dependencies.proveCollectionIdentity({
|
|
351
|
+
root: inferScopeRoot(plan.canonicalDir),
|
|
352
|
+
canonicalDir: plan.canonicalDir,
|
|
353
|
+
providerDir: plan.providerDir,
|
|
354
|
+
});
|
|
355
|
+
if (current.status !== 'exact-link' ||
|
|
356
|
+
!collectionProofMatches(plan.proof, current) ||
|
|
357
|
+
current.providerLink.device !== plan.createdLink.device ||
|
|
358
|
+
current.providerLink.inode !== plan.createdLink.inode ||
|
|
359
|
+
current.linkText !== plan.createdLink.linkText) {
|
|
360
|
+
blockTransition('rejected', 'collection transition blocked; alias identity changed after planning, so ownership and the replacement path were preserved');
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const removed = await dependencies.removeCollectionSymlinkIfUnchanged(plan.providerDir, plan.createdLink);
|
|
364
|
+
if (!removed) {
|
|
365
|
+
blockTransition('partial', 'collection transition blocked; this runtime cannot safely unlink the exact alias, so ownership and child operations were preserved; remove the alias manually, then replan and retry');
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
removedTransitionPlans.add(plan);
|
|
369
|
+
nextManifest = detachCollectionOwnership(nextManifest, plan);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
blockTransition('rejected', 'collection transition blocked; alias identity could not be safely re-proven, so ownership and the provider path were preserved');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const hasManifestMutation = actionable.length > 0 ||
|
|
376
|
+
detachments.some((plan) => !plan.transitionToPerEntry) ||
|
|
377
|
+
removedTransitionPlans.size > 0 ||
|
|
378
|
+
absentTransitionPlans.size > 0;
|
|
379
|
+
if (hasManifestMutation) {
|
|
380
|
+
await dependencies.saveManifest(manifestPath, nextManifest);
|
|
381
|
+
}
|
|
382
|
+
const detachmentResults = [];
|
|
383
|
+
for (const plan of detachments) {
|
|
384
|
+
const blocked = blockedTransitionResults.get(plan);
|
|
385
|
+
if (blocked) {
|
|
386
|
+
detachmentResults.push(blocked);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (plan.transitionToPerEntry) {
|
|
390
|
+
detachmentResults.push({
|
|
391
|
+
provider: plan.provider,
|
|
392
|
+
contentType: plan.contentType,
|
|
393
|
+
action: plan.action,
|
|
394
|
+
ownership: plan.ownership,
|
|
395
|
+
status: 'changed',
|
|
396
|
+
reason: absentTransitionPlans.has(plan)
|
|
397
|
+
? 'manually cleared collection destination remained absent through apply; ownership detached and per-entry reconciliation may proceed'
|
|
398
|
+
: 'exact durably identified collection alias was removed before ownership detachment; per-entry reconciliation may proceed',
|
|
399
|
+
});
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
let reason = plan.reason;
|
|
403
|
+
let status = 'changed';
|
|
404
|
+
if (plan.ownership === 'oat-created' &&
|
|
405
|
+
plan.proof.status === 'exact-link' &&
|
|
406
|
+
plan.createdLink !== undefined) {
|
|
407
|
+
try {
|
|
408
|
+
const current = await dependencies.proveCollectionIdentity({
|
|
409
|
+
root: inferScopeRoot(plan.canonicalDir),
|
|
410
|
+
canonicalDir: plan.canonicalDir,
|
|
411
|
+
providerDir: plan.providerDir,
|
|
412
|
+
});
|
|
413
|
+
if (current.status === 'exact-link' &&
|
|
414
|
+
collectionProofMatches(plan.proof, current) &&
|
|
415
|
+
current.providerLink.device === plan.createdLink.device &&
|
|
416
|
+
current.providerLink.inode === plan.createdLink.inode &&
|
|
417
|
+
current.linkText === plan.createdLink.linkText) {
|
|
418
|
+
const removed = await dependencies.removeCollectionSymlinkIfUnchanged(plan.providerDir, plan.createdLink);
|
|
419
|
+
if (!removed) {
|
|
420
|
+
status = 'partial';
|
|
421
|
+
reason =
|
|
422
|
+
'collection ownership detached, but this runtime cannot safely unlink the exact alias; the alias was preserved for manual removal';
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
reason =
|
|
427
|
+
'collection ownership detached; alias identity changed after planning and was preserved';
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
status = 'partial';
|
|
432
|
+
reason =
|
|
433
|
+
'collection ownership detached; alias removal could not be proven safe and was preserved';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else if (plan.ownership === 'oat-created') {
|
|
437
|
+
reason =
|
|
438
|
+
'collection ownership detached; durable creation identity was unavailable and the alias was preserved';
|
|
439
|
+
}
|
|
440
|
+
detachmentResults.push({
|
|
441
|
+
provider: plan.provider,
|
|
442
|
+
contentType: plan.contentType,
|
|
443
|
+
action: plan.action,
|
|
444
|
+
ownership: plan.ownership,
|
|
445
|
+
status,
|
|
446
|
+
reason,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
manifest: nextManifest,
|
|
451
|
+
results: [
|
|
452
|
+
...passiveResults,
|
|
453
|
+
...actionable.map((plan) => ({
|
|
454
|
+
provider: plan.provider,
|
|
455
|
+
contentType: plan.contentType,
|
|
456
|
+
action: plan.action,
|
|
457
|
+
ownership: plan.ownership,
|
|
458
|
+
status: plan.action === 'inherit-collection'
|
|
459
|
+
? 'current'
|
|
460
|
+
: 'changed',
|
|
461
|
+
reason: plan.reason,
|
|
462
|
+
})),
|
|
463
|
+
...detachmentResults,
|
|
464
|
+
],
|
|
465
|
+
attempted: true,
|
|
466
|
+
persisted: hasManifestMutation,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
const rollback = await Promise.all([...createdCollections]
|
|
471
|
+
.reverse()
|
|
472
|
+
.map(({ plan, created }) => dependencies.removeCollectionSymlinkIfUnchanged(plan.providerDir, created)));
|
|
473
|
+
const partial = rollback.some((removed) => !removed) || removedTransitionPlans.size > 0;
|
|
474
|
+
const guardedCreationUnavailable = hasErrorCode(error, 'E_COLLECTION_LINK_UNSAFE');
|
|
475
|
+
return {
|
|
476
|
+
manifest,
|
|
477
|
+
results: [
|
|
478
|
+
...passiveResults,
|
|
479
|
+
...actionable.map((plan) => ({
|
|
480
|
+
provider: plan.provider,
|
|
481
|
+
contentType: plan.contentType,
|
|
482
|
+
action: plan.action,
|
|
483
|
+
ownership: plan.ownership,
|
|
484
|
+
status: partial ? 'partial' : 'failed',
|
|
485
|
+
reason: guardedCreationUnavailable
|
|
486
|
+
? 'collection creation is disabled because this runtime cannot create a link relative to a securely guarded parent; configure explicit symlink/copy per-entry sync or create the exact collection alias manually and retry for adoption'
|
|
487
|
+
: partial
|
|
488
|
+
? 'collection transaction failed; automatic rollback unlink is unavailable, so any created alias was preserved for manual recovery'
|
|
489
|
+
: 'collection transaction failed; newly created links were rolled back',
|
|
490
|
+
})),
|
|
491
|
+
...detachments.map((plan) => {
|
|
492
|
+
const blocked = blockedTransitionResults.get(plan);
|
|
493
|
+
if (blocked)
|
|
494
|
+
return blocked;
|
|
495
|
+
return {
|
|
496
|
+
provider: plan.provider,
|
|
497
|
+
contentType: plan.contentType,
|
|
498
|
+
action: plan.action,
|
|
499
|
+
ownership: plan.ownership,
|
|
500
|
+
status: removedTransitionPlans.has(plan)
|
|
501
|
+
? 'partial'
|
|
502
|
+
: 'failed',
|
|
503
|
+
reason: removedTransitionPlans.has(plan)
|
|
504
|
+
? 'exact alias was safely removed, but manifest ownership could not be detached; no per-entry child operations were attempted'
|
|
505
|
+
: 'collection transaction failed before ownership detachment',
|
|
506
|
+
};
|
|
507
|
+
}),
|
|
508
|
+
],
|
|
509
|
+
attempted: true,
|
|
510
|
+
persisted: false,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
129
513
|
}
|
|
130
514
|
export async function executeSyncPlan(plan, manifest, manifestPath, dependencies = DEFAULT_EXECUTE_SYNC_PLAN_DEPENDENCIES) {
|
|
515
|
+
const saveManifest = dependencies.saveManifest ?? persistManifest;
|
|
516
|
+
const collectionDependencies = {
|
|
517
|
+
saveManifest,
|
|
518
|
+
createCollectionSymlinkNoClobber: dependencies.createCollectionSymlinkNoClobber ??
|
|
519
|
+
createCollectionSymlinkNoClobber,
|
|
520
|
+
removeCollectionSymlinkIfUnchanged: dependencies.removeCollectionSymlinkIfUnchanged ??
|
|
521
|
+
removeCollectionSymlinkIfUnchanged,
|
|
522
|
+
proveCollectionIdentity: dependencies.proveCollectionIdentity ?? proveCollectionIdentity,
|
|
523
|
+
};
|
|
131
524
|
let nextManifest = manifest;
|
|
132
525
|
let beforeFirstMutationCalled = false;
|
|
133
|
-
const
|
|
134
|
-
applied: 0,
|
|
135
|
-
failed: 0,
|
|
136
|
-
skipped: 0,
|
|
137
|
-
};
|
|
526
|
+
const operationResults = [];
|
|
138
527
|
const operations = [...plan.entries, ...plan.removals];
|
|
528
|
+
const beforeMutation = async () => {
|
|
529
|
+
if (!beforeFirstMutationCalled) {
|
|
530
|
+
beforeFirstMutationCalled = true;
|
|
531
|
+
await dependencies.beforeFirstMutation?.();
|
|
532
|
+
}
|
|
533
|
+
};
|
|
139
534
|
for (const operation of operations) {
|
|
140
|
-
if (mutatesProviderPath(operation)
|
|
535
|
+
if (mutatesProviderPath(operation) &&
|
|
536
|
+
!operation.deferredUntilCollectionDetached) {
|
|
141
537
|
await assertSafeEntryProviderPath(operation);
|
|
142
538
|
}
|
|
143
539
|
}
|
|
540
|
+
const blockedDeferredDirectoryTransitions = new Set(operations
|
|
541
|
+
.filter((operation) => (operation.operation === 'create_copy' ||
|
|
542
|
+
operation.operation === 'create_symlink') &&
|
|
543
|
+
operation.deferredUntilCollectionDetached &&
|
|
544
|
+
!operation.canonical.isFile)
|
|
545
|
+
.map((operation) => `${operation.provider}::${operation.canonical.type}`));
|
|
546
|
+
const collectionTransaction = await executeCollectionTransaction(plan.collections ?? [], nextManifest, blockedDeferredDirectoryTransitions, collectionDependencies, manifestPath, beforeMutation);
|
|
547
|
+
nextManifest = collectionTransaction.manifest;
|
|
548
|
+
const blockedTransitions = new Map(collectionTransaction.results
|
|
549
|
+
.filter(({ action, status }) => action === 'detach-collection' && status !== 'changed')
|
|
550
|
+
.map((result) => [
|
|
551
|
+
`${result.provider}::${result.contentType}`,
|
|
552
|
+
result.reason,
|
|
553
|
+
]));
|
|
144
554
|
for (const operation of operations) {
|
|
555
|
+
if (operation.deferredUntilCollectionDetached &&
|
|
556
|
+
blockedTransitions.has(`${operation.provider}::${operation.canonical.type}`)) {
|
|
557
|
+
operationResults.push(operationEvidence(plan.scope, operation, 'failed', blockedTransitions.get(`${operation.provider}::${operation.canonical.type}`) ??
|
|
558
|
+
'Collection alias could not be safely cleared; resolve the reported collection conflict and retry.'));
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
145
561
|
if (operation.operation === 'skip') {
|
|
146
562
|
nextManifest = await ensureSkipEntryManaged(operation, nextManifest);
|
|
147
|
-
|
|
563
|
+
operationResults.push(operationEvidence(plan.scope, operation, 'current'));
|
|
148
564
|
continue;
|
|
149
565
|
}
|
|
566
|
+
const destinationPolicy = operation.deferredUntilCollectionDetached
|
|
567
|
+
? 'preserve-existing'
|
|
568
|
+
: 'ordinary';
|
|
150
569
|
try {
|
|
151
570
|
if (mutatesProviderPath(operation)) {
|
|
152
|
-
|
|
153
|
-
beforeFirstMutationCalled = true;
|
|
154
|
-
await dependencies.beforeFirstMutation?.();
|
|
155
|
-
}
|
|
571
|
+
await beforeMutation();
|
|
156
572
|
await assertSafeEntryProviderPath(operation);
|
|
157
573
|
}
|
|
158
|
-
nextManifest = await applyEntry(operation, nextManifest);
|
|
159
|
-
|
|
574
|
+
nextManifest = await applyEntry(operation, nextManifest, destinationPolicy, dependencies.copyDirectory ?? copyDirectory, dependencies.createSymlinkNoClobber ?? createSymlinkNoClobber);
|
|
575
|
+
operationResults.push(operationEvidence(plan.scope, operation, 'changed'));
|
|
160
576
|
}
|
|
161
|
-
catch {
|
|
162
|
-
|
|
577
|
+
catch (error) {
|
|
578
|
+
const failure = classifyFailure(error, destinationPolicy);
|
|
579
|
+
operationResults.push(operationEvidence(plan.scope, operation, failure.status, failure.failure));
|
|
163
580
|
}
|
|
164
581
|
}
|
|
165
|
-
|
|
166
|
-
|
|
582
|
+
const entryApplySucceeded = operationResults.some(({ status }) => status === 'changed' || status === 'current');
|
|
583
|
+
const successfulNoOperationApply = operations.length === 0 && !collectionTransaction.attempted;
|
|
584
|
+
if (entryApplySucceeded || successfulNoOperationApply) {
|
|
585
|
+
await saveManifest(manifestPath, nextManifest);
|
|
586
|
+
}
|
|
587
|
+
const collectionApplied = collectionTransaction.results.filter(({ status }) => status === 'changed').length;
|
|
588
|
+
const collectionFailed = collectionTransaction.results.filter(({ status }) => status === 'failed' || status === 'partial' || status === 'rejected').length;
|
|
589
|
+
const collectionSkipped = collectionTransaction.results.filter(({ status }) => status === 'current' || status === 'fallback').length;
|
|
590
|
+
return {
|
|
591
|
+
applied: operationResults.filter(({ status }) => status === 'changed').length +
|
|
592
|
+
collectionApplied,
|
|
593
|
+
failed: operationResults.filter(({ status }) => status === 'failed' || status === 'missing').length + collectionFailed,
|
|
594
|
+
skipped: operationResults.filter(({ status }) => status === 'current').length +
|
|
595
|
+
collectionSkipped,
|
|
596
|
+
operations: operationResults,
|
|
597
|
+
collectionResults: collectionTransaction.results,
|
|
598
|
+
};
|
|
167
599
|
}
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export type { HookInstallInfo, HookStatus } from './hook.js';
|
|
|
6
6
|
export { hasMarker, insertMarker, OAT_DIRECTORY_SENTINEL, OAT_MARKER_PREFIX, writeDirectorySentinel, } from './markers.js';
|
|
7
7
|
export { assertSafeProviderMutationPath } from './provider-path-safety.js';
|
|
8
8
|
export type { CanonicalEntry } from './scanner.js';
|
|
9
|
-
export { scanBundledManagedAgents, scanBundledManagedCodexAgents, scanCanonical, } from './scanner.js';
|
|
9
|
+
export { materializationCanonicalPathAllowed, mergeUserScopeMaterializationEntries, scanBundledManagedAgents, scanBundledManagedCodexAgents, scanCanonical, } from './scanner.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,aAAa,GACd,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,SAAS,EACT,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,aAAa,GACd,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,aAAa,GACd,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,SAAS,EACT,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,mCAAmC,EACnC,oCAAoC,EACpC,wBAAwB,EACxB,6BAA6B,EAC7B,aAAa,GACd,MAAM,WAAW,CAAC"}
|
package/dist/engine/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { executeSyncPlan } from './execute-plan.js';
|
|
|
4
4
|
export { configureLocalHooksPath, getHookInstallInfo, HOOK_DRIFT_WARNING, HOOK_MARKER_END, HOOK_MARKER_START, HOOK_STRAY_INFO, installHook, isHookInstalled, REPO_GITHOOKS_PATH, runHookCheck, uninstallHook, } from './hook.js';
|
|
5
5
|
export { hasMarker, insertMarker, OAT_DIRECTORY_SENTINEL, OAT_MARKER_PREFIX, writeDirectorySentinel, } from './markers.js';
|
|
6
6
|
export { assertSafeProviderMutationPath } from './provider-path-safety.js';
|
|
7
|
-
export { scanBundledManagedAgents, scanBundledManagedCodexAgents, scanCanonical, } from './scanner.js';
|
|
7
|
+
export { materializationCanonicalPathAllowed, mergeUserScopeMaterializationEntries, scanBundledManagedAgents, scanBundledManagedCodexAgents, scanCanonical, } from './scanner.js';
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare function assertSafeProviderMutationPath(scopeRoot: string, providerPath: string): Promise<void>;
|
|
2
|
+
export declare function assertSafeProviderCollectionPath(scopeRoot: string, canonicalDir: string, providerDir: string): Promise<void>;
|
|
2
3
|
//# sourceMappingURL=provider-path-safety.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-path-safety.d.ts","sourceRoot":"","sources":["../../src/engine/provider-path-safety.ts"],"names":[],"mappings":"AAiBA,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAuDf"}
|
|
1
|
+
{"version":3,"file":"provider-path-safety.d.ts","sourceRoot":"","sources":["../../src/engine/provider-path-safety.ts"],"names":[],"mappings":"AAiBA,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAuDf;AAYD,wBAAsB,gCAAgC,CACpD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
|
@@ -52,3 +52,18 @@ export async function assertSafeProviderMutationPath(scopeRoot, providerPath) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
function isNestedPath(parent, candidate) {
|
|
56
|
+
const relativePath = relative(resolve(parent), resolve(candidate));
|
|
57
|
+
return (relativePath === '' ||
|
|
58
|
+
(!isAbsolute(relativePath) &&
|
|
59
|
+
relativePath !== '..' &&
|
|
60
|
+
!relativePath.startsWith(`..${sep}`)));
|
|
61
|
+
}
|
|
62
|
+
export async function assertSafeProviderCollectionPath(scopeRoot, canonicalDir, providerDir) {
|
|
63
|
+
if (isNestedPath(canonicalDir, providerDir) ||
|
|
64
|
+
isNestedPath(providerDir, canonicalDir)) {
|
|
65
|
+
throw new Error('Canonical and provider collection paths must not be nested.');
|
|
66
|
+
}
|
|
67
|
+
await assertSafeProviderMutationPath(scopeRoot, canonicalDir);
|
|
68
|
+
await assertSafeProviderMutationPath(scopeRoot, providerDir);
|
|
69
|
+
}
|
package/dist/engine/scanner.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Scope } from '../shared/types.js';
|
|
1
|
+
import { type CanonicalScanTarget, type Scope } from '../shared/types.js';
|
|
2
2
|
type ConcreteScope = Exclude<Scope, 'all'>;
|
|
3
3
|
export interface CanonicalEntry {
|
|
4
4
|
name: string;
|
|
@@ -7,8 +7,15 @@ export interface CanonicalEntry {
|
|
|
7
7
|
isFile: boolean;
|
|
8
8
|
}
|
|
9
9
|
export declare function scanBundledManagedAgents(): Promise<CanonicalEntry[]>;
|
|
10
|
+
/**
|
|
11
|
+
* User-scope Codex/Cursor materialization reads the bundled managed role files,
|
|
12
|
+
* not the installed copies under `~/.agents/agents/`. Concatenating both lists
|
|
13
|
+
* produces the same Cursor/Codex role name twice and aborts the whole sync.
|
|
14
|
+
*/
|
|
15
|
+
export declare function mergeUserScopeMaterializationEntries(canonicalEntries: CanonicalEntry[], bundledManagedAgents: CanonicalEntry[]): CanonicalEntry[];
|
|
16
|
+
export declare function materializationCanonicalPathAllowed(scopeRoot: string, canonicalEntry: CanonicalEntry, allowedCanonicalPaths?: string[]): boolean;
|
|
10
17
|
/** @deprecated Use scanBundledManagedAgents for provider-neutral materialization. */
|
|
11
18
|
export declare const scanBundledManagedCodexAgents: typeof scanBundledManagedAgents;
|
|
12
|
-
export declare function scanCanonical(basePath: string, scope: ConcreteScope): Promise<CanonicalEntry[]>;
|
|
19
|
+
export declare function scanCanonical(basePath: string, scope: ConcreteScope, targets?: readonly CanonicalScanTarget[]): Promise<CanonicalEntry[]>;
|
|
13
20
|
export {};
|
|
14
21
|
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/engine/scanner.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAEvB,KAAK,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AA0DD,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAsB1E;AAED,qFAAqF;AACrF,eAAO,MAAM,6BAA6B,iCAA2B,CAAC;AAEtE,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/engine/scanner.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAEvB,KAAK,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AA0DD,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAsB1E;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAClD,gBAAgB,EAAE,cAAc,EAAE,EAClC,oBAAoB,EAAE,cAAc,EAAE,GACrC,cAAc,EAAE,CAqBlB;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,EAC9B,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAC/B,OAAO,CAqBT;AAED,qFAAqF;AACrF,eAAO,MAAM,6BAA6B,iCAA2B,CAAC;AAEtE,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,GACvC,OAAO,CAAC,cAAc,EAAE,CAAC,CA6C3B"}
|