@aiwg/cli 2026.8.1 → 2026.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/handlers/install.js +42 -4
- package/dist/src/cli/handlers/marketplace.js +375 -122
- package/dist/src/cli/handlers/subcommands.js +1 -0
- package/dist/src/cli/handlers/use.js +3 -1
- package/dist/src/config/aiwg-config.js +5 -0
- package/dist/src/extensions/project-local-doctor.js +10 -26
- package/dist/src/extensions/project-local-remove.js +42 -5
- package/dist/src/marketplace/exchange.js +602 -0
- package/dist/src/marketplace/provenance-types.js +19 -0
- package/dist/src/marketplace/provenance.js +834 -0
- package/dist/src/packages/adapters/git.js +79 -29
- package/dist/src/packages/package-discovery.js +81 -0
- package/dist/src/packages/package-registry.js +2 -0
- package/dist/src/packages/registry.js +119 -20
- package/package.json +2 -2
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import path from 'path';
|
|
21
21
|
import { installPackage } from '../../packages/registry.js';
|
|
22
22
|
import { recordDeployment } from '../../packages/package-registry.js';
|
|
23
|
+
import { marketplaceConfigDir, resolveVerificationPolicy } from '../../marketplace/exchange.js';
|
|
23
24
|
import { createScriptRunner } from './script-runner.js';
|
|
24
25
|
import { handlerResultFromError } from '../errors.js';
|
|
25
26
|
import * as ui from '../ui.js';
|
|
@@ -59,6 +60,12 @@ export const installHandler = {
|
|
|
59
60
|
' --provider <name> Target provider (claude, copilot, cursor...)',
|
|
60
61
|
' --target <dir> Project directory to deploy into',
|
|
61
62
|
' --refresh Force re-pull even if cached',
|
|
63
|
+
' --ref <tag-or-sha> Resolve this ref and lock its immutable commit',
|
|
64
|
+
' --package <id> Select one wrapper when a repository contains several',
|
|
65
|
+
' --verify Require a publisher signature trusted by local policy',
|
|
66
|
+
' --policy <name|path> Named trust policy or JSON policy file',
|
|
67
|
+
' --project-local Store registry, lock, receipts, and index under <target>/.aiwg',
|
|
68
|
+
' --global Store package state in the user AIWG directory (default)',
|
|
62
69
|
].join('\n'),
|
|
63
70
|
};
|
|
64
71
|
}
|
|
@@ -66,6 +73,15 @@ export const installHandler = {
|
|
|
66
73
|
const refresh = hasFlag(ctx.args, '--refresh');
|
|
67
74
|
const provider = parseFlag(ctx.args, '--provider') ?? 'claude';
|
|
68
75
|
const target = parseFlag(ctx.args, '--target') ?? ctx.cwd;
|
|
76
|
+
const projectLocal = hasFlag(ctx.args, '--project-local');
|
|
77
|
+
const global = hasFlag(ctx.args, '--global');
|
|
78
|
+
if (projectLocal && global) {
|
|
79
|
+
return { exitCode: 1, message: 'Error: Choose either --project-local or --global, not both' };
|
|
80
|
+
}
|
|
81
|
+
const verify = hasFlag(ctx.args, '--verify');
|
|
82
|
+
const policyName = parseFlag(ctx.args, '--policy');
|
|
83
|
+
const scope = { projectLocal, projectDir: target };
|
|
84
|
+
const configDir = marketplaceConfigDir(scope);
|
|
69
85
|
ui.blank();
|
|
70
86
|
console.log(` ${ui.brandMark()} ${ui.bold('aiwg install')} ${ui.dimText(rawRef)}`);
|
|
71
87
|
ui.rule();
|
|
@@ -75,8 +91,23 @@ export const installHandler = {
|
|
|
75
91
|
let key;
|
|
76
92
|
let type;
|
|
77
93
|
let namespace;
|
|
94
|
+
let lockId;
|
|
95
|
+
let verificationStatus;
|
|
78
96
|
try {
|
|
79
|
-
|
|
97
|
+
const resolvedPolicy = await resolveVerificationPolicy(policyName, scope);
|
|
98
|
+
const installed = await installPackage(rawRef, {
|
|
99
|
+
refresh,
|
|
100
|
+
ref: parseFlag(ctx.args, '--ref'),
|
|
101
|
+
packageSelector: parseFlag(ctx.args, '--package'),
|
|
102
|
+
verify,
|
|
103
|
+
verificationPolicy: resolvedPolicy.policy,
|
|
104
|
+
trustStore: resolvedPolicy.trustStore,
|
|
105
|
+
configDir,
|
|
106
|
+
actor: 'local-user',
|
|
107
|
+
});
|
|
108
|
+
({ cachePath, key, type, namespace } = installed);
|
|
109
|
+
lockId = installed.lock.lockId;
|
|
110
|
+
verificationStatus = installed.verification.status;
|
|
80
111
|
}
|
|
81
112
|
catch (error) {
|
|
82
113
|
// Preserve AiwgError.exitCode while keeping the "Error: " prefix users
|
|
@@ -85,9 +116,12 @@ export const installHandler = {
|
|
|
85
116
|
return { ...result, message: `Error: ${result.message}` };
|
|
86
117
|
}
|
|
87
118
|
ui.success(`Installed: ${key} (${type})`);
|
|
88
|
-
ui.
|
|
119
|
+
ui.dim(` Cache: ${cachePath}`);
|
|
120
|
+
ui.dim(` Lock: ${lockId}`);
|
|
121
|
+
ui.dim(` Verification: ${verificationStatus}`);
|
|
122
|
+
ui.dim(` Scope: ${projectLocal ? 'project-local' : 'global'}`);
|
|
89
123
|
if (namespace !== 'aiwg') {
|
|
90
|
-
ui.
|
|
124
|
+
ui.dim(` Namespace: ${namespace}`);
|
|
91
125
|
}
|
|
92
126
|
// Optionally deploy
|
|
93
127
|
if (deploy) {
|
|
@@ -98,6 +132,10 @@ export const installHandler = {
|
|
|
98
132
|
'--deploy-commands',
|
|
99
133
|
'--deploy-skills',
|
|
100
134
|
'--deploy-rules',
|
|
135
|
+
// External packages do not participate in AIWG's global artifact
|
|
136
|
+
// index. Copy their complete skill payload into the target so a
|
|
137
|
+
// successful install cannot silently deploy agents/rules only.
|
|
138
|
+
'--copy-all',
|
|
101
139
|
'--provider', provider,
|
|
102
140
|
'--target', target,
|
|
103
141
|
'--namespace', namespace,
|
|
@@ -116,7 +154,7 @@ export const installHandler = {
|
|
|
116
154
|
projectPath: target,
|
|
117
155
|
provider,
|
|
118
156
|
deployedAt: new Date().toISOString(),
|
|
119
|
-
});
|
|
157
|
+
}, configDir);
|
|
120
158
|
}
|
|
121
159
|
}
|
|
122
160
|
ui.blank();
|
|
@@ -1,41 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Git-native Fortemi marketplace command handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* OpenClaw, etc.).
|
|
7
|
-
*
|
|
8
|
-
* Subcommands:
|
|
9
|
-
* search <query> Fan-out search to all adapters, display with source attribution
|
|
10
|
-
* list List installed marketplace packages
|
|
11
|
-
*
|
|
12
|
-
* Flags:
|
|
13
|
-
* --source <id> Limit to a single adapter (e.g. clawhub, openclaw, local)
|
|
14
|
-
* --json Emit structured JSON for programmatic use
|
|
4
|
+
* Catalog results are observations, never endorsements. Direct Git and catalog
|
|
5
|
+
* installs share the same immutable lock and local receipt pipeline.
|
|
15
6
|
*
|
|
16
7
|
* @implements #805
|
|
8
|
+
* @implements #2009
|
|
17
9
|
*/
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
18
12
|
import { searchSkills } from '../../skills/registry.js';
|
|
19
|
-
import { listInstalledPackages } from '../../packages/registry.js';
|
|
13
|
+
import { installPackage, listInstalledPackages } from '../../packages/registry.js';
|
|
14
|
+
import { GitAdapter } from '../../packages/adapters/git.js';
|
|
15
|
+
import { inspectGitCheckout } from '../../marketplace/provenance.js';
|
|
16
|
+
import { exportPortablePackage, findIndexedPackage, importPortablePackage, marketplaceConfigDir, publishLocalPackage, readCatalogEnvelope, readTrustStore, registerCatalog, removeCatalog, resolveCatalogEntry, resolveVerificationPolicy, searchCatalogs, verifyIndexedPackage, } from '../../marketplace/exchange.js';
|
|
20
17
|
import * as ui from '../ui.js';
|
|
21
|
-
// ── Usage string ───────────────────────────────────────────────────────────
|
|
22
18
|
const USAGE = [
|
|
23
19
|
'Usage:',
|
|
24
|
-
' aiwg marketplace
|
|
20
|
+
' aiwg marketplace add <catalog-git-url> [--ref <tag-or-sha>]',
|
|
21
|
+
' aiwg marketplace search <query> Search packages and signed catalogs',
|
|
22
|
+
' aiwg marketplace info <package> Show provenance and catalog observations',
|
|
23
|
+
' aiwg marketplace install <url|package> Install and lock direct Git or catalog package',
|
|
24
|
+
' aiwg marketplace verify <package|lock> Verify cached content and receipts offline',
|
|
25
|
+
' aiwg marketplace export <package> --output <archive.json>',
|
|
26
|
+
' aiwg marketplace import <archive.json> [--project-local]',
|
|
27
|
+
' aiwg marketplace publish <source> --key <pem> --publisher <id>',
|
|
28
|
+
' aiwg marketplace remove <catalog-id>',
|
|
25
29
|
' aiwg marketplace list List installed marketplace packages',
|
|
26
30
|
'',
|
|
27
|
-
'
|
|
28
|
-
' --
|
|
29
|
-
' --
|
|
30
|
-
'',
|
|
31
|
-
'
|
|
32
|
-
' aiwg marketplace search parallel-dispatch',
|
|
33
|
-
' aiwg marketplace search auth --source clawhub',
|
|
34
|
-
' aiwg marketplace search auth --json',
|
|
35
|
-
' aiwg marketplace list',
|
|
36
|
-
' aiwg marketplace list --json',
|
|
31
|
+
'Common flags:',
|
|
32
|
+
' --json Output structured JSON',
|
|
33
|
+
' --project-local Store/read state below the target project .aiwg directory',
|
|
34
|
+
' --global Store/read state in the user AIWG directory (default)',
|
|
35
|
+
' --policy <name> Named trust policy or JSON policy path',
|
|
37
36
|
].join('\n');
|
|
38
|
-
// ── Flag helpers ───────────────────────────────────────────────────────────
|
|
39
37
|
function parseFlag(args, flag) {
|
|
40
38
|
const idx = args.indexOf(flag);
|
|
41
39
|
if (idx === -1 || idx + 1 >= args.length)
|
|
@@ -45,124 +43,379 @@ function parseFlag(args, flag) {
|
|
|
45
43
|
function hasFlag(args, flag) {
|
|
46
44
|
return args.includes(flag);
|
|
47
45
|
}
|
|
48
|
-
|
|
46
|
+
function scopeFromContext(ctx) {
|
|
47
|
+
const projectLocal = hasFlag(ctx.args, '--project-local');
|
|
48
|
+
if (projectLocal && hasFlag(ctx.args, '--global'))
|
|
49
|
+
throw new Error('Choose either --project-local or --global, not both');
|
|
50
|
+
return { projectLocal, projectDir: parseFlag(ctx.args, '--target') ?? ctx.cwd };
|
|
51
|
+
}
|
|
52
|
+
function jsonOrMessage(jsonMode, value, message) {
|
|
53
|
+
if (jsonMode) {
|
|
54
|
+
console.log(JSON.stringify(value, null, 2));
|
|
55
|
+
return { exitCode: 0 };
|
|
56
|
+
}
|
|
57
|
+
return { exitCode: 0, message };
|
|
58
|
+
}
|
|
59
|
+
function failure(error) {
|
|
60
|
+
return { exitCode: 1, message: `Error: ${error instanceof Error ? error.message : String(error)}` };
|
|
61
|
+
}
|
|
62
|
+
function requiredArg(ctx, usage) {
|
|
63
|
+
const value = ctx.args[1];
|
|
64
|
+
return !value || value.startsWith('--') ? { exitCode: 1, message: `Error: ${usage}` } : value;
|
|
65
|
+
}
|
|
49
66
|
export const marketplaceHandler = {
|
|
50
67
|
id: 'marketplace',
|
|
51
68
|
name: 'Marketplace',
|
|
52
|
-
description: '
|
|
69
|
+
description: 'Federated Git marketplace with Fortemi provenance and offline verification',
|
|
53
70
|
category: 'framework',
|
|
54
71
|
aliases: ['market'],
|
|
55
72
|
async execute(ctx) {
|
|
56
73
|
const subcommand = ctx.args[0];
|
|
57
|
-
if (!subcommand)
|
|
74
|
+
if (!subcommand)
|
|
58
75
|
return { exitCode: 0, message: USAGE };
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
if (subcommand === 'add')
|
|
77
|
+
return handleAdd(ctx);
|
|
78
|
+
if (subcommand === 'search')
|
|
61
79
|
return handleSearch(ctx);
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
if (subcommand === 'info')
|
|
81
|
+
return handleInfo(ctx);
|
|
82
|
+
if (subcommand === 'install')
|
|
83
|
+
return handleInstall(ctx);
|
|
84
|
+
if (subcommand === 'verify')
|
|
85
|
+
return handleVerify(ctx);
|
|
86
|
+
if (subcommand === 'export')
|
|
87
|
+
return handleExport(ctx);
|
|
88
|
+
if (subcommand === 'import')
|
|
89
|
+
return handleImport(ctx);
|
|
90
|
+
if (subcommand === 'publish')
|
|
91
|
+
return handlePublish(ctx);
|
|
92
|
+
if (subcommand === 'remove')
|
|
93
|
+
return handleRemove(ctx);
|
|
94
|
+
if (subcommand === 'list')
|
|
64
95
|
return handleList(ctx);
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
exitCode: 1,
|
|
68
|
-
message: `Error: Unknown subcommand '${subcommand}'\n\n${USAGE}`,
|
|
69
|
-
};
|
|
96
|
+
return { exitCode: 1, message: `Error: Unknown subcommand '${subcommand}'\n\n${USAGE}` };
|
|
70
97
|
},
|
|
71
98
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
async function handleAdd(ctx) {
|
|
100
|
+
const argument = requiredArg(ctx, 'Catalog Git URL required');
|
|
101
|
+
if (typeof argument !== 'string')
|
|
102
|
+
return argument;
|
|
103
|
+
try {
|
|
104
|
+
const scope = scopeFromContext(ctx);
|
|
105
|
+
const requestedRef = parseFlag(ctx.args, '--ref') ?? 'HEAD';
|
|
106
|
+
const adapter = new GitAdapter();
|
|
107
|
+
const source = { gitUrl: argument, ref: requestedRef, label: argument };
|
|
108
|
+
const cachePath = await adapter.fetch(source, { refresh: hasFlag(ctx.args, '--refresh') });
|
|
109
|
+
const identity = await inspectGitCheckout(cachePath);
|
|
110
|
+
const candidates = [
|
|
111
|
+
path.join(cachePath, 'aiwg-marketplace-catalog.json'),
|
|
112
|
+
path.join(cachePath, '.aiwg', 'marketplace', 'catalog.json'),
|
|
113
|
+
];
|
|
114
|
+
const catalogPath = candidates.find((candidate) => fs.existsSync(candidate));
|
|
115
|
+
if (!catalogPath)
|
|
116
|
+
throw new Error('Catalog repository has no aiwg-marketplace-catalog.json or .aiwg/marketplace/catalog.json');
|
|
117
|
+
const trustStore = await readTrustStore({ ...scope, path: parseFlag(ctx.args, '--trust-store') });
|
|
118
|
+
const record = await registerCatalog({
|
|
119
|
+
...scope,
|
|
120
|
+
catalogPath,
|
|
121
|
+
source: identity.canonicalRemote,
|
|
122
|
+
requestedRef: source.ref ?? requestedRef,
|
|
123
|
+
resolvedCommit: identity.resolvedCommit,
|
|
124
|
+
cachePath,
|
|
125
|
+
trustStore,
|
|
126
|
+
});
|
|
127
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), record, `Added verified catalog ${record.catalogId} at ${record.resolvedCommit}`);
|
|
88
128
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (source) {
|
|
92
|
-
ui.dim(` Source: ${source}`);
|
|
129
|
+
catch (error) {
|
|
130
|
+
return failure(error);
|
|
93
131
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
132
|
+
}
|
|
133
|
+
async function handleSearch(ctx) {
|
|
134
|
+
const argument = requiredArg(ctx, 'Query required\n\nUsage: aiwg marketplace search <query>');
|
|
135
|
+
if (typeof argument !== 'string')
|
|
136
|
+
return argument;
|
|
137
|
+
try {
|
|
138
|
+
const source = parseFlag(ctx.args, '--source');
|
|
139
|
+
const scope = scopeFromContext(ctx);
|
|
140
|
+
const [skillResults, catalogResults] = await Promise.all([
|
|
141
|
+
!source || !source.startsWith('catalog:') ? searchSkills(argument, source) : Promise.resolve([]),
|
|
142
|
+
searchCatalogs(argument, scope),
|
|
143
|
+
]);
|
|
144
|
+
const filteredCatalogs = source?.startsWith('catalog:')
|
|
145
|
+
? catalogResults.filter((result) => result.catalogId === source.slice('catalog:'.length))
|
|
146
|
+
: source ? [] : catalogResults;
|
|
147
|
+
const results = [
|
|
148
|
+
...skillResults,
|
|
149
|
+
...filteredCatalogs.map((entry) => ({
|
|
150
|
+
name: `${entry.identity}@${entry.version}`,
|
|
151
|
+
description: entry.description,
|
|
152
|
+
source: `catalog:${entry.catalogId}`,
|
|
153
|
+
package: entry.identity,
|
|
154
|
+
provenanceCompleteness: entry.provenanceCompleteness,
|
|
155
|
+
verificationStatus: entry.verificationStatus,
|
|
156
|
+
observation: entry.observation,
|
|
157
|
+
})),
|
|
158
|
+
];
|
|
159
|
+
if (hasFlag(ctx.args, '--json')) {
|
|
160
|
+
console.log(JSON.stringify(results, null, 2));
|
|
161
|
+
return { exitCode: 0 };
|
|
162
|
+
}
|
|
97
163
|
ui.blank();
|
|
98
|
-
ui.
|
|
164
|
+
console.log(` ${ui.brandMark()} ${ui.bold(`Marketplace Search: "${argument}"`)}`);
|
|
165
|
+
if (source)
|
|
166
|
+
ui.dim(` Source: ${source}`);
|
|
167
|
+
ui.rule();
|
|
168
|
+
if (results.length === 0) {
|
|
169
|
+
ui.dim(' No results found.');
|
|
170
|
+
ui.blank();
|
|
171
|
+
return { exitCode: 0 };
|
|
172
|
+
}
|
|
173
|
+
const nameWidth = Math.max(12, ...results.map((result) => result.name.length));
|
|
174
|
+
const sourceWidth = Math.max(8, ...results.map((result) => result.source.length));
|
|
175
|
+
const pkgWidth = Math.max(10, ...results.map((result) => (result.package ?? '').length));
|
|
176
|
+
const header = ['Name'.padEnd(nameWidth), 'Source'.padEnd(sourceWidth), 'Package'.padEnd(pkgWidth), 'Description'].join(' ');
|
|
177
|
+
ui.dim(` ${header}`);
|
|
178
|
+
ui.dim(` ${'─'.repeat(header.length)}`);
|
|
179
|
+
for (const result of results) {
|
|
180
|
+
console.log(` ${[
|
|
181
|
+
result.name.padEnd(nameWidth),
|
|
182
|
+
result.source.padEnd(sourceWidth),
|
|
183
|
+
(result.package ?? '').padEnd(pkgWidth),
|
|
184
|
+
result.description.length > 60 ? `${result.description.slice(0, 57)}...` : result.description,
|
|
185
|
+
].join(' ')}`);
|
|
186
|
+
}
|
|
99
187
|
ui.blank();
|
|
188
|
+
ui.dim(` ${results.length} result${results.length === 1 ? '' : 's'}; catalog inclusion is not an endorsement`);
|
|
100
189
|
return { exitCode: 0 };
|
|
101
190
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return { exitCode: 0 };
|
|
191
|
+
catch (error) {
|
|
192
|
+
return failure(error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function handleInfo(ctx) {
|
|
196
|
+
const argument = requiredArg(ctx, 'Package query required');
|
|
197
|
+
if (typeof argument !== 'string')
|
|
198
|
+
return argument;
|
|
199
|
+
try {
|
|
200
|
+
const scope = scopeFromContext(ctx);
|
|
201
|
+
const installed = await findIndexedPackage(argument, scope);
|
|
202
|
+
const catalogs = (await searchCatalogs(argument, scope)).filter((entry) => entry.identity === argument || `${entry.identity}@${entry.version}` === argument || entry.lockId === argument);
|
|
203
|
+
if (!installed && catalogs.length === 0)
|
|
204
|
+
return { exitCode: 1, message: `Error: Marketplace package '${argument}' was not found` };
|
|
205
|
+
const result = {
|
|
206
|
+
query: argument,
|
|
207
|
+
installed: installed ? {
|
|
208
|
+
lock: installed.lock,
|
|
209
|
+
verificationStatus: installed.verificationStatus,
|
|
210
|
+
installedAt: installed.installedAt,
|
|
211
|
+
catalogs: installed.catalogs,
|
|
212
|
+
} : null,
|
|
213
|
+
catalogObservations: catalogs,
|
|
214
|
+
endorsement: false,
|
|
215
|
+
};
|
|
216
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), result, [
|
|
217
|
+
`Package: ${argument}`,
|
|
218
|
+
`Installed: ${installed ? 'yes' : 'no'}`,
|
|
219
|
+
...(installed ? [`Lock: ${installed.lock.lockId}`, `Verification: ${installed.verificationStatus}`] : []),
|
|
220
|
+
`Catalog observations: ${catalogs.length} (not endorsements)`,
|
|
221
|
+
...catalogs.map((entry) => ` ${entry.catalogId}: ${entry.provenanceCompleteness}% provenance, ${entry.verificationStatus}`),
|
|
222
|
+
].join('\n'));
|
|
135
223
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
224
|
+
catch (error) {
|
|
225
|
+
return failure(error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function looksLikeGitUrl(value) {
|
|
229
|
+
return /^(?:https?:\/\/|ssh:\/\/|git@)/.test(value);
|
|
230
|
+
}
|
|
231
|
+
async function handleInstall(ctx) {
|
|
232
|
+
const argument = requiredArg(ctx, 'Git URL or catalog package required');
|
|
233
|
+
if (typeof argument !== 'string')
|
|
234
|
+
return argument;
|
|
235
|
+
try {
|
|
236
|
+
const scope = scopeFromContext(ctx);
|
|
237
|
+
const configDir = marketplaceConfigDir(scope);
|
|
238
|
+
const resolvedPolicy = await resolveVerificationPolicy(parseFlag(ctx.args, '--policy'), scope);
|
|
239
|
+
let rawRef = argument;
|
|
240
|
+
let expectedEnvelope;
|
|
241
|
+
let expectedLockId;
|
|
242
|
+
let catalogId;
|
|
243
|
+
let ref = parseFlag(ctx.args, '--ref');
|
|
244
|
+
if (!looksLikeGitUrl(argument)) {
|
|
245
|
+
const catalog = await resolveCatalogEntry(argument, scope);
|
|
246
|
+
expectedEnvelope = await readCatalogEnvelope(catalog, scope);
|
|
247
|
+
rawRef = catalog.canonicalRemote;
|
|
248
|
+
expectedLockId = catalog.lockId;
|
|
249
|
+
catalogId = catalog.catalogId;
|
|
250
|
+
ref = catalog.resolvedCommit;
|
|
251
|
+
}
|
|
252
|
+
const installed = await installPackage(rawRef, {
|
|
253
|
+
configDir,
|
|
254
|
+
ref,
|
|
255
|
+
packageSelector: parseFlag(ctx.args, '--package'),
|
|
256
|
+
refresh: hasFlag(ctx.args, '--refresh'),
|
|
257
|
+
verify: hasFlag(ctx.args, '--verify'),
|
|
258
|
+
verificationPolicy: resolvedPolicy.policy,
|
|
259
|
+
trustStore: resolvedPolicy.trustStore,
|
|
260
|
+
expectedEnvelope,
|
|
261
|
+
expectedLockId,
|
|
262
|
+
catalogId,
|
|
263
|
+
actor: 'local-user',
|
|
264
|
+
});
|
|
265
|
+
const result = {
|
|
266
|
+
package: installed.key,
|
|
267
|
+
version: installed.envelope.package.version,
|
|
268
|
+
lockId: installed.lock.lockId,
|
|
269
|
+
resolvedCommit: installed.lock.resolvedCommit,
|
|
270
|
+
verificationStatus: installed.verification.status,
|
|
271
|
+
scope: scope.projectLocal ? 'project-local' : 'global',
|
|
272
|
+
catalog: catalogId ?? null,
|
|
273
|
+
};
|
|
274
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), result, `Installed ${result.package}@${result.version}\nLock: ${result.lockId}\nVerification: ${result.verificationStatus}`);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
return failure(error);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function handleVerify(ctx) {
|
|
281
|
+
const argument = requiredArg(ctx, 'Package or lock query required');
|
|
282
|
+
if (typeof argument !== 'string')
|
|
283
|
+
return argument;
|
|
284
|
+
try {
|
|
285
|
+
const scope = scopeFromContext(ctx);
|
|
286
|
+
const resolved = await resolveVerificationPolicy(parseFlag(ctx.args, '--policy'), scope);
|
|
287
|
+
const result = await verifyIndexedPackage({
|
|
288
|
+
...scope,
|
|
289
|
+
query: argument,
|
|
290
|
+
policy: resolved.policy,
|
|
291
|
+
trustStore: resolved.trustStore,
|
|
292
|
+
requireSignature: hasFlag(ctx.args, '--require-signature'),
|
|
293
|
+
actor: 'local-user',
|
|
294
|
+
});
|
|
295
|
+
if (!result.verification.ok)
|
|
296
|
+
return { exitCode: 1, message: `Verification failed: ${result.verification.errors.join('; ')}` };
|
|
297
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), result.verification, `Verified ${result.entry.lock.identity}@${result.entry.lock.version} offline\nStatus: ${result.verification.status}\nLock: ${result.entry.lock.lockId}`);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
return failure(error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async function handleExport(ctx) {
|
|
304
|
+
const argument = requiredArg(ctx, 'Package query required');
|
|
305
|
+
if (typeof argument !== 'string')
|
|
306
|
+
return argument;
|
|
307
|
+
const output = parseFlag(ctx.args, '--output');
|
|
308
|
+
if (!output)
|
|
309
|
+
return { exitCode: 1, message: 'Error: Usage: aiwg marketplace export <package> --output <archive.json>' };
|
|
310
|
+
try {
|
|
311
|
+
const result = await exportPortablePackage({ ...scopeFromContext(ctx), query: argument, output, actor: 'local-user' });
|
|
312
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), { output: result.output, lockId: result.bundle.lock.lockId, receiptId: result.receipt.receiptId }, `Exported ${result.bundle.lock.identity}@${result.bundle.lock.version} to ${result.output}`);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
return failure(error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function handleImport(ctx) {
|
|
319
|
+
const argument = requiredArg(ctx, 'Portable archive path required');
|
|
320
|
+
if (typeof argument !== 'string')
|
|
321
|
+
return argument;
|
|
322
|
+
try {
|
|
323
|
+
const scope = scopeFromContext(ctx);
|
|
324
|
+
const resolved = await resolveVerificationPolicy(parseFlag(ctx.args, '--policy'), scope);
|
|
325
|
+
const result = await importPortablePackage({
|
|
326
|
+
...scope,
|
|
327
|
+
input: argument,
|
|
328
|
+
verify: hasFlag(ctx.args, '--verify'),
|
|
329
|
+
policy: resolved.policy,
|
|
330
|
+
trustStore: resolved.trustStore,
|
|
331
|
+
actor: 'local-user',
|
|
332
|
+
});
|
|
333
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), { lock: result.entry.lock, verification: result.verification, receipt: result.receipt }, `Imported ${result.entry.lock.identity}@${result.entry.lock.version}\nLock: ${result.entry.lock.lockId}\nVerification: ${result.verification.status}`);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
return failure(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function handlePublish(ctx) {
|
|
340
|
+
const argument = requiredArg(ctx, 'Package source directory required');
|
|
341
|
+
if (typeof argument !== 'string')
|
|
342
|
+
return argument;
|
|
343
|
+
const privateKeyPath = parseFlag(ctx.args, '--key');
|
|
344
|
+
const publisher = parseFlag(ctx.args, '--publisher');
|
|
345
|
+
if (!privateKeyPath || !publisher) {
|
|
346
|
+
return { exitCode: 1, message: 'Error: Usage: aiwg marketplace publish <source> --key <ed25519.pem> --publisher <id> [--output <dir>]' };
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const outputDir = parseFlag(ctx.args, '--output') ?? path.join(path.resolve(argument), '.aiwg', 'marketplace');
|
|
350
|
+
const sequenceText = parseFlag(ctx.args, '--sequence');
|
|
351
|
+
const sequence = sequenceText === undefined ? undefined : Number(sequenceText);
|
|
352
|
+
if (sequence !== undefined && (!Number.isSafeInteger(sequence) || sequence < 1))
|
|
353
|
+
throw new Error('--sequence must be a positive integer');
|
|
354
|
+
const published = await publishLocalPackage({
|
|
355
|
+
sourceDir: argument,
|
|
356
|
+
outputDir,
|
|
357
|
+
privateKeyPath,
|
|
358
|
+
publicKeyPath: parseFlag(ctx.args, '--public-key'),
|
|
359
|
+
keyId: parseFlag(ctx.args, '--key-id'),
|
|
360
|
+
publisher,
|
|
361
|
+
requestedRef: parseFlag(ctx.args, '--ref'),
|
|
362
|
+
packageSelector: parseFlag(ctx.args, '--package'),
|
|
363
|
+
sequence,
|
|
364
|
+
actor: publisher,
|
|
365
|
+
});
|
|
366
|
+
return jsonOrMessage(hasFlag(ctx.args, '--json'), published, `Published signed envelope ${published.lock.lockId}\nEnvelope: ${published.envelopePath}\nFortemi: ${published.shardPath}`);
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
return failure(error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function handleRemove(ctx) {
|
|
373
|
+
const argument = requiredArg(ctx, 'Catalog ID required');
|
|
374
|
+
if (typeof argument !== 'string')
|
|
375
|
+
return argument;
|
|
376
|
+
try {
|
|
377
|
+
const removed = await removeCatalog(argument, scopeFromContext(ctx));
|
|
378
|
+
return {
|
|
379
|
+
exitCode: removed ? 0 : 1,
|
|
380
|
+
message: removed
|
|
381
|
+
? `Removed catalog ${argument}; existing immutable package locks remain valid`
|
|
382
|
+
: `Catalog '${argument}' was not registered`,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
return failure(error);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async function handleList(ctx) {
|
|
390
|
+
try {
|
|
391
|
+
const scope = scopeFromContext(ctx);
|
|
392
|
+
const packages = await listInstalledPackages(marketplaceConfigDir(scope));
|
|
393
|
+
if (hasFlag(ctx.args, '--json')) {
|
|
394
|
+
console.log(JSON.stringify(packages, null, 2));
|
|
395
|
+
return { exitCode: 0 };
|
|
396
|
+
}
|
|
141
397
|
ui.blank();
|
|
142
|
-
ui.
|
|
398
|
+
console.log(` ${ui.brandMark()} ${ui.bold('Installed Marketplace Packages')}`);
|
|
399
|
+
ui.rule();
|
|
400
|
+
if (packages.length === 0) {
|
|
401
|
+
ui.dim(' No packages installed.');
|
|
402
|
+
ui.blank();
|
|
403
|
+
return { exitCode: 0 };
|
|
404
|
+
}
|
|
405
|
+
const keyWidth = Math.max(12, ...packages.map((pkg) => pkg.key.length));
|
|
406
|
+
const versionWidth = Math.max(7, ...packages.map((pkg) => pkg.version.length));
|
|
407
|
+
const header = ['Package'.padEnd(keyWidth), 'Version'.padEnd(versionWidth), 'Type', 'Verification'].join(' ');
|
|
408
|
+
ui.dim(` ${header}`);
|
|
409
|
+
ui.dim(` ${'─'.repeat(header.length)}`);
|
|
410
|
+
for (const pkg of packages) {
|
|
411
|
+
console.log(` ${[pkg.key.padEnd(keyWidth), pkg.version.padEnd(versionWidth), pkg.type, pkg.verificationStatus ?? 'legacy'].join(' ')}`);
|
|
412
|
+
}
|
|
143
413
|
ui.blank();
|
|
414
|
+
ui.dim(` ${packages.length} package${packages.length === 1 ? '' : 's'} installed`);
|
|
144
415
|
return { exitCode: 0 };
|
|
145
416
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
'Package'.padEnd(keyWidth),
|
|
150
|
-
'Version'.padEnd(versionWidth),
|
|
151
|
-
'Type',
|
|
152
|
-
].join(' ');
|
|
153
|
-
ui.dim(` ${header}`);
|
|
154
|
-
ui.dim(` ${'─'.repeat(header.length)}`);
|
|
155
|
-
for (const pkg of packages) {
|
|
156
|
-
const row = [
|
|
157
|
-
pkg.key.padEnd(keyWidth),
|
|
158
|
-
pkg.version.padEnd(versionWidth),
|
|
159
|
-
pkg.type,
|
|
160
|
-
].join(' ');
|
|
161
|
-
console.log(` ${row}`);
|
|
162
|
-
}
|
|
163
|
-
ui.blank();
|
|
164
|
-
ui.dim(` ${packages.length} package${packages.length !== 1 ? 's' : ''} installed`);
|
|
165
|
-
ui.blank();
|
|
166
|
-
return { exitCode: 0 };
|
|
417
|
+
catch (error) {
|
|
418
|
+
return failure(error);
|
|
419
|
+
}
|
|
167
420
|
}
|
|
168
421
|
//# sourceMappingURL=marketplace.js.map
|
|
@@ -1062,6 +1062,7 @@ export const installPluginHandler = {
|
|
|
1062
1062
|
const runner = createScriptRunner(frameworkRoot);
|
|
1063
1063
|
return runner.run("tools/plugin/plugin-installer-cli.mjs", ctx.args, {
|
|
1064
1064
|
cwd: ctx.cwd,
|
|
1065
|
+
env: { AIWG_ROOT: frameworkRoot },
|
|
1065
1066
|
});
|
|
1066
1067
|
},
|
|
1067
1068
|
};
|