@aopslabs/aops 0.2.0

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.
Files changed (171) hide show
  1. package/LICENSE +19 -0
  2. package/NOTICE +7 -0
  3. package/README.md +203 -0
  4. package/assets/agent-assets/core/SKILL.md +49 -0
  5. package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
  6. package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
  7. package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
  8. package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
  9. package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
  10. package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
  11. package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
  12. package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
  13. package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
  14. package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
  15. package/assets/agent-assets/core/references/view/SKILL.md +58 -0
  16. package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
  17. package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
  18. package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
  19. package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
  20. package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
  21. package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
  22. package/assets/agent-assets/core/user-guides/docman.md +162 -0
  23. package/assets/agent-assets/core/user-guides/projectman.md +212 -0
  24. package/assets/agent-assets/core/user-guides/sys.md +94 -0
  25. package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
  26. package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
  27. package/assets/r6-remote-session-baseline.json +19 -0
  28. package/assets/skills/aops-install/SKILL.md +231 -0
  29. package/assets/skills/aops-install/agents/openai.yaml +4 -0
  30. package/dist/commands/activity.js +352 -0
  31. package/dist/commands/agent-profile.js +653 -0
  32. package/dist/commands/agent.js +710 -0
  33. package/dist/commands/agents-md.js +125 -0
  34. package/dist/commands/api.js +84 -0
  35. package/dist/commands/archive.js +873 -0
  36. package/dist/commands/artifact.js +535 -0
  37. package/dist/commands/assets.js +1414 -0
  38. package/dist/commands/auth/device-login.js +1 -0
  39. package/dist/commands/auth/legacy-export.js +177 -0
  40. package/dist/commands/auth/login.js +426 -0
  41. package/dist/commands/chat.js +1363 -0
  42. package/dist/commands/chatv3.js +1684 -0
  43. package/dist/commands/checkpoint.js +549 -0
  44. package/dist/commands/commercial-artifact.js +475 -0
  45. package/dist/commands/commercial-license.js +594 -0
  46. package/dist/commands/community-auth.js +62 -0
  47. package/dist/commands/community-cockpit.js +185 -0
  48. package/dist/commands/community-console.js +109 -0
  49. package/dist/commands/community-doctor.js +335 -0
  50. package/dist/commands/community-server.js +2062 -0
  51. package/dist/commands/community-setup.js +284 -0
  52. package/dist/commands/discuss.js +1448 -0
  53. package/dist/commands/doc.js +5219 -0
  54. package/dist/commands/experience.js +922 -0
  55. package/dist/commands/host.js +1127 -0
  56. package/dist/commands/init.js +118 -0
  57. package/dist/commands/memory.js +3129 -0
  58. package/dist/commands/mission.js +562 -0
  59. package/dist/commands/official-catalog.js +134 -0
  60. package/dist/commands/plan.js +84 -0
  61. package/dist/commands/playbook.js +415 -0
  62. package/dist/commands/pm/context.js +6 -0
  63. package/dist/commands/pm/index.js +701 -0
  64. package/dist/commands/pm/projectman.js +6266 -0
  65. package/dist/commands/project.js +1080 -0
  66. package/dist/commands/prompt.js +1024 -0
  67. package/dist/commands/repo-sync.js +1907 -0
  68. package/dist/commands/resource.js +527 -0
  69. package/dist/commands/skill.js +1211 -0
  70. package/dist/commands/start-disciplines.js +510 -0
  71. package/dist/commands/start.js +1343 -0
  72. package/dist/commands/target.js +309 -0
  73. package/dist/commands/version.js +42 -0
  74. package/dist/commands/view.js +1852 -0
  75. package/dist/launchers/cockpit.js +28 -0
  76. package/dist/launchers/options.js +74 -0
  77. package/dist/launchers/server.js +41 -0
  78. package/dist/lib/agent-assets/envelope.js +83 -0
  79. package/dist/lib/agent-assets/gateway.js +15 -0
  80. package/dist/lib/agent-assets/guards.js +23 -0
  81. package/dist/lib/agent-assets/hosted-discovery.js +148 -0
  82. package/dist/lib/agent-assets/hosted-package-input.js +154 -0
  83. package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
  84. package/dist/lib/agent-assets/native-fs.js +589 -0
  85. package/dist/lib/agent-assets/package-manifest.js +324 -0
  86. package/dist/lib/agent-assets/portable-path.js +67 -0
  87. package/dist/lib/agent-assets/release-input.js +177 -0
  88. package/dist/lib/agent-assets/roots.js +45 -0
  89. package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
  90. package/dist/lib/agent-assets/runtime-targets.js +50 -0
  91. package/dist/lib/agent-assets/store-reader.js +1212 -0
  92. package/dist/lib/agent-assets/store-types.js +1 -0
  93. package/dist/lib/agent-assets/store-writer.js +1484 -0
  94. package/dist/lib/agent-assets/types.js +1 -0
  95. package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
  96. package/dist/lib/commercial-final-readiness.js +504 -0
  97. package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
  98. package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
  99. package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
  100. package/dist/lib/community-client-contract.js +164 -0
  101. package/dist/lib/community-cockpit-lifecycle.js +601 -0
  102. package/dist/lib/community-command-abort.js +59 -0
  103. package/dist/lib/community-diagnostic.js +237 -0
  104. package/dist/lib/community-docker-adapter.js +498 -0
  105. package/dist/lib/community-home.js +31 -0
  106. package/dist/lib/community-instance-contract.js +148 -0
  107. package/dist/lib/community-instance-layout.js +97 -0
  108. package/dist/lib/community-lifecycle.js +1631 -0
  109. package/dist/lib/community-migration-snapshot.js +770 -0
  110. package/dist/lib/community-native-application-recovery.js +425 -0
  111. package/dist/lib/community-native-child.js +240 -0
  112. package/dist/lib/community-native-database-recovery.js +456 -0
  113. package/dist/lib/community-native-lifecycle.js +2458 -0
  114. package/dist/lib/community-native-migration.js +339 -0
  115. package/dist/lib/community-native-postgres.js +596 -0
  116. package/dist/lib/community-operation-journal.js +773 -0
  117. package/dist/lib/community-operation-lock.js +596 -0
  118. package/dist/lib/community-release-resolver.js +291 -0
  119. package/dist/lib/community-release-verifier.js +475 -0
  120. package/dist/lib/community-repo-discovery.js +252 -0
  121. package/dist/lib/community-setup-server-env.js +203 -0
  122. package/dist/lib/external-url.js +110 -0
  123. package/dist/lib/official-catalog-gateway.js +108 -0
  124. package/dist/lib/official-catalog.js +364 -0
  125. package/dist/lib/server-bootstrap.js +240 -0
  126. package/dist/lib/setup-agent-assets-bridge.js +176 -0
  127. package/dist/lib/setup-agent-assets-release.js +52 -0
  128. package/dist/lib/setup-external-postgres.js +108 -0
  129. package/dist/lib/setup-init-orchestrator.js +990 -0
  130. package/dist/lib/setup-install-guide.js +52 -0
  131. package/dist/lib/setup-installer-release.js +47 -0
  132. package/dist/lib/setup-local-postgres.js +329 -0
  133. package/dist/lib/setup-official-catalog-bridge.js +148 -0
  134. package/dist/lib/setup-readiness.js +888 -0
  135. package/dist/lib/tui-launcher.js +59 -0
  136. package/dist/main.js +150 -0
  137. package/dist/seeds/agents-md.js +54 -0
  138. package/dist/utils/agent-gateway.js +275 -0
  139. package/dist/utils/agents-md.js +281 -0
  140. package/dist/utils/api.js +255 -0
  141. package/dist/utils/chatv3-session-store.js +185 -0
  142. package/dist/utils/command.js +67 -0
  143. package/dist/utils/community-host-runtime.js +376 -0
  144. package/dist/utils/config.js +1015 -0
  145. package/dist/utils/discussion-workspace.js +552 -0
  146. package/dist/utils/experience-workspace.js +326 -0
  147. package/dist/utils/guide-paths.js +67 -0
  148. package/dist/utils/hosted-sugar.js +56 -0
  149. package/dist/utils/hosted-workspace.js +229 -0
  150. package/dist/utils/memory-workspace.js +537 -0
  151. package/dist/utils/operator-runtime.js +199 -0
  152. package/dist/utils/password-crypto.js +39 -0
  153. package/dist/utils/playbook-workspace.js +479 -0
  154. package/dist/utils/project-context.js +255 -0
  155. package/dist/utils/prompts.js +70 -0
  156. package/dist/utils/refresh-race.js +28 -0
  157. package/dist/utils/repo-config.js +109 -0
  158. package/dist/utils/repo-first-projectman.js +194 -0
  159. package/dist/utils/repo-first-storage.js +13 -0
  160. package/dist/utils/repo-first-view.js +305 -0
  161. package/dist/utils/session-guidance.js +278 -0
  162. package/dist/utils/session-state.js +137 -0
  163. package/dist/utils/table.js +39 -0
  164. package/dist/utils/transient-fs.js +49 -0
  165. package/dist/utils/workspace-root.js +1 -0
  166. package/launchers/aops-cockpit.ps1 +15 -0
  167. package/launchers/aops-cockpit.sh +16 -0
  168. package/launchers/aops-server.ps1 +15 -0
  169. package/launchers/aops-server.sh +16 -0
  170. package/native/tui/win32-x64/aops-tui.exe +0 -0
  171. package/package.json +81 -0
