@aopslabs/aops 0.3.31 → 0.3.33
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/README.md +11 -3
- package/aops-assets-release/aops-assets.json.gz +0 -0
- package/aops-assets-release/disciplines/build-review-chat/SKILL.md +33 -0
- package/aops-assets-release/disciplines/coordinator-loop/SKILL.md +33 -0
- package/aops-assets-release/disciplines/design-first-consensus/SKILL.md +30 -0
- package/aops-assets-release/disciplines/solo-pm-loop/SKILL.md +31 -0
- package/aops-assets-release/docs/user-guides/agentspace-user-guide.md +384 -0
- package/aops-assets-release/docs/user-guides/aops-cli-user-guide.md +2067 -0
- package/aops-assets-release/docs/user-guides/chatv3-user-guide.md +500 -0
- package/aops-assets-release/docs/user-guides/docman-user-guide.md +1009 -0
- package/aops-assets-release/docs/user-guides/projectman-user-guide.md +763 -0
- package/aops-assets-release/docs/user-guides/tasker-user-guide.md +244 -0
- package/aops-assets-release/release.json +6 -0
- package/aops-assets-release/roles/coordinator/ROLE.md +28 -0
- package/aops-assets-release/roles/implementer/ROLE.md +27 -0
- package/aops-assets-release/roles/reviewer/ROLE.md +27 -0
- package/aops-assets-release/skills/aops/SKILL.md +112 -0
- package/aops-assets-release/skills/aops-bootstrapper-authoring/SKILL.md +245 -0
- package/aops-assets-release/skills/aops-cli-agentspace/SKILL.md +186 -0
- package/aops-assets-release/skills/aops-cli-board-lifecycle/SKILL.md +64 -0
- package/aops-assets-release/skills/aops-cli-chat/SKILL.md +204 -0
- package/aops-assets-release/skills/aops-cli-collab/SKILL.md +42 -0
- package/aops-assets-release/skills/aops-cli-core/SKILL.md +219 -0
- package/aops-assets-release/skills/aops-cli-discuss/SKILL.md +172 -0
- package/aops-assets-release/skills/aops-cli-docman/SKILL.md +192 -0
- package/aops-assets-release/skills/aops-cli-fileman/SKILL.md +169 -0
- package/aops-assets-release/skills/aops-cli-mission/SKILL.md +128 -0
- package/aops-assets-release/skills/aops-cli-operator-brief/SKILL.md +67 -0
- package/aops-assets-release/skills/aops-cli-projectman/SKILL.md +235 -0
- package/aops-assets-release/skills/aops-cli-sugar-authoring/SKILL.md +188 -0
- package/aops-assets-release/skills/aops-cli-tasker/SKILL.md +108 -0
- package/aops-assets-release/skills/aops-cli-tooling-agent/SKILL.md +112 -0
- package/aops-assets-release/skills/aops-cli-view/SKILL.md +210 -0
- package/aops-assets-release/skills/aops-collaborative-work/SKILL.md +304 -0
- package/aops-assets-release/skills/aops-interactive/SKILL.md +70 -0
- package/aops-assets-release/skills/aops-loop-interactive/SKILL.md +314 -0
- package/aops-assets-release/skills/aops-working-disciplines/SKILL.md +310 -0
- package/aops-assets-release/skills/feature-retirement-flow/SKILL.md +126 -0
- package/assets/skills/aops-install/SKILL.md +18 -17
- package/dist/commands/assets.js +93 -1264
- package/dist/commands/community-server.js +27 -8
- package/dist/commands/community-setup.js +1 -2
- package/dist/commands/discuss.js +1 -1
- package/dist/commands/doc.js +88 -27
- package/dist/commands/docs.js +68 -0
- package/dist/commands/global-update.js +28 -0
- package/dist/commands/init.js +3 -1
- package/dist/commands/loop.js +6379 -0
- package/dist/commands/memory.js +2 -1
- package/dist/commands/pm/index.js +11 -9
- package/dist/commands/pm/projectman.js +33 -2
- package/dist/commands/project.js +6 -6
- package/dist/commands/prompt.js +1 -1
- package/dist/commands/repo-sync.js +92 -27
- package/dist/commands/runner.js +257 -0
- package/dist/commands/skill.js +1 -1
- package/dist/commands/start.js +1 -1
- package/dist/commands/tasker.js +361 -0
- package/dist/commands/view.js +53 -9
- package/dist/lib/commercial-setup-readiness-adapter.js +0 -1
- package/dist/lib/community-migration-snapshot.js +7 -9
- package/dist/lib/community-native-database-recovery.js +5 -14
- package/dist/lib/community-native-lifecycle.js +13 -0
- package/dist/lib/setup-agent-assets-bridge.js +28 -148
- package/dist/lib/setup-agent-assets-release.js +16 -44
- package/dist/lib/setup-init-orchestrator.js +11 -15
- package/dist/lib/simple-agent-assets.js +447 -0
- package/dist/lib/user-guide-sections.js +488 -0
- package/dist/main.js +10 -2
- package/dist/seeds/agents-md.js +3 -3
- package/dist/utils/agents-md.js +1 -1
- package/dist/utils/guide-paths.js +7 -7
- package/dist/utils/hosted-workspace.js +2 -1
- package/dist/utils/repo-first-storage.js +26 -1
- package/dist/utils/session-state.js +2 -7
- package/launchers/aops-cockpit.sh +0 -0
- package/launchers/aops-server.sh +0 -0
- package/package.json +67 -35
- package/THIRD_PARTY_NOTICES +0 -16169
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/SKILL.md +0 -47
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/agentspace/SKILL.md +0 -72
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/aops-cli-core/SKILL.md +0 -110
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/chatv3/SKILL.md +0 -59
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/collaborative-work/SKILL.md +0 -81
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/discuss/SKILL.md +0 -66
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/docman/SKILL.md +0 -75
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/projectman/SKILL.md +0 -92
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/sys/SKILL.md +0 -36
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/view/SKILL.md +0 -58
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/references/working-disciplines/SKILL.md +0 -66
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/agent-assets.md +0 -81
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/agentspace.md +0 -183
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/aops-cli.md +0 -1108
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/aops-system.md +0 -102
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/chatv3.md +0 -113
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/docman.md +0 -223
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/projectman.md +0 -233
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/sys.md +0 -94
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/files/user-guides/working-disciplines.md +0 -482
- package/agent-assets-release/agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/manifest.json +0 -117
- package/agent-assets-release/agent-assets/gateway/aops/SKILL.md +0 -14
- package/agent-assets-release/agent-assets/inventory.json +0 -29
- package/agent-assets-release/agent-assets/projection.json +0 -8
- package/agent-assets-release/agent-assets-release.json +0 -171
- package/agent-assets-release/agent-assets-release.sigstore.json +0 -67
- package/assets/agent-assets/core/SKILL.md +0 -47
- package/assets/agent-assets/core/references/agentspace/SKILL.md +0 -72
- package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +0 -110
- package/assets/agent-assets/core/references/chatv3/SKILL.md +0 -59
- package/assets/agent-assets/core/references/collaborative-work/SKILL.md +0 -81
- package/assets/agent-assets/core/references/discuss/SKILL.md +0 -66
- package/assets/agent-assets/core/references/docman/SKILL.md +0 -75
- package/assets/agent-assets/core/references/projectman/SKILL.md +0 -92
- package/assets/agent-assets/core/references/sys/SKILL.md +0 -36
- package/assets/agent-assets/core/references/view/SKILL.md +0 -58
- package/assets/agent-assets/core/references/working-disciplines/SKILL.md +0 -66
- package/assets/agent-assets/core/user-guides/agent-assets.md +0 -81
- package/assets/agent-assets/core/user-guides/agentspace.md +0 -183
- package/assets/agent-assets/core/user-guides/aops-cli.md +0 -1108
- package/assets/agent-assets/core/user-guides/aops-system.md +0 -102
- package/assets/agent-assets/core/user-guides/chatv3.md +0 -113
- package/assets/agent-assets/core/user-guides/docman.md +0 -223
- package/assets/agent-assets/core/user-guides/projectman.md +0 -233
- package/assets/agent-assets/core/user-guides/sys.md +0 -94
- package/assets/agent-assets/core/user-guides/working-disciplines.md +0 -482
- package/assets/agent-assets/gateway/aops/SKILL.md +0 -14
- package/dist/lib/agent-assets/gateway.js +0 -15
- package/dist/lib/agent-assets/guards.js +0 -23
- package/dist/lib/agent-assets/hosted-discovery.js +0 -148
- package/dist/lib/agent-assets/hosted-package-input.js +0 -154
- package/dist/lib/agent-assets/legacy-pointer-migration.js +0 -677
- package/dist/lib/agent-assets/native-fs.js +0 -589
- package/dist/lib/agent-assets/roots.js +0 -45
- package/dist/lib/agent-assets/runtime-binding-reader.js +0 -545
- package/dist/lib/agent-assets/runtime-targets.js +0 -50
- package/dist/lib/agent-assets/store-reader.js +0 -1212
- package/dist/lib/agent-assets/store-writer.js +0 -1484
- package/native/bin/darwin-arm64/aops-agent-assets-fs +0 -0
- package/native/bin/darwin-x64/aops-agent-assets-fs +0 -0
- package/native/bin/linux-arm64/aops-agent-assets-fs +0 -0
- package/native/bin/linux-x64/aops-agent-assets-fs +0 -0
- package/native/bin/win32-x64/aops-agent-assets-fs.exe +0 -0
- package/native/manifest.json +0 -49
- package/native/qualifications/win32-x64.json +0 -23
- package/native/tui/darwin-arm64/aops-tui +0 -0
- package/native/tui/linux-x64/aops-tui +0 -0
- package/native/tui/win32-x64/aops-tui.exe +0 -0
|
@@ -1,677 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { closeSync, constants, existsSync, fstatSync, lstatSync, openSync, readFileSync, realpathSync, unlinkSync, } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { AgentAssetsError } from './envelope.js';
|
|
5
|
-
import { AOPS_AGENT_ASSETS_GATEWAY, AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH, AOPS_AGENT_ASSETS_GATEWAY_SHA256, } from './gateway.js';
|
|
6
|
-
import { openAgentAssetsNativePublicationSession, } from './native-fs.js';
|
|
7
|
-
import { agentAssetsRuntimeHomeId, inspectRuntimeGatewayBinding, parseRuntimeBindingV1, parseRuntimeGatewayOwnerMarkerV1, } from './runtime-binding-reader.js';
|
|
8
|
-
import { canonicalJsonSha256V1, canonicalJsonV1, readAgentAssetsStoreSnapshot, } from './store-reader.js';
|
|
9
|
-
const MAX_LEGACY_POINTER_BYTES = 64 * 1024;
|
|
10
|
-
const MAX_MANAGED_STATE_BYTES = 1024 * 1024;
|
|
11
|
-
const OWNER_MARKER_RELATIVE_PATH = 'skills/aops/.aops-gateway-owner.json';
|
|
12
|
-
export const LEGACY_POINTER_GENERATOR_CONTRACT_V1 = Object.freeze({
|
|
13
|
-
schemaVersion: 1,
|
|
14
|
-
canonicalOwner: 'aops-cli.assets.migrate.legacy-pointers',
|
|
15
|
-
retiredGenerators: Object.freeze([
|
|
16
|
-
Object.freeze({
|
|
17
|
-
template: 'setup-agent-assets-v1',
|
|
18
|
-
source: 'apps/aops-cli/src/utils/agent-assets.ts',
|
|
19
|
-
}),
|
|
20
|
-
Object.freeze({
|
|
21
|
-
template: 'workspace-pointer-sync-v2',
|
|
22
|
-
source: '<workspace>/scripts/sync-aops-skills-to-codex.mjs',
|
|
23
|
-
}),
|
|
24
|
-
]),
|
|
25
|
-
});
|
|
26
|
-
function migrationError(code, message, details) {
|
|
27
|
-
return new AgentAssetsError(code, message, {
|
|
28
|
-
nextActions: [
|
|
29
|
-
'Run `aops assets migrate inspect --json` and leave unknown/user-owned files untouched.',
|
|
30
|
-
],
|
|
31
|
-
...(details === undefined ? {} : { details }),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function sha256(bytes) {
|
|
35
|
-
return createHash('sha256').update(bytes).digest('hex');
|
|
36
|
-
}
|
|
37
|
-
function jsonBytes(value) {
|
|
38
|
-
return Buffer.from(canonicalJsonV1(value), 'utf8');
|
|
39
|
-
}
|
|
40
|
-
function isSameFile(before, after) {
|
|
41
|
-
return before.dev === after.dev && before.ino === after.ino && before.size === after.size;
|
|
42
|
-
}
|
|
43
|
-
function pathWithin(root, candidate) {
|
|
44
|
-
const relative = path.relative(root, candidate);
|
|
45
|
-
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
46
|
-
}
|
|
47
|
-
function linkedExistingAncestor(absolutePath) {
|
|
48
|
-
const normalized = path.normalize(absolutePath);
|
|
49
|
-
const parsed = path.parse(normalized);
|
|
50
|
-
let cursor = parsed.root;
|
|
51
|
-
for (const segment of normalized.slice(parsed.root.length).split(path.sep).filter(Boolean)) {
|
|
52
|
-
cursor = path.join(cursor, segment);
|
|
53
|
-
if (!existsSync(cursor))
|
|
54
|
-
break;
|
|
55
|
-
if (lstatSync(cursor).isSymbolicLink())
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
function safeReadBoundedFile(rootPath, relativePath, maximumBytes) {
|
|
61
|
-
if (!path.isAbsolute(rootPath))
|
|
62
|
-
return { state: 'unsafe-path', reason: 'root-not-absolute' };
|
|
63
|
-
const root = path.normalize(rootPath);
|
|
64
|
-
if (linkedExistingAncestor(root))
|
|
65
|
-
return { state: 'unsafe-path', reason: 'root-or-parent-link' };
|
|
66
|
-
if (!existsSync(root))
|
|
67
|
-
return { state: 'absent' };
|
|
68
|
-
const segments = relativePath.split('/');
|
|
69
|
-
if (segments.some((segment) => !segment || segment === '.' || segment === '..' || segment.includes('\\'))) {
|
|
70
|
-
return { state: 'unsafe-path', reason: 'unsafe-relative-path' };
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
const rootStat = lstatSync(root);
|
|
74
|
-
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
75
|
-
return { state: 'unsafe-path', reason: 'root-link-or-special' };
|
|
76
|
-
}
|
|
77
|
-
const realRoot = realpathSync.native(root);
|
|
78
|
-
let cursor = root;
|
|
79
|
-
for (const segment of segments.slice(0, -1)) {
|
|
80
|
-
cursor = path.join(cursor, segment);
|
|
81
|
-
if (!existsSync(cursor))
|
|
82
|
-
return { state: 'absent' };
|
|
83
|
-
const stat = lstatSync(cursor);
|
|
84
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
85
|
-
return { state: 'unsafe-path', reason: 'ancestor-link-or-special' };
|
|
86
|
-
}
|
|
87
|
-
if (!pathWithin(realRoot, realpathSync.native(cursor))) {
|
|
88
|
-
return { state: 'unsafe-path', reason: 'ancestor-escaped-root' };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const filePath = path.join(cursor, segments.at(-1));
|
|
92
|
-
if (!existsSync(filePath))
|
|
93
|
-
return { state: 'absent' };
|
|
94
|
-
const before = lstatSync(filePath);
|
|
95
|
-
if (!before.isFile() || before.isSymbolicLink() || before.size < 1 || before.size > maximumBytes) {
|
|
96
|
-
return { state: 'unsafe-path', reason: 'file-link-special-or-unbounded' };
|
|
97
|
-
}
|
|
98
|
-
if (!pathWithin(realRoot, realpathSync.native(filePath))) {
|
|
99
|
-
return { state: 'unsafe-path', reason: 'file-escaped-root' };
|
|
100
|
-
}
|
|
101
|
-
const noFollow = process.platform === 'win32' ? 0 : (constants.O_NOFOLLOW ?? 0);
|
|
102
|
-
const fd = openSync(filePath, constants.O_RDONLY | noFollow);
|
|
103
|
-
try {
|
|
104
|
-
const opened = fstatSync(fd);
|
|
105
|
-
if (!opened.isFile() || !isSameFile(before, opened)) {
|
|
106
|
-
return { state: 'unsafe-path', reason: 'file-changed-during-open' };
|
|
107
|
-
}
|
|
108
|
-
const bytes = readFileSync(fd);
|
|
109
|
-
const after = lstatSync(filePath);
|
|
110
|
-
if (!isSameFile(opened, after) || after.isSymbolicLink()) {
|
|
111
|
-
return { state: 'unsafe-path', reason: 'file-changed-during-read' };
|
|
112
|
-
}
|
|
113
|
-
if (!pathWithin(realRoot, realpathSync.native(filePath))) {
|
|
114
|
-
return { state: 'unsafe-path', reason: 'file-escaped-after-read' };
|
|
115
|
-
}
|
|
116
|
-
return { state: 'ready', bytes };
|
|
117
|
-
}
|
|
118
|
-
finally {
|
|
119
|
-
closeSync(fd);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
return { state: 'unsafe-path', reason: error instanceof Error ? error.name : 'read-failed' };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function parseDescriptionHeader(lines) {
|
|
127
|
-
if (lines[0] !== '---'
|
|
128
|
-
|| lines[1] !== 'name: aops'
|
|
129
|
-
|| !lines[2]?.startsWith('description: ')
|
|
130
|
-
|| lines[3] !== '---'
|
|
131
|
-
|| lines[4] !== '')
|
|
132
|
-
return false;
|
|
133
|
-
try {
|
|
134
|
-
const description = JSON.parse(lines[2].slice('description: '.length));
|
|
135
|
-
return typeof description === 'string' && description.length > 0 && description.length <= 8_192;
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const SETUP_AGENT_ASSETS_V1_BODY = Object.freeze([
|
|
142
|
-
'# aops (pointer)',
|
|
143
|
-
'',
|
|
144
|
-
'This skill is a thin pointer to the single source of truth. The canonical content lives in the hosted skill mirror inside the active repo.',
|
|
145
|
-
'',
|
|
146
|
-
'**Canonical file:** `.aops/hosted/skills/aops.md` (cwd-relative; mirrored via `aops-cli sync pull --apply --hosted-project-slug aops --yes --json`).',
|
|
147
|
-
'',
|
|
148
|
-
'## When triggered',
|
|
149
|
-
'',
|
|
150
|
-
'1. Read `.aops/hosted/skills/aops.md` from the current working directory.',
|
|
151
|
-
'2. If found, follow its instructions verbatim. The hosted file is canonical and may have been updated since this pointer was authored; do not duplicate or paraphrase its content here.',
|
|
152
|
-
'3. If missing, bootstrap the mirror with the `sync pull` command above. If the active repo is out of context for this skill, say that briefly and stop.',
|
|
153
|
-
'',
|
|
154
|
-
]);
|
|
155
|
-
const WORKSPACE_POINTER_V2_PREFIX = Object.freeze([
|
|
156
|
-
'# aops (pointer)',
|
|
157
|
-
'',
|
|
158
|
-
'This skill is a thin pointer to the single source of truth. The canonical content lives in the hosted skill mirror inside the active repo.',
|
|
159
|
-
'',
|
|
160
|
-
'**Canonical file:** `.aops/hosted/skills/aops.md` (cwd-relative; hosted project slug: `aops`).',
|
|
161
|
-
'',
|
|
162
|
-
'Known repo candidates from the last global sync:',
|
|
163
|
-
'',
|
|
164
|
-
]);
|
|
165
|
-
const WORKSPACE_POINTER_V2_SUFFIX = Object.freeze([
|
|
166
|
-
'',
|
|
167
|
-
'## When triggered',
|
|
168
|
-
'',
|
|
169
|
-
'1. First read `.aops/hosted/skills/aops.md` from the current working directory.',
|
|
170
|
-
'2. If found, follow that file verbatim. It may have changed since this pointer was authored; do not duplicate or paraphrase it here.',
|
|
171
|
-
'3. If the cwd-relative file is missing, read the first existing known repo candidate above that matches this skill.',
|
|
172
|
-
'4. If every candidate is missing or stale, refresh hosted mirrors from the relevant repo with `aops-cli sync pull --apply --hosted-project-slug aops --json`, then rerun the pointer sync.',
|
|
173
|
-
'5. If the active repo is not one of the known candidates and no source file exists, say the skill does not apply in this cwd and stop.',
|
|
174
|
-
'',
|
|
175
|
-
]);
|
|
176
|
-
function exactLines(actual, expected) {
|
|
177
|
-
return actual.length === expected.length && actual.every((line, index) => line === expected[index]);
|
|
178
|
-
}
|
|
179
|
-
function validLegacyCandidateLine(line, expectedIndex) {
|
|
180
|
-
const match = line.match(/^([1-9]\d*)\. `([^`\r\n]+)` \((hosted|repo-local); project: aops\)$/);
|
|
181
|
-
if (!match || Number.parseInt(match[1], 10) !== expectedIndex)
|
|
182
|
-
return false;
|
|
183
|
-
const candidatePath = match[2];
|
|
184
|
-
if (!path.win32.isAbsolute(candidatePath) && !path.posix.isAbsolute(candidatePath))
|
|
185
|
-
return false;
|
|
186
|
-
const normalized = candidatePath.replaceAll('\\', '/');
|
|
187
|
-
return match[3] === 'hosted'
|
|
188
|
-
? normalized.endsWith('/.aops/hosted/skills/aops.md')
|
|
189
|
-
: normalized.endsWith('/skills/aops/SKILL.md');
|
|
190
|
-
}
|
|
191
|
-
export function parseRecognizedLegacyAopsPointer(bytes) {
|
|
192
|
-
const buffer = Buffer.from(bytes);
|
|
193
|
-
if (buffer.byteLength < 1 || buffer.byteLength > MAX_LEGACY_POINTER_BYTES)
|
|
194
|
-
return null;
|
|
195
|
-
const text = buffer.toString('utf8');
|
|
196
|
-
if (!Buffer.from(text, 'utf8').equals(buffer) || text.includes('\0') || /\r(?!\n)/.test(text))
|
|
197
|
-
return null;
|
|
198
|
-
const normalized = text.replaceAll('\r\n', '\n');
|
|
199
|
-
if (!normalized.endsWith('\n') || normalized.endsWith('\n\n'))
|
|
200
|
-
return null;
|
|
201
|
-
const lines = normalized.split('\n');
|
|
202
|
-
if (!parseDescriptionHeader(lines))
|
|
203
|
-
return null;
|
|
204
|
-
const body = lines.slice(5);
|
|
205
|
-
if (exactLines(body, SETUP_AGENT_ASSETS_V1_BODY)) {
|
|
206
|
-
return Object.freeze({ template: 'setup-agent-assets-v1', sha256: sha256(buffer), bytes: buffer });
|
|
207
|
-
}
|
|
208
|
-
if (!exactLines(body.slice(0, WORKSPACE_POINTER_V2_PREFIX.length), WORKSPACE_POINTER_V2_PREFIX))
|
|
209
|
-
return null;
|
|
210
|
-
const suffixStart = body.length - WORKSPACE_POINTER_V2_SUFFIX.length;
|
|
211
|
-
if (suffixStart <= WORKSPACE_POINTER_V2_PREFIX.length)
|
|
212
|
-
return null;
|
|
213
|
-
if (!exactLines(body.slice(suffixStart), WORKSPACE_POINTER_V2_SUFFIX))
|
|
214
|
-
return null;
|
|
215
|
-
const candidates = body.slice(WORKSPACE_POINTER_V2_PREFIX.length, suffixStart);
|
|
216
|
-
if (candidates.length < 1 || candidates.length > 16)
|
|
217
|
-
return null;
|
|
218
|
-
if (!candidates.every((line, index) => validLegacyCandidateLine(line, index + 1)))
|
|
219
|
-
return null;
|
|
220
|
-
return Object.freeze({ template: 'workspace-pointer-sync-v2', sha256: sha256(buffer), bytes: buffer });
|
|
221
|
-
}
|
|
222
|
-
function classifyRuntime(assetRoot, runtime, runtimeHome, expectedRuntimeRootIdentitySha256) {
|
|
223
|
-
const binding = inspectRuntimeGatewayBinding({
|
|
224
|
-
assetRoot,
|
|
225
|
-
runtime,
|
|
226
|
-
runtimeHome,
|
|
227
|
-
...(expectedRuntimeRootIdentitySha256 ? { expectedRuntimeRootIdentitySha256 } : {}),
|
|
228
|
-
});
|
|
229
|
-
const read = safeReadBoundedFile(runtimeHome, AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH, MAX_LEGACY_POINTER_BYTES);
|
|
230
|
-
const base = {
|
|
231
|
-
schemaVersion: 1,
|
|
232
|
-
runtime,
|
|
233
|
-
runtimeHomeId: agentAssetsRuntimeHomeId(runtime, runtimeHome),
|
|
234
|
-
relativePath: AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH,
|
|
235
|
-
bindingState: binding.state,
|
|
236
|
-
};
|
|
237
|
-
if (read.state === 'unsafe-path') {
|
|
238
|
-
return Object.freeze({
|
|
239
|
-
...base,
|
|
240
|
-
state: 'unsafe-path',
|
|
241
|
-
eligible: false,
|
|
242
|
-
reasons: Object.freeze([read.reason]),
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
if (read.state === 'absent') {
|
|
246
|
-
return Object.freeze({
|
|
247
|
-
...base,
|
|
248
|
-
state: 'absent',
|
|
249
|
-
eligible: false,
|
|
250
|
-
reasons: Object.freeze(['gateway-absent']),
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
const contentSha256 = sha256(read.bytes);
|
|
254
|
-
if (contentSha256 === AOPS_AGENT_ASSETS_GATEWAY_SHA256
|
|
255
|
-
&& read.bytes.equals(Buffer.from(AOPS_AGENT_ASSETS_GATEWAY, 'utf8'))) {
|
|
256
|
-
const managedReady = binding.state === 'ready';
|
|
257
|
-
return Object.freeze({
|
|
258
|
-
...base,
|
|
259
|
-
state: managedReady ? 'managed-ready' : 'ownership-conflict',
|
|
260
|
-
eligible: false,
|
|
261
|
-
contentSha256,
|
|
262
|
-
reasons: Object.freeze(managedReady ? [] : ['canonical-gateway-without-ready-binding']),
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
const legacy = parseRecognizedLegacyAopsPointer(read.bytes);
|
|
266
|
-
if (!legacy) {
|
|
267
|
-
return Object.freeze({
|
|
268
|
-
...base,
|
|
269
|
-
state: 'unknown-user-owned',
|
|
270
|
-
eligible: false,
|
|
271
|
-
contentSha256,
|
|
272
|
-
reasons: Object.freeze(['content-does-not-match-a-frozen-generated-template']),
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
const eligible = (binding.storeBinding === 'absent'
|
|
276
|
-
&& binding.ownerMarker === 'absent') || (binding.storeBinding === 'ready'
|
|
277
|
-
&& (binding.ownerMarker === 'absent' || binding.ownerMarker === 'ready'));
|
|
278
|
-
return Object.freeze({
|
|
279
|
-
...base,
|
|
280
|
-
state: 'recognized-legacy',
|
|
281
|
-
eligible,
|
|
282
|
-
contentSha256,
|
|
283
|
-
legacyTemplate: legacy.template,
|
|
284
|
-
reasons: Object.freeze(eligible ? [] : [...binding.reasons]),
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
export function inspectLegacyAopsPointers(options) {
|
|
288
|
-
const classifications = [];
|
|
289
|
-
for (const runtime of ['codex', 'claude']) {
|
|
290
|
-
const selected = options.runtimeHomes[runtime];
|
|
291
|
-
if (!selected)
|
|
292
|
-
continue;
|
|
293
|
-
classifications.push(classifyRuntime(path.resolve(options.assetRoot), runtime, path.resolve(selected), options.expectedRuntimeRootIdentitySha256?.[runtime]));
|
|
294
|
-
}
|
|
295
|
-
return Object.freeze(classifications);
|
|
296
|
-
}
|
|
297
|
-
function nearestExistingAnchor(assetRoot) {
|
|
298
|
-
let cursor = path.resolve(assetRoot);
|
|
299
|
-
while (!existsSync(cursor)) {
|
|
300
|
-
const parent = path.dirname(cursor);
|
|
301
|
-
if (parent === cursor)
|
|
302
|
-
throw migrationError('atomic_primitive_unavailable', 'No trusted bootstrap anchor exists.');
|
|
303
|
-
cursor = parent;
|
|
304
|
-
}
|
|
305
|
-
const stat = lstatSync(cursor);
|
|
306
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
307
|
-
throw migrationError('link_unsafe_path', 'The nearest bootstrap anchor is not a real directory.');
|
|
308
|
-
}
|
|
309
|
-
return realpathSync.native(cursor);
|
|
310
|
-
}
|
|
311
|
-
function publicationCapability(capability) {
|
|
312
|
-
switch (capability.capabilityClass) {
|
|
313
|
-
case 'linux-posix-durable-v1': return 'posix-durable-v1';
|
|
314
|
-
case 'macos-exclusive-durable-v1': return 'macos-durable-v1';
|
|
315
|
-
case 'windows-ntfs-crash-recoverable-v1': return 'windows-ntfs-crash-recoverable-v1';
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
function assertAuthorityMatchesCapability(authority, capability) {
|
|
319
|
-
if (authority.boundMachineId !== capability.machineIdentitySha256) {
|
|
320
|
-
throw migrationError('different_machine_store', 'The agent-assets store is bound to another machine.');
|
|
321
|
-
}
|
|
322
|
-
if (authority.rootIdentitySha256 !== capability.rootIdentitySha256) {
|
|
323
|
-
throw migrationError('store_identity_mismatch', 'The agent-assets store root identity changed.');
|
|
324
|
-
}
|
|
325
|
-
if (authority.publicationCapability !== publicationCapability(capability)
|
|
326
|
-
|| authority.capabilityEvidenceSha256 !== capability.capabilityEvidenceSha256) {
|
|
327
|
-
throw migrationError('durability_unavailable', 'The native publication capability does not match store authority.');
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
async function ensureDirectory(root, relativePath, create) {
|
|
331
|
-
let cursor = path.resolve(root);
|
|
332
|
-
let current = '';
|
|
333
|
-
for (const segment of relativePath.split('/')) {
|
|
334
|
-
current = current ? `${current}/${segment}` : segment;
|
|
335
|
-
cursor = path.join(cursor, segment);
|
|
336
|
-
if (!existsSync(cursor)) {
|
|
337
|
-
await create(current);
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const stat = lstatSync(cursor);
|
|
341
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
342
|
-
throw migrationError('link_unsafe_path', 'A migration directory is not a plain directory.', { relativePath: current });
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
function requireSafeFile(root, relativePath, maximumBytes = MAX_MANAGED_STATE_BYTES) {
|
|
347
|
-
const read = safeReadBoundedFile(root, relativePath, maximumBytes);
|
|
348
|
-
if (read.state === 'ready')
|
|
349
|
-
return read.bytes;
|
|
350
|
-
if (read.state === 'unsafe-path') {
|
|
351
|
-
throw migrationError('link_unsafe_path', 'A managed migration file is unsafe.', { relativePath, reason: read.reason });
|
|
352
|
-
}
|
|
353
|
-
throw migrationError('not_found', 'A managed migration file is missing.', { relativePath });
|
|
354
|
-
}
|
|
355
|
-
async function publishNoReplaceOrVerify(publish, root, relativePath, content) {
|
|
356
|
-
try {
|
|
357
|
-
await publish(relativePath, content);
|
|
358
|
-
}
|
|
359
|
-
catch (error) {
|
|
360
|
-
if (!(error instanceof AgentAssetsError) || error.code !== 'publication_conflict')
|
|
361
|
-
throw error;
|
|
362
|
-
if (!requireSafeFile(root, relativePath).equals(Buffer.from(content)))
|
|
363
|
-
throw error;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
async function issueWriterFence(session, assetRoot, authority, now) {
|
|
367
|
-
await ensureDirectory(assetRoot, 'state/authorities', (relativePath) => session.createDirectory(relativePath));
|
|
368
|
-
const authorityBytes = jsonBytes(authority);
|
|
369
|
-
const authoritySha256 = canonicalJsonSha256V1(authority);
|
|
370
|
-
await publishNoReplaceOrVerify((relativePath, content) => session.publishFileNoReplace(relativePath, content), assetRoot, `state/authorities/${authority.authorityRevision}-${authoritySha256}.json`, authorityBytes);
|
|
371
|
-
const next = {
|
|
372
|
-
...authority,
|
|
373
|
-
authorityRevision: authority.authorityRevision + 1,
|
|
374
|
-
lastIssuedFenceEpoch: authority.lastIssuedFenceEpoch + 1,
|
|
375
|
-
previousAuthoritySha256: authoritySha256,
|
|
376
|
-
updatedAt: now,
|
|
377
|
-
};
|
|
378
|
-
const current = requireSafeFile(assetRoot, 'state/store-authority.json');
|
|
379
|
-
await session.publishFileReplace('state/store-authority.json', sha256(current), jsonBytes(next));
|
|
380
|
-
return next;
|
|
381
|
-
}
|
|
382
|
-
function markerFor(binding) {
|
|
383
|
-
return {
|
|
384
|
-
schemaVersion: 1,
|
|
385
|
-
owner: 'aops-cli-agent-assets',
|
|
386
|
-
storeId: binding.storeId,
|
|
387
|
-
runtime: binding.runtime,
|
|
388
|
-
bindingId: binding.bindingId,
|
|
389
|
-
bindingGeneration: binding.bindingGeneration,
|
|
390
|
-
relativePath: binding.relativePath,
|
|
391
|
-
contentSha256: binding.contentSha256,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
function readCurrentBinding(assetRoot, runtime) {
|
|
395
|
-
try {
|
|
396
|
-
return parseRuntimeBindingV1(JSON.parse(requireSafeFile(assetRoot, `state/bindings/${runtime}.json`).toString('utf8')));
|
|
397
|
-
}
|
|
398
|
-
catch (error) {
|
|
399
|
-
if (error instanceof AgentAssetsError)
|
|
400
|
-
throw error;
|
|
401
|
-
throw migrationError('schema_incompatible', `${runtime} binding is not valid JSON.`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
function assertRecoverableBinding(binding, prepared, storeId) {
|
|
405
|
-
if (!prepared.legacy
|
|
406
|
-
|| binding.storeId !== storeId
|
|
407
|
-
|| binding.runtime !== prepared.runtime
|
|
408
|
-
|| binding.runtimeHomeId !== agentAssetsRuntimeHomeId(prepared.runtime, prepared.runtimeHome)
|
|
409
|
-
|| binding.runtimeRootIdentitySha256 !== prepared.nativeRoot.rootIdentitySha256
|
|
410
|
-
|| binding.contentSha256 !== AOPS_AGENT_ASSETS_GATEWAY_SHA256
|
|
411
|
-
|| binding.previousContentSha256 !== prepared.legacy.sha256) {
|
|
412
|
-
throw migrationError('binding_conflict', `${prepared.runtime} has an unrelated managed binding.`, {
|
|
413
|
-
runtime: prepared.runtime,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
function assertMarkerMatches(binding, marker) {
|
|
418
|
-
if (canonicalJsonV1(markerFor(binding)) !== canonicalJsonV1(marker)) {
|
|
419
|
-
throw migrationError('binding_conflict', `${binding.runtime} ownership marker belongs to another binding.`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
async function publishMarker(prepared, binding) {
|
|
423
|
-
const marker = markerFor(binding);
|
|
424
|
-
const read = safeReadBoundedFile(prepared.runtimeHome, OWNER_MARKER_RELATIVE_PATH, MAX_LEGACY_POINTER_BYTES);
|
|
425
|
-
if (read.state === 'unsafe-path') {
|
|
426
|
-
throw migrationError('link_unsafe_path', `${prepared.runtime} ownership marker path is unsafe.`);
|
|
427
|
-
}
|
|
428
|
-
if (read.state === 'ready') {
|
|
429
|
-
try {
|
|
430
|
-
assertMarkerMatches(binding, parseRuntimeGatewayOwnerMarkerV1(JSON.parse(read.bytes.toString('utf8'))));
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
catch (error) {
|
|
434
|
-
if (error instanceof AgentAssetsError)
|
|
435
|
-
throw error;
|
|
436
|
-
throw migrationError('binding_conflict', `${prepared.runtime} ownership marker is not recognized.`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
await prepared.nativeRoot.publishFileNoReplace(OWNER_MARKER_RELATIVE_PATH, jsonBytes(marker));
|
|
440
|
-
}
|
|
441
|
-
async function createBinding(session, assetRoot, prepared, authority, active, now, randomId) {
|
|
442
|
-
if (!prepared.legacy)
|
|
443
|
-
throw migrationError('schema_incompatible', 'Legacy pointer bytes are required for migration.');
|
|
444
|
-
const bindingGeneration = 1;
|
|
445
|
-
const bindingId = `binding-${prepared.runtime}-legacy-${randomId()}`;
|
|
446
|
-
const bindingReceiptId = `binding-receipt-${prepared.runtime}-${bindingGeneration}-${authority.lastIssuedFenceEpoch}-${randomId()}`;
|
|
447
|
-
const draft = {
|
|
448
|
-
schemaVersion: 1,
|
|
449
|
-
storeId: authority.storeId,
|
|
450
|
-
bindingId,
|
|
451
|
-
bindingGeneration,
|
|
452
|
-
runtime: prepared.runtime,
|
|
453
|
-
runtimeHomeId: agentAssetsRuntimeHomeId(prepared.runtime, prepared.runtimeHome),
|
|
454
|
-
runtimeRootIdentitySha256: prepared.nativeRoot.rootIdentitySha256,
|
|
455
|
-
gatewayName: 'aops',
|
|
456
|
-
relativePath: AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH,
|
|
457
|
-
ownerMarkerRelativePath: OWNER_MARKER_RELATIVE_PATH,
|
|
458
|
-
contentSha256: AOPS_AGENT_ASSETS_GATEWAY_SHA256,
|
|
459
|
-
activationReceiptId: active.receiptId,
|
|
460
|
-
activationReceiptSha256: active.receiptSha256,
|
|
461
|
-
bindingReceiptId,
|
|
462
|
-
previousContentSha256: prepared.legacy.sha256,
|
|
463
|
-
installedAt: now,
|
|
464
|
-
writerFenceEpoch: authority.lastIssuedFenceEpoch,
|
|
465
|
-
authorityRevision: authority.authorityRevision,
|
|
466
|
-
};
|
|
467
|
-
const marker = markerFor(draft);
|
|
468
|
-
const receipt = {
|
|
469
|
-
...draft,
|
|
470
|
-
ownerMarkerSha256: canonicalJsonSha256V1(marker),
|
|
471
|
-
};
|
|
472
|
-
const binding = {
|
|
473
|
-
...receipt,
|
|
474
|
-
bindingReceiptSha256: canonicalJsonSha256V1(receipt),
|
|
475
|
-
};
|
|
476
|
-
await ensureDirectory(assetRoot, `state/bindings/receipts/${prepared.runtime}`, (relativePath) => (session.createDirectory(relativePath)));
|
|
477
|
-
await session.publishFileNoReplace(`state/bindings/receipts/${prepared.runtime}/${bindingGeneration}-${bindingReceiptId}.json`, jsonBytes(receipt));
|
|
478
|
-
await session.publishFileNoReplace(`state/bindings/${prepared.runtime}.json`, jsonBytes(binding));
|
|
479
|
-
return binding;
|
|
480
|
-
}
|
|
481
|
-
async function prepareRuntimes(session, assetRoot, runtimeHomes) {
|
|
482
|
-
const prepared = [];
|
|
483
|
-
for (const runtime of ['codex', 'claude']) {
|
|
484
|
-
const selected = runtimeHomes[runtime];
|
|
485
|
-
if (!selected)
|
|
486
|
-
continue;
|
|
487
|
-
const runtimeHome = path.resolve(selected);
|
|
488
|
-
const nativeRoot = await session.registerRuntimeRoot(runtimeHome);
|
|
489
|
-
const classification = classifyRuntime(assetRoot, runtime, runtimeHome, nativeRoot.rootIdentitySha256);
|
|
490
|
-
const gateway = safeReadBoundedFile(runtimeHome, AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH, MAX_LEGACY_POINTER_BYTES);
|
|
491
|
-
const legacy = gateway.state === 'ready' ? parseRecognizedLegacyAopsPointer(gateway.bytes) : null;
|
|
492
|
-
prepared.push(Object.freeze({
|
|
493
|
-
runtime,
|
|
494
|
-
runtimeHome,
|
|
495
|
-
nativeRoot,
|
|
496
|
-
classification,
|
|
497
|
-
...(legacy ? { legacy } : {}),
|
|
498
|
-
}));
|
|
499
|
-
}
|
|
500
|
-
return Object.freeze(prepared);
|
|
501
|
-
}
|
|
502
|
-
export async function migrateLegacyAopsPointers(options) {
|
|
503
|
-
const assetRoot = path.resolve(options.assetRoot);
|
|
504
|
-
const openNative = options.openNative ?? openAgentAssetsNativePublicationSession;
|
|
505
|
-
const now = (options.now ?? (() => new Date()))().toISOString();
|
|
506
|
-
const randomId = options.randomId ?? randomUUID;
|
|
507
|
-
const session = await openNative({
|
|
508
|
-
agentAssetsRoot: assetRoot,
|
|
509
|
-
bootstrapAnchor: options.bootstrapAnchor ?? nearestExistingAnchor(assetRoot),
|
|
510
|
-
requiredDurability: 'process-crash',
|
|
511
|
-
});
|
|
512
|
-
try {
|
|
513
|
-
const snapshot = readAgentAssetsStoreSnapshot({
|
|
514
|
-
assetRoot,
|
|
515
|
-
expectedMachineId: session.capability.machineIdentitySha256,
|
|
516
|
-
expectedRootIdentitySha256: session.capability.rootIdentitySha256,
|
|
517
|
-
});
|
|
518
|
-
if (!snapshot?.active || !snapshot.receipt) {
|
|
519
|
-
throw migrationError('not_found', 'Install the verified AOPS core before migrating legacy runtime pointers.');
|
|
520
|
-
}
|
|
521
|
-
assertAuthorityMatchesCapability(snapshot.authority, session.capability);
|
|
522
|
-
const prepared = await prepareRuntimes(session, assetRoot, options.runtimeHomes);
|
|
523
|
-
const conflicts = prepared.filter((item) => (item.classification.state !== 'recognized-legacy'
|
|
524
|
-
&& item.classification.state !== 'managed-ready'
|
|
525
|
-
&& item.classification.state !== 'absent') || (item.classification.state === 'recognized-legacy' && !item.classification.eligible));
|
|
526
|
-
if (conflicts.length > 0) {
|
|
527
|
-
throw migrationError('binding_conflict', 'Legacy pointer migration found an unknown or conflicting runtime file.', {
|
|
528
|
-
runtimes: conflicts.map((item) => item.runtime),
|
|
529
|
-
states: conflicts.map((item) => item.classification.state),
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
const candidates = prepared.filter((item) => item.classification.state === 'recognized-legacy');
|
|
533
|
-
if (candidates.length === 0) {
|
|
534
|
-
return {
|
|
535
|
-
idempotent: true,
|
|
536
|
-
migrated: Object.freeze([]),
|
|
537
|
-
unchanged: Object.freeze(prepared.map((item) => item.runtime)),
|
|
538
|
-
authority: snapshot.authority,
|
|
539
|
-
active: snapshot.active,
|
|
540
|
-
receipt: snapshot.receipt,
|
|
541
|
-
capability: session.capability,
|
|
542
|
-
classifications: inspectLegacyAopsPointers({ assetRoot, runtimeHomes: options.runtimeHomes }),
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
await ensureDirectory(assetRoot, 'state/bindings', (relativePath) => session.createDirectory(relativePath));
|
|
546
|
-
let authority = snapshot.authority;
|
|
547
|
-
const requireNewBinding = candidates.some((item) => item.classification.bindingState === 'ownership-conflict'
|
|
548
|
-
&& inspectRuntimeGatewayBinding({
|
|
549
|
-
assetRoot,
|
|
550
|
-
runtime: item.runtime,
|
|
551
|
-
runtimeHome: item.runtimeHome,
|
|
552
|
-
expectedRuntimeRootIdentitySha256: item.nativeRoot.rootIdentitySha256,
|
|
553
|
-
}).storeBinding === 'absent');
|
|
554
|
-
if (requireNewBinding)
|
|
555
|
-
authority = await issueWriterFence(session, assetRoot, authority, now);
|
|
556
|
-
for (const preparedRuntime of candidates) {
|
|
557
|
-
const inspection = inspectRuntimeGatewayBinding({
|
|
558
|
-
assetRoot,
|
|
559
|
-
runtime: preparedRuntime.runtime,
|
|
560
|
-
runtimeHome: preparedRuntime.runtimeHome,
|
|
561
|
-
expectedRuntimeRootIdentitySha256: preparedRuntime.nativeRoot.rootIdentitySha256,
|
|
562
|
-
});
|
|
563
|
-
let binding;
|
|
564
|
-
if (inspection.storeBinding === 'ready') {
|
|
565
|
-
binding = readCurrentBinding(assetRoot, preparedRuntime.runtime);
|
|
566
|
-
assertRecoverableBinding(binding, preparedRuntime, authority.storeId);
|
|
567
|
-
}
|
|
568
|
-
else if (inspection.storeBinding === 'absent') {
|
|
569
|
-
binding = await createBinding(session, assetRoot, preparedRuntime, authority, snapshot.active, now, randomId);
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
throw migrationError('binding_conflict', `${preparedRuntime.runtime} store binding cannot be migrated safely.`);
|
|
573
|
-
}
|
|
574
|
-
await ensureDirectory(preparedRuntime.runtimeHome, 'skills/aops', (relativePath) => preparedRuntime.nativeRoot.createDirectory(relativePath));
|
|
575
|
-
await publishMarker(preparedRuntime, binding);
|
|
576
|
-
const currentGateway = requireSafeFile(preparedRuntime.runtimeHome, AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH, MAX_LEGACY_POINTER_BYTES);
|
|
577
|
-
const legacy = parseRecognizedLegacyAopsPointer(currentGateway);
|
|
578
|
-
if (!legacy || legacy.sha256 !== binding.previousContentSha256) {
|
|
579
|
-
throw migrationError('binding_conflict', `${preparedRuntime.runtime} pointer changed after migration preflight.`);
|
|
580
|
-
}
|
|
581
|
-
await preparedRuntime.nativeRoot.publishFileReplace(AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH, legacy.sha256, Buffer.from(AOPS_AGENT_ASSETS_GATEWAY, 'utf8'));
|
|
582
|
-
const after = inspectRuntimeGatewayBinding({
|
|
583
|
-
assetRoot,
|
|
584
|
-
runtime: preparedRuntime.runtime,
|
|
585
|
-
runtimeHome: preparedRuntime.runtimeHome,
|
|
586
|
-
expectedRuntimeRootIdentitySha256: preparedRuntime.nativeRoot.rootIdentitySha256,
|
|
587
|
-
});
|
|
588
|
-
if (after.state !== 'ready') {
|
|
589
|
-
throw migrationError('atomic_replace_blocked', `${preparedRuntime.runtime} gateway did not become ready.`, {
|
|
590
|
-
state: after.state,
|
|
591
|
-
reasons: after.reasons,
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
const afterSnapshot = readAgentAssetsStoreSnapshot({
|
|
596
|
-
assetRoot,
|
|
597
|
-
expectedMachineId: session.capability.machineIdentitySha256,
|
|
598
|
-
expectedRootIdentitySha256: session.capability.rootIdentitySha256,
|
|
599
|
-
});
|
|
600
|
-
if (!afterSnapshot?.active || !afterSnapshot.receipt) {
|
|
601
|
-
throw migrationError('atomic_replace_blocked', 'Migration lost the active verified core chain.');
|
|
602
|
-
}
|
|
603
|
-
return {
|
|
604
|
-
idempotent: false,
|
|
605
|
-
migrated: Object.freeze(candidates.map((item) => item.runtime)),
|
|
606
|
-
unchanged: Object.freeze(prepared
|
|
607
|
-
.filter((item) => item.classification.state !== 'recognized-legacy')
|
|
608
|
-
.map((item) => item.runtime)),
|
|
609
|
-
authority: afterSnapshot.authority,
|
|
610
|
-
active: afterSnapshot.active,
|
|
611
|
-
receipt: afterSnapshot.receipt,
|
|
612
|
-
capability: session.capability,
|
|
613
|
-
classifications: inspectLegacyAopsPointers({ assetRoot, runtimeHomes: options.runtimeHomes }),
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
finally {
|
|
617
|
-
await session.close();
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
function unlinkVerifiedPointerFile(root, relativePath, expectedSha256) {
|
|
621
|
-
const read = safeReadBoundedFile(root, relativePath, MAX_MANAGED_STATE_BYTES);
|
|
622
|
-
if (read.state !== 'ready') {
|
|
623
|
-
throw migrationError(read.state === 'unsafe-path' ? 'link_unsafe_path' : 'not_found', 'A global AOPS gateway file changed after removal preflight.', { relativePath, state: read.state });
|
|
624
|
-
}
|
|
625
|
-
if (sha256(read.bytes) !== expectedSha256) {
|
|
626
|
-
throw migrationError('binding_conflict', 'A global AOPS gateway file changed after removal preflight.', {
|
|
627
|
-
relativePath,
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
unlinkSync(path.join(path.resolve(root), ...relativePath.split('/')));
|
|
631
|
-
}
|
|
632
|
-
export function removeAopsGatewayPointers(options) {
|
|
633
|
-
const assetRoot = path.resolve(options.assetRoot);
|
|
634
|
-
const before = inspectLegacyAopsPointers({ assetRoot, runtimeHomes: options.runtimeHomes });
|
|
635
|
-
const conflicts = before.filter((item) => (item.state !== 'absent'
|
|
636
|
-
&& item.state !== 'recognized-legacy'
|
|
637
|
-
&& item.state !== 'managed-ready') || (item.state === 'recognized-legacy' && !item.eligible));
|
|
638
|
-
if (conflicts.length > 0) {
|
|
639
|
-
throw migrationError('binding_conflict', 'Gateway removal found an unknown, unsafe, or unowned runtime file.', {
|
|
640
|
-
runtimes: conflicts.map((item) => item.runtime),
|
|
641
|
-
states: conflicts.map((item) => item.state),
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
const removed = [];
|
|
645
|
-
const unchanged = [];
|
|
646
|
-
const retainedManagedBindings = [];
|
|
647
|
-
for (const classification of before) {
|
|
648
|
-
if (classification.state === 'absent') {
|
|
649
|
-
unchanged.push(classification.runtime);
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
const runtimeHome = options.runtimeHomes[classification.runtime];
|
|
653
|
-
if (!runtimeHome || !classification.contentSha256) {
|
|
654
|
-
throw migrationError('schema_incompatible', 'Gateway removal lost its selected runtime context.');
|
|
655
|
-
}
|
|
656
|
-
if (classification.state === 'managed-ready') {
|
|
657
|
-
const binding = readCurrentBinding(assetRoot, classification.runtime);
|
|
658
|
-
const marker = safeReadBoundedFile(runtimeHome, binding.ownerMarkerRelativePath, MAX_LEGACY_POINTER_BYTES);
|
|
659
|
-
if (marker.state !== 'ready' || sha256(marker.bytes) !== binding.ownerMarkerSha256) {
|
|
660
|
-
throw migrationError('binding_conflict', `${classification.runtime} ownership marker changed after preflight.`);
|
|
661
|
-
}
|
|
662
|
-
unlinkVerifiedPointerFile(runtimeHome, binding.relativePath, binding.contentSha256);
|
|
663
|
-
unlinkVerifiedPointerFile(runtimeHome, binding.ownerMarkerRelativePath, binding.ownerMarkerSha256);
|
|
664
|
-
retainedManagedBindings.push(classification.runtime);
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
unlinkVerifiedPointerFile(runtimeHome, classification.relativePath, classification.contentSha256);
|
|
668
|
-
}
|
|
669
|
-
removed.push(classification.runtime);
|
|
670
|
-
}
|
|
671
|
-
return Object.freeze({
|
|
672
|
-
removed: Object.freeze(removed),
|
|
673
|
-
unchanged: Object.freeze(unchanged),
|
|
674
|
-
retainedManagedBindings: Object.freeze(retainedManagedBindings),
|
|
675
|
-
classifications: inspectLegacyAopsPointers({ assetRoot, runtimeHomes: options.runtimeHomes }),
|
|
676
|
-
});
|
|
677
|
-
}
|