@a5c-ai/extension-mux 5.0.1-staging.04ca6ab00d21
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 +58 -0
- package/dist/binTemplates.d.ts +7 -0
- package/dist/binTemplates.d.ts.map +1 -0
- package/dist/binTemplates.js +292 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +299 -0
- package/dist/compiler.d.ts +15 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +118 -0
- package/dist/diff.d.ts +9 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +183 -0
- package/dist/emit.d.ts +3 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +42 -0
- package/dist/hookRegistration.d.ts +8 -0
- package/dist/hookRegistration.d.ts.map +1 -0
- package/dist/hookRegistration.js +9 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +200 -0
- package/dist/installInstructions.d.ts +3 -0
- package/dist/installInstructions.d.ts.map +1 -0
- package/dist/installInstructions.js +150 -0
- package/dist/installSharedGenerator.d.ts +3 -0
- package/dist/installSharedGenerator.d.ts.map +1 -0
- package/dist/installSharedGenerator.js +229 -0
- package/dist/manifestGenerators.d.ts +10 -0
- package/dist/manifestGenerators.d.ts.map +1 -0
- package/dist/manifestGenerators.js +11 -0
- package/dist/marketplaceGenerator.d.ts +3 -0
- package/dist/marketplaceGenerator.d.ts.map +1 -0
- package/dist/marketplaceGenerator.js +46 -0
- package/dist/proxiedHookTemplates.d.ts +10 -0
- package/dist/proxiedHookTemplates.d.ts.map +1 -0
- package/dist/proxiedHookTemplates.js +122 -0
- package/dist/resolve.d.ts +3 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +106 -0
- package/dist/schema.d.ts +231 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +340 -0
- package/dist/sdkConfig.d.ts +20 -0
- package/dist/sdkConfig.d.ts.map +1 -0
- package/dist/sdkConfig.js +41 -0
- package/dist/targets/adapters/base.d.ts +10 -0
- package/dist/targets/adapters/base.d.ts.map +1 -0
- package/dist/targets/adapters/base.js +16 -0
- package/dist/targets/adapters/claude-code.d.ts +9 -0
- package/dist/targets/adapters/claude-code.d.ts.map +1 -0
- package/dist/targets/adapters/claude-code.js +83 -0
- package/dist/targets/adapters/codex.d.ts +13 -0
- package/dist/targets/adapters/codex.d.ts.map +1 -0
- package/dist/targets/adapters/codex.js +103 -0
- package/dist/targets/adapters/cursor.d.ts +9 -0
- package/dist/targets/adapters/cursor.d.ts.map +1 -0
- package/dist/targets/adapters/cursor.js +57 -0
- package/dist/targets/adapters/gemini.d.ts +9 -0
- package/dist/targets/adapters/gemini.d.ts.map +1 -0
- package/dist/targets/adapters/gemini.js +86 -0
- package/dist/targets/adapters/github-copilot.d.ts +9 -0
- package/dist/targets/adapters/github-copilot.d.ts.map +1 -0
- package/dist/targets/adapters/github-copilot.js +61 -0
- package/dist/targets/adapters/hermes.d.ts +13 -0
- package/dist/targets/adapters/hermes.d.ts.map +1 -0
- package/dist/targets/adapters/hermes.js +96 -0
- package/dist/targets/adapters/hooks-utils.d.ts +12 -0
- package/dist/targets/adapters/hooks-utils.d.ts.map +1 -0
- package/dist/targets/adapters/hooks-utils.js +60 -0
- package/dist/targets/adapters/index.d.ts +27 -0
- package/dist/targets/adapters/index.d.ts.map +1 -0
- package/dist/targets/adapters/index.js +64 -0
- package/dist/targets/adapters/interface.d.ts +8 -0
- package/dist/targets/adapters/interface.d.ts.map +1 -0
- package/dist/targets/adapters/interface.js +2 -0
- package/dist/targets/adapters/oh-my-pi.d.ts +11 -0
- package/dist/targets/adapters/oh-my-pi.d.ts.map +1 -0
- package/dist/targets/adapters/oh-my-pi.js +88 -0
- package/dist/targets/adapters/openclaw.d.ts +14 -0
- package/dist/targets/adapters/openclaw.d.ts.map +1 -0
- package/dist/targets/adapters/openclaw.js +165 -0
- package/dist/targets/adapters/opencode.d.ts +10 -0
- package/dist/targets/adapters/opencode.d.ts.map +1 -0
- package/dist/targets/adapters/opencode.js +93 -0
- package/dist/targets/adapters/pi.d.ts +11 -0
- package/dist/targets/adapters/pi.d.ts.map +1 -0
- package/dist/targets/adapters/pi.js +90 -0
- package/dist/targets/index.d.ts +7 -0
- package/dist/targets/index.d.ts.map +1 -0
- package/dist/targets/index.js +77 -0
- package/dist/transform.d.ts +4 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +243 -0
- package/dist/transformEmitters.d.ts +8 -0
- package/dist/transformEmitters.d.ts.map +1 -0
- package/dist/transformEmitters.js +340 -0
- package/dist/transformHelpers.d.ts +13 -0
- package/dist/transformHelpers.d.ts.map +1 -0
- package/dist/transformHelpers.js +239 -0
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +187 -0
- package/dist/validate.d.ts +3 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +188 -0
- package/dist/verify.d.ts +6 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +294 -0
- package/package.json +68 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// Manifest generators and file copy functions for the transform stage
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { getAdapter } from './targets/adapters/index.js';
|
|
5
|
+
import { generateProgrammaticExtension } from './proxiedHookTemplates.js';
|
|
6
|
+
import { generateCliBinScript, generateInstallScript, generateUninstallScript } from './binTemplates.js';
|
|
7
|
+
import { generateInstallInstructions } from './installInstructions.js';
|
|
8
|
+
import { generateTeamInstall, resolveExtraFiles, } from './transformHelpers.js';
|
|
9
|
+
import { generateInstallShared } from './installSharedGenerator.js';
|
|
10
|
+
import { resolveSdkConfig, resolveTargetCliName, resolveTargetNpmPackageName, } from './sdkConfig.js';
|
|
11
|
+
import { getCommandPaths } from './utils.js';
|
|
12
|
+
function toOutputPath(value) {
|
|
13
|
+
return value.replace(/\\/g, '/');
|
|
14
|
+
}
|
|
15
|
+
function getRequiredSurfaceFilename(targetProfile) {
|
|
16
|
+
return targetProfile.requiredSurfaceFile ?? null;
|
|
17
|
+
}
|
|
18
|
+
function buildSurfaceFallback(manifest, targetProfile, filename) {
|
|
19
|
+
return `# ${filename}
|
|
20
|
+
|
|
21
|
+
This file was generated by @a5c-ai/extension-mux for target \`${targetProfile.name}\`.
|
|
22
|
+
|
|
23
|
+
The plugin manifest for \`${manifest.name}\` does not declare a dedicated ${filename} source file.
|
|
24
|
+
Add \`agents\` and/or \`contextFiles.${targetProfile.name}\` in \`plugin.json\` to replace this placeholder with real target guidance.
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
function readExistingTextFile(sourceDir, relativePath) {
|
|
28
|
+
const fullPath = path.join(sourceDir, relativePath);
|
|
29
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return fs.readFileSync(fullPath, 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
function resolveAgentSurfaceContent(sourceDir, manifest) {
|
|
35
|
+
if (!manifest.agents) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const agentPaths = typeof manifest.agents === 'string' ? [manifest.agents] : manifest.agents;
|
|
39
|
+
const contents = agentPaths
|
|
40
|
+
.map((agentPath) => readExistingTextFile(sourceDir, agentPath))
|
|
41
|
+
.filter((content) => content !== null);
|
|
42
|
+
if (contents.length === 0) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return contents.join('\n\n');
|
|
46
|
+
}
|
|
47
|
+
function generateCjsWrapper(moduleBasename, exportOnly = false) {
|
|
48
|
+
if (exportOnly) {
|
|
49
|
+
return `'use strict';\n\nmodule.exports = require('./${moduleBasename}.js');\n`;
|
|
50
|
+
}
|
|
51
|
+
return `#!/usr/bin/env node
|
|
52
|
+
'use strict';
|
|
53
|
+
|
|
54
|
+
require('./${moduleBasename}.js');
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
export function generateManifests(sourceDir, manifest, targetProfile, _diagnostics) {
|
|
58
|
+
const files = [];
|
|
59
|
+
// Filter manifest hooks to only those supported by this target
|
|
60
|
+
// and resolve handler paths through hookFilePattern
|
|
61
|
+
const sdkCfg = resolveSdkConfig(manifest);
|
|
62
|
+
const filteredManifest = { ...manifest };
|
|
63
|
+
if (manifest.hooks) {
|
|
64
|
+
const hookFilePattern = manifest.targets?.[targetProfile.name]?.hookFilePattern
|
|
65
|
+
?? manifest.hookFilePattern;
|
|
66
|
+
const pat = typeof hookFilePattern === 'string' ? hookFilePattern : undefined;
|
|
67
|
+
const toSlug = (s) => s
|
|
68
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
|
|
69
|
+
.replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
70
|
+
const toNativeSlug = (s) => s.replace(/[._]/g, '-').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
71
|
+
const filtered = {};
|
|
72
|
+
for (const [canonical, handler] of Object.entries(manifest.hooks)) {
|
|
73
|
+
if (handler === null || !targetProfile.supportedHooks.has(canonical))
|
|
74
|
+
continue;
|
|
75
|
+
if (typeof handler === 'string' && handler !== 'proxy' && pat) {
|
|
76
|
+
const native = targetProfile.supportedHooks.get(canonical) || canonical;
|
|
77
|
+
const resolved = 'hooks/' + pat
|
|
78
|
+
.replace(/\{\{name\}\}/g, manifest.name)
|
|
79
|
+
.replace(/\{\{slug\}\}/g, toSlug(canonical))
|
|
80
|
+
.replace(/\{\{native\}\}/g, toNativeSlug(native));
|
|
81
|
+
filtered[canonical] = resolved;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
filtered[canonical] = handler;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
filteredManifest.hooks = filtered;
|
|
88
|
+
}
|
|
89
|
+
// Add target name to end of keywords if not already present
|
|
90
|
+
if (filteredManifest.keywords) {
|
|
91
|
+
const kw = [...filteredManifest.keywords];
|
|
92
|
+
if (!kw.includes(targetProfile.name)) {
|
|
93
|
+
kw.push(targetProfile.name);
|
|
94
|
+
}
|
|
95
|
+
filteredManifest.keywords = kw;
|
|
96
|
+
}
|
|
97
|
+
const adapter = getAdapter(targetProfile.name);
|
|
98
|
+
if (adapter) {
|
|
99
|
+
const adapterFiles = adapter.generateManifestFiles(sourceDir, filteredManifest, targetProfile, _diagnostics, manifest);
|
|
100
|
+
files.push(...adapterFiles);
|
|
101
|
+
}
|
|
102
|
+
// Generate package.json for npm-distributed targets that don't already have one
|
|
103
|
+
if ((targetProfile.distribution === 'npm-cli' || (targetProfile.distribution === 'both' && targetProfile.npmPublishable)) &&
|
|
104
|
+
!files.some(f => f.path === 'package.json')) {
|
|
105
|
+
const packageMetadata = targetProfile.packageMetadata ?? {};
|
|
106
|
+
const npmPkg = resolveTargetNpmPackageName(manifest, targetProfile);
|
|
107
|
+
const cliName = resolveTargetCliName(manifest, targetProfile);
|
|
108
|
+
const isEsm = packageMetadata.moduleType === 'module';
|
|
109
|
+
const ext = packageMetadata.binScriptExt ?? (isEsm ? '.cjs' : '.js');
|
|
110
|
+
const scripts = {
|
|
111
|
+
deploy: 'npm publish --access public',
|
|
112
|
+
'deploy:staging': 'npm publish --access public --tag staging',
|
|
113
|
+
};
|
|
114
|
+
if (packageMetadata.installLifecycle === 'plugin-scripts') {
|
|
115
|
+
scripts['plugin:install'] = `node bin/install${ext} --global`;
|
|
116
|
+
scripts['plugin:uninstall'] = `node bin/uninstall${ext} --global`;
|
|
117
|
+
}
|
|
118
|
+
else if (packageMetadata.installLifecycle === 'postinstall') {
|
|
119
|
+
scripts.postinstall = `node bin/install${ext}`;
|
|
120
|
+
scripts.preuninstall = `node bin/uninstall${ext}`;
|
|
121
|
+
}
|
|
122
|
+
scripts['team:install'] = `node scripts/team-install${ext}`;
|
|
123
|
+
for (const [scriptName, scriptValue] of Object.entries(packageMetadata.extraScripts ?? {})) {
|
|
124
|
+
scripts[scriptName] = scriptValue;
|
|
125
|
+
}
|
|
126
|
+
const packageFiles = ['bin/', ...(packageMetadata.extraPackageFiles ?? [])];
|
|
127
|
+
packageFiles.push('hooks/', 'skills/', 'commands/', 'scripts/', 'plugin.json');
|
|
128
|
+
packageFiles.push('README.md', 'versions.json', 'package.json');
|
|
129
|
+
const pkgJson = {
|
|
130
|
+
name: npmPkg,
|
|
131
|
+
version: manifest.version,
|
|
132
|
+
description: manifest.description,
|
|
133
|
+
scripts,
|
|
134
|
+
bin: { [cliName]: `bin/cli${ext}` },
|
|
135
|
+
files: packageFiles,
|
|
136
|
+
keywords: [manifest.name, targetProfile.name, 'orchestration'],
|
|
137
|
+
author: manifest.author,
|
|
138
|
+
license: manifest.license,
|
|
139
|
+
publishConfig: { access: 'public' },
|
|
140
|
+
dependencies: { [sdkCfg.package]: manifest.version },
|
|
141
|
+
};
|
|
142
|
+
if (isEsm)
|
|
143
|
+
pkgJson.type = 'module';
|
|
144
|
+
if (manifest.repository) {
|
|
145
|
+
let repoUrl = typeof manifest.repository === 'string' ? manifest.repository : manifest.repository.url;
|
|
146
|
+
if (!repoUrl.startsWith('git+'))
|
|
147
|
+
repoUrl = `git+${repoUrl}`;
|
|
148
|
+
if (!repoUrl.endsWith('.git'))
|
|
149
|
+
repoUrl = `${repoUrl}.git`;
|
|
150
|
+
const directory = `plugins/${npmPkg.split('/').pop()}`;
|
|
151
|
+
const baseUrl = repoUrl.replace(/\.git$/, '').replace(/^git\+/, '');
|
|
152
|
+
pkgJson.repository = { type: 'git', url: repoUrl, directory };
|
|
153
|
+
pkgJson.homepage = `${baseUrl}/tree/main/${directory}#readme`;
|
|
154
|
+
pkgJson.bugs = { url: `${baseUrl}/issues` };
|
|
155
|
+
}
|
|
156
|
+
if (targetProfile.adapterFamily === 'programmatic') {
|
|
157
|
+
const peerPkg = packageMetadata.peerDependencyPackage;
|
|
158
|
+
if (peerPkg)
|
|
159
|
+
pkgJson.peerDependencies = { [peerPkg]: '*' };
|
|
160
|
+
}
|
|
161
|
+
files.push({ path: 'package.json', content: JSON.stringify(pkgJson, null, 2) + '\n' });
|
|
162
|
+
}
|
|
163
|
+
return files;
|
|
164
|
+
}
|
|
165
|
+
export function copyAgentFiles(sourceDir, manifest, targetProfile, _diagnostics) {
|
|
166
|
+
if (targetProfile.componentSupport?.agents !== 'native') {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
if (getRequiredSurfaceFilename(targetProfile) === 'AGENTS.md') {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const content = resolveAgentSurfaceContent(sourceDir, manifest);
|
|
173
|
+
if (!content) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
return [{
|
|
177
|
+
path: 'AGENTS.md',
|
|
178
|
+
content,
|
|
179
|
+
}];
|
|
180
|
+
}
|
|
181
|
+
export function copyContextFiles(sourceDir, manifest, targetProfile, _diagnostics) {
|
|
182
|
+
const requiredFilename = getRequiredSurfaceFilename(targetProfile);
|
|
183
|
+
if (!requiredFilename) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
const contextPath = manifest.contextFiles?.[targetProfile.name]
|
|
187
|
+
?? (targetProfile.name === 'gemini-cli' ? manifest.contextFiles?.gemini : undefined);
|
|
188
|
+
const declaredContextContent = contextPath ? readExistingTextFile(sourceDir, contextPath) : null;
|
|
189
|
+
const agentContent = resolveAgentSurfaceContent(sourceDir, manifest);
|
|
190
|
+
const content = declaredContextContent
|
|
191
|
+
?? agentContent
|
|
192
|
+
?? buildSurfaceFallback(manifest, targetProfile, requiredFilename);
|
|
193
|
+
return [{
|
|
194
|
+
path: toOutputPath(requiredFilename),
|
|
195
|
+
content,
|
|
196
|
+
}];
|
|
197
|
+
}
|
|
198
|
+
export function copyIncludedFiles(sourceDir, manifest, excludePatterns = []) {
|
|
199
|
+
const files = [];
|
|
200
|
+
if (!manifest.include || manifest.include.length === 0) {
|
|
201
|
+
return files;
|
|
202
|
+
}
|
|
203
|
+
for (const pattern of manifest.include) {
|
|
204
|
+
if (excludePatterns.some(ex => pattern === ex || pattern.startsWith(ex)))
|
|
205
|
+
continue;
|
|
206
|
+
if (!pattern.includes('*')) {
|
|
207
|
+
const fullPath = path.join(sourceDir, pattern);
|
|
208
|
+
if (fs.existsSync(fullPath)) {
|
|
209
|
+
const stat = fs.statSync(fullPath);
|
|
210
|
+
if (stat.isFile()) {
|
|
211
|
+
files.push({
|
|
212
|
+
path: toOutputPath(pattern),
|
|
213
|
+
content: fs.readFileSync(fullPath, 'utf-8'),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else if (stat.isDirectory()) {
|
|
217
|
+
collectDir(sourceDir, pattern, files);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Simple *.ext glob in a directory
|
|
223
|
+
const dir = path.dirname(pattern);
|
|
224
|
+
const ext = path.extname(pattern);
|
|
225
|
+
const fullDir = path.join(sourceDir, dir);
|
|
226
|
+
if (fs.existsSync(fullDir) && fs.statSync(fullDir).isDirectory()) {
|
|
227
|
+
for (const entry of fs.readdirSync(fullDir)) {
|
|
228
|
+
if (ext && !entry.endsWith(ext))
|
|
229
|
+
continue;
|
|
230
|
+
const entryPath = toOutputPath(path.join(dir, entry));
|
|
231
|
+
const fullEntry = path.join(sourceDir, entryPath);
|
|
232
|
+
if (fs.statSync(fullEntry).isFile()) {
|
|
233
|
+
files.push({
|
|
234
|
+
path: entryPath,
|
|
235
|
+
content: fs.readFileSync(fullEntry, 'utf-8'),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return files;
|
|
242
|
+
}
|
|
243
|
+
export function collectDir(sourceDir, relDir, files) {
|
|
244
|
+
const fullDir = path.join(sourceDir, relDir);
|
|
245
|
+
for (const entry of fs.readdirSync(fullDir)) {
|
|
246
|
+
const relPath = toOutputPath(path.join(relDir, entry));
|
|
247
|
+
const fullPath = path.join(sourceDir, relPath);
|
|
248
|
+
const stat = fs.statSync(fullPath);
|
|
249
|
+
if (stat.isFile()) {
|
|
250
|
+
files.push({
|
|
251
|
+
path: relPath,
|
|
252
|
+
content: fs.readFileSync(fullPath, 'utf-8'),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else if (stat.isDirectory()) {
|
|
256
|
+
collectDir(sourceDir, relPath, files);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export function generateExtraFiles(sourceDir, manifest, targetProfile, _diagnostics) {
|
|
261
|
+
const files = [];
|
|
262
|
+
const sdkCfg = resolveSdkConfig(manifest);
|
|
263
|
+
// Generate programmatic extensions (Pi, oh-my-pi, and other programmatic targets)
|
|
264
|
+
if (targetProfile.adapterFamily === 'programmatic') {
|
|
265
|
+
const cmdPaths = getCommandPaths(sourceDir, manifest);
|
|
266
|
+
const extensionsContent = generateProgrammaticExtension(manifest, targetProfile, cmdPaths);
|
|
267
|
+
files.push({
|
|
268
|
+
path: 'extensions/index.ts',
|
|
269
|
+
content: extensionsContent,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Generate CLI bin scripts for npm-cli distribution targets
|
|
273
|
+
if (targetProfile.distribution === 'npm-cli' || (targetProfile.distribution === 'both' && targetProfile.npmPublishable)) {
|
|
274
|
+
const packageMetadata = targetProfile.packageMetadata ?? {};
|
|
275
|
+
const isEsm = packageMetadata.moduleType === 'module';
|
|
276
|
+
const ext = packageMetadata.binScriptExt ?? (isEsm ? '.cjs' : '.js');
|
|
277
|
+
files.push({ path: `bin/cli${ext}`, content: generateCliBinScript(manifest, targetProfile), executable: true });
|
|
278
|
+
files.push({ path: `bin/install${ext}`, content: generateInstallScript(manifest, targetProfile), executable: true });
|
|
279
|
+
files.push({ path: `bin/uninstall${ext}`, content: generateUninstallScript(manifest, targetProfile), executable: true });
|
|
280
|
+
files.push({ path: `bin/install-shared${ext}`, content: generateInstallShared(manifest, targetProfile, sourceDir) });
|
|
281
|
+
files.push({ path: `scripts/team-install${ext}`, content: generateTeamInstall(manifest, targetProfile, ext), executable: true });
|
|
282
|
+
if (packageMetadata.emitCjsWrappers && ext !== '.cjs') {
|
|
283
|
+
files.push({ path: 'bin/cli.cjs', content: generateCjsWrapper('cli'), executable: true });
|
|
284
|
+
files.push({ path: 'bin/install.cjs', content: generateCjsWrapper('install'), executable: true });
|
|
285
|
+
files.push({ path: 'bin/uninstall.cjs', content: generateCjsWrapper('uninstall'), executable: true });
|
|
286
|
+
files.push({ path: 'bin/install-shared.cjs', content: generateCjsWrapper('install-shared', true) });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Generate installation instructions for all targets
|
|
290
|
+
files.push({
|
|
291
|
+
path: 'README.md',
|
|
292
|
+
content: generateInstallInstructions(manifest, targetProfile, sourceDir),
|
|
293
|
+
});
|
|
294
|
+
// .gitignore
|
|
295
|
+
files.push({
|
|
296
|
+
path: '.gitignore',
|
|
297
|
+
content: `node_modules/\ndist/\n${sdkCfg.stateDir}/runs/\n${sdkCfg.stateDir}/logs/\n${sdkCfg.stateDir}/processes/\n${sdkCfg.stateDir}/artifacts/\n${sdkCfg.stateDir}/session.json\n${sdkCfg.stateDir}/current-run.json\n${sdkCfg.stateDir}/observer.json\n${sdkCfg.stateDir}/index/\n${sdkCfg.stateDir}/team/\n${sdkCfg.stateDir}/config/rules.local.json\n*.sqlite\n*.sqlite-shm\n*.sqlite-wal\n*.log\n.DS_Store\n`,
|
|
298
|
+
});
|
|
299
|
+
// Emit target-override extraFiles
|
|
300
|
+
const extraFiles = resolveExtraFiles(manifest, targetProfile);
|
|
301
|
+
if (Object.keys(extraFiles).length > 0) {
|
|
302
|
+
for (const [outputPath, value] of Object.entries(extraFiles)) {
|
|
303
|
+
if (value.startsWith('file:')) {
|
|
304
|
+
const srcPath = value.slice(5);
|
|
305
|
+
const fullPath = path.join(sourceDir, srcPath);
|
|
306
|
+
if (fs.existsSync(fullPath)) {
|
|
307
|
+
const isBinary = /\.(png|jpg|jpeg|gif|ico|woff2?|ttf|eot|svg|pdf)$/i.test(outputPath);
|
|
308
|
+
if (isBinary) {
|
|
309
|
+
files.push({
|
|
310
|
+
path: outputPath,
|
|
311
|
+
content: '',
|
|
312
|
+
binaryContent: fs.readFileSync(fullPath),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
files.push({
|
|
317
|
+
path: outputPath,
|
|
318
|
+
content: fs.readFileSync(fullPath, 'utf-8'),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
files.push({ path: outputPath, content: value });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (manifest.postInstall) {
|
|
329
|
+
const postInstallPath = path.join(sourceDir, manifest.postInstall);
|
|
330
|
+
if (fs.existsSync(postInstallPath)) {
|
|
331
|
+
files.push({ path: 'scripts/post-install.js', content: fs.readFileSync(postInstallPath, 'utf-8'), executable: true });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const adapter = getAdapter(targetProfile.name);
|
|
335
|
+
if (adapter) {
|
|
336
|
+
const adapterExtraFiles = adapter.generateExtraTargetFiles(sourceDir, manifest, targetProfile, _diagnostics);
|
|
337
|
+
files.push(...adapterExtraFiles);
|
|
338
|
+
}
|
|
339
|
+
return files;
|
|
340
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { A5cPluginManifest, TargetProfile } from './types.js';
|
|
2
|
+
export declare function resolveExtraFiles(manifest: A5cPluginManifest, targetProfile: TargetProfile): Record<string, string>;
|
|
3
|
+
export declare function resolveHarnessInstallSurfaceExports(manifest: A5cPluginManifest, targetProfile: TargetProfile): string[];
|
|
4
|
+
export declare function generatePs1Wrapper(hookSlug: string, adapterName: string, _sourceScript: string): string;
|
|
5
|
+
export declare function generateJsBridge(_name: string, shellScript: string, targetProfile: TargetProfile): string;
|
|
6
|
+
export declare function generateHarnessManifest(manifest: A5cPluginManifest, targetProfile: TargetProfile): string;
|
|
7
|
+
export declare function generateTeamInstall(manifest: A5cPluginManifest, _targetProfile: TargetProfile, ext?: string): string;
|
|
8
|
+
export declare function generateOpenClawNativeHooksSection(manifest: A5cPluginManifest, targetProfile: TargetProfile): Record<string, string>;
|
|
9
|
+
export declare function generateOpenCodeAccomplishSkill(manifest: A5cPluginManifest): string | null;
|
|
10
|
+
export declare function generateTsHookStub(nativeHook: string, nativeSlug: string, shellScriptName: string, targetProfile: TargetProfile): string;
|
|
11
|
+
export declare function generateGeminiPostinstall(pluginName: string): string;
|
|
12
|
+
export declare function generateGeminiPreuninstall(pluginName: string): string;
|
|
13
|
+
//# sourceMappingURL=transformHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformHelpers.d.ts","sourceRoot":"","sources":["../src/transformHelpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwBnE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxB;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,MAAM,EAAE,CAmBV;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,MAAM,CAcR;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,MAAM,CA4BR;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAkBR;AAGD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,cAAc,EAAE,aAAa,EAC7B,GAAG,SAAQ,GACV,MAAM,CA0BR;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,iBAAiB,GAC1B,MAAM,GAAG,IAAI,CAkBf;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,aAAa,GAC3B,MAAM,CAyBR;AAED,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBpE;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBrE"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Helper functions for the transform stage
|
|
2
|
+
// Extracted to keep transform.ts under the max-lines limit
|
|
3
|
+
function buildTemplateVars(manifest, targetProfile) {
|
|
4
|
+
const override = manifest.targets?.[targetProfile.name];
|
|
5
|
+
const overrideVars = override?.templateVars ?? {};
|
|
6
|
+
const targetDir = typeof overrideVars.targetDir === 'string' ? overrideVars.targetDir : targetProfile.name;
|
|
7
|
+
return {
|
|
8
|
+
target: targetProfile.name,
|
|
9
|
+
targetName: targetProfile.name,
|
|
10
|
+
targetDir,
|
|
11
|
+
...overrideVars,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function interpolateTemplate(value, vars) {
|
|
15
|
+
return value.replace(/\{\{([a-zA-Z0-9_-]+)\}\}/g, (match, key) => vars[key] ?? match);
|
|
16
|
+
}
|
|
17
|
+
export function resolveExtraFiles(manifest, targetProfile) {
|
|
18
|
+
const override = manifest.targets?.[targetProfile.name];
|
|
19
|
+
const vars = buildTemplateVars(manifest, targetProfile);
|
|
20
|
+
const resolved = {};
|
|
21
|
+
for (const setName of override?.extraFileSets ?? []) {
|
|
22
|
+
const set = manifest.extraFileSets?.[setName];
|
|
23
|
+
if (!set)
|
|
24
|
+
continue;
|
|
25
|
+
for (const [outputPath, value] of Object.entries(set)) {
|
|
26
|
+
resolved[interpolateTemplate(outputPath, vars)] = interpolateTemplate(value, vars);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const [outputPath, value] of Object.entries(override?.extraFiles ?? {})) {
|
|
30
|
+
resolved[interpolateTemplate(outputPath, vars)] = interpolateTemplate(value, vars);
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
export function resolveHarnessInstallSurfaceExports(manifest, targetProfile) {
|
|
35
|
+
const override = manifest.targets?.[targetProfile.name];
|
|
36
|
+
const resolved = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
const addEntries = (entries) => {
|
|
39
|
+
if (!Array.isArray(entries))
|
|
40
|
+
return;
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (typeof entry !== 'string' || seen.has(entry))
|
|
43
|
+
continue;
|
|
44
|
+
seen.add(entry);
|
|
45
|
+
resolved.push(entry);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
for (const setName of override?.harnessInstallSurfaceExportSets ?? []) {
|
|
49
|
+
addEntries(manifest.harnessInstallSurfaceExportSets?.[setName]);
|
|
50
|
+
}
|
|
51
|
+
addEntries(override?.harnessInstallSurfaceExports);
|
|
52
|
+
return resolved;
|
|
53
|
+
}
|
|
54
|
+
export function generatePs1Wrapper(hookSlug, adapterName, _sourceScript) {
|
|
55
|
+
return `# PowerShell hook wrapper — sets env vars and delegates to bash
|
|
56
|
+
$env:HOOK_TYPE = '${hookSlug}'
|
|
57
|
+
$env:ADAPTER_NAME = '${adapterName}'
|
|
58
|
+
$env:PLUGIN_ROOT = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
59
|
+
|
|
60
|
+
$input_data = [Console]::In.ReadToEnd()
|
|
61
|
+
$result = $input_data | & bash "$PSScriptRoot/../$($MyInvocation.MyCommand.Name -replace '\\.ps1$','.sh')" 2>$null
|
|
62
|
+
if ($LASTEXITCODE -eq 0 -and $result) {
|
|
63
|
+
Write-Output $result
|
|
64
|
+
} else {
|
|
65
|
+
Write-Output '{}'
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
export function generateJsBridge(_name, shellScript, targetProfile) {
|
|
70
|
+
const pluginRootEnvVar = targetProfile.pluginRootEnvVarForExtension || 'PLUGIN_ROOT';
|
|
71
|
+
const defaultAdapterName = targetProfile.adapterName;
|
|
72
|
+
return `#!/usr/bin/env node
|
|
73
|
+
"use strict";
|
|
74
|
+
var execSync = require("child_process").execSync;
|
|
75
|
+
var path = require("path");
|
|
76
|
+
var readFileSync = require("fs").readFileSync;
|
|
77
|
+
|
|
78
|
+
var PLUGIN_ROOT = process.env.${pluginRootEnvVar} || process.env.PLUGIN_ROOT || path.resolve(__dirname, "..");
|
|
79
|
+
var stdin = "";
|
|
80
|
+
try { stdin = readFileSync(0, "utf8"); } catch {}
|
|
81
|
+
try {
|
|
82
|
+
var result = execSync("bash " + JSON.stringify(path.join(PLUGIN_ROOT, "${shellScript}")), {
|
|
83
|
+
input: stdin,
|
|
84
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
85
|
+
timeout: 30000,
|
|
86
|
+
env: Object.assign({}, process.env, {
|
|
87
|
+
HOOK_TYPE: process.env.HOOK_TYPE || "",
|
|
88
|
+
ADAPTER_NAME: process.env.ADAPTER_NAME || "${defaultAdapterName}",
|
|
89
|
+
PLUGIN_ROOT: PLUGIN_ROOT
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
process.stdout.write(result);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
process.stdout.write("{}\\n");
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
export function generateHarnessManifest(manifest, targetProfile) {
|
|
99
|
+
const override = manifest.targets?.[targetProfile.name];
|
|
100
|
+
const base = {
|
|
101
|
+
name: manifest.name,
|
|
102
|
+
version: manifest.version,
|
|
103
|
+
description: manifest.description,
|
|
104
|
+
author: manifest.author,
|
|
105
|
+
license: manifest.license,
|
|
106
|
+
};
|
|
107
|
+
if (manifest.repository)
|
|
108
|
+
base.repository = manifest.repository;
|
|
109
|
+
if (manifest.keywords)
|
|
110
|
+
base.keywords = manifest.keywords;
|
|
111
|
+
if (override?.harnessManifest) {
|
|
112
|
+
Object.assign(base, override.harnessManifest);
|
|
113
|
+
}
|
|
114
|
+
return JSON.stringify(base, null, 2) + '\n';
|
|
115
|
+
}
|
|
116
|
+
export function generateTeamInstall(manifest, _targetProfile, ext = '.js') {
|
|
117
|
+
const sharedRequire = ext === '.js' ? '../bin/install-shared' : `../bin/install-shared${ext}`;
|
|
118
|
+
return `#!/usr/bin/env node
|
|
119
|
+
'use strict';
|
|
120
|
+
|
|
121
|
+
var path = require('path');
|
|
122
|
+
var shared = require('${sharedRequire}');
|
|
123
|
+
|
|
124
|
+
var workspace = process.cwd();
|
|
125
|
+
for (var i = 0; i < process.argv.length; i++) {
|
|
126
|
+
if (process.argv[i] === '--workspace' && process.argv[i + 1]) {
|
|
127
|
+
workspace = path.resolve(process.argv[i + 1]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var src = process.env.PLUGIN_PACKAGE_ROOT || path.resolve(__dirname, '..');
|
|
132
|
+
var dest = shared.getHomePluginRoot('workspace');
|
|
133
|
+
console.log('[${manifest.name}] Team install to ' + dest);
|
|
134
|
+
|
|
135
|
+
shared.copyPluginBundle(src, dest);
|
|
136
|
+
if (typeof shared.harnessTeamInstall === 'function') {
|
|
137
|
+
shared.harnessTeamInstall(src, dest, workspace);
|
|
138
|
+
}
|
|
139
|
+
shared.runPostInstall(dest);
|
|
140
|
+
console.log('[${manifest.name}] Team install complete.');
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
export function generateOpenClawNativeHooksSection(manifest, targetProfile) {
|
|
144
|
+
const hooks = {};
|
|
145
|
+
if (manifest.hooks) {
|
|
146
|
+
for (const [canonicalHook, handlerPath] of Object.entries(manifest.hooks)) {
|
|
147
|
+
if (handlerPath === null)
|
|
148
|
+
continue;
|
|
149
|
+
const nativeHook = targetProfile.supportedHooks.get(canonicalHook);
|
|
150
|
+
if (nativeHook) {
|
|
151
|
+
hooks[nativeHook] = `extensions/hooks/${nativeHook.replace(/[._]/g, '-').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}.ts`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return hooks;
|
|
156
|
+
}
|
|
157
|
+
export function generateOpenCodeAccomplishSkill(manifest) {
|
|
158
|
+
if (!manifest.skills || manifest.skills.length === 0)
|
|
159
|
+
return null;
|
|
160
|
+
const primarySkill = manifest.skills[0];
|
|
161
|
+
return `---
|
|
162
|
+
name: ${manifest.name}
|
|
163
|
+
description: ${manifest.description}
|
|
164
|
+
command: /${primarySkill.name}
|
|
165
|
+
verified: true
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# ${manifest.name}
|
|
169
|
+
|
|
170
|
+
${manifest.description}
|
|
171
|
+
|
|
172
|
+
(This is a specialized accomplish-mode variant for OpenCode's accomplish workflow.)
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
export function generateTsHookStub(nativeHook, nativeSlug, shellScriptName, targetProfile) {
|
|
176
|
+
const handlerName = nativeHook
|
|
177
|
+
.replace(/[._]([a-z])/g, (_, c) => c.toUpperCase())
|
|
178
|
+
+ 'Handler';
|
|
179
|
+
return `/**
|
|
180
|
+
* ${targetProfile.displayName} ${nativeHook} hook — delegates to shell script.
|
|
181
|
+
*/
|
|
182
|
+
import { execFileSync } from "node:child_process";
|
|
183
|
+
import { resolve, dirname } from "node:path";
|
|
184
|
+
import { fileURLToPath } from "node:url";
|
|
185
|
+
|
|
186
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
187
|
+
const PLUGIN_ROOT = resolve(__dirname, "../..");
|
|
188
|
+
|
|
189
|
+
export async function ${handlerName}(context: Record<string, unknown>): Promise<void> {
|
|
190
|
+
try {
|
|
191
|
+
execFileSync("bash", [resolve(PLUGIN_ROOT, "hooks/${shellScriptName}")], {
|
|
192
|
+
input: JSON.stringify(context),
|
|
193
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
194
|
+
timeout: 30000,
|
|
195
|
+
env: { ...process.env, ADAPTER_NAME: "${targetProfile.adapterName}", PLUGIN_ROOT },
|
|
196
|
+
});
|
|
197
|
+
} catch { /* best-effort */ }
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
export function generateGeminiPostinstall(pluginName) {
|
|
202
|
+
return `#!/usr/bin/env node
|
|
203
|
+
'use strict';
|
|
204
|
+
var path = require('path');
|
|
205
|
+
var spawnSync = require('child_process').spawnSync;
|
|
206
|
+
var fs = require('fs');
|
|
207
|
+
|
|
208
|
+
var PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
209
|
+
var extDir = path.join(require('os').homedir(), '.gemini', 'extensions', '${pluginName}');
|
|
210
|
+
|
|
211
|
+
if (!process.env.npm_config_global) process.exit(0);
|
|
212
|
+
try { if (fs.lstatSync(extDir).isSymbolicLink()) process.exit(0); } catch {}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
var result = spawnSync('gemini', ['extensions', 'install', PACKAGE_ROOT], { stdio: 'inherit', timeout: 60000 });
|
|
216
|
+
if (result.status === 0) process.exit(0);
|
|
217
|
+
} catch {}
|
|
218
|
+
|
|
219
|
+
console.log('[${pluginName}-gemini] Gemini CLI not found. Run: ${pluginName}-gemini install');
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
export function generateGeminiPreuninstall(pluginName) {
|
|
223
|
+
return `#!/usr/bin/env node
|
|
224
|
+
'use strict';
|
|
225
|
+
var path = require('path');
|
|
226
|
+
var spawnSync = require('child_process').spawnSync;
|
|
227
|
+
var fs = require('fs');
|
|
228
|
+
|
|
229
|
+
var extDir = path.join(require('os').homedir(), '.gemini', 'extensions', '${pluginName}');
|
|
230
|
+
|
|
231
|
+
try { if (!fs.existsSync(extDir) || fs.lstatSync(extDir).isSymbolicLink()) process.exit(0); } catch {}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
spawnSync('gemini', ['extensions', 'uninstall', '${pluginName}'], { stdio: 'inherit', timeout: 30000 });
|
|
235
|
+
} catch {
|
|
236
|
+
try { fs.rmSync(extDir, { recursive: true, force: true }); } catch {}
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
}
|