@@ -0,0 +1,475 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { lstatSync, readFileSync, realpathSync, statSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { bundleFromJSON } from '@sigstore/bundle';
5
+ import { TrustedRoot } from '@sigstore/protobuf-specs';
6
+ import sigstoreTufSeeds from '@sigstore/tuf/seeds.json' with { type: 'json' };
7
+ import { toSignedEntity, toTrustMaterial, Verifier } from '@sigstore/verify';
8
+ import { verify as verifySigstore } from 'sigstore';
9
+ import { parseCommunityRelease } from './community-lifecycle.js';
10
+ const SHA256 = /^sha256:[a-f0-9]{64}$/;
11
+ const SHA256_HEX = /^[a-f0-9]{64}$/;
12
+ const NPM_INTEGRITY_SHA512 = /^sha512-[A-Za-z0-9+/]{86}==$/;
13
+ export const COMMUNITY_GITHUB_OIDC_ISSUER = 'https://token.actions.githubusercontent.com';
14
+ export const COMMUNITY_PUBLIC_SOURCE_REPOSITORY = 'git+https://github.com/eeemzs/aops-community';
15
+ export const COMMUNITY_PUBLIC_CLI_PACKAGE_NAME = '@aopslabs/aops';
16
+ export const COMMUNITY_AGENT_ASSETS_MANIFEST_REF = 'agent-assets-release.json';
17
+ export const COMMUNITY_AGENT_ASSETS_SIGNATURE_REF = 'agent-assets-release.sigstore.json';
18
+ export const COMMUNITY_AGENT_ASSETS_CERTIFICATE_IDENTITY_URI = '^https://github\\.com/eeemzs/[A-Za-z0-9._-]+/\\.github/workflows/sign-community-agent-assets\\.yml@refs/heads/main$';
19
+ function fail(code, detail) {
20
+ throw new Error(detail ? `${code}:${detail}` : code);
21
+ }
22
+ function digestFile(filePath) {
23
+ return `sha256:${createHash('sha256').update(readFileSync(filePath)).digest('hex')}`;
24
+ }
25
+ function npmIntegrityFile(filePath) {
26
+ return `sha512-${createHash('sha512').update(readFileSync(filePath)).digest('base64')}`;
27
+ }
28
+ function assertExactObject(value, keys, code) {
29
+ if (!value || typeof value !== 'object' || Array.isArray(value))
30
+ fail(code);
31
+ const actual = Object.keys(value).sort();
32
+ const expected = [...keys].sort();
33
+ if (JSON.stringify(actual) !== JSON.stringify(expected))
34
+ fail(code);
35
+ }
36
+ function requireConfinedFile(root, ref, code) {
37
+ if (typeof ref !== 'string' || !ref || path.isAbsolute(ref))
38
+ fail(`${code}_ref_invalid`);
39
+ const normalizedRef = ref.replaceAll('\\', '/');
40
+ if (normalizedRef.split('/').some((segment) => segment === '..' || segment === ''))
41
+ fail(`${code}_ref_invalid`);
42
+ const candidate = path.resolve(root, ...normalizedRef.split('/'));
43
+ const relative = path.relative(root, candidate);
44
+ if (relative.startsWith('..') || path.isAbsolute(relative))
45
+ fail(`${code}_ref_escape`);
46
+ let cursor = root;
47
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
48
+ cursor = path.join(cursor, segment);
49
+ const stat = lstatSync(cursor);
50
+ if (stat.isSymbolicLink())
51
+ fail(`${code}_reparse_refused`, cursor);
52
+ }
53
+ const stat = statSync(candidate);
54
+ if (!stat.isFile() || stat.size <= 0)
55
+ fail(`${code}_file_invalid`, candidate);
56
+ const realRoot = realpathSync(root);
57
+ const realCandidate = realpathSync(candidate);
58
+ const realRelative = path.relative(realRoot, realCandidate);
59
+ if (realRelative.startsWith('..') || path.isAbsolute(realRelative))
60
+ fail(`${code}_real_escape`);
61
+ return candidate;
62
+ }
63
+ function assertDigest(value, code) {
64
+ if (typeof value !== 'string' || !SHA256.test(value))
65
+ fail(code);
66
+ }
67
+ function assertHexDigest(value, code) {
68
+ if (typeof value !== 'string' || !SHA256_HEX.test(value))
69
+ fail(code);
70
+ }
71
+ function compareUnsignedUtf8(left, right) {
72
+ return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
73
+ }
74
+ function agentAssetsSetSha256(files) {
75
+ const hash = createHash('sha256');
76
+ for (const file of files) {
77
+ hash.update(Buffer.from(file.ref, 'utf8'));
78
+ hash.update(Buffer.from([0]));
79
+ hash.update(file.sha256, 'ascii');
80
+ hash.update(Buffer.from('\n', 'ascii'));
81
+ }
82
+ return hash.digest('hex');
83
+ }
84
+ function assertAgentAssetsPackageRecord(value, code) {
85
+ assertExactObject(value, ['name', 'version', 'versionId', 'packageSha256', 'entryFile', 'manifestRef', 'optional'], code);
86
+ const record = value;
87
+ if (typeof record.name !== 'string' || !record.name ||
88
+ typeof record.version !== 'string' || !record.version ||
89
+ typeof record.versionId !== 'string' || !record.versionId ||
90
+ record.entryFile !== 'SKILL.md' ||
91
+ typeof record.manifestRef !== 'string' || !record.manifestRef.startsWith('agent-assets/') ||
92
+ typeof record.optional !== 'boolean')
93
+ fail(code);
94
+ assertHexDigest(record.packageSha256, `${code}_package_digest_invalid`);
95
+ }
96
+ function assertAgentAssetsRelease(value) {
97
+ assertExactObject(value, ['schemaVersion', 'kind', 'root', 'inventory', 'projection', 'gateway', 'core', 'catalog', 'files', 'setSha256'], 'community_release_agent_assets_invalid');
98
+ const record = value;
99
+ if (record.schemaVersion !== 1 ||
100
+ record.kind !== 'aops-community-agent-assets-release-v1' ||
101
+ record.root !== 'agent-assets')
102
+ fail('community_release_agent_assets_identity_invalid');
103
+ assertExactObject(record.inventory, ['ref', 'sha256'], 'community_release_agent_assets_inventory_invalid');
104
+ assertExactObject(record.projection, ['ref', 'sha256'], 'community_release_agent_assets_projection_invalid');
105
+ assertExactObject(record.gateway, ['name', 'ref', 'sha256', 'byteLength'], 'community_release_agent_assets_gateway_invalid');
106
+ for (const pointer of [record.inventory, record.projection, record.gateway]) {
107
+ if (typeof pointer.ref !== 'string' || !pointer.ref.startsWith('agent-assets/')) {
108
+ fail('community_release_agent_assets_ref_invalid');
109
+ }
110
+ assertHexDigest(pointer.sha256, 'community_release_agent_assets_digest_invalid');
111
+ }
112
+ if (record.gateway.name !== 'aops' ||
113
+ !Number.isSafeInteger(record.gateway.byteLength) || record.gateway.byteLength < 1)
114
+ fail('community_release_agent_assets_gateway_invalid');
115
+ assertAgentAssetsPackageRecord(record.core, 'community_release_agent_assets_core_invalid');
116
+ if (record.core.name !== 'aops' || record.core.optional !== false)
117
+ fail('community_release_agent_assets_core_invalid');
118
+ assertExactObject(record.catalog, ['mode', 'defaultActivation', 'packages'], 'community_release_agent_assets_catalog_invalid');
119
+ if (record.catalog.mode !== 'optional-inert' ||
120
+ record.catalog.defaultActivation !== false ||
121
+ !Array.isArray(record.catalog.packages))
122
+ fail('community_release_agent_assets_catalog_invalid');
123
+ for (const packageRecord of record.catalog.packages) {
124
+ assertAgentAssetsPackageRecord(packageRecord, 'community_release_agent_assets_catalog_package_invalid');
125
+ if (packageRecord.optional !== true)
126
+ fail('community_release_agent_assets_catalog_package_invalid');
127
+ }
128
+ if (!Array.isArray(record.files) || record.files.length === 0)
129
+ fail('community_release_agent_assets_files_invalid');
130
+ const refs = record.files.map((file) => {
131
+ assertExactObject(file, ['ref', 'sha256', 'byteLength'], 'community_release_agent_assets_file_invalid');
132
+ if (typeof file.ref !== 'string' || !file.ref.startsWith('agent-assets/') ||
133
+ !Number.isSafeInteger(file.byteLength) || file.byteLength < 0)
134
+ fail('community_release_agent_assets_file_invalid');
135
+ assertHexDigest(file.sha256, 'community_release_agent_assets_file_digest_invalid');
136
+ return file.ref;
137
+ });
138
+ if (JSON.stringify(refs) !== JSON.stringify([...new Set(refs)].sort(compareUnsignedUtf8))) {
139
+ fail('community_release_agent_assets_file_order_invalid');
140
+ }
141
+ assertHexDigest(record.setSha256, 'community_release_agent_assets_set_digest_invalid');
142
+ if (record.setSha256 !== agentAssetsSetSha256(record.files))
143
+ fail('community_release_agent_assets_set_digest_mismatch');
144
+ const closure = new Set(refs);
145
+ for (const ref of [
146
+ record.inventory.ref,
147
+ record.projection.ref,
148
+ record.gateway.ref,
149
+ record.core.manifestRef,
150
+ ...record.catalog.packages.map((entry) => entry.manifestRef),
151
+ ]) {
152
+ if (!closure.has(ref))
153
+ fail('community_release_agent_assets_pointer_not_in_closure', ref);
154
+ }
155
+ }
156
+ function parseManifest(content) {
157
+ let value;
158
+ try {
159
+ value = JSON.parse(content);
160
+ }
161
+ catch {
162
+ fail('community_release_manifest_json_invalid');
163
+ }
164
+ if (!value || typeof value !== 'object' || Array.isArray(value))
165
+ fail('community_release_manifest_invalid');
166
+ const manifest = value;
167
+ if (manifest.schemaVersion !== 1 ||
168
+ typeof manifest.releaseVersion !== 'string' ||
169
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(manifest.releaseVersion)) {
170
+ fail('community_release_manifest_invalid');
171
+ }
172
+ if (!manifest.source || !manifest.cli || !manifest.compose || !manifest.migrations || !manifest.agentAssets || !manifest.legal || !manifest.evidence) {
173
+ fail('community_release_manifest_incomplete');
174
+ }
175
+ if (manifest.source.repository !== COMMUNITY_PUBLIC_SOURCE_REPOSITORY) {
176
+ fail('community_release_source_repository_invalid');
177
+ }
178
+ if (manifest.image?.repository !== 'ghcr.io/eeemzs/aops-community' ||
179
+ manifest.image.tag !== `v${manifest.releaseVersion}`) {
180
+ fail('community_release_image_identity_invalid');
181
+ }
182
+ assertDigest(manifest.image.indexDigest, 'community_release_index_digest_invalid');
183
+ assertDigest(manifest.source.treeDigest, 'community_release_source_digest_invalid');
184
+ assertExactObject(manifest.cli, [
185
+ 'packageName',
186
+ 'version',
187
+ 'commandSchemaVersion',
188
+ 'bundleSha256',
189
+ 'bundleByteLength',
190
+ 'npmDistTag',
191
+ 'artifactRef',
192
+ 'artifactSha256',
193
+ 'npmIntegrity',
194
+ ], 'community_release_cli_identity_invalid');
195
+ if (manifest.cli.packageName !== COMMUNITY_PUBLIC_CLI_PACKAGE_NAME ||
196
+ manifest.cli.version !== manifest.releaseVersion ||
197
+ manifest.cli.artifactRef !== `aopslab-aops-${manifest.releaseVersion}.tgz` ||
198
+ manifest.cli.npmDistTag !== (manifest.releaseVersion.includes('-') ? 'next' : 'latest') ||
199
+ !Number.isSafeInteger(manifest.cli.commandSchemaVersion) ||
200
+ manifest.cli.commandSchemaVersion <= 0 ||
201
+ !Number.isSafeInteger(manifest.cli.bundleByteLength) ||
202
+ manifest.cli.bundleByteLength <= 0 ||
203
+ !NPM_INTEGRITY_SHA512.test(manifest.cli.npmIntegrity)) {
204
+ fail('community_release_cli_identity_invalid');
205
+ }
206
+ if (manifest.cli.commandSchemaVersion !== 1)
207
+ fail('community_release_cli_command_schema_unsupported');
208
+ assertDigest(manifest.cli.bundleSha256, 'community_release_cli_bundle_digest_invalid');
209
+ assertDigest(manifest.cli.artifactSha256, 'community_release_cli_digest_invalid');
210
+ if (manifest.compose.ref !== 'compose.yaml')
211
+ fail('community_release_compose_ref_invalid');
212
+ assertDigest(manifest.compose.sha256, 'community_release_compose_digest_invalid');
213
+ assertDigest(manifest.migrations.setDigest, 'community_release_migration_digest_invalid');
214
+ for (const [kind, expectedRef] of [
215
+ ['license', 'LICENSE'],
216
+ ['notice', 'NOTICE'],
217
+ ['thirdPartyNotices', 'THIRD_PARTY_NOTICES'],
218
+ ['thirdPartyInventory', 'THIRD_PARTY_NOTICES.inventory.json'],
219
+ ]) {
220
+ const entry = manifest.legal[kind];
221
+ if (entry?.ref !== expectedRef)
222
+ fail(`community_release_legal_${kind}_ref_invalid`);
223
+ assertDigest(entry.sha256, `community_release_legal_${kind}_digest_invalid`);
224
+ }
225
+ assertDigest(manifest.evidence.sbom?.sha256, 'community_release_sbom_digest_invalid');
226
+ assertDigest(manifest.evidence.provenance?.sha256, 'community_release_provenance_digest_invalid');
227
+ if (manifest.evidence.signature?.bundleRef !== 'release.sigstore.json') {
228
+ fail('community_release_signature_ref_invalid');
229
+ }
230
+ if (!Array.isArray(manifest.migrations.files) || manifest.migrations.files.length === 0) {
231
+ fail('community_release_migration_files_invalid');
232
+ }
233
+ for (const entry of manifest.migrations.files)
234
+ assertDigest(entry?.sha256, 'community_release_migration_file_digest_invalid');
235
+ assertAgentAssetsRelease(manifest.agentAssets);
236
+ return manifest;
237
+ }
238
+ function parseAgentAssetsManifest(content) {
239
+ let value;
240
+ try {
241
+ value = JSON.parse(content);
242
+ }
243
+ catch {
244
+ fail('community_agent_assets_manifest_json_invalid');
245
+ }
246
+ assertExactObject(value, ['schemaVersion', 'kind', 'releaseVersion', 'source', 'agentAssets', 'evidence'], 'community_agent_assets_manifest_invalid');
247
+ const manifest = value;
248
+ if (manifest.schemaVersion !== 1 ||
249
+ manifest.kind !== 'aops-community-agent-assets-bundle-v1' ||
250
+ typeof manifest.releaseVersion !== 'string' ||
251
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(manifest.releaseVersion))
252
+ fail('community_agent_assets_manifest_identity_invalid');
253
+ assertExactObject(manifest.source, ['repository', 'commit', 'root'], 'community_agent_assets_manifest_source_invalid');
254
+ if (manifest.source.repository !== COMMUNITY_PUBLIC_SOURCE_REPOSITORY ||
255
+ !/^[a-f0-9]{40}$/.test(manifest.source.commit) ||
256
+ manifest.source.root !== 'apps/aops-cli/assets/agent-assets')
257
+ fail('community_agent_assets_manifest_source_invalid');
258
+ assertExactObject(manifest.evidence, ['signature'], 'community_agent_assets_manifest_evidence_invalid');
259
+ assertExactObject(manifest.evidence.signature, ['bundleRef'], 'community_agent_assets_manifest_signature_invalid');
260
+ if (manifest.evidence.signature.bundleRef !== COMMUNITY_AGENT_ASSETS_SIGNATURE_REF) {
261
+ fail('community_agent_assets_manifest_signature_invalid');
262
+ }
263
+ assertAgentAssetsRelease(manifest.agentAssets);
264
+ return manifest;
265
+ }
266
+ function defaultCertificateIdentity(manifest) {
267
+ const tag = manifest.image.tag || `v${manifest.releaseVersion}`;
268
+ const publicSourceRepository = COMMUNITY_PUBLIC_SOURCE_REPOSITORY.replace(/^git\+/, '');
269
+ return `${publicSourceRepository}/.github/workflows/community-release.yml@refs/tags/${tag}`;
270
+ }
271
+ function exactRegex(value) {
272
+ return `^${value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`;
273
+ }
274
+ function parseSignatureBundle(content) {
275
+ try {
276
+ return JSON.parse(content);
277
+ }
278
+ catch {
279
+ fail('community_release_signature_bundle_json_invalid');
280
+ }
281
+ }
282
+ function embeddedSigstoreTrustedRoot() {
283
+ const seeds = sigstoreTufSeeds;
284
+ const encoded = seeds['https://tuf-repo-cdn.sigstore.dev']?.targets?.['trusted_root.json'];
285
+ if (typeof encoded !== 'string' || encoded.length === 0) {
286
+ fail('community_release_offline_trusted_root_missing');
287
+ }
288
+ try {
289
+ return TrustedRoot.fromJSON(JSON.parse(Buffer.from(encoded, 'base64').toString('utf8')));
290
+ }
291
+ catch {
292
+ fail('community_release_offline_trusted_root_invalid');
293
+ }
294
+ }
295
+ async function verifySigstoreOffline(bundle, payload, options) {
296
+ const verifier = new Verifier(toTrustMaterial(embeddedSigstoreTrustedRoot()));
297
+ return verifier.verify(toSignedEntity(bundleFromJSON(bundle), payload), {
298
+ subjectAlternativeName: options.certificateIdentityURI,
299
+ extensions: { issuer: options.certificateIssuer },
300
+ });
301
+ }
302
+ async function verifyManifestSignature(options) {
303
+ const certificateIdentity = options.certificateIdentity ?? defaultCertificateIdentity(options.manifest);
304
+ const certificateOidcIssuer = options.certificateOidcIssuer ?? COMMUNITY_GITHUB_OIDC_ISSUER;
305
+ const verificationMode = options.verificationMode ?? 'online';
306
+ try {
307
+ const bundle = parseSignatureBundle(options.signatureBundleContent);
308
+ const payload = Buffer.from(options.manifestContent);
309
+ const verifierOptions = {
310
+ certificateIssuer: certificateOidcIssuer,
311
+ certificateIdentityURI: exactRegex(certificateIdentity),
312
+ verificationMode,
313
+ };
314
+ if (options.signatureVerifier) {
315
+ await options.signatureVerifier(bundle, payload, verifierOptions);
316
+ }
317
+ else if (verificationMode === 'offline') {
318
+ await verifySigstoreOffline(bundle, payload, verifierOptions);
319
+ }
320
+ else {
321
+ await verifySigstore(bundle, payload, verifierOptions);
322
+ }
323
+ }
324
+ catch (error) {
325
+ if (error instanceof Error && error.message.startsWith('community_release_signature_bundle_json_invalid'))
326
+ throw error;
327
+ fail('community_release_sigstore_verification_failed', error instanceof Error ? error.message : String(error));
328
+ }
329
+ return { certificateIdentity, certificateOidcIssuer };
330
+ }
331
+ async function verifyAgentAssetsManifestSignature(options) {
332
+ const certificateIdentity = options.certificateIdentity ?? COMMUNITY_AGENT_ASSETS_CERTIFICATE_IDENTITY_URI;
333
+ const certificateOidcIssuer = options.certificateOidcIssuer ?? COMMUNITY_GITHUB_OIDC_ISSUER;
334
+ const verificationMode = options.verificationMode ?? 'online';
335
+ try {
336
+ const bundle = parseSignatureBundle(options.signatureBundleContent);
337
+ const payload = Buffer.from(options.manifestContent);
338
+ const verifierOptions = {
339
+ certificateIssuer: certificateOidcIssuer,
340
+ certificateIdentityURI: options.certificateIdentity === undefined
341
+ ? certificateIdentity
342
+ : exactRegex(certificateIdentity),
343
+ verificationMode,
344
+ };
345
+ if (options.signatureVerifier) {
346
+ await options.signatureVerifier(bundle, payload, verifierOptions);
347
+ }
348
+ else if (verificationMode === 'offline') {
349
+ await verifySigstoreOffline(bundle, payload, verifierOptions);
350
+ }
351
+ else {
352
+ await verifySigstore(bundle, payload, verifierOptions);
353
+ }
354
+ }
355
+ catch (error) {
356
+ if (error instanceof Error && error.message.startsWith('community_release_signature_bundle_json_invalid'))
357
+ throw error;
358
+ fail('community_agent_assets_sigstore_verification_failed', error instanceof Error ? error.message : String(error));
359
+ }
360
+ return { certificateIdentity, certificateOidcIssuer };
361
+ }
362
+ export async function verifyCommunityReleaseDescriptor(options) {
363
+ const manifest = parseManifest(options.manifestContent);
364
+ if (options.expectedReleaseVersion !== undefined && manifest.releaseVersion !== options.expectedReleaseVersion) {
365
+ fail('community_release_descriptor_version_mismatch', `expected=${options.expectedReleaseVersion}:actual=${manifest.releaseVersion}`);
366
+ }
367
+ const identity = parseCommunityRelease(options.manifestContent, options.composeContent);
368
+ const certificate = await verifyManifestSignature({
369
+ manifest,
370
+ manifestContent: options.manifestContent,
371
+ signatureBundleContent: options.signatureBundleContent,
372
+ certificateIdentity: options.certificateIdentity,
373
+ certificateOidcIssuer: options.certificateOidcIssuer,
374
+ signatureVerifier: options.signatureVerifier,
375
+ verificationMode: options.verificationMode,
376
+ });
377
+ return {
378
+ manifestContent: options.manifestContent,
379
+ composeContent: options.composeContent,
380
+ identity,
381
+ ...certificate,
382
+ verifiedArtifactCount: 3,
383
+ agentAssets: manifest.agentAssets,
384
+ };
385
+ }
386
+ export async function verifyCommunityReleaseBundle(options) {
387
+ const releaseRoot = path.resolve(options.releaseRoot);
388
+ const rootStat = lstatSync(releaseRoot);
389
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink())
390
+ fail('community_release_root_invalid');
391
+ const manifestPath = requireConfinedFile(releaseRoot, 'release.json', 'community_release_manifest');
392
+ const manifestContent = readFileSync(manifestPath, 'utf8');
393
+ const manifest = parseManifest(manifestContent);
394
+ const artifacts = [
395
+ { ref: manifest.source.treeRef, sha256: manifest.source.treeDigest, kind: 'source' },
396
+ { ref: manifest.cli.artifactRef, sha256: manifest.cli.artifactSha256, kind: 'cli' },
397
+ { ref: manifest.compose.ref, sha256: manifest.compose.sha256, kind: 'compose' },
398
+ ...manifest.migrations.files.map((entry) => ({ ...entry, kind: 'migration' })),
399
+ ...manifest.agentAssets.files.map((entry) => ({
400
+ ref: entry.ref,
401
+ sha256: `sha256:${entry.sha256}`,
402
+ kind: 'agent_assets',
403
+ })),
404
+ { ref: manifest.legal.license.ref, sha256: manifest.legal.license.sha256, kind: 'legal_license' },
405
+ { ref: manifest.legal.notice.ref, sha256: manifest.legal.notice.sha256, kind: 'legal_notice' },
406
+ { ref: manifest.legal.thirdPartyNotices.ref, sha256: manifest.legal.thirdPartyNotices.sha256, kind: 'legal_third_party_notices' },
407
+ { ref: manifest.legal.thirdPartyInventory.ref, sha256: manifest.legal.thirdPartyInventory.sha256, kind: 'legal_third_party_inventory' },
408
+ { ref: manifest.evidence.sbom.ref, sha256: manifest.evidence.sbom.sha256, kind: 'sbom' },
409
+ { ref: manifest.evidence.provenance.ref, sha256: manifest.evidence.provenance.sha256, kind: 'provenance' },
410
+ ];
411
+ let composePath = '';
412
+ for (const artifact of artifacts) {
413
+ const artifactPath = requireConfinedFile(releaseRoot, artifact.ref, `community_release_${artifact.kind}`);
414
+ if (digestFile(artifactPath) !== artifact.sha256)
415
+ fail(`community_release_${artifact.kind}_digest_mismatch`, artifact.ref);
416
+ if (artifact.kind === 'cli' && npmIntegrityFile(artifactPath) !== manifest.cli.npmIntegrity) {
417
+ fail('community_release_cli_npm_integrity_mismatch', artifact.ref);
418
+ }
419
+ if (artifact.kind === 'compose')
420
+ composePath = artifactPath;
421
+ }
422
+ const signatureBundlePath = requireConfinedFile(releaseRoot, manifest.evidence.signature?.bundleRef, 'community_release_signature');
423
+ const composeContent = readFileSync(composePath, 'utf8');
424
+ const descriptor = await verifyCommunityReleaseDescriptor({
425
+ manifestContent,
426
+ composeContent,
427
+ signatureBundleContent: readFileSync(signatureBundlePath, 'utf8'),
428
+ certificateIdentity: options.certificateIdentity,
429
+ certificateOidcIssuer: options.certificateOidcIssuer,
430
+ signatureVerifier: options.signatureVerifier,
431
+ verificationMode: options.verificationMode,
432
+ });
433
+ return {
434
+ releaseRoot,
435
+ manifestPath,
436
+ signatureBundlePath,
437
+ ...descriptor,
438
+ verifiedArtifactCount: artifacts.length + 2,
439
+ };
440
+ }
441
+ export async function verifyCommunityAgentAssetsReleaseBundle(options) {
442
+ const releaseRoot = path.resolve(options.releaseRoot);
443
+ const rootStat = lstatSync(releaseRoot);
444
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink())
445
+ fail('community_release_root_invalid');
446
+ const manifestPath = requireConfinedFile(releaseRoot, COMMUNITY_AGENT_ASSETS_MANIFEST_REF, 'community_agent_assets_manifest');
447
+ const manifestContent = readFileSync(manifestPath, 'utf8');
448
+ const manifest = parseAgentAssetsManifest(manifestContent);
449
+ for (const artifact of manifest.agentAssets.files) {
450
+ const artifactPath = requireConfinedFile(releaseRoot, artifact.ref, 'community_release_agent_assets');
451
+ if (digestFile(artifactPath) !== `sha256:${artifact.sha256}`) {
452
+ fail('community_release_agent_assets_digest_mismatch', artifact.ref);
453
+ }
454
+ }
455
+ const signatureBundlePath = requireConfinedFile(releaseRoot, COMMUNITY_AGENT_ASSETS_SIGNATURE_REF, 'community_agent_assets_signature');
456
+ const certificate = await verifyAgentAssetsManifestSignature({
457
+ manifestContent,
458
+ signatureBundleContent: readFileSync(signatureBundlePath, 'utf8'),
459
+ certificateIdentity: options.certificateIdentity,
460
+ certificateOidcIssuer: options.certificateOidcIssuer,
461
+ signatureVerifier: options.signatureVerifier,
462
+ verificationMode: options.verificationMode,
463
+ });
464
+ return {
465
+ releaseRoot,
466
+ manifestPath,
467
+ signatureBundlePath,
468
+ manifestContent,
469
+ releaseVersion: manifest.releaseVersion,
470
+ source: manifest.source,
471
+ ...certificate,
472
+ verifiedArtifactCount: manifest.agentAssets.files.length + 2,
473
+ agentAssets: manifest.agentAssets,
474
+ };
475
+ }