@aiwg/cli 2026.8.11 → 2026.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aiwg.mjs +2 -0
- package/dist/src/api/index.d.ts +6 -0
- package/dist/src/api/index.js +6 -0
- package/dist/src/cli/handlers/artifact-verify.js +171 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/setup-manifest.js +52 -3
- package/dist/src/cli/handlers/setup.js +15 -2
- package/dist/src/cli/handlers/use.js +71 -6
- package/dist/src/cli/scope-resolver.js +6 -1
- package/dist/src/cli/services/deployment-verification.js +65 -7
- package/dist/src/config/aiwg-config.js +4 -3
- package/dist/src/config/cli.js +3 -1
- package/dist/src/config/gitignore.js +67 -21
- package/dist/src/config/workspace.js +8 -1
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-quickref.js +9 -0
- package/dist/src/marketplace/artifact-attestation.js +195 -0
- package/dist/src/marketplace/exchange.js +437 -79
- package/dist/src/marketplace/provenance-types.js +1 -0
- package/dist/src/marketplace/provenance.js +7 -1
- package/dist/src/providers/hermes-home.js +20 -0
- package/dist/src/providers/provider-definitions.js +5 -4
- package/dist/src/providers/transformation-receipt-integration.js +448 -0
- package/dist/src/providers/transformation-receipt.js +215 -0
- package/dist/src/resources/web-release.d.ts +11 -0
- package/dist/src/resources/web-release.js +61 -6
- package/dist/src/security/artifact-attestation.js +117 -0
- package/dist/src/security/artifact-trust.js +557 -0
- package/dist/src/security/artifact-verifier.js +478 -0
- package/dist/src/skills/deployer.js +5 -1
- package/dist/src/tracker/capability-protocol.js +7 -2
- package/package.json +5 -1
- package/schemas/security/aiwg-artifact-attestation.v1.schema.json +106 -0
- package/schemas/security/aiwg-artifact-provenance.v1.schema.json +204 -0
- package/schemas/security/aiwg-artifact-trust-root.v1.schema.json +59 -0
- package/schemas/security/aiwg-artifact-trust-state.v1.schema.json +32 -0
- package/schemas/security/aiwg-artifact-verification-result.v1.schema.json +46 -0
- package/schemas/security/threat-assessment-input.v1.schema.json +57 -0
- package/schemas/security/threat-assessment-report.v1.schema.json +104 -0
- package/tools/agents/deploy-agents.mjs +8 -2
- package/tools/agents/providers/base.mjs +29 -2
- package/tools/agents/providers/hermes.mjs +163 -19
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
+
import { resolveHermesHomePath } from './hermes-home.js';
|
|
4
5
|
import { getProviderCapabilities, } from './capability-matrix.js';
|
|
5
6
|
const ArtifactPathsSchema = z.object({
|
|
6
7
|
agents: z.string().nullable(),
|
|
@@ -464,7 +465,7 @@ const BUILT_IN_SEEDS = [
|
|
|
464
465
|
id: 'hermes',
|
|
465
466
|
aliases: [],
|
|
466
467
|
builtIn: true,
|
|
467
|
-
surfaces: { primary: 'hermes', compatibility: [], precedence: ['
|
|
468
|
+
surfaces: { primary: 'hermes', compatibility: [], precedence: ['.hermes.md', 'AGENTS.md', resolveHermesHomePath('skills')], related: [] },
|
|
468
469
|
detection: {
|
|
469
470
|
env: [],
|
|
470
471
|
process: ['hermes'],
|
|
@@ -474,18 +475,18 @@ const BUILT_IN_SEEDS = [
|
|
|
474
475
|
artifacts: {
|
|
475
476
|
agents: null,
|
|
476
477
|
commands: null,
|
|
477
|
-
skills: '
|
|
478
|
+
skills: resolveHermesHomePath('skills', '.aiwg'),
|
|
478
479
|
rules: null,
|
|
479
480
|
behaviors: null,
|
|
480
481
|
},
|
|
481
|
-
kernelSkills: '
|
|
482
|
+
kernelSkills: resolveHermesHomePath('skills'),
|
|
482
483
|
configFile: 'AGENTS.md',
|
|
483
484
|
contextFiles: { aiwgMd: true, agentsMd: true, claudeMdHook: false, hookFile: '.hermes.md', contextFile: 'AGENTS.md' },
|
|
484
485
|
},
|
|
485
486
|
smithPaths: {
|
|
486
487
|
agents: null,
|
|
487
488
|
commands: null,
|
|
488
|
-
skills: '
|
|
489
|
+
skills: resolveHermesHomePath('skills'),
|
|
489
490
|
rules: null,
|
|
490
491
|
fileExtension: '.md',
|
|
491
492
|
configFile: 'AGENTS.md',
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { access, lstat, readFile, readdir } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { readAiwgConfig } from '../config/aiwg-config.js';
|
|
6
|
+
import { readUserRegistry } from '../config/user-registry.js';
|
|
7
|
+
import { USER_SCOPE_PATHS } from '../cli/scope-resolver.js';
|
|
8
|
+
import { getProviderDefinition, getProviderKernelSkillPath, normalizeProviderDefinitionId, resolveProviderPathValue, } from './provider-definitions.js';
|
|
9
|
+
import { createProviderTransformationReceipt, diagnoseProviderTransformationReceipt, providerTransformationReceiptPath, writeProviderTransformationReceipt, } from './transformation-receipt.js';
|
|
10
|
+
const TRANSFORMER_ID = 'aiwg-provider-transformer';
|
|
11
|
+
const TRANSFORMER_VERSION = '1';
|
|
12
|
+
const MANAGED_SIDECAR = '.aiwg-manifest.json';
|
|
13
|
+
const MANAGED_DIRECTORY_MARKER = '.aiwg-managed';
|
|
14
|
+
const MANAGED_FILE_MARKER = /^(?:<!-- aiwg:managed |# aiwg:managed )/m;
|
|
15
|
+
const FRAMEWORK_BUNDLE_DIRS = {
|
|
16
|
+
all: 'sdlc-complete',
|
|
17
|
+
sdlc: 'sdlc-complete',
|
|
18
|
+
marketing: 'media-marketing-kit',
|
|
19
|
+
'media-curator': 'media-curator',
|
|
20
|
+
research: 'research-complete',
|
|
21
|
+
forensics: 'forensics-complete',
|
|
22
|
+
dfir: 'forensics-complete',
|
|
23
|
+
'security-engineering': 'security-engineering',
|
|
24
|
+
ops: 'ops-complete',
|
|
25
|
+
validation: 'validation-complete',
|
|
26
|
+
'knowledge-base': 'knowledge-base',
|
|
27
|
+
};
|
|
28
|
+
function sha256(value) {
|
|
29
|
+
return createHash('sha256').update(value).digest('hex');
|
|
30
|
+
}
|
|
31
|
+
function stable(value) {
|
|
32
|
+
if (Array.isArray(value))
|
|
33
|
+
return `[${value.map(stable).join(',')}]`;
|
|
34
|
+
if (value && typeof value === 'object') {
|
|
35
|
+
const record = value;
|
|
36
|
+
return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${stable(record[key])}`).join(',')}}`;
|
|
37
|
+
}
|
|
38
|
+
return JSON.stringify(value) ?? 'null';
|
|
39
|
+
}
|
|
40
|
+
function portablePath(root, absolute) {
|
|
41
|
+
const relative = path.relative(root, absolute);
|
|
42
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
43
|
+
return null;
|
|
44
|
+
return relative.split(path.sep).join('/');
|
|
45
|
+
}
|
|
46
|
+
async function regularFiles(root) {
|
|
47
|
+
const files = [];
|
|
48
|
+
let entries;
|
|
49
|
+
try {
|
|
50
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return files;
|
|
54
|
+
}
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const candidate = path.join(root, entry.name);
|
|
57
|
+
if (entry.isDirectory())
|
|
58
|
+
files.push(...await regularFiles(candidate));
|
|
59
|
+
else if (entry.isFile())
|
|
60
|
+
files.push(candidate);
|
|
61
|
+
}
|
|
62
|
+
return files;
|
|
63
|
+
}
|
|
64
|
+
async function addEntry(files, candidate) {
|
|
65
|
+
try {
|
|
66
|
+
const metadata = await lstat(candidate);
|
|
67
|
+
if (metadata.isFile())
|
|
68
|
+
files.add(candidate);
|
|
69
|
+
else if (metadata.isDirectory()) {
|
|
70
|
+
for (const file of await regularFiles(candidate))
|
|
71
|
+
files.add(file);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// A registry or sidecar entry can describe a stale partial deployment.
|
|
76
|
+
// Receipt finalization binds only successful on-disk outputs; an existing
|
|
77
|
+
// receipt remains responsible for diagnosing later deletion.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function collectOwnedDirectory(directory, exactEntries, files) {
|
|
81
|
+
for (const entry of exactEntries)
|
|
82
|
+
await addEntry(files, path.join(directory, entry));
|
|
83
|
+
const sidecarPath = path.join(directory, MANAGED_SIDECAR);
|
|
84
|
+
try {
|
|
85
|
+
const sidecar = JSON.parse(await readFile(sidecarPath, 'utf8'));
|
|
86
|
+
const managed = sidecar.managed && typeof sidecar.managed === 'object'
|
|
87
|
+
? Object.keys(sidecar.managed)
|
|
88
|
+
: [];
|
|
89
|
+
if (managed.length > 0)
|
|
90
|
+
files.add(sidecarPath);
|
|
91
|
+
for (const name of managed) {
|
|
92
|
+
if (name === path.basename(name) && name !== MANAGED_SIDECAR) {
|
|
93
|
+
await addEntry(files, path.join(directory, name));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Missing or invalid sidecars are not ownership evidence.
|
|
99
|
+
}
|
|
100
|
+
let entries;
|
|
101
|
+
try {
|
|
102
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const candidate = path.join(directory, entry.name);
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
try {
|
|
111
|
+
const marker = await lstat(path.join(candidate, MANAGED_DIRECTORY_MARKER));
|
|
112
|
+
if (!marker.isFile())
|
|
113
|
+
continue;
|
|
114
|
+
await addEntry(files, candidate);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Unmarked directories remain operator-owned.
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (!entry.isFile() || entry.name === MANAGED_SIDECAR)
|
|
122
|
+
continue;
|
|
123
|
+
try {
|
|
124
|
+
const content = await readFile(candidate, 'utf8');
|
|
125
|
+
if (MANAGED_FILE_MARKER.test(content))
|
|
126
|
+
files.add(candidate);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Unreadable files cannot establish an ownership signal.
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function artifactPaths(provider, scope) {
|
|
134
|
+
const definition = getProviderDefinition(provider);
|
|
135
|
+
if (!definition)
|
|
136
|
+
return null;
|
|
137
|
+
if (scope === 'user')
|
|
138
|
+
return USER_SCOPE_PATHS[provider] ?? null;
|
|
139
|
+
return {
|
|
140
|
+
agents: definition.paths.artifacts.agents ?? '',
|
|
141
|
+
commands: definition.paths.artifacts.commands ?? '',
|
|
142
|
+
skills: definition.paths.artifacts.skills ?? '',
|
|
143
|
+
rules: definition.paths.artifacts.rules ?? '',
|
|
144
|
+
behaviors: definition.paths.artifacts.behaviors ?? '',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async function installedEntries(options) {
|
|
148
|
+
if (options.scope === 'user' && options.provider !== 'openhuman') {
|
|
149
|
+
return (await readUserRegistry()).installed;
|
|
150
|
+
}
|
|
151
|
+
return (await readAiwgConfig(options.projectRoot))?.installed ?? {};
|
|
152
|
+
}
|
|
153
|
+
function exactUserEntries(installed, bundles, provider, type) {
|
|
154
|
+
const entries = new Set();
|
|
155
|
+
for (const bundle of bundles) {
|
|
156
|
+
const deployment = installed[bundle]?.deployedTo[provider];
|
|
157
|
+
for (const name of deployment?.entries?.[type] ?? []) {
|
|
158
|
+
if (name === path.basename(name) && name !== '.' && name !== '..')
|
|
159
|
+
entries.add(name);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return [...entries].sort();
|
|
163
|
+
}
|
|
164
|
+
async function sourceBundleFiles(root) {
|
|
165
|
+
const files = [];
|
|
166
|
+
const walk = async (directory) => {
|
|
167
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const candidate = path.join(directory, entry.name);
|
|
170
|
+
if (entry.isSymbolicLink())
|
|
171
|
+
throw new Error(`canonical source bundle contains a symbolic link: ${candidate}`);
|
|
172
|
+
if (entry.isDirectory())
|
|
173
|
+
await walk(candidate);
|
|
174
|
+
else if (entry.isFile())
|
|
175
|
+
files.push(candidate);
|
|
176
|
+
else
|
|
177
|
+
throw new Error(`canonical source bundle contains a non-regular entry: ${candidate}`);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
await walk(root);
|
|
181
|
+
const inventory = await Promise.all(files.map(async (absolutePath) => {
|
|
182
|
+
const content = await readFile(absolutePath);
|
|
183
|
+
return {
|
|
184
|
+
path: path.relative(root, absolutePath).split(path.sep).join('/'),
|
|
185
|
+
sha256: sha256(content),
|
|
186
|
+
bytes: content.byteLength,
|
|
187
|
+
absolutePath,
|
|
188
|
+
};
|
|
189
|
+
}));
|
|
190
|
+
return inventory.sort((left, right) => left.path.localeCompare(right.path));
|
|
191
|
+
}
|
|
192
|
+
async function resolveSourceBundle(options, bundle, entry) {
|
|
193
|
+
const candidates = [];
|
|
194
|
+
if (entry?.source === 'project-local' && entry.localPath) {
|
|
195
|
+
candidates.push(path.resolve(options.projectRoot, entry.localPath, 'manifest.json'));
|
|
196
|
+
}
|
|
197
|
+
const frameworkDir = FRAMEWORK_BUNDLE_DIRS[bundle] ?? bundle;
|
|
198
|
+
candidates.push(path.join(options.frameworkRoot, 'agentic', 'code', 'frameworks', frameworkDir, 'manifest.json'), path.join(options.frameworkRoot, 'agentic', 'code', 'addons', bundle, 'manifest.json'), path.join(options.frameworkRoot, 'agentic', 'code', 'extensions', bundle, 'manifest.json'));
|
|
199
|
+
for (const candidate of candidates) {
|
|
200
|
+
try {
|
|
201
|
+
await access(candidate);
|
|
202
|
+
const root = path.dirname(candidate);
|
|
203
|
+
const files = await sourceBundleFiles(root);
|
|
204
|
+
if (files.length === 0)
|
|
205
|
+
continue;
|
|
206
|
+
return {
|
|
207
|
+
digest: sha256(stable(files.map(({ path: file, sha256: digest, bytes }) => ({ path: file, sha256: digest, bytes })))),
|
|
208
|
+
root,
|
|
209
|
+
files,
|
|
210
|
+
canonicalBytesAvailable: true,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Continue through the canonical source locations.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const recorded = entry?.manifestHash?.replace(/^sha256:/, '');
|
|
218
|
+
return recorded && /^[a-f0-9]{64}$/.test(recorded)
|
|
219
|
+
? { digest: recorded, root: '', files: [], canonicalBytesAvailable: false }
|
|
220
|
+
: null;
|
|
221
|
+
}
|
|
222
|
+
function receiptBundles(installed, options) {
|
|
223
|
+
const deployed = Object.keys(installed)
|
|
224
|
+
.filter(bundle => Boolean(installed[bundle]?.deployedTo[options.provider]))
|
|
225
|
+
.sort();
|
|
226
|
+
return deployed.length > 0 ? deployed : [...new Set(options.requestedBundles)].sort();
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Convert an already signature-verified web release into the stable verifier
|
|
230
|
+
* result contract for the complete canonical bundle consumed by deployment.
|
|
231
|
+
* Every local file must match its signed descriptor; a registry's self-recorded
|
|
232
|
+
* manifestHash is never an authentication boundary.
|
|
233
|
+
*/
|
|
234
|
+
export async function sourceVerificationsFromSignedWebRelease(options, release) {
|
|
235
|
+
const installed = await installedEntries(options);
|
|
236
|
+
const results = {};
|
|
237
|
+
for (const bundle of receiptBundles(installed, options)) {
|
|
238
|
+
const entry = installed[bundle];
|
|
239
|
+
if (entry?.source === 'project-local')
|
|
240
|
+
continue;
|
|
241
|
+
const source = await resolveSourceBundle(options, bundle, entry);
|
|
242
|
+
if (!source?.canonicalBytesAvailable)
|
|
243
|
+
continue;
|
|
244
|
+
let matched = true;
|
|
245
|
+
for (const file of source.files) {
|
|
246
|
+
const relative = path.relative(options.frameworkRoot, file.absolutePath).split(path.sep).join('/');
|
|
247
|
+
if (!relative || relative === '..' || relative.startsWith('../')) {
|
|
248
|
+
matched = false;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
const descriptor = release.descriptors.get(`raw/${relative}`);
|
|
252
|
+
if (!descriptor || descriptor.sha256 !== file.sha256 || descriptor.size !== file.bytes) {
|
|
253
|
+
matched = false;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!matched)
|
|
258
|
+
continue;
|
|
259
|
+
const artifactName = `aiwg:bundle-inventory:${bundle}`;
|
|
260
|
+
results[bundle] = {
|
|
261
|
+
schemaVersion: 'aiwg.verify.result.v1',
|
|
262
|
+
status: 'verified',
|
|
263
|
+
exitCode: 0,
|
|
264
|
+
artifact: { name: artifactName, sha256: source.digest },
|
|
265
|
+
policy: 'aiwg-signed-web-release',
|
|
266
|
+
identities: ['aiwg-release-publisher'],
|
|
267
|
+
...(release.channelSequence === undefined ? {} : {
|
|
268
|
+
freshness: {
|
|
269
|
+
namespace: 'aiwg',
|
|
270
|
+
channel: release.selector,
|
|
271
|
+
sequence: release.channelSequence,
|
|
272
|
+
version: release.version,
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
275
|
+
diagnostics: [{
|
|
276
|
+
code: 'SIGNED_RELEASE_DESCRIPTOR',
|
|
277
|
+
message: `Exact canonical source bytes match signed release manifest ${release.manifestDigest}`,
|
|
278
|
+
}],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return results;
|
|
282
|
+
}
|
|
283
|
+
async function sourceEvidence(options, installed) {
|
|
284
|
+
const bundles = [...new Set(options.requestedBundles)].sort();
|
|
285
|
+
const materials = [];
|
|
286
|
+
let authenticated = Boolean(options.sourceVerifications);
|
|
287
|
+
for (const bundle of bundles) {
|
|
288
|
+
const entry = installed[bundle];
|
|
289
|
+
const source = await resolveSourceBundle(options, bundle, entry);
|
|
290
|
+
if (!source) {
|
|
291
|
+
return {
|
|
292
|
+
subject: `aiwg:deployment:${sha256(bundles.join('\0')).slice(0, 24)}`,
|
|
293
|
+
sha256: sha256(stable(materials)),
|
|
294
|
+
verification: 'failed',
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const verification = options.sourceVerifications?.[bundle];
|
|
298
|
+
if (!source.canonicalBytesAvailable
|
|
299
|
+
|| verification?.status !== 'verified'
|
|
300
|
+
|| verification.artifact.sha256 !== source.digest) {
|
|
301
|
+
authenticated = false;
|
|
302
|
+
}
|
|
303
|
+
materials.push({
|
|
304
|
+
bundle,
|
|
305
|
+
digest: source.digest,
|
|
306
|
+
source: entry?.source ?? 'unknown',
|
|
307
|
+
version: entry?.version ?? 'unknown',
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const digest = sha256(stable(materials));
|
|
311
|
+
return {
|
|
312
|
+
subject: `aiwg:deployment:${digest.slice(0, 24)}`,
|
|
313
|
+
sha256: digest,
|
|
314
|
+
verification: options.sourceVerifications
|
|
315
|
+
? authenticated ? 'verified' : 'failed'
|
|
316
|
+
: 'policy-exempt',
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function transformerEvidence(provider, scope) {
|
|
320
|
+
const definition = getProviderDefinition(provider);
|
|
321
|
+
const adapterContract = definition
|
|
322
|
+
? {
|
|
323
|
+
adapters: definition.adapters,
|
|
324
|
+
artifacts: definition.paths.artifacts,
|
|
325
|
+
kernelSkills: definition.paths.kernelSkills,
|
|
326
|
+
scope,
|
|
327
|
+
userArtifacts: scope === 'user' ? USER_SCOPE_PATHS[provider] ?? null : null,
|
|
328
|
+
}
|
|
329
|
+
: { provider, scope };
|
|
330
|
+
return {
|
|
331
|
+
id: TRANSFORMER_ID,
|
|
332
|
+
version: TRANSFORMER_VERSION,
|
|
333
|
+
providerAdapter: provider,
|
|
334
|
+
providerAdapterVersion: sha256(stable(adapterContract)),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
export async function resolveProviderReceiptRuntimeEvidence(rawOptions) {
|
|
338
|
+
const provider = normalizeProviderDefinitionId(rawOptions.provider) ?? rawOptions.provider;
|
|
339
|
+
const options = { ...rawOptions, provider };
|
|
340
|
+
const installed = await installedEntries(options);
|
|
341
|
+
const deployedBundles = receiptBundles(installed, options);
|
|
342
|
+
const evidenceOptions = deployedBundles.length > 0
|
|
343
|
+
? { ...options, requestedBundles: deployedBundles }
|
|
344
|
+
: options;
|
|
345
|
+
const paths = artifactPaths(provider, options.scope);
|
|
346
|
+
const outputRoot = path.resolve(options.outputRoot ?? (options.scope === 'user' ? homedir() : options.projectRoot));
|
|
347
|
+
const ownedFiles = new Set();
|
|
348
|
+
if (paths) {
|
|
349
|
+
for (const type of ['agents', 'commands', 'skills', 'rules', 'behaviors']) {
|
|
350
|
+
const configured = paths[type];
|
|
351
|
+
if (!configured)
|
|
352
|
+
continue;
|
|
353
|
+
const directory = resolveProviderPathValue(configured, outputRoot);
|
|
354
|
+
const entries = options.scope === 'user'
|
|
355
|
+
? exactUserEntries(installed, evidenceOptions.requestedBundles, provider, type)
|
|
356
|
+
: [];
|
|
357
|
+
await collectOwnedDirectory(directory, entries, ownedFiles);
|
|
358
|
+
}
|
|
359
|
+
if (options.scope === 'project') {
|
|
360
|
+
const kernel = resolveProviderPathValue(getProviderKernelSkillPath(provider), outputRoot);
|
|
361
|
+
if (kernel)
|
|
362
|
+
await collectOwnedDirectory(kernel, [], ownedFiles);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const outputPaths = [...ownedFiles]
|
|
366
|
+
.map(file => portablePath(outputRoot, file))
|
|
367
|
+
.filter((file) => Boolean(file))
|
|
368
|
+
.sort();
|
|
369
|
+
return {
|
|
370
|
+
provider,
|
|
371
|
+
outputRoot,
|
|
372
|
+
outputPaths,
|
|
373
|
+
source: await sourceEvidence(evidenceOptions, installed),
|
|
374
|
+
transformer: transformerEvidence(provider, options.scope),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
export async function finalizeProviderTransformationReceipt(options) {
|
|
378
|
+
if (!options.sourceVerifications) {
|
|
379
|
+
return {
|
|
380
|
+
status: 'skipped',
|
|
381
|
+
receiptPath: null,
|
|
382
|
+
outputCount: 0,
|
|
383
|
+
reason: 'authenticated canonical source evidence was not supplied',
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const evidence = await resolveProviderReceiptRuntimeEvidence(options);
|
|
387
|
+
if (evidence.source.verification !== 'verified') {
|
|
388
|
+
return {
|
|
389
|
+
status: 'skipped',
|
|
390
|
+
receiptPath: null,
|
|
391
|
+
outputCount: 0,
|
|
392
|
+
reason: evidence.source.verification === 'failed'
|
|
393
|
+
? 'canonical source verification failed'
|
|
394
|
+
: 'authenticated canonical source evidence was not supplied',
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (evidence.outputPaths.length === 0) {
|
|
398
|
+
return { status: 'skipped', receiptPath: null, outputCount: 0, reason: 'no managed provider outputs were found' };
|
|
399
|
+
}
|
|
400
|
+
const verifiedSource = {
|
|
401
|
+
subject: evidence.source.subject,
|
|
402
|
+
sha256: evidence.source.sha256,
|
|
403
|
+
verification: evidence.source.verification,
|
|
404
|
+
};
|
|
405
|
+
const receipt = await createProviderTransformationReceipt({
|
|
406
|
+
projectRoot: options.projectRoot,
|
|
407
|
+
outputRoot: evidence.outputRoot,
|
|
408
|
+
provider: evidence.provider,
|
|
409
|
+
scope: options.scope,
|
|
410
|
+
generatedAt: options.generatedAt,
|
|
411
|
+
source: verifiedSource,
|
|
412
|
+
transformer: evidence.transformer,
|
|
413
|
+
outputPaths: evidence.outputPaths,
|
|
414
|
+
});
|
|
415
|
+
return {
|
|
416
|
+
status: 'written',
|
|
417
|
+
receiptPath: await writeProviderTransformationReceipt(options.projectRoot, receipt),
|
|
418
|
+
outputCount: receipt.outputs.length,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
export async function diagnoseIntegratedProviderTransformationReceipt(options) {
|
|
422
|
+
const provider = normalizeProviderDefinitionId(options.provider) ?? options.provider;
|
|
423
|
+
const receiptPath = providerTransformationReceiptPath(options.projectRoot, provider, options.scope);
|
|
424
|
+
try {
|
|
425
|
+
await access(receiptPath);
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
return {
|
|
429
|
+
status: 'missing-receipt',
|
|
430
|
+
receiptPath,
|
|
431
|
+
checkedOutputs: 0,
|
|
432
|
+
findings: [{
|
|
433
|
+
kind: 'missing-receipt',
|
|
434
|
+
message: 'No transformation receipt exists for this provider deployment.',
|
|
435
|
+
}],
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
const evidence = await resolveProviderReceiptRuntimeEvidence(options);
|
|
439
|
+
return diagnoseProviderTransformationReceipt({
|
|
440
|
+
projectRoot: options.projectRoot,
|
|
441
|
+
outputRoot: evidence.outputRoot,
|
|
442
|
+
provider: evidence.provider,
|
|
443
|
+
scope: options.scope,
|
|
444
|
+
source: evidence.source,
|
|
445
|
+
transformer: evidence.transformer,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
//# sourceMappingURL=transformation-receipt-integration.js.map
